2 * Copyright (c) 2017 Red Hat, Inc. 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.rfc8040.streams;
10 import static org.hamcrest.CoreMatchers.allOf;
11 import static org.hamcrest.CoreMatchers.containsString;
12 import static org.hamcrest.MatcherAssert.assertThat;
13 import static org.junit.Assert.fail;
15 import com.google.common.util.concurrent.Uninterruptibles;
16 import java.io.IOException;
18 import java.net.URISyntaxException;
19 import java.nio.file.Files;
20 import java.nio.file.Paths;
21 import java.util.concurrent.CountDownLatch;
22 import java.util.concurrent.TimeUnit;
23 import org.json.JSONException;
24 import org.json.JSONObject;
25 import org.junit.AfterClass;
26 import org.junit.Before;
27 import org.junit.BeforeClass;
28 import org.junit.Test;
29 import org.opendaylight.mdsal.binding.api.DataBroker;
30 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest;
31 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
32 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
34 import org.opendaylight.restconf.api.query.ChangedLeafNodesOnlyParam;
35 import org.opendaylight.restconf.api.query.ChildNodesOnlyParam;
36 import org.opendaylight.restconf.api.query.LeafNodesOnlyParam;
37 import org.opendaylight.restconf.api.query.SkipNotificationDataParam;
38 import org.opendaylight.restconf.nb.rfc8040.ReceiveEventsParams;
39 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
40 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
41 import org.opendaylight.restconf.server.mdsal.MdsalRestconfStreamRegistry;
42 import org.opendaylight.restconf.server.mdsal.streams.dtcl.DataTreeChangeSource;
43 import org.opendaylight.restconf.server.spi.RestconfStream;
44 import org.opendaylight.restconf.server.spi.RestconfStream.EncodingName;
45 import org.opendaylight.restconf.server.spi.RestconfStream.Sender;
46 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.PatchCont1Builder;
47 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice1.PatchCase1Builder;
48 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.PatchCase11Builder;
49 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.patch.case11.patch.sub.choice11.PatchSubCase11Builder;
50 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.patch.case11.patch.sub.choice11.patch.sub.case11.patch.sub.sub.choice11.PatchSubSubCase11Builder;
51 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
52 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchContBuilder;
53 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
54 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Builder;
55 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Key;
56 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev231103.NotificationOutputTypeGrouping.NotificationOutputType;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
59 import org.opendaylight.yangtools.yang.common.QName;
60 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
61 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
62 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
63 import org.skyscreamer.jsonassert.JSONAssert;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66 import org.xmlunit.assertj.XmlAssert;
68 public class DataTreeChangeStreamTest extends AbstractConcurrentDataBrokerTest {
69 private static final class TestHandler implements Sender {
70 private CountDownLatch notificationLatch = new CountDownLatch(1);
71 private volatile String lastNotification;
74 public void endOfStream() {
79 public void sendDataMessage(final String data) {
80 lastNotification = data;
81 notificationLatch.countDown();
84 void assertGot(final String json) throws Exception {
85 // FIXME: use awaitility
86 if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
87 fail("Timed out waiting for notification for: " + json);
90 LOG.info("lastNotification: {}", lastNotification);
91 final String withFakeDate = withFakeDate(lastNotification);
92 LOG.info("Comparing: \n{}\n{}", json, withFakeDate);
94 JSONAssert.assertEquals(json, withFakeDate, false);
95 lastNotification = null;
96 notificationLatch = new CountDownLatch(1);
99 void assertXmlSimilar(final String xml) throws Exception {
100 awaitUntilNotification(xml);
102 LOG.info("lastNotification: {}", lastNotification);
103 final String withFakeDate = withFakeXmlDate(lastNotification);
104 LOG.info("Comparing: \n{}\n{}", xml, withFakeDate);
106 XmlAssert.assertThat(xml).and(withFakeDate).ignoreWhitespace().ignoreChildNodesOrder().areSimilar();
107 lastNotification = null;
108 notificationLatch = new CountDownLatch(1);
111 String awaitUntilNotification(final String xml) {
112 // FIXME: use awaitility
113 if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
114 fail("Timed out waiting for notification for: " + xml);
116 return lastNotification;
120 private static final Logger LOG = LoggerFactory.getLogger(DataTreeChangeStreamTest.class);
122 private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json";
123 private static final String JSON_NOTIF_LEAVES_UPDATE = "/listener-adapter-test/notif-leaves-update.json";
124 private static final String JSON_NOTIF_LEAVES_DELETE = "/listener-adapter-test/notif-leaves-delete.json";
125 private static final String JSON_NOTIF_CHANGED_LEAVES_CREATE =
126 "/listener-adapter-test/notif-changed-leaves-create.json";
127 private static final String JSON_NOTIF_CHANGED_LEAVES_UPDATE =
128 "/listener-adapter-test/notif-changed-leaves-update.json";
129 private static final String JSON_NOTIF_CHANGED_LEAVES_DELETE =
130 "/listener-adapter-test/notif-changed-leaves-delete.json";
131 private static final String JSON_NOTIF_CHILD_NODES_ONLY_CREATE =
132 "/listener-adapter-test/notif-child-nodes-only-create.json";
133 private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
134 "/listener-adapter-test/notif-child-nodes-only-update1.json";
135 private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
136 "/listener-adapter-test/notif-child-nodes-only-update2.json";
137 private static final String JSON_NOTIF_CHILD_NODES_ONLY_DELETE =
138 "/listener-adapter-test/notif-child-nodes-only-delete.json";
140 private static final String XML_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.xml";
141 private static final String XML_NOTIF_LEAVES_UPDATE = "/listener-adapter-test/notif-leaves-update.xml";
142 private static final String XML_NOTIF_LEAVES_DELETE = "/listener-adapter-test/notif-leaves-delete.xml";
143 private static final String XML_NOTIF_CHANGED_LEAVES_CREATE =
144 "/listener-adapter-test/notif-changed-leaves-create.xml";
145 private static final String XML_NOTIF_CHANGED_LEAVES_UPDATE =
146 "/listener-adapter-test/notif-changed-leaves-update.xml";
147 private static final String XML_NOTIF_CHANGED_LEAVES_DELETE =
148 "/listener-adapter-test/notif-changed-leaves-delete.xml";
149 private static final String XML_NOTIF_CHILD_NODES_ONLY_CREATE =
150 "/listener-adapter-test/notif-child-nodes-only-create.xml";
151 private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
152 "/listener-adapter-test/notif-child-nodes-only-update1.xml";
153 private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
154 "/listener-adapter-test/notif-child-nodes-only-update2.xml";
155 private static final String XML_NOTIF_CHILD_NODES_ONLY_DELETE =
156 "/listener-adapter-test/notif-child-nodes-only-delete.xml";
158 private static final String JSON_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.json";
159 private static final String JSON_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.json";
160 private static final String JSON_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.json";
161 private static final String JSON_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.json";
162 private static final String JSON_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.json";
163 private static final String JSON_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.json";
164 private static final String JSON_NOTIF_WITHOUT_DATA_CONT_CREATE =
165 "/listener-adapter-test/notif-without-data-cont-create.json";
166 private static final String JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE =
167 "/listener-adapter-test/notif-without-data-cont-update.json";
168 private static final String JSON_NOTIF_WITHOUT_DATA_CONT_DELETE =
169 "/listener-adapter-test/notif-without-data-cont-delete.json";
170 private static final String JSON_NOTIF_WITHOUT_DATA_LIST_CREATE =
171 "/listener-adapter-test/notif-without-data-list-create.json";
172 private static final String JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE =
173 "/listener-adapter-test/notif-without-data-list-update.json";
174 private static final String JSON_NOTIF_WITHOUT_DATA_LIST_DELETE =
175 "/listener-adapter-test/notif-without-data-list-delete.json";
177 private static final String XML_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.xml";
178 private static final String XML_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.xml";
179 private static final String XML_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.xml";
180 private static final String XML_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.xml";
181 private static final String XML_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.xml";
182 private static final String XML_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.xml";
183 private static final String XML_NOTIF_WITHOUT_DATA_CONT_CREATE =
184 "/listener-adapter-test/notif-without-data-cont-create.xml";
185 private static final String XML_NOTIF_WITHOUT_DATA_CONT_UPDATE =
186 "/listener-adapter-test/notif-without-data-cont-update.xml";
187 private static final String XML_NOTIF_WITHOUT_DATA_CONT_DELETE =
188 "/listener-adapter-test/notif-without-data-cont-delete.xml";
189 private static final String XML_NOTIF_WITHOUT_DATA_LIST_CREATE =
190 "/listener-adapter-test/notif-without-data-list-create.xml";
191 private static final String XML_NOTIF_WITHOUT_DATA_LIST_UPDATE =
192 "/listener-adapter-test/notif-without-data-list-update.xml";
193 private static final String XML_NOTIF_WITHOUT_DATA_LIST_DELETE =
194 "/listener-adapter-test/notif-without-data-list-delete.xml";
196 private static final YangInstanceIdentifier PATCH_CONT_YIID = YangInstanceIdentifier.of(PatchCont.QNAME);
198 private static final YangInstanceIdentifier MY_LIST1_YIID = YangInstanceIdentifier.builder()
199 .node(PatchCont.QNAME)
201 .nodeWithKey(MyList1.QNAME, QName.create(PatchCont.QNAME.getModule(), "name"), "Althea")
204 private static EffectiveModelContext SCHEMA_CONTEXT;
206 private DataBroker dataBroker;
207 private DOMDataBroker domDataBroker;
208 private DatabindProvider databindProvider;
209 private RestconfStream.Registry streamRegistry;
212 public static void beforeClass() {
213 SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/instanceidentifier/yang");
217 public static void afterClass() {
218 SCHEMA_CONTEXT = null;
222 public void setUp() throws Exception {
223 dataBroker = getDataBroker();
224 domDataBroker = getDomBroker();
225 databindProvider = () -> DatabindContext.ofModel(SCHEMA_CONTEXT);
226 streamRegistry = new MdsalRestconfStreamRegistry(domDataBroker);
229 TestHandler createHandler(final YangInstanceIdentifier path, final String streamName,
230 final NotificationOutputType outputType, final boolean leafNodesOnly, final boolean skipNotificationData,
231 final boolean changedLeafNodesOnly, final boolean childNodesOnly) throws Exception {
232 final var stream = streamRegistry.createStream(URI.create("baseURI"),
233 new DataTreeChangeSource(databindProvider,
234 domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class),
235 LogicalDatastoreType.CONFIGURATION, path), "test")
237 final var handler = new TestHandler();
238 stream.addSubscriber(handler,
239 switch (outputType) {
240 case JSON -> EncodingName.RFC8040_JSON;
241 case XML -> EncodingName.RFC8040_XML;
243 new ReceiveEventsParams(null, null, null,
244 leafNodesOnly ? LeafNodesOnlyParam.of(true) : null,
245 skipNotificationData ? SkipNotificationDataParam.of(true) : null,
246 changedLeafNodesOnly ? ChangedLeafNodesOnlyParam.of(true) : null,
247 childNodesOnly ? ChildNodesOnlyParam.of(true) : null));
252 public void testJsonNotifsLeaves() throws Exception {
253 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
254 true, false, false, false);
256 var writeTransaction = dataBroker.newWriteOnlyTransaction();
257 final var iid = InstanceIdentifier.create(PatchCont.class);
258 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
259 .addAugmentation(new PatchCont1Builder()
260 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
261 .setLeaf1("AugmentLeaf")
263 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
265 writeTransaction.commit();
266 handler.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
268 writeTransaction = dataBroker.newWriteOnlyTransaction();
269 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
270 .addAugmentation(new PatchCont1Builder()
271 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
272 .setLeaf1("AugmentLeaf")
274 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
276 writeTransaction.commit();
277 handler.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
279 writeTransaction = dataBroker.newWriteOnlyTransaction();
280 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
281 writeTransaction.commit();
282 handler.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DELETE));
286 public void testJsonNotifsChangedLeaves() throws Exception {
287 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON, false, false, true,
290 var writeTransaction = dataBroker.newWriteOnlyTransaction();
291 final var iid = InstanceIdentifier.create(PatchCont.class);
292 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
293 .addAugmentation(new PatchCont1Builder()
294 .setPatchChoice2(new PatchCase11Builder()
295 .setPatchSubChoice11(new PatchSubCase11Builder()
296 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
299 .setLeaf1("AugmentLeaf")
301 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
303 writeTransaction.commit();
304 handler.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_CREATE));
306 writeTransaction = dataBroker.newWriteOnlyTransaction();
307 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
308 .addAugmentation(new PatchCont1Builder()
309 .setPatchChoice2(new PatchCase11Builder()
310 .setPatchSubChoice11(new PatchSubCase11Builder()
311 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
314 .setLeaf1("AugmentLeaf")
316 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
318 writeTransaction.commit();
319 handler.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_UPDATE));
321 writeTransaction = dataBroker.newWriteOnlyTransaction();
322 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
323 writeTransaction.commit();
324 handler.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_DELETE));
328 public void testJsonChildNodesOnly() throws Exception {
329 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON, false, false, false,
332 final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
333 var writeTransaction = dataBroker.newWriteOnlyTransaction();
334 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
335 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
336 writeTransaction.commit();
337 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_CREATE));
339 writeTransaction = dataBroker.newWriteOnlyTransaction();
340 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
341 new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
342 writeTransaction.commit();
343 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1));
345 writeTransaction = dataBroker.newWriteOnlyTransaction();
346 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
347 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
348 writeTransaction.commit();
349 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2));
351 writeTransaction = dataBroker.newWriteOnlyTransaction();
352 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
353 writeTransaction.commit();
354 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_DELETE));
358 public void testXmlLeavesOnly() throws Exception {
359 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML, true, false, false,
362 var writeTransaction = dataBroker.newWriteOnlyTransaction();
363 final var iid = InstanceIdentifier.create(PatchCont.class);
364 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
365 .addAugmentation(new PatchCont1Builder()
366 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
367 .setLeaf1("AugmentLeaf")
369 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
371 writeTransaction.commit();
372 handler.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_CREATE));
374 writeTransaction = dataBroker.newWriteOnlyTransaction();
375 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
376 .addAugmentation(new PatchCont1Builder()
377 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
378 .setLeaf1("AugmentLeaf")
380 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
382 writeTransaction.commit();
383 handler.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_UPDATE));
385 writeTransaction = dataBroker.newWriteOnlyTransaction();
386 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
387 writeTransaction.commit();
389 // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
390 final String notification = handler.awaitUntilNotification("");
391 assertThat(notification, allOf(
392 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
393 + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
394 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
395 + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
396 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
397 + "/a:my-list1[a:name='Althea']/a:name</path>"),
398 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
399 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
400 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
401 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf1</path>")));
405 public void testXmlChangedLeavesOnly() throws Exception {
406 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML, false, false, true,
409 var writeTransaction = dataBroker.newWriteOnlyTransaction();
410 final var iid = InstanceIdentifier.create(PatchCont.class);
411 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
412 .addAugmentation(new PatchCont1Builder()
413 .setPatchChoice2(new PatchCase11Builder()
414 .setPatchSubChoice11(new PatchSubCase11Builder()
415 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
418 .setLeaf1("AugmentLeaf")
420 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
422 writeTransaction.commit();
423 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_CREATE));
425 writeTransaction = dataBroker.newWriteOnlyTransaction();
426 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
427 .addAugmentation(new PatchCont1Builder()
428 .setPatchChoice2(new PatchCase11Builder()
429 .setPatchSubChoice11(new PatchSubCase11Builder()
430 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
433 .setLeaf1("AugmentLeaf")
435 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
437 writeTransaction.commit();
438 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_UPDATE));
440 writeTransaction = dataBroker.newWriteOnlyTransaction();
441 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
442 writeTransaction.commit();
444 // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
445 final String notification = handler.awaitUntilNotification("");
446 assertThat(notification, allOf(
447 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
448 + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
449 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
450 + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
451 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
452 + "/a:my-list1[a:name='Althea']/a:name</path>"),
453 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
454 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
455 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
456 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf11</path>")));
460 public void testXmlChildNodesOnly() throws Exception {
461 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML, false, false, false,
464 final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
465 var writeTransaction = dataBroker.newWriteOnlyTransaction();
466 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
467 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
468 writeTransaction.commit();
469 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_CREATE));
471 writeTransaction = dataBroker.newWriteOnlyTransaction();
472 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
473 new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
474 writeTransaction.commit();
475 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE1));
477 writeTransaction = dataBroker.newWriteOnlyTransaction();
478 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
479 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
480 writeTransaction.commit();
481 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE2));
483 writeTransaction = dataBroker.newWriteOnlyTransaction();
484 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
485 writeTransaction.commit();
486 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_DELETE));
490 public void testJsonContNotifications() throws Exception {
491 jsonNotifications(PATCH_CONT_YIID, false, JSON_NOTIF_CONT_CREATE,
492 JSON_NOTIF_CONT_UPDATE, JSON_NOTIF_CONT_DELETE);
496 public void testJsonListNotifications() throws Exception {
497 jsonNotifications(MY_LIST1_YIID, false, JSON_NOTIF_LIST_CREATE,
498 JSON_NOTIF_LIST_UPDATE, JSON_NOTIF_LIST_DELETE);
502 public void testJsonContNotificationsWithoutData() throws Exception {
503 jsonNotifications(PATCH_CONT_YIID, true, JSON_NOTIF_WITHOUT_DATA_CONT_CREATE,
504 JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE, JSON_NOTIF_WITHOUT_DATA_CONT_DELETE);
508 public void testJsonListNotificationsWithoutData() throws Exception {
509 jsonNotifications(MY_LIST1_YIID, true, JSON_NOTIF_WITHOUT_DATA_LIST_CREATE,
510 JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE, JSON_NOTIF_WITHOUT_DATA_LIST_DELETE);
514 public void testXmlContNotifications() throws Exception {
515 xmlNotifications(PATCH_CONT_YIID, false, XML_NOTIF_CONT_CREATE,
516 XML_NOTIF_CONT_UPDATE, XML_NOTIF_CONT_DELETE);
520 public void testXmlListNotifications() throws Exception {
521 xmlNotifications(MY_LIST1_YIID, false, XML_NOTIF_LIST_CREATE,
522 XML_NOTIF_LIST_UPDATE, XML_NOTIF_LIST_DELETE);
526 public void testXmlContNotificationsWithoutData() throws Exception {
527 xmlNotifications(PATCH_CONT_YIID, true, XML_NOTIF_WITHOUT_DATA_CONT_CREATE,
528 XML_NOTIF_WITHOUT_DATA_CONT_UPDATE, XML_NOTIF_WITHOUT_DATA_CONT_DELETE);
532 public void testXmlListNotificationsWithoutData() throws Exception {
533 xmlNotifications(MY_LIST1_YIID, true, XML_NOTIF_WITHOUT_DATA_LIST_CREATE,
534 XML_NOTIF_WITHOUT_DATA_LIST_UPDATE, XML_NOTIF_WITHOUT_DATA_LIST_DELETE);
537 static String withFakeDate(final String in) throws JSONException {
538 final JSONObject doc = new JSONObject(in);
539 final JSONObject notification = doc.getJSONObject("ietf-restconf:notification");
540 if (notification == null) {
543 notification.put("event-time", "someDate");
544 return doc.toString();
547 static String withFakeXmlDate(final String in) {
548 return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
551 private String getNotifJson(final String path) throws IOException, URISyntaxException, JSONException {
552 return withFakeDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
555 private String getResultXml(final String path) throws IOException, URISyntaxException, JSONException {
556 return withFakeXmlDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
559 private void jsonNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
560 final String jsonNotifCreate, final String jsonNotifUpdate, final String jsonNotifDelete) throws Exception {
561 final var handler = createHandler(pathYiid, "Casey", NotificationOutputType.JSON, false, skipData, false,
564 var writeTransaction = dataBroker.newWriteOnlyTransaction();
565 var builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
566 final var iid = InstanceIdentifier.create(PatchCont.class)
567 .child(MyList1.class, new MyList1Key("Althea"));
568 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
569 writeTransaction.commit();
570 handler.assertGot(getNotifJson(jsonNotifCreate));
572 writeTransaction = dataBroker.newWriteOnlyTransaction();
573 builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
574 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
575 writeTransaction.commit();
576 handler.assertGot(getNotifJson(jsonNotifUpdate));
578 writeTransaction = dataBroker.newWriteOnlyTransaction();
579 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
580 writeTransaction.commit();
581 handler.assertGot(getNotifJson(jsonNotifDelete));
584 private void xmlNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
585 final String xmlNotifCreate, final String xmlNotifUpdate, final String xmlNotifDelete) throws Exception {
586 final var handler = createHandler(pathYiid, "Casey", NotificationOutputType.XML, false, skipData, false, false);
588 var writeTransaction = dataBroker.newWriteOnlyTransaction();
589 var builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
590 final var iid = InstanceIdentifier.create(PatchCont.class)
591 .child(MyList1.class, new MyList1Key("Althea"));
592 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
593 writeTransaction.commit();
594 handler.assertXmlSimilar(getResultXml(xmlNotifCreate));
596 writeTransaction = dataBroker.newWriteOnlyTransaction();
597 builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
598 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
599 writeTransaction.commit();
600 handler.assertXmlSimilar(getResultXml(xmlNotifUpdate));
602 writeTransaction = dataBroker.newWriteOnlyTransaction();
603 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
604 writeTransaction.commit();
605 handler.assertXmlSimilar(getResultXml(xmlNotifDelete));