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 org.junit.jupiter.api.BeforeEach;
16 import org.junit.jupiter.api.Test;
17 import org.junit.jupiter.api.extension.ExtendWith;
18 import org.mockito.Mock;
19 import org.mockito.junit.jupiter.MockitoExtension;
20 import org.opendaylight.mdsal.common.api.CommitInfo;
21 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
23 import org.opendaylight.restconf.server.spi.YangPatchStatusBody;
25 @ExtendWith(MockitoExtension.class)
26 class RestconfDataPatchTest extends AbstractRestconfTest {
28 private DOMDataTreeReadWriteTransaction tx;
32 doReturn(tx).when(dataBroker).newReadWriteTransaction();
36 void testPatchData() {
37 doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
38 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
39 doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
40 doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
42 final var body = assertEntity(YangPatchStatusBody.class, 200,
43 ar -> restconf.dataYangJsonPATCH(stringInputStream("""
45 "ietf-yang-patch:yang-patch" : {
46 "patch-id" : "test patch id",
49 "edit-id" : "create data",
50 "operation" : "create",
51 "target" : "/example-jukebox:jukebox",
61 "edit-id" : "replace data",
62 "operation" : "replace",
63 "target" : "/example-jukebox:jukebox",
73 "edit-id" : "delete data",
74 "operation" : "delete",
75 "target" : "/example-jukebox:jukebox/player/gap"
82 {"ietf-yang-patch:yang-patch-status":{"patch-id":"test patch id","ok":[null]}}""", body::formatToJSON);
86 void testPatchDataDeleteNotExist() {
87 doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
88 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
89 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
90 doReturn(true).when(tx).cancel();
92 final var body = assertEntity(YangPatchStatusBody.class, 409, ar -> restconf.dataYangJsonPATCH(
95 "ietf-yang-patch:yang-patch" : {
96 "patch-id" : "test patch id",
99 "edit-id" : "create data",
100 "operation" : "create",
101 "target" : "/example-jukebox:jukebox",
111 "edit-id" : "remove data",
112 "operation" : "remove",
113 "target" : "/example-jukebox:jukebox/player/gap"
116 "edit-id" : "delete data",
117 "operation" : "delete",
118 "target" : "/example-jukebox:jukebox/player/gap"
125 {"ietf-yang-patch:yang-patch-status":{"patch-id":"test patch id","edit-status":{"edit":[\
126 {"edit-id":"create data","ok":[null]},\
127 {"edit-id":"remove data","ok":[null]},\
128 {"edit-id":"delete data","errors":{"error":[{"error-type":"protocol","error-tag":"data-missing",\
129 "error-path":"/example-jukebox:jukebox/player/gap","error-message":"Data does not exist"}]}}]}}}""",
134 void testPatchDataMountPoint() throws Exception {
135 doNothing().when(tx).delete(LogicalDatastoreType.CONFIGURATION, GAP_IID);
136 doReturn(immediateFalseFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
137 doReturn(immediateTrueFluentFuture()).when(tx).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
138 doReturn(CommitInfo.emptyFluentFuture()).when(tx).commit();
140 final var body = assertEntity(YangPatchStatusBody.class, 200,
141 ar -> restconf.dataYangXmlPATCH(stringInputStream("""
142 <yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
143 <patch-id>test patch id</patch-id>
145 <edit-id>create data</edit-id>
146 <operation>create</operation>
147 <target>/example-jukebox:jukebox</target>
149 <jukebox xmlns="http://example.com/ns/example-jukebox">
157 <edit-id>replace data</edit-id>
158 <operation>replace</operation>
159 <target>/example-jukebox:jukebox</target>
161 <jukebox xmlns="http://example.com/ns/example-jukebox">
169 <edit-id>delete data</edit-id>
170 <operation>delete</operation>
171 <target>/example-jukebox:jukebox/player/gap</target>
173 </yang-patch>"""), ar));
176 <yang-patch-status xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">\
177 <patch-id>test patch id</patch-id><ok/>\
178 </yang-patch-status>""", body::formatToXML);