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.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;
67 public class CutDataToCorrectDepthTest extends JerseyTest {
69 private static final Logger LOG = LoggerFactory.getLogger(JerseyTest.class);
71 private static NormalizedNode<?, ?> depth1Cont;
72 private static NormalizedNode<?, ?> depth2Cont1;
73 private NormalizedNode<?, ?> globalPayload;
74 private static SchemaContext schemaContextModules;
77 public class RestImpl {
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) {
85 final InstanceIdentifierContext<?> iiWithData = ControllerContext.getInstance().toInstanceIdentifier(
88 NormalizedNode<?, ?> data = null;
89 if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
91 } else if (identifier.equals("nested-module:depth1-cont")) {
95 final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
96 return new NormalizedNodeContext(iiWithData, data, writerParameters);
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);
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();
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);
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);
134 final UnkeyedListNode listAsUnkeyedList = unkeyedList(
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")));
141 final MapNode listAsMap = mapNode(
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")));
146 depth1Cont = container(
150 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
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"));
158 depth2Cont1 = listAsUnkeyedList;
161 // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
162 // properly in readers
164 public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
165 getDataWithUriDepthParameter("application/json");
166 getDataWithUriDepthParameter("application/xml");
169 public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
170 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
173 // Test config with depth 1
174 response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
176 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
177 verifyResponse(nodeDataDepth1());
179 // Test config with depth 2
180 response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
182 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
183 verifyResponse(nodeDataDepth2());
185 // Test config with depth 3
186 response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
188 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
189 verifyResponse(nodeDataDepth3());
191 // Test config with depth 4
192 response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
194 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
195 verifyResponse(nodeDataDepth4());
197 // Test config with depth 5
198 response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
200 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
201 verifyResponse(nodeDataDepth5());
203 // Test config with depth unbounded
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());
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));
217 private void verifyResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
218 assertNotNull(this.globalPayload);
219 assertEquals(this.globalPayload, nodeData);
220 this.globalPayload = null;
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;
233 private static LeafNode<?> leaf(final String localName, final Object value) {
234 return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
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);
243 containerBuilder.withNodeIdentifier(toIdentifier(localName));
244 return containerBuilder.build();
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);
256 return builder.build();
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);
267 return builder.build();
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);
276 return builder.build();
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());
287 builder.withNodeIdentifier(toIdentifier(localName, keys));
289 for (final DataContainerChild<?, ?> child : children) {
290 builder.withChild(child);
292 return builder.build();
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());
302 return builder.build();
305 private static NodeIdentifier toIdentifier(final String localName) {
306 return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-3", localName));
309 private static NodeIdentifierWithPredicates toIdentifier(final String localName, final Map<QName, Object> keys) {
310 return new NodeIdentifierWithPredicates(QName.create("urn:nested:module", "2014-06-3", localName),
314 private static NodeWithValue<?> toIdentifier(final String localName, final Object value) {
315 return new NodeWithValue<>(QName.create("urn:nested:module", "2014-06-3", localName), value);
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"));
324 private static ContainerNode nodeDataDepth5() {
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"),
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"));
346 private static ContainerNode nodeDataDepth4() {
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"),
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"));
360 private static ContainerNode nodeDataDepth3() {
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"));
373 private static ContainerNode nodeDataDepth2() {
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"));
384 private static ContainerNode nodeDataDepth1() {
385 return container("depth1-cont");