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.NormalizedNodeJsonBodyWriter;
36 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
37 import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
38 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
39 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
40 import org.opendaylight.netconf.sal.restconf.impl.QueryParametersParser;
41 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
42 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
43 import org.opendaylight.restconf.common.context.WriterParameters;
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.LeafSetEntryNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
58 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
59 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
60 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
61 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
62 import org.opendaylight.yangtools.yang.model.api.Module;
63 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
64 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
68 public class CutDataToCorrectDepthTest extends JerseyTest {
70 private static final Logger LOG = LoggerFactory.getLogger(JerseyTest.class);
72 private static NormalizedNode<?, ?> depth1Cont;
73 private static NormalizedNode<?, ?> depth2Cont1;
74 private NormalizedNode<?, ?> globalPayload;
75 private static SchemaContext schemaContextModules;
77 private final ControllerContext controllerContext =
78 TestRestconfUtils.newControllerContext(schemaContextModules, null);
81 public class RestImpl {
84 @Path("/config/{identifier:.+}")
85 @Produces({ "application/json", "application/xml" })
86 public NormalizedNodeContext getData(@Encoded @PathParam("identifier") final String identifier,
87 @Context final UriInfo uriInfo) {
89 final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
91 NormalizedNode<?, ?> data = null;
92 if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
94 } else if (identifier.equals("nested-module:depth1-cont")) {
98 final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
99 return new NormalizedNodeContext(iiWithData, data, writerParameters);
103 @Path("/operational/{identifier:.+}")
104 @Produces({ "application/json", "application/xml" })
105 public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") final String identifier,
106 @Context final UriInfo uriInfo) {
107 return getData(identifier, uriInfo);
111 @Path("/config/{identifier:.+}")
112 @Consumes({ "application/json", "application/xml" })
113 public void normalizedData(@Encoded @PathParam("identifier") final String identifier,
114 final NormalizedNodeContext payload) throws InterruptedException {
115 LOG.info("Payload: {}.", payload);
116 LOG.info("Instance identifier of payload: {}.",
117 payload.getInstanceIdentifierContext().getInstanceIdentifier());
118 LOG.info("Data of payload: {}.", payload.getData());
119 CutDataToCorrectDepthTest.this.globalPayload = payload.getData();
123 @Path("/operational/{identifier:.+}")
124 @Consumes({ "application/json", "application/xml" })
125 public void normalizedDataOperational(@Encoded @PathParam("identifier") final String identifier,
126 final NormalizedNodeContext payload) throws InterruptedException {
127 normalizedData(identifier, payload);
132 public static void initialize() throws FileNotFoundException, ReactorException {
133 schemaContextModules = TestUtils.loadSchemaContext("/modules");
134 final Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
135 assertNotNull(module);
137 final UnkeyedListNode listAsUnkeyedList = unkeyedList(
139 unkeyedEntry("depth2-cont1",
140 container("depth3-cont1",
141 container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
142 leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
144 final MapNode listAsMap = mapNode(
146 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
147 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
149 depth1Cont = container(
153 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
156 container("depth3-cont2",
157 container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
158 leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
159 leaf("depth2-leaf1", "depth2-leaf1-value"));
161 depth2Cont1 = listAsUnkeyedList;
164 // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
165 // properly in readers
167 public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
168 getDataWithUriDepthParameter("application/json");
169 getDataWithUriDepthParameter("application/xml");
172 public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
175 // Test config with depth 1
176 response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
178 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
179 verifyResponse(nodeDataDepth1());
181 // Test config with depth 2
182 response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
184 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
185 verifyResponse(nodeDataDepth2());
187 // Test config with depth 3
188 response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
190 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
191 verifyResponse(nodeDataDepth3());
193 // Test config with depth 4
194 response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
196 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
197 verifyResponse(nodeDataDepth4());
199 // Test config with depth 5
200 response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
202 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
203 verifyResponse(nodeDataDepth5());
205 // Test config with depth unbounded
207 response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
208 .request(mediaType).get();
209 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
210 verifyResponse(nodeDataDepth5());
213 private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
214 final String responseStr = response.readEntity(String.class);
215 LOG.info("Response entity message: {}.", responseStr);
216 target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType));
219 private void verifyResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
220 assertNotNull(this.globalPayload);
221 assertEquals(this.globalPayload, nodeData);
222 this.globalPayload = null;
226 protected Application configure() {
227 ResourceConfig resourceConfig = new ResourceConfig();
228 resourceConfig = resourceConfig.registerInstances(new RestImpl());
229 resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
230 JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
231 RestconfDocumentedExceptionMapper.class);
232 return resourceConfig;
235 private static LeafNode<?> leaf(final String localName, final Object value) {
236 return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
239 private static ContainerNode container(final String localName, final DataContainerChild<?, ?>... children) {
240 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder =
241 Builders.containerBuilder();
242 for (final DataContainerChild<?, ?> child : children) {
243 containerBuilder.withChild(child);
245 containerBuilder.withNodeIdentifier(toIdentifier(localName));
246 return containerBuilder.build();
249 private static UnkeyedListNode unkeyedList(
250 final String localName,
251 final UnkeyedListEntryNode... entryNodes) {
252 final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
253 final NodeIdentifier identifier = toIdentifier(localName);
254 builder.withNodeIdentifier(identifier);
255 for (final UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
256 builder.withChild(unkeyedListEntryNode);
258 return builder.build();
261 private static UnkeyedListEntryNode unkeyedEntry(final String localName,
262 final DataContainerChild<?, ?>... children) {
263 final DataContainerNodeAttrBuilder<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, MapNode> 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 DataContainerNodeAttrBuilder<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].getNodeType(), children[i].getValue());
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, LeafSetEntryNode<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 new NodeIdentifierWithPredicates(QName.create("urn:nested:module", "2014-06-03", localName),
316 private static NodeWithValue<?> toIdentifier(final String localName, final Object value) {
317 return new NodeWithValue<>(QName.create("urn:nested:module", "2014-06-03", localName), value);
320 private static UnkeyedListEntryNode nodeDataDepth3Operational() {
321 return unkeyedEntry("depth2-cont1",
322 container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
323 leaf("depth3-leaf1", "depth3-leaf1-value"));
326 private static ContainerNode nodeDataDepth5() {
331 unkeyedEntry("depth2-cont1",
332 container("depth3-cont1",
333 container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
334 leaf("depth4-leaf1", "depth4-leaf1-value")),
335 leaf("depth3-leaf1", "depth3-leaf1-value"))),
336 mapNode("depth2-list2",
337 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
338 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
339 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
342 container("depth3-cont2",
343 container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
344 leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
345 leaf("depth2-leaf1", "depth2-leaf1-value"));
348 private static ContainerNode nodeDataDepth4() {
351 unkeyedList("depth2-cont1", nodeDataDepth3Operational()),
352 mapNode("depth2-list2",
353 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
354 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
355 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
358 container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
359 leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
362 private static ContainerNode nodeDataDepth3() {
365 unkeyedList("depth2-cont1",
366 unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
367 mapNode("depth2-list2",
368 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
369 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
370 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
371 container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
372 leaf("depth2-leaf1", "depth2-leaf1-value"));
375 private static ContainerNode nodeDataDepth2() {
378 unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
379 mapNode("depth2-list2",
380 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
381 leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
382 // leafList("depth2-lfLst1"),
383 leaf("depth2-leaf1", "depth2-leaf1-value"));
386 private static ContainerNode nodeDataDepth1() {
387 return container("depth1-cont");