2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.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;
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;
29 @ExtendWith(MockitoExtension.class)
30 class RestconfDataPatchTest extends AbstractRestconfTest {
32 private DOMDataTreeReadWriteTransaction tx;
34 private UriInfo uriInfo;
38 doReturn(tx).when(dataBroker).newReadWriteTransaction();
39 doReturn(new MultivaluedHashMap<>(Map.of(PrettyPrintParam.uriName, "true"))).when(uriInfo).getQueryParameters();
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();
49 final var body = assertEntity(YangPatchStatusBody.class, 200,
50 ar -> restconf.dataYangJsonPATCH(stringInputStream("""
52 "ietf-yang-patch:yang-patch" : {
53 "patch-id" : "test patch id",
56 "edit-id" : "create data",
57 "operation" : "create",
58 "target" : "/example-jukebox:jukebox",
68 "edit-id" : "replace data",
69 "operation" : "replace",
70 "target" : "/example-jukebox:jukebox",
80 "edit-id" : "delete data",
81 "operation" : "delete",
82 "target" : "/example-jukebox:jukebox/player/gap"
90 "ietf-yang-patch:yang-patch-status": {
91 "patch-id": "test patch id",
96 }""", body::formatToJSON);
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();
106 final var body = assertEntity(YangPatchStatusBody.class, 409, ar -> restconf.dataYangJsonPATCH(
107 stringInputStream("""
109 "ietf-yang-patch:yang-patch" : {
110 "patch-id" : "test patch id",
113 "edit-id" : "create data",
114 "operation" : "create",
115 "target" : "/example-jukebox:jukebox",
125 "edit-id" : "remove data",
126 "operation" : "remove",
127 "target" : "/example-jukebox:jukebox/player/gap"
130 "edit-id" : "delete data",
131 "operation" : "delete",
132 "target" : "/example-jukebox:jukebox/player/gap"
136 }"""), uriInfo, ar));
140 "ietf-yang-patch:yang-patch-status": {
141 "patch-id": "test patch id",
145 "edit-id": "create data",
151 "edit-id": "remove data",
157 "edit-id": "delete data",
161 "error-type": "protocol",
162 "error-tag": "data-missing",
163 "error-path": "/example-jukebox:jukebox/player/gap",
164 "error-message": "Data does not exist"
172 }""", body::formatToJSON);
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();
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>
187 <edit-id>create data</edit-id>
188 <operation>create</operation>
189 <target>/example-jukebox:jukebox</target>
191 <jukebox xmlns="http://example.com/ns/example-jukebox">
199 <edit-id>replace data</edit-id>
200 <operation>replace</operation>
201 <target>/example-jukebox:jukebox</target>
203 <jukebox xmlns="http://example.com/ns/example-jukebox">
211 <edit-id>delete data</edit-id>
212 <operation>delete</operation>
213 <target>/example-jukebox:jukebox/player/gap</target>
215 </yang-patch>"""), uriInfo, ar));
218 <yang-patch-status xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
219 <patch-id>test patch id</patch-id>
221 </yang-patch-status>""", body::formatToXML);