Propagate query parameters to YANG dataPATCH()
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / jaxrs / RestconfDataPatchTest.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.mockito.Mockito.doNothing;
11 import static org.mockito.Mockito.doReturn;
12 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFalseFluentFuture;
13 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture;
14
15 import javax.ws.rs.core.MultivaluedHashMap;
16 import javax.ws.rs.core.UriInfo;
17 import org.junit.jupiter.api.BeforeEach;
18 import org.junit.jupiter.api.Test;
19 import org.junit.jupiter.api.extension.ExtendWith;
20 import org.mockito.Mock;
21 import org.mockito.junit.jupiter.MockitoExtension;
22 import org.opendaylight.mdsal.common.api.CommitInfo;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
25 import org.opendaylight.restconf.server.spi.YangPatchStatusBody;
26
27 @ExtendWith(MockitoExtension.class)
28 class RestconfDataPatchTest extends AbstractRestconfTest {
29     @Mock
30     private DOMDataTreeReadWriteTransaction tx;
31     @Mock
32     private UriInfo uriInfo;
33
34     @BeforeEach
35     void beforeEach() {
36         doReturn(tx).when(dataBroker).newReadWriteTransaction();
37         doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
38     }
39
40     @Test
41     void testPatchData() {
42         doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
43         doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
44         doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
45         doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
46
47         final var body = assertEntity(YangPatchStatusBody.class, 200,
48             ar -> restconf.dataYangJsonPATCH(stringInputStream("""
49                 {
50                   "ietf-yang-patch:yang-patch" : {
51                     "patch-id" : "test patch id",
52                     "edit" : [
53                       {
54                         "edit-id" : "create data",
55                         "operation" : "create",
56                         "target" : "/example-jukebox:jukebox",
57                         "value" : {
58                           "jukebox" : {
59                             "player" : {
60                               "gap" : "0.2"
61                             }
62                           }
63                         }
64                       },
65                       {
66                         "edit-id" : "replace data",
67                         "operation" : "replace",
68                         "target" : "/example-jukebox:jukebox",
69                         "value" : {
70                           "jukebox" : {
71                             "player" : {
72                               "gap" : "0.3"
73                             }
74                           }
75                         }
76                       },
77                       {
78                         "edit-id" : "delete data",
79                         "operation" : "delete",
80                         "target" : "/example-jukebox:jukebox/player/gap"
81                       }
82                     ]
83                   }
84                 }"""), uriInfo, ar));
85
86         assertFormat("""
87             {"ietf-yang-patch:yang-patch-status":{"patch-id":"test patch id","ok":[null]}}""", body::formatToJSON);
88     }
89
90     @Test
91     void testPatchDataDeleteNotExist() {
92         doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
93         doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
94         doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
95         doReturn(true).when(tx).cancel();
96
97         final var body = assertEntity(YangPatchStatusBody.class, 409, ar -> restconf.dataYangJsonPATCH(
98             stringInputStream("""
99                 {
100                   "ietf-yang-patch:yang-patch" : {
101                     "patch-id" : "test patch id",
102                     "edit" : [
103                       {
104                         "edit-id" : "create data",
105                         "operation" : "create",
106                         "target" : "/example-jukebox:jukebox",
107                         "value" : {
108                           "jukebox" : {
109                             "player" : {
110                               "gap" : "0.2"
111                             }
112                           }
113                         }
114                       },
115                       {
116                         "edit-id" : "remove data",
117                         "operation" : "remove",
118                         "target" : "/example-jukebox:jukebox/player/gap"
119                       },
120                       {
121                         "edit-id" : "delete data",
122                         "operation" : "delete",
123                         "target" : "/example-jukebox:jukebox/player/gap"
124                       }
125                     ]
126                   }
127                 }"""), uriInfo, ar));
128
129         assertFormat("""
130             {"ietf-yang-patch:yang-patch-status":{"patch-id":"test patch id","edit-status":{"edit":[\
131             {"edit-id":"create data","ok":[null]},\
132             {"edit-id":"remove data","ok":[null]},\
133             {"edit-id":"delete data","errors":{"error":[{"error-type":"protocol","error-tag":"data-missing",\
134             "error-path":"/example-jukebox:jukebox/player/gap","error-message":"Data does not exist"}]}}]}}}""",
135             body::formatToJSON);
136     }
137
138     @Test
139     void testPatchDataMountPoint() throws Exception {
140         doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
141         doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
142         doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
143         doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
144
145         final var body = assertEntity(YangPatchStatusBody.class, 200,
146             ar -> restconf.dataYangXmlPATCH(stringInputStream("""
147                 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
148                   <patch-id>test patch id</patch-id>
149                   <edit>
150                     <edit-id>create data</edit-id>
151                     <operation>create</operation>
152                     <target>/example-jukebox:jukebox</target>
153                     <value>
154                       <jukebox xmlns="http://example.com/ns/example-jukebox">
155                         <player>
156                           <gap>0.2</gap>
157                         </player>
158                       </jukebox>
159                     </value>
160                   </edit>
161                   <edit>
162                     <edit-id>replace data</edit-id>
163                     <operation>replace</operation>
164                     <target>/example-jukebox:jukebox</target>
165                     <value>
166                       <jukebox xmlns="http://example.com/ns/example-jukebox">
167                         <player>
168                           <gap>0.3</gap>
169                         </player>
170                       </jukebox>
171                     </value>
172                   </edit>
173                   <edit>
174                     <edit-id>delete data</edit-id>
175                     <operation>delete</operation>
176                     <target>/example-jukebox:jukebox/player/gap</target>
177                   </edit>
178                 </yang-patch>"""), uriInfo, ar));
179
180         assertFormat("""
181             <yang-patch-status xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">\
182             <patch-id>test patch id</patch-id><ok/>\
183             </yang-patch-status>""", body::formatToXML);
184     }
185 }