bb8f4a42dc6ff8d9f0be35f336ec1fb730c88e7b
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / jaxrs / RestconfDataPostTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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.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;
22
23 import java.net.URI;
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;
46
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";
52     @Mock
53     private DOMDataTreeReadWriteTransaction tx;
54     @Mock
55     private DOMDataTreeReadTransaction readTx;
56     @Mock
57     private AsyncResponse asyncResponse;
58
59     @BeforeEach
60     void beforeEach() {
61         lenient().doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
62         doReturn(tx).when(dataBroker).newReadWriteTransaction();
63     }
64
65     @Test
66     void testPostData() {
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();
72
73         assertEquals(URI.create("http://localhost:8181/rests/data/example-jukebox:jukebox"),
74             assertResponse(201, ar -> restconf.postDataJSON(stringInputStream("""
75                 {
76                   "example-jukebox:jukebox" : {
77                   }
78                 }"""), uriInfo, ar)).getLocation());
79     }
80
81     @Test
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();
88
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("""
91                 {
92                   "example-jukebox:playlist" : {
93                     "name" : "name of band",
94                     "description" : "band description"
95                   }
96                 }"""), uriInfo, ar)).getLocation());
97     }
98
99     @Test
100     public void testPostExistingData() {
101         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
102         doReturn(immediateTrueFluentFuture())
103             .when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
104
105         final var ex = assertThrows(RestconfDocumentedException.class, () -> restconf
106             .postDataJSON(stringInputStream("""
107                 {
108                   "example-jukebox:jukebox" : {
109                   }
110                 }"""),
111             uriInfo, asyncResponse));
112
113         final var error = ex.getErrors().get(0);
114         assertEquals(ErrorType.PROTOCOL, error.getErrorType());
115         assertEquals(ErrorTag.DATA_EXISTS, error.getErrorTag());
116     }
117
118     @Test
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);
124
125         final var ex = assertThrows(RestconfDocumentedException.class, () -> restconf.postDataJSON(JUKEBOX_API_PATH,
126             stringInputStream("""
127                 {
128                   "example-jukebox:playlist" : {
129                     "name" : "name of band",
130                     "description" : "band description"
131                   }
132                 }"""),
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")
137             .build();
138         assertEquals(expectedPath, actualPath);
139     }
140
141     @Test
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();
147
148         doReturn(immediateFalseFluentFuture()).when(tx)
149             .exists(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class));
150
151         doNothing().when(tx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
152             any(NormalizedNode.class));
153         doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
154
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("""
158                     {
159                       "example-jukebox:song" : [
160                         {
161                            "index": "3"
162                         }
163                       ]
164                     }"""), uriInfo, ar)).getLocation());
165         verify(tx, times(1)).put(any(), any(), any());
166     }
167
168     @Test
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();
175
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));
181
182         doNothing().when(tx).put(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class),
183                 any(NormalizedNode.class));
184         doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
185
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("""
189                     {
190                       "example-jukebox:song" : [
191                         {
192                            "index": "3"
193                         }
194                       ]
195                     }"""), uriInfo, ar)).getLocation());
196         verify(tx, times(3)).put(any(), any(), any());
197     }
198
199     @Test
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();
207
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));
213
214         doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
215
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("""
219                     {
220                       "example-jukebox:song" : [
221                         {
222                            "index": "3",
223                            "id" = "C"
224                         }
225                       ]
226                     }"""), uriInfo, ar)).getLocation());
227
228         verify(tx, times(3)).put(any(), any(), any());
229     }
230
231     @Test
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();
239
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));
244
245         doReturn(UriBuilder.fromUri(BASE_URI)).when(uriInfo).getBaseUriBuilder();
246
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("""
250                     {
251                       "example-jukebox:song" : [
252                         {
253                            "index": "3",
254                            "id" = "C"
255                         }
256                       ]
257                     }"""), uriInfo, ar)).getLocation());
258
259         verify(tx, times(3)).put(any(), any(), any());
260     }
261 }