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