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
8 package org.opendaylight.restconf.nb.jaxrs;
10 import static org.junit.Assert.assertThrows;
11 import static org.junit.jupiter.api.Assertions.assertEquals;
12 import static org.mockito.ArgumentMatchers.any;
13 import static org.mockito.ArgumentMatchers.eq;
14 import static org.mockito.Mockito.doNothing;
15 import static org.mockito.Mockito.doReturn;
16 import static org.mockito.Mockito.lenient;
17 import static org.mockito.Mockito.times;
18 import static org.mockito.Mockito.verify;
19 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
20 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
21 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
24 import java.util.List;
25 import java.util.Optional;
26 import javax.ws.rs.container.AsyncResponse;
27 import javax.ws.rs.core.MultivaluedHashMap;
28 import javax.ws.rs.core.UriBuilder;
29 import org.junit.jupiter.api.BeforeEach;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.extension.ExtendWith;
32 import org.mockito.Mock;
33 import org.mockito.junit.jupiter.MockitoExtension;
34 import org.opendaylight.mdsal.common.api.CommitInfo;
35 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
38 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
39 import org.opendaylight.yangtools.yang.common.ErrorTag;
40 import org.opendaylight.yangtools.yang.common.ErrorType;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
47 @ExtendWith(MockitoExtension.class)
48 class RestconfDataPostTest extends AbstractRestconfTest {
49 private static final String BASE_URI = "http://localhost:8181/rests/";
50 private static final String INSERT = "insert";
51 private static final String POINT = "point";
53 private DOMDataTreeReadWriteTransaction tx;
55 private DOMDataTreeReadTransaction readTx;
57 private AsyncResponse asyncResponse;
61 lenient().doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
62 doReturn(tx).when(dataBroker).newReadWriteTransaction();
67 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
68 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
69 doNothing().when(tx).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID,
70 ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(JUKEBOX_QNAME)).build());
71 doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
73 assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox"),
74 assertResponse(201, ar -> restconf.postDataJSON(stringInputStream("""
76 "example-jukebox:jukebox" : {
78 }"""), uriInfo, ar)).getLocation());
82 public void testPostMapEntryData() {
83 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
84 final var node = PLAYLIST_IID.node(BAND_ENTRY.name());
85 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, node);
86 doNothing().when(tx).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
87 doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
89 assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=name%20of%20band"),
90 assertResponse(201, ar -> restconf.postDataJSON(JUKEBOX_API_PATH, stringInputStream("""
92 "example-jukebox:playlist" : {
93 "name" : "name of band",
94 "description" : "band description"
96 }"""), uriInfo, ar)).getLocation());
100 public void testPostExistingData() {
101 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
102 doReturn(immediateTrueFluentFuture())
103 .when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
105 final var ex = assertThrows(RestconfDocumentedException.class, () -> restconf
106 .postDataJSON(stringInputStream("""
108 "example-jukebox:jukebox" : {
111 uriInfo, asyncResponse));
113 final var error = ex.getErrors().get(0);
114 assertEquals(ErrorType.PROTOCOL, error.getErrorType());
115 assertEquals(ErrorTag.DATA_EXISTS, error.getErrorTag());
119 public void testPostExistingListsDataErrorPath() {
120 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
121 final var node = PLAYLIST_IID.node(BAND_ENTRY.name());
122 doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, node);
123 doNothing().when(tx).put(LogicalDatastoreType.CONFIGURATION, node, BAND_ENTRY);
125 final var ex = assertThrows(RestconfDocumentedException.class, () -> restconf.postDataJSON(JUKEBOX_API_PATH,
126 stringInputStream("""
128 "example-jukebox:playlist" : {
129 "name" : "name of band",
130 "description" : "band description"
133 uriInfo, asyncResponse));
134 final var actualPath = ex.getErrors().get(0).getErrorPath();
135 final var expectedPath = YangInstanceIdentifier.builder(PLAYLIST_IID)
136 .nodeWithKey(PLAYLIST_QNAME, QName.create(JUKEBOX_QNAME, "name"), "name of band")
138 assertEquals(expectedPath, actualPath);
142 public void testPostDataWithInsertLast() {
143 // Mocking the query parameters to include 'insert=last'
144 final var queryParams = new MultivaluedHashMap<String, String>();
145 queryParams.put(INSERT, List.of("last"));
146 doReturn(queryParams).when(uriInfo).getQueryParameters();
148 doReturn(immediateFalseFluentFuture()).when(tx)
149 .exists(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
151 doNothing().when(tx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
152 any(NormalizedNode.class));
153 doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
155 assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=0/song=3"),
156 assertResponse(201, ar -> restconf.postDataJSON(
157 apiPath("example-jukebox:jukebox/playlist=0"), stringInputStream("""
159 "example-jukebox:song" : [
164 }"""), uriInfo, ar)).getLocation());
165 verify(tx, times(1)).put(any(), any(), any());
169 public void testPostDataWithInsertFirst() {
170 // Mocking the query parameters to include 'insert=first'
171 final var queryParams = new MultivaluedHashMap<String, String>();
172 queryParams.put(INSERT, List.of("first"));
173 doReturn(queryParams).when(uriInfo).getQueryParameters();
174 doReturn(readTx).when(dataBroker).newReadOnlyTransaction();
176 doReturn(immediateFalseFluentFuture()).when(readTx)
177 .exists(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
178 // Mocking existed playlist with two songs in DS
179 doReturn(immediateFluentFuture(Optional.of(PLAYLIST_WITH_SONGS))).when(tx)
180 .read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
182 doNothing().when(tx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
183 any(NormalizedNode.class));
184 doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
186 assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=0/song=3"),
187 assertResponse(201, ar -> restconf.postDataJSON(
188 apiPath("example-jukebox:jukebox/playlist=0"), stringInputStream("""
190 "example-jukebox:song" : [
195 }"""), uriInfo, ar)).getLocation());
196 verify(tx, times(3)).put(any(), any(), any());
200 public void testPostDataWithInsertBefore() {
201 // Mocking the query parameters to include 'insert=before' and 'point=example-jukebox:jukebox/playlist=0/song=2'
202 final var queryParams = new MultivaluedHashMap<String, String>();
203 queryParams.put(INSERT, List.of("before"));
204 queryParams.put(POINT, List.of("example-jukebox:jukebox/playlist=0/song=2"));
205 doReturn(queryParams).when(uriInfo).getQueryParameters();
206 doReturn(readTx).when(dataBroker).newReadOnlyTransaction();
208 doReturn(immediateFalseFluentFuture()).when(readTx)
209 .exists(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
210 // Mocking existed playlist with two songs in DS
211 doReturn(immediateFluentFuture(Optional.of(PLAYLIST_WITH_SONGS))).when(tx)
212 .read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
214 doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
216 assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=0/song=3"),
217 assertResponse(201, ar -> restconf.postDataJSON(
218 apiPath("example-jukebox:jukebox/playlist=0"), stringInputStream("""
220 "example-jukebox:song" : [
226 }"""), uriInfo, ar)).getLocation());
228 verify(tx, times(3)).put(any(), any(), any());
232 public void testPostDataWithInsertAfter() {
233 // Mocking the query parameters to include 'insert=after' and 'point=example-jukebox:jukebox/playlist=0/song=1'
234 final var queryParams = new MultivaluedHashMap<String, String>();
235 queryParams.put(INSERT, List.of("after"));
236 queryParams.put(POINT, List.of("example-jukebox:jukebox/playlist=0/song=1"));
237 doReturn(queryParams).when(uriInfo).getQueryParameters();
238 doReturn(readTx).when(dataBroker).newReadOnlyTransaction();
240 doReturn(immediateFalseFluentFuture()).when(readTx)
241 .exists(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
242 doReturn(immediateFluentFuture(Optional.of(PLAYLIST_WITH_SONGS))).when(tx)
243 .read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
245 doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
247 assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox/playlist=0/song=3"),
248 assertResponse(201, ar -> restconf.postDataJSON(
249 apiPath("example-jukebox:jukebox/playlist=0"), stringInputStream("""
251 "example-jukebox:song" : [
257 }"""), uriInfo, ar)).getLocation());
259 verify(tx, times(3)).put(any(), any(), any());