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;
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;
27 @ExtendWith(MockitoExtension.class)
28 class RestconfDataPatchTest extends AbstractRestconfTest {
30 private DOMDataTreeReadWriteTransaction tx;
32 private UriInfo uriInfo;
36 doReturn(tx).when(dataBroker).newReadWriteTransaction();
37 doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters();
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();
47 final var body = assertEntity(YangPatchStatusBody.class, 200,
48 ar -> restconf.dataYangJsonPATCH(stringInputStream("""
50 "ietf-yang-patch:yang-patch" : {
51 "patch-id" : "test patch id",
54 "edit-id" : "create data",
55 "operation" : "create",
56 "target" : "/example-jukebox:jukebox",
66 "edit-id" : "replace data",
67 "operation" : "replace",
68 "target" : "/example-jukebox:jukebox",
78 "edit-id" : "delete data",
79 "operation" : "delete",
80 "target" : "/example-jukebox:jukebox/player/gap"
87 {"ietf-yang-patch:yang-patch-status":{"patch-id":"test patch id","ok":[null]}}""", body::formatToJSON);
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();
97 final var body = assertEntity(YangPatchStatusBody.class, 409, ar -> restconf.dataYangJsonPATCH(
100 "ietf-yang-patch:yang-patch" : {
101 "patch-id" : "test patch id",
104 "edit-id" : "create data",
105 "operation" : "create",
106 "target" : "/example-jukebox:jukebox",
116 "edit-id" : "remove data",
117 "operation" : "remove",
118 "target" : "/example-jukebox:jukebox/player/gap"
121 "edit-id" : "delete data",
122 "operation" : "delete",
123 "target" : "/example-jukebox:jukebox/player/gap"
127 }"""), uriInfo, ar));
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"}]}}]}}}""",
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();
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>
150 <edit-id>create data</edit-id>
151 <operation>create</operation>
152 <target>/example-jukebox:jukebox</target>
154 <jukebox xmlns="http://example.com/ns/example-jukebox">
162 <edit-id>replace data</edit-id>
163 <operation>replace</operation>
164 <target>/example-jukebox:jukebox</target>
166 <jukebox xmlns="http://example.com/ns/example-jukebox">
174 <edit-id>delete data</edit-id>
175 <operation>delete</operation>
176 <target>/example-jukebox:jukebox/player/gap</target>
178 </yang-patch>"""), uriInfo, ar));
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);