2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.restconf.nb.jaxrs;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertNotNull;
13 import static org.junit.jupiter.api.Assertions.assertNull;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.Mockito.doReturn;
16 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
18 import java.util.Optional;
19 import javax.ws.rs.core.MultivaluedHashMap;
20 import org.junit.jupiter.api.BeforeEach;
21 import org.junit.jupiter.api.Test;
22 import org.junit.jupiter.api.extension.ExtendWith;
23 import org.mockito.Mock;
24 import org.mockito.junit.jupiter.MockitoExtension;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
28 import org.opendaylight.mdsal.dom.api.DOMRpcService;
29 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
30 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
31 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
32 import org.opendaylight.yangtools.yang.common.ErrorTag;
33 import org.opendaylight.yangtools.yang.common.ErrorType;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
38 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 @ExtendWith(MockitoExtension.class)
42 class RestconfDataGetTest extends AbstractRestconfTest {
43 private static final NodeIdentifier PLAYLIST_NID = new NodeIdentifier(PLAYLIST_QNAME);
44 private static final NodeIdentifier LIBRARY_NID = new NodeIdentifier(LIBRARY_QNAME);
46 // config contains one child the same as in operational and one additional
47 private static final ContainerNode CONFIG_JUKEBOX = Builders.containerBuilder()
48 .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
49 .withChild(CONT_PLAYER)
50 .withChild(Builders.containerBuilder().withNodeIdentifier(LIBRARY_NID).build())
52 // operational contains one child the same as in config and one additional
53 private static final ContainerNode OPER_JUKEBOX = Builders.containerBuilder()
54 .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
55 .withChild(CONT_PLAYER)
56 .withChild(Builders.mapBuilder().withNodeIdentifier(PLAYLIST_NID).build())
60 private DOMDataTreeReadTransaction tx;
64 doReturn(tx).when(dataBroker).newReadOnlyTransaction();
69 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
70 doReturn(immediateFluentFuture(Optional.of(EMPTY_JUKEBOX))).when(tx)
71 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
72 doReturn(immediateFluentFuture(Optional.empty()))
73 .when(tx).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
75 assertEquals(EMPTY_JUKEBOX, assertNormalizedNode(200, ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar)));
79 void testReadRootData() {
80 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
81 doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(CONFIG_JUKEBOX))))
83 .read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.of());
84 doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(OPER_JUKEBOX))))
86 .read(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.of());
88 final var data = assertInstanceOf(ContainerNode.class,
89 assertNormalizedNode(200, ar -> restconf.dataGET(uriInfo, ar)));
90 final var rootNodes = data.body();
91 assertEquals(1, rootNodes.size());
92 final var allDataChildren = assertInstanceOf(ContainerNode.class, rootNodes.iterator().next()).body();
93 assertEquals(3, allDataChildren.size());
96 private static ContainerNode wrapNodeByDataRootContainer(final DataContainerChild data) {
97 return Builders.containerBuilder()
98 .withNodeIdentifier(NodeIdentifier.create(SchemaContext.NAME))
104 * Test read data from mount point when both {@link LogicalDatastoreType#CONFIGURATION} and
105 * {@link LogicalDatastoreType#OPERATIONAL} contains the same data and some additional data to be merged.
108 void testReadDataMountPoint() {
109 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
110 doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(tx)
111 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
112 doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(tx)
113 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
115 doReturn(Optional.of(mountPoint)).when(mountPointService)
116 .getMountPoint(any(YangInstanceIdentifier.class));
117 doReturn(Optional.of(FixedDOMSchemaService.of(JUKEBOX_SCHEMA))).when(mountPoint)
118 .getService(DOMSchemaService.class);
119 doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
120 doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
121 doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
123 // response must contain all child nodes from config and operational containers merged in one container
124 final var data = assertInstanceOf(ContainerNode.class,
125 assertNormalizedNode(200, ar -> restconf.dataGET(
126 apiPath("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox"), uriInfo, ar)));
127 assertEquals(3, data.size());
128 assertNotNull(data.childByArg(CONT_PLAYER.name()));
129 assertNotNull(data.childByArg(LIBRARY_NID));
130 assertNotNull(data.childByArg(PLAYLIST_NID));
134 void testReadDataNoData() {
135 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
136 doReturn(immediateFluentFuture(Optional.empty()))
137 .when(tx).read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
138 doReturn(immediateFluentFuture(Optional.empty()))
139 .when(tx).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
141 final var error = assertError(ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar));
142 assertEquals(ErrorType.PROTOCOL, error.getErrorType());
143 assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
144 assertEquals("Request could not be completed because the relevant data model content does not exist",
145 error.getErrorMessage());
149 * Read data from config datastore according to content parameter.
152 void testReadDataConfigTest() {
153 final var parameters = new MultivaluedHashMap<String, String>();
154 parameters.putSingle("content", "config");
156 doReturn(parameters).when(uriInfo).getQueryParameters();
157 doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(tx)
158 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
160 // response must contain only config data
161 final var data = assertInstanceOf(ContainerNode.class,
162 assertNormalizedNode(200, ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar)));
163 // config data present
164 assertNotNull(data.childByArg(CONT_PLAYER.name()));
165 assertNotNull(data.childByArg(LIBRARY_NID));
167 assertNull(data.childByArg(PLAYLIST_NID));
171 * Read data from operational datastore according to content parameter.
174 void testReadDataOperationalTest() {
175 final var parameters = new MultivaluedHashMap<String, String>();
176 parameters.putSingle("content", "nonconfig");
178 doReturn(parameters).when(uriInfo).getQueryParameters();
179 doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(tx)
180 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
182 // response must contain only operational data
183 final var data = assertInstanceOf(ContainerNode.class, assertNormalizedNode(200,
184 ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar)));
185 // state data present
186 assertNotNull(data.childByArg(CONT_PLAYER.name()));
187 assertNotNull(data.childByArg(PLAYLIST_NID));
189 // config data absent
190 assertNull(data.childByArg(LIBRARY_NID));