2 * Copyright (c) 2016 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
9 package org.opendaylight.restconf.restful.services.impl;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.mockito.Mockito.doNothing;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.mock;
19 import com.google.common.base.Optional;
20 import com.google.common.util.concurrent.Futures;
21 import java.lang.reflect.Field;
22 import java.util.ArrayList;
23 import java.util.List;
24 import javax.ws.rs.core.MultivaluedHashMap;
25 import javax.ws.rs.core.Response;
26 import javax.ws.rs.core.UriBuilder;
27 import javax.ws.rs.core.UriInfo;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.mockito.Mock;
31 import org.mockito.Mockito;
32 import org.mockito.MockitoAnnotations;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
37 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
38 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
39 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
40 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
41 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
42 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
43 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
44 import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
45 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
46 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
47 import org.opendaylight.restconf.RestConnectorProvider;
48 import org.opendaylight.restconf.common.references.SchemaContextRef;
49 import org.opendaylight.restconf.handlers.SchemaContextHandler;
50 import org.opendaylight.restconf.handlers.TransactionChainHandler;
51 import org.opendaylight.yangtools.yang.common.QName;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
57 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
58 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
59 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
62 public class RestconfDataServiceImplTest {
64 private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/jukebox";
66 private ContainerNode buildBaseCont;
67 private SchemaContextRef contextRef;
68 private YangInstanceIdentifier iidBase;
69 private DataSchemaNode schemaNode;
70 private RestconfDataServiceImpl dataService;
71 private QName baseQName;
72 private QName containerQname;
73 private QName leafQname;
74 private ContainerNode buildBaseContToReplace;
77 private TransactionChainHandler transactionChainHandler;
79 private DOMTransactionChain domTransactionChain;
81 private UriInfo uriInfo;
83 private DOMDataReadWriteTransaction readWrite;
85 private DOMDataReadOnlyTransaction read;
87 private DOMDataWriteTransaction write;
90 public void setUp() throws Exception {
91 baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
92 containerQname = QName.create(baseQName, "player");
93 leafQname = QName.create(baseQName, "gap");
94 LeafNode buildLeaf = Builders.leafBuilder()
95 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
99 ContainerNode buildPlayerCont = Builders.containerBuilder()
100 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
101 .withChild(buildLeaf)
103 buildBaseCont = Builders.containerBuilder()
104 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
105 .withChild(buildPlayerCont)
107 buildLeaf = Builders.leafBuilder()
108 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
111 buildPlayerCont = Builders.containerBuilder()
112 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
113 .withChild(buildLeaf)
115 buildBaseContToReplace = Builders.containerBuilder()
116 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
117 .withChild(buildPlayerCont)
119 iidBase = YangInstanceIdentifier.builder()
123 contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
124 schemaNode = DataSchemaContextTree.from(contextRef.get()).getChild(iidBase).getDataSchemaNode();
125 MockitoAnnotations.initMocks(this);
126 final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
128 schemaContextHandler.onGlobalContextUpdated(contextRef.get());
129 dataService = new RestconfDataServiceImpl(schemaContextHandler, transactionChainHandler);
130 doReturn(domTransactionChain).when(transactionChainHandler).get();
131 doReturn(read).when(domTransactionChain).newReadOnlyTransaction();
132 doReturn(readWrite).when(domTransactionChain).newReadWriteTransaction();
133 doReturn(write).when(domTransactionChain).newWriteOnlyTransaction();
137 public void testReadData() {
138 doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
139 doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
140 .read(LogicalDatastoreType.CONFIGURATION, iidBase);
141 doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL, iidBase);
142 final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
143 assertNotNull(response);
144 assertEquals(200, response.getStatus());
145 assertEquals(buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
148 @Test(expected = RestconfDocumentedException.class)
149 public void testReadDataNoData() {
150 doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
151 doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION,
153 doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL,
155 final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
159 public void testPutData() {
160 final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
161 final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
163 doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
164 .read(LogicalDatastoreType.CONFIGURATION, iidBase);
165 doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
166 doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
167 final Response response = dataService.putData(null, payload);
168 assertNotNull(response);
169 assertEquals(200, response.getStatus());
173 public void testPutDataWithMountPoint() {
174 final DOMDataBroker dataBroker = Mockito.mock(DOMDataBroker.class);
175 final DOMMountPoint mountPoint = Mockito.mock(DOMMountPoint.class);
176 doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
177 doReturn(transactionChainHandler.get()).when(dataBroker).createTransactionChain(RestConnectorProvider.transactionListener);
178 final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, mountPoint, contextRef.get());
179 final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
181 doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
182 .read(LogicalDatastoreType.CONFIGURATION, iidBase);
183 doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
184 doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
185 final Response response = dataService.putData(null, payload);
186 assertNotNull(response);
187 assertEquals(200, response.getStatus());
191 public void testPostData() {
192 final QName listQname = QName.create(baseQName, "playlist");
193 final QName listKeyQname = QName.create(baseQName, "name");
194 final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
195 new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band");
196 final LeafNode<Object> content = Builders.leafBuilder()
197 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
198 .withValue("name of band")
200 final LeafNode<Object> content2 = Builders.leafBuilder()
201 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
202 .withValue("band description")
204 final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
205 .withNodeIdentifier(nodeWithKey)
209 final MapNode buildList = Builders.mapBuilder()
210 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listQname))
211 .withChild(mapEntryNode)
214 doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
215 final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, null, null, contextRef.get());
216 final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildList);
217 doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iidBase);
218 final MapNode data = (MapNode) payload.getData();
219 final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier = data.getValue().iterator().next().getIdentifier();
220 final YangInstanceIdentifier node = payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
221 doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
222 doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
223 doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
224 doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(uriInfo).getBaseUriBuilder();
226 final Response response = dataService.postData(null, payload, uriInfo);
227 assertEquals(201, response.getStatus());
231 public void testDeleteData() {
232 doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
233 doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
234 doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
235 final Response response = dataService.deleteData("example-jukebox:jukebox");
236 assertNotNull(response);
237 assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
241 public void testPatchData() throws Exception {
242 final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
243 final List<PATCHEntity> entity = new ArrayList<>();
244 final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
245 .node(containerQname)
248 entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
249 entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseContToReplace));
250 entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
251 final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
253 doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
254 .read(LogicalDatastoreType.CONFIGURATION, iidBase);
255 doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
256 doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
257 doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
258 doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
259 doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
260 doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
261 final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
262 assertTrue(status.isOk());
263 assertEquals(3, status.getEditCollection().size());
264 assertEquals("replace data", status.getEditCollection().get(1).getEditId());
268 public void testPatchDataDeleteNotExist() throws Exception {
269 final Field handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
270 final Field broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
272 handler.setAccessible(true);
273 handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
275 broker.setAccessible(true);
276 broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
277 final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
278 final List<PATCHEntity> entity = new ArrayList<>();
279 final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
280 .node(containerQname)
283 entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
284 entity.add(new PATCHEntity("remove data", "REMOVE", iidleaf));
285 entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
286 final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
288 doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
289 .read(LogicalDatastoreType.CONFIGURATION, iidBase);
290 doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
291 doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
292 doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
293 doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
294 doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
295 doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
296 doReturn(true).when(readWrite).cancel();
297 final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
299 handler.set(RestConnectorProvider.class, null);
300 handler.setAccessible(false);
302 broker.set(RestConnectorProvider.class, null);
303 broker.setAccessible(false);
305 assertFalse(status.isOk());
306 assertEquals(3, status.getEditCollection().size());
307 assertTrue(status.getEditCollection().get(0).isOk());
308 assertTrue(status.getEditCollection().get(1).isOk());
309 assertFalse(status.getEditCollection().get(2).isOk());
310 assertFalse(status.getEditCollection().get(2).getEditErrors().isEmpty());
311 final String errorMessage = status.getEditCollection().get(2).getEditErrors().get(0).getErrorMessage();
312 assertEquals("Data does not exist", errorMessage);