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;
63 public class CutDataToCorrectDepthTest extends JerseyTest {
65 private static NormalizedNode<?, ?> depth1Cont;
66 private static NormalizedNode<?, ?> depth2Cont1;
67 private NormalizedNode<?, ?> globalPayload;
68 private static SchemaContext schemaContextModules;
71 public class RestImpl {
74 @Path("/config/{identifier:.+}")
75 @Produces({ "application/json", "application/xml" })
76 public NormalizedNodeContext getData(@Encoded @PathParam("identifier") String identifier,
77 @Context UriInfo uriInfo) {
79 final InstanceIdentifierContext iiWithData = ControllerContext.getInstance().toInstanceIdentifier(
82 NormalizedNode<?, ?> data = null;
83 if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
85 } else if (identifier.equals("nested-module:depth1-cont")) {
89 final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
90 return new NormalizedNodeContext(iiWithData, data, writerParameters);
94 @Path("/operational/{identifier:.+}")
95 @Produces({ "application/json", "application/xml" })
96 public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") String identifier,
97 @Context UriInfo uriInfo) {
98 return getData(identifier, uriInfo);
102 @Path("/config/{identifier:.+}")
103 @Consumes({ "application/json", "application/xml" })
104 public void normalizedData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload) throws InterruptedException {
105 System.out.println(payload);
106 System.out.println(payload.getInstanceIdentifierContext().getInstanceIdentifier());
107 System.out.println(payload.getData());
108 globalPayload = payload.getData();
112 @Path("/operational/{identifier:.+}")
113 @Consumes({ "application/json", "application/xml" })
114 public void normalizedDataOperational(@Encoded @PathParam("identifier") String identifier,
115 NormalizedNodeContext payload) throws InterruptedException {
116 normalizedData(identifier, payload);
121 public static void initialize() throws FileNotFoundException {
122 schemaContextModules = TestUtils.loadSchemaContext("/modules");
123 Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
124 assertNotNull(module);
126 UnkeyedListNode listAsUnkeyedList = unkeyedList(
128 unkeyedEntry("depth2-cont1",
129 container("depth3-cont1",
130 container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
131 leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
133 MapNode listAsMap = mapNode(
135 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
136 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
138 depth1Cont = container(
142 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
145 container("depth3-cont2",
146 container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
147 leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
148 leaf("depth2-leaf1", "depth2-leaf1-value"));
150 depth2Cont1 = listAsUnkeyedList;
153 // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
154 // properly in readers
156 public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
157 getDataWithUriDepthParameter("application/json");
158 getDataWithUriDepthParameter("application/xml");
161 public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
162 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
165 // Test config with depth 1
166 response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
168 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
169 verifyResponse(nodeDataDepth1());
171 // Test config with depth 2
172 response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
174 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
175 verifyResponse(nodeDataDepth2());
177 // Test config with depth 3
178 response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
180 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
181 verifyResponse(nodeDataDepth3());
183 // Test config with depth 4
184 response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
186 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
187 verifyResponse(nodeDataDepth4());
189 // Test config with depth 5
190 response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
192 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
193 verifyResponse(nodeDataDepth5());
195 // Test config with depth unbounded
197 response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
198 .request(mediaType).get();
199 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
200 verifyResponse(nodeDataDepth5());
203 private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
204 String responseStr = response.readEntity(String.class);
205 System.out.println(responseStr);
206 target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType));
209 private void verifyResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
210 assertNotNull(globalPayload);
211 assertEquals(globalPayload, nodeData);
212 globalPayload = null;
216 protected Application configure() {
217 ResourceConfig resourceConfig = new ResourceConfig();
218 resourceConfig = resourceConfig.registerInstances(new RestImpl());
219 resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
220 JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
221 RestconfDocumentedExceptionMapper.class);
222 return resourceConfig;
225 private static LeafNode<?> leaf(final String localName, final Object value) {
226 return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
229 private static ContainerNode container(final String localName, final DataContainerChild<?, ?>... children) {
230 DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders.containerBuilder();
231 for (DataContainerChild<?, ?> child : children) {
232 containerBuilder.withChild(child);
234 containerBuilder.withNodeIdentifier(toIdentifier(localName));
235 return containerBuilder.build();
238 private static UnkeyedListNode unkeyedList(
239 final String localName,
240 final UnkeyedListEntryNode... entryNodes) {
241 CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
242 final NodeIdentifier identifier = toIdentifier(localName);
243 builder.withNodeIdentifier(identifier);
244 for (UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
245 builder.withChild(unkeyedListEntryNode);
247 return builder.build();
250 private static UnkeyedListEntryNode unkeyedEntry(final String localName,
251 final DataContainerChild<?, ?>... children) {
252 DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = Builders.unkeyedListEntryBuilder();
253 builder.withNodeIdentifier(toIdentifier(localName));
254 for (DataContainerChild<?, ?> child : children) {
255 builder.withChild(child);
257 return builder.build();
260 private static MapNode mapNode(final String localName, final MapEntryNode... entryNodes) {
261 CollectionNodeBuilder<MapEntryNode, MapNode> builder = Builders.mapBuilder();
262 builder.withNodeIdentifier(toIdentifier(localName));
263 for (MapEntryNode mapEntryNode : entryNodes) {
264 builder.withChild(mapEntryNode);
266 return builder.build();
269 private static MapEntryNode mapEntryNode(final String localName, final int keysNumber,
270 final DataContainerChild<?, ?>... children) {
271 DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders.mapEntryBuilder();
272 Map<QName, Object> keys = new HashMap<>();
273 for (int i = 0; i < keysNumber; i++) {
274 keys.put(children[i].getNodeType(), children[i].getValue());
276 builder.withNodeIdentifier(toIdentifier(localName, keys));
278 for (DataContainerChild<?, ?> child : children) {
279 builder.withChild(child);
281 return builder.build();
284 private static LeafSetNode<?> leafList(final String localName, final String... children) {
285 ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.leafSetBuilder();
286 builder.withNodeIdentifier(toIdentifier(localName));
287 for (String child : children) {
288 builder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(toIdentifier(localName, child))
289 .withValue(child).build());
291 return builder.build();
294 private static NodeIdentifier toIdentifier(String localName) {
295 return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-3", localName));
298 private static NodeIdentifierWithPredicates toIdentifier(String localName, Map<QName, Object> keys) {
299 return new NodeIdentifierWithPredicates(QName.create("urn:nested:module", "2014-06-3", localName),
303 private static NodeWithValue toIdentifier(final String localName, final Object value) {
304 return new NodeWithValue(QName.create("urn:nested:module", "2014-06-3", localName), value);
309 private UnkeyedListEntryNode nodeDataDepth3Operational() {
310 return unkeyedEntry("depth2-cont1",
311 container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
312 leaf("depth3-leaf1", "depth3-leaf1-value"));
315 private ContainerNode nodeDataDepth5() {
320 unkeyedEntry("depth2-cont1",
321 container("depth3-cont1",
322 container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
323 leaf("depth4-leaf1", "depth4-leaf1-value")),
324 leaf("depth3-leaf1", "depth3-leaf1-value"))),
325 mapNode("depth2-list2",
326 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
327 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
328 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
331 container("depth3-cont2",
332 container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
333 leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
334 leaf("depth2-leaf1", "depth2-leaf1-value"));
337 private ContainerNode nodeDataDepth4() {
340 unkeyedList("depth2-cont1", nodeDataDepth3Operational()),
341 mapNode("depth2-list2",
342 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
343 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
344 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
347 container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
348 leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
351 private ContainerNode nodeDataDepth3() {
354 unkeyedList("depth2-cont1",
355 unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
356 mapNode("depth2-list2",
357 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
358 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
359 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
360 container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
361 leaf("depth2-leaf1", "depth2-leaf1-value"));
364 private ContainerNode nodeDataDepth2() {
367 unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
368 mapNode("depth2-list2",
369 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
370 leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
371 // leafList("depth2-lfLst1"),
372 leaf("depth2-leaf1", "depth2-leaf1-value"));
375 private ContainerNode nodeDataDepth1() {
376 return container("depth1-cont");