ff75a2c7eaa134f28689d882178d3c50884ae08c
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / jaxrs / RestconfDataGetTest.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.nb.jaxrs;
9
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;
17
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.DOMActionService;
27 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
29 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
30 import org.opendaylight.mdsal.dom.api.DOMRpcService;
31 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
32 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
33 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
34 import org.opendaylight.yangtools.yang.common.ErrorTag;
35 import org.opendaylight.yangtools.yang.common.ErrorType;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
38 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
40 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42
43 @ExtendWith(MockitoExtension.class)
44 class RestconfDataGetTest extends AbstractRestconfTest {
45     private static final NodeIdentifier PLAYLIST_NID = new NodeIdentifier(PLAYLIST_QNAME);
46     private static final NodeIdentifier LIBRARY_NID = new NodeIdentifier(LIBRARY_QNAME);
47
48     // config contains one child the same as in operational and one additional
49     private static final ContainerNode CONFIG_JUKEBOX = ImmutableNodes.newContainerBuilder()
50             .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
51             .withChild(CONT_PLAYER)
52             .withChild(ImmutableNodes.newContainerBuilder().withNodeIdentifier(LIBRARY_NID).build())
53             .build();
54     // operational contains one child the same as in config and one additional
55     private static final ContainerNode OPER_JUKEBOX = ImmutableNodes.newContainerBuilder()
56             .withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME))
57             .withChild(CONT_PLAYER)
58             .withChild(ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(PLAYLIST_NID).build())
59             .build();
60
61     @Mock
62     private DOMDataTreeReadTransaction tx;
63
64     @BeforeEach
65     void beforeEach() {
66         doReturn(tx).when(dataBroker).newReadOnlyTransaction();
67     }
68
69     @Test
70     void testReadData() {
71         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
72         doReturn(immediateFluentFuture(Optional.of(EMPTY_JUKEBOX))).when(tx)
73                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
74         doReturn(immediateFluentFuture(Optional.empty()))
75                 .when(tx).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
76
77         assertEquals(EMPTY_JUKEBOX, assertNormalizedNode(200, ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar)));
78     }
79
80     @Test
81     void testReadRootData() {
82         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
83         doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(CONFIG_JUKEBOX))))
84                 .when(tx)
85                 .read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.of());
86         doReturn(immediateFluentFuture(Optional.of(wrapNodeByDataRootContainer(OPER_JUKEBOX))))
87                 .when(tx)
88                 .read(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.of());
89
90         final var data = assertInstanceOf(ContainerNode.class,
91             assertNormalizedNode(200, ar -> restconf.dataGET(uriInfo, ar)));
92         final var rootNodes = data.body();
93         assertEquals(1, rootNodes.size());
94         final var allDataChildren = assertInstanceOf(ContainerNode.class, rootNodes.iterator().next()).body();
95         assertEquals(3, allDataChildren.size());
96     }
97
98     private static ContainerNode wrapNodeByDataRootContainer(final DataContainerChild data) {
99         return ImmutableNodes.newContainerBuilder()
100             .withNodeIdentifier(NodeIdentifier.create(SchemaContext.NAME))
101             .withChild(data)
102             .build();
103     }
104
105     /**
106      * Test read data from mount point when both {@link LogicalDatastoreType#CONFIGURATION} and
107      * {@link LogicalDatastoreType#OPERATIONAL} contains the same data and some additional data to be merged.
108      */
109     @Test
110     void testReadDataMountPoint() {
111         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
112         doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(tx)
113                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
114         doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(tx)
115                 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
116
117         doReturn(Optional.of(mountPoint)).when(mountPointService)
118             .getMountPoint(any(YangInstanceIdentifier.class));
119         doReturn(Optional.of(new FixedDOMSchemaService(JUKEBOX_SCHEMA))).when(mountPoint)
120             .getService(DOMSchemaService.class);
121         doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
122         doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
123         doReturn(Optional.empty()).when(mountPoint).getService(DOMActionService.class);
124         doReturn(Optional.empty()).when(mountPoint).getService(DOMMountPointService.class);
125         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
126
127         // response must contain all child nodes from config and operational containers merged in one container
128         final var data = assertInstanceOf(ContainerNode.class,
129             assertNormalizedNode(200, ar -> restconf.dataGET(
130                apiPath("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox"), uriInfo, ar)));
131         assertEquals(3, data.size());
132         assertNotNull(data.childByArg(CONT_PLAYER.name()));
133         assertNotNull(data.childByArg(LIBRARY_NID));
134         assertNotNull(data.childByArg(PLAYLIST_NID));
135     }
136
137     @Test
138     void testReadDataNoData() {
139         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
140         doReturn(immediateFluentFuture(Optional.empty()))
141                 .when(tx).read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
142         doReturn(immediateFluentFuture(Optional.empty()))
143                 .when(tx).read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
144
145         final var error = assertError(ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar));
146         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
147         assertEquals(ErrorTag.DATA_MISSING, error.getErrorTag());
148         assertEquals("Request could not be completed because the relevant data model content does not exist",
149             error.getErrorMessage());
150     }
151
152     /**
153      * Read data from config datastore according to content parameter.
154      */
155     @Test
156     void testReadDataConfigTest() {
157         final var parameters = new MultivaluedHashMap<String, String>();
158         parameters.putSingle("content", "config");
159
160         doReturn(parameters).when(uriInfo).getQueryParameters();
161         doReturn(immediateFluentFuture(Optional.of(CONFIG_JUKEBOX))).when(tx)
162                 .read(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
163
164         // response must contain only config data
165         final var data = assertInstanceOf(ContainerNode.class,
166             assertNormalizedNode(200, ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar)));
167         // config data present
168         assertNotNull(data.childByArg(CONT_PLAYER.name()));
169         assertNotNull(data.childByArg(LIBRARY_NID));
170         // state data absent
171         assertNull(data.childByArg(PLAYLIST_NID));
172     }
173
174     /**
175      * Read data from operational datastore according to content parameter.
176      */
177     @Test
178     void testReadDataOperationalTest() {
179         final var parameters = new MultivaluedHashMap<String, String>();
180         parameters.putSingle("content", "nonconfig");
181
182         doReturn(parameters).when(uriInfo).getQueryParameters();
183         doReturn(immediateFluentFuture(Optional.of(OPER_JUKEBOX))).when(tx)
184                 .read(LogicalDatastoreType.OPERATIONAL, JUKEBOX_IID);
185
186         // response must contain only operational data
187         final var data = assertInstanceOf(ContainerNode.class, assertNormalizedNode(200,
188             ar -> restconf.dataGET(JUKEBOX_API_PATH, uriInfo, ar)));
189         // state data present
190         assertNotNull(data.childByArg(CONT_PLAYER.name()));
191         assertNotNull(data.childByArg(PLAYLIST_NID));
192
193         // config data absent
194         assertNull(data.childByArg(LIBRARY_NID));
195     }
196 }