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;
14 import static org.mockito.Mockito.mock;
16 import com.google.common.util.concurrent.Uninterruptibles;
17 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.DOMMountPointService;
34 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
35 import org.opendaylight.restconf.api.query.ChangedLeafNodesOnlyParam;
36 import org.opendaylight.restconf.api.query.ChildNodesOnlyParam;
37 import org.opendaylight.restconf.api.query.LeafNodesOnlyParam;
38 import org.opendaylight.restconf.api.query.SkipNotificationDataParam;
39 import org.opendaylight.restconf.nb.rfc8040.ReceiveEventsParams;
40 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
41 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
42 import org.opendaylight.restconf.nb.rfc8040.streams.RestconfStream.EncodingName;
43 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.PatchCont1Builder;
44 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice1.PatchCase1Builder;
45 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.PatchCase11Builder;
46 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.patch.case11.patch.sub.choice11.PatchSubCase11Builder;
47 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;
48 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
49 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchContBuilder;
50 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
51 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Builder;
52 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Key;
53 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev231103.NotificationOutputTypeGrouping.NotificationOutputType;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
56 import org.opendaylight.yangtools.yang.common.QName;
57 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
58 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
59 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
60 import org.skyscreamer.jsonassert.JSONAssert;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63 import org.xmlunit.assertj.XmlAssert;
65 public class DataTreeChangeStreamTest extends AbstractConcurrentDataBrokerTest {
66 private static final class TestHandler implements StreamSessionHandler {
67 private CountDownLatch notificationLatch = new CountDownLatch(1);
68 private volatile String lastNotification;
71 public void endOfStream() {
76 public void sendDataMessage(final String data) {
77 lastNotification = data;
78 notificationLatch.countDown();
81 void assertGot(final String json) throws Exception {
82 // FIXME: use awaitility
83 if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
84 fail("Timed out waiting for notification for: " + json);
87 LOG.info("lastNotification: {}", lastNotification);
88 final String withFakeDate = withFakeDate(lastNotification);
89 LOG.info("Comparing: \n{}\n{}", json, withFakeDate);
91 JSONAssert.assertEquals(json, withFakeDate, false);
92 lastNotification = null;
93 notificationLatch = new CountDownLatch(1);
96 void assertXmlSimilar(final String xml) throws Exception {
97 awaitUntilNotification(xml);
99 LOG.info("lastNotification: {}", lastNotification);
100 final String withFakeDate = withFakeXmlDate(lastNotification);
101 LOG.info("Comparing: \n{}\n{}", xml, withFakeDate);
103 XmlAssert.assertThat(xml).and(withFakeDate).ignoreWhitespace().ignoreChildNodesOrder().areSimilar();
104 lastNotification = null;
105 notificationLatch = new CountDownLatch(1);
108 String awaitUntilNotification(final String xml) {
109 // FIXME: use awaitility
110 if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
111 fail("Timed out waiting for notification for: " + xml);
113 return lastNotification;
117 private static final Logger LOG = LoggerFactory.getLogger(DataTreeChangeStreamTest.class);
119 private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json";
120 private static final String JSON_NOTIF_LEAVES_UPDATE = "/listener-adapter-test/notif-leaves-update.json";
121 private static final String JSON_NOTIF_LEAVES_DELETE = "/listener-adapter-test/notif-leaves-delete.json";
122 private static final String JSON_NOTIF_CHANGED_LEAVES_CREATE =
123 "/listener-adapter-test/notif-changed-leaves-create.json";
124 private static final String JSON_NOTIF_CHANGED_LEAVES_UPDATE =
125 "/listener-adapter-test/notif-changed-leaves-update.json";
126 private static final String JSON_NOTIF_CHANGED_LEAVES_DELETE =
127 "/listener-adapter-test/notif-changed-leaves-delete.json";
128 private static final String JSON_NOTIF_CHILD_NODES_ONLY_CREATE =
129 "/listener-adapter-test/notif-child-nodes-only-create.json";
130 private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
131 "/listener-adapter-test/notif-child-nodes-only-update1.json";
132 private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
133 "/listener-adapter-test/notif-child-nodes-only-update2.json";
134 private static final String JSON_NOTIF_CHILD_NODES_ONLY_DELETE =
135 "/listener-adapter-test/notif-child-nodes-only-delete.json";
137 private static final String XML_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.xml";
138 private static final String XML_NOTIF_LEAVES_UPDATE = "/listener-adapter-test/notif-leaves-update.xml";
139 private static final String XML_NOTIF_LEAVES_DELETE = "/listener-adapter-test/notif-leaves-delete.xml";
140 private static final String XML_NOTIF_CHANGED_LEAVES_CREATE =
141 "/listener-adapter-test/notif-changed-leaves-create.xml";
142 private static final String XML_NOTIF_CHANGED_LEAVES_UPDATE =
143 "/listener-adapter-test/notif-changed-leaves-update.xml";
144 private static final String XML_NOTIF_CHANGED_LEAVES_DELETE =
145 "/listener-adapter-test/notif-changed-leaves-delete.xml";
146 private static final String XML_NOTIF_CHILD_NODES_ONLY_CREATE =
147 "/listener-adapter-test/notif-child-nodes-only-create.xml";
148 private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
149 "/listener-adapter-test/notif-child-nodes-only-update1.xml";
150 private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
151 "/listener-adapter-test/notif-child-nodes-only-update2.xml";
152 private static final String XML_NOTIF_CHILD_NODES_ONLY_DELETE =
153 "/listener-adapter-test/notif-child-nodes-only-delete.xml";
155 private static final String JSON_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.json";
156 private static final String JSON_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.json";
157 private static final String JSON_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.json";
158 private static final String JSON_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.json";
159 private static final String JSON_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.json";
160 private static final String JSON_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.json";
161 private static final String JSON_NOTIF_WITHOUT_DATA_CONT_CREATE =
162 "/listener-adapter-test/notif-without-data-cont-create.json";
163 private static final String JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE =
164 "/listener-adapter-test/notif-without-data-cont-update.json";
165 private static final String JSON_NOTIF_WITHOUT_DATA_CONT_DELETE =
166 "/listener-adapter-test/notif-without-data-cont-delete.json";
167 private static final String JSON_NOTIF_WITHOUT_DATA_LIST_CREATE =
168 "/listener-adapter-test/notif-without-data-list-create.json";
169 private static final String JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE =
170 "/listener-adapter-test/notif-without-data-list-update.json";
171 private static final String JSON_NOTIF_WITHOUT_DATA_LIST_DELETE =
172 "/listener-adapter-test/notif-without-data-list-delete.json";
174 private static final String XML_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.xml";
175 private static final String XML_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.xml";
176 private static final String XML_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.xml";
177 private static final String XML_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.xml";
178 private static final String XML_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.xml";
179 private static final String XML_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.xml";
180 private static final String XML_NOTIF_WITHOUT_DATA_CONT_CREATE =
181 "/listener-adapter-test/notif-without-data-cont-create.xml";
182 private static final String XML_NOTIF_WITHOUT_DATA_CONT_UPDATE =
183 "/listener-adapter-test/notif-without-data-cont-update.xml";
184 private static final String XML_NOTIF_WITHOUT_DATA_CONT_DELETE =
185 "/listener-adapter-test/notif-without-data-cont-delete.xml";
186 private static final String XML_NOTIF_WITHOUT_DATA_LIST_CREATE =
187 "/listener-adapter-test/notif-without-data-list-create.xml";
188 private static final String XML_NOTIF_WITHOUT_DATA_LIST_UPDATE =
189 "/listener-adapter-test/notif-without-data-list-update.xml";
190 private static final String XML_NOTIF_WITHOUT_DATA_LIST_DELETE =
191 "/listener-adapter-test/notif-without-data-list-delete.xml";
193 private static final YangInstanceIdentifier PATCH_CONT_YIID = YangInstanceIdentifier.of(PatchCont.QNAME);
195 private static final YangInstanceIdentifier MY_LIST1_YIID = YangInstanceIdentifier.builder()
196 .node(PatchCont.QNAME)
198 .nodeWithKey(MyList1.QNAME, QName.create(PatchCont.QNAME.getModule(), "name"), "Althea")
201 private static EffectiveModelContext SCHEMA_CONTEXT;
203 private DataBroker dataBroker;
204 private DOMDataBroker domDataBroker;
205 private DatabindProvider databindProvider;
206 private ListenersBroker listenersBroker;
209 public static void beforeClass() {
210 SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/instanceidentifier/yang");
214 public static void afterClass() {
215 SCHEMA_CONTEXT = null;
219 public void setUp() throws Exception {
220 dataBroker = getDataBroker();
221 domDataBroker = getDomBroker();
222 databindProvider = () -> DatabindContext.ofModel(SCHEMA_CONTEXT);
223 listenersBroker = new ListenersBroker.ServerSentEvents(domDataBroker, mock(DOMNotificationService.class),
224 mock(DOMMountPointService.class));
227 TestHandler createHandler(final YangInstanceIdentifier path, final String streamName,
228 final NotificationOutputType outputType, final boolean leafNodesOnly, final boolean skipNotificationData,
229 final boolean changedLeafNodesOnly, final boolean childNodesOnly) throws Exception {
230 final var stream = listenersBroker.createStream("test", "baseURI",
231 new DataTreeChangeSource(databindProvider, domDataBroker, LogicalDatastoreType.CONFIGURATION, path))
233 final var handler = new TestHandler();
234 stream.addSubscriber(handler,
235 switch (outputType) {
236 case JSON -> EncodingName.RFC8040_JSON;
237 case XML -> EncodingName.RFC8040_XML;
239 new ReceiveEventsParams(null, null, null,
240 leafNodesOnly ? LeafNodesOnlyParam.of(true) : null,
241 skipNotificationData ? SkipNotificationDataParam.of(true) : null,
242 changedLeafNodesOnly ? ChangedLeafNodesOnlyParam.of(true) : null,
243 childNodesOnly ? ChildNodesOnlyParam.of(true) : null));
248 public void testJsonNotifsLeaves() throws Exception {
249 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
250 true, false, false, false);
252 var writeTransaction = dataBroker.newWriteOnlyTransaction();
253 final var iid = InstanceIdentifier.create(PatchCont.class);
254 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
255 .addAugmentation(new PatchCont1Builder()
256 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
257 .setLeaf1("AugmentLeaf")
259 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
261 writeTransaction.commit();
262 handler.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
264 writeTransaction = dataBroker.newWriteOnlyTransaction();
265 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
266 .addAugmentation(new PatchCont1Builder()
267 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
268 .setLeaf1("AugmentLeaf")
270 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
272 writeTransaction.commit();
273 handler.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
275 writeTransaction = dataBroker.newWriteOnlyTransaction();
276 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
277 writeTransaction.commit();
278 handler.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DELETE));
282 public void testJsonNotifsChangedLeaves() throws Exception {
283 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON, false, false, true,
286 var writeTransaction = dataBroker.newWriteOnlyTransaction();
287 final var iid = InstanceIdentifier.create(PatchCont.class);
288 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
289 .addAugmentation(new PatchCont1Builder()
290 .setPatchChoice2(new PatchCase11Builder()
291 .setPatchSubChoice11(new PatchSubCase11Builder()
292 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
295 .setLeaf1("AugmentLeaf")
297 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
299 writeTransaction.commit();
300 handler.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_CREATE));
302 writeTransaction = dataBroker.newWriteOnlyTransaction();
303 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
304 .addAugmentation(new PatchCont1Builder()
305 .setPatchChoice2(new PatchCase11Builder()
306 .setPatchSubChoice11(new PatchSubCase11Builder()
307 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
310 .setLeaf1("AugmentLeaf")
312 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
314 writeTransaction.commit();
315 handler.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_UPDATE));
317 writeTransaction = dataBroker.newWriteOnlyTransaction();
318 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
319 writeTransaction.commit();
320 handler.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_DELETE));
324 public void testJsonChildNodesOnly() throws Exception {
325 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON, false, false, false,
328 final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
329 var writeTransaction = dataBroker.newWriteOnlyTransaction();
330 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
331 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
332 writeTransaction.commit();
333 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_CREATE));
335 writeTransaction = dataBroker.newWriteOnlyTransaction();
336 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
337 new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
338 writeTransaction.commit();
339 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1));
341 writeTransaction = dataBroker.newWriteOnlyTransaction();
342 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
343 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
344 writeTransaction.commit();
345 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2));
347 writeTransaction = dataBroker.newWriteOnlyTransaction();
348 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
349 writeTransaction.commit();
350 handler.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_DELETE));
354 public void testXmlLeavesOnly() throws Exception {
355 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML, true, false, false,
358 var writeTransaction = dataBroker.newWriteOnlyTransaction();
359 final var iid = InstanceIdentifier.create(PatchCont.class);
360 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
361 .addAugmentation(new PatchCont1Builder()
362 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
363 .setLeaf1("AugmentLeaf")
365 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
367 writeTransaction.commit();
368 handler.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_CREATE));
370 writeTransaction = dataBroker.newWriteOnlyTransaction();
371 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
372 .addAugmentation(new PatchCont1Builder()
373 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
374 .setLeaf1("AugmentLeaf")
376 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
378 writeTransaction.commit();
379 handler.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_UPDATE));
381 writeTransaction = dataBroker.newWriteOnlyTransaction();
382 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
383 writeTransaction.commit();
385 // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
386 final String notification = handler.awaitUntilNotification("");
387 assertThat(notification, allOf(
388 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
389 + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
390 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
391 + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
392 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
393 + "/a:my-list1[a:name='Althea']/a:name</path>"),
394 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
395 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
396 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
397 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf1</path>")));
401 public void testXmlChangedLeavesOnly() throws Exception {
402 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML, false, false, true,
405 var writeTransaction = dataBroker.newWriteOnlyTransaction();
406 final var iid = InstanceIdentifier.create(PatchCont.class);
407 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
408 .addAugmentation(new PatchCont1Builder()
409 .setPatchChoice2(new PatchCase11Builder()
410 .setPatchSubChoice11(new PatchSubCase11Builder()
411 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
414 .setLeaf1("AugmentLeaf")
416 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
418 writeTransaction.commit();
419 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_CREATE));
421 writeTransaction = dataBroker.newWriteOnlyTransaction();
422 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
423 .addAugmentation(new PatchCont1Builder()
424 .setPatchChoice2(new PatchCase11Builder()
425 .setPatchSubChoice11(new PatchSubCase11Builder()
426 .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
429 .setLeaf1("AugmentLeaf")
431 .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
433 writeTransaction.commit();
434 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_UPDATE));
436 writeTransaction = dataBroker.newWriteOnlyTransaction();
437 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
438 writeTransaction.commit();
440 // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
441 final String notification = handler.awaitUntilNotification("");
442 assertThat(notification, allOf(
443 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
444 + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
445 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
446 + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
447 containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
448 + "/a:my-list1[a:name='Althea']/a:name</path>"),
449 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
450 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
451 containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
452 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf11</path>")));
456 public void testXmlChildNodesOnly() throws Exception {
457 final var handler = createHandler(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML, false, false, false,
460 final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
461 var writeTransaction = dataBroker.newWriteOnlyTransaction();
462 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
463 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
464 writeTransaction.commit();
465 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_CREATE));
467 writeTransaction = dataBroker.newWriteOnlyTransaction();
468 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
469 new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
470 writeTransaction.commit();
471 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE1));
473 writeTransaction = dataBroker.newWriteOnlyTransaction();
474 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
475 new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
476 writeTransaction.commit();
477 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE2));
479 writeTransaction = dataBroker.newWriteOnlyTransaction();
480 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
481 writeTransaction.commit();
482 handler.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_DELETE));
486 public void testJsonContNotifications() throws Exception {
487 jsonNotifications(PATCH_CONT_YIID, false, JSON_NOTIF_CONT_CREATE,
488 JSON_NOTIF_CONT_UPDATE, JSON_NOTIF_CONT_DELETE);
492 public void testJsonListNotifications() throws Exception {
493 jsonNotifications(MY_LIST1_YIID, false, JSON_NOTIF_LIST_CREATE,
494 JSON_NOTIF_LIST_UPDATE, JSON_NOTIF_LIST_DELETE);
498 public void testJsonContNotificationsWithoutData() throws Exception {
499 jsonNotifications(PATCH_CONT_YIID, true, JSON_NOTIF_WITHOUT_DATA_CONT_CREATE,
500 JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE, JSON_NOTIF_WITHOUT_DATA_CONT_DELETE);
504 public void testJsonListNotificationsWithoutData() throws Exception {
505 jsonNotifications(MY_LIST1_YIID, true, JSON_NOTIF_WITHOUT_DATA_LIST_CREATE,
506 JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE, JSON_NOTIF_WITHOUT_DATA_LIST_DELETE);
510 public void testXmlContNotifications() throws Exception {
511 xmlNotifications(PATCH_CONT_YIID, false, XML_NOTIF_CONT_CREATE,
512 XML_NOTIF_CONT_UPDATE, XML_NOTIF_CONT_DELETE);
516 public void testXmlListNotifications() throws Exception {
517 xmlNotifications(MY_LIST1_YIID, false, XML_NOTIF_LIST_CREATE,
518 XML_NOTIF_LIST_UPDATE, XML_NOTIF_LIST_DELETE);
522 public void testXmlContNotificationsWithoutData() throws Exception {
523 xmlNotifications(PATCH_CONT_YIID, true, XML_NOTIF_WITHOUT_DATA_CONT_CREATE,
524 XML_NOTIF_WITHOUT_DATA_CONT_UPDATE, XML_NOTIF_WITHOUT_DATA_CONT_DELETE);
528 public void testXmlListNotificationsWithoutData() throws Exception {
529 xmlNotifications(MY_LIST1_YIID, true, XML_NOTIF_WITHOUT_DATA_LIST_CREATE,
530 XML_NOTIF_WITHOUT_DATA_LIST_UPDATE, XML_NOTIF_WITHOUT_DATA_LIST_DELETE);
533 static String withFakeDate(final String in) throws JSONException {
534 final JSONObject doc = new JSONObject(in);
535 final JSONObject notification = doc.getJSONObject("ietf-restconf:notification");
536 if (notification == null) {
539 notification.put("event-time", "someDate");
540 return doc.toString();
543 static String withFakeXmlDate(final String in) {
544 return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
547 private String getNotifJson(final String path) throws IOException, URISyntaxException, JSONException {
548 return withFakeDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
551 private String getResultXml(final String path) throws IOException, URISyntaxException, JSONException {
552 return withFakeXmlDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
555 private void jsonNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
556 final String jsonNotifCreate, final String jsonNotifUpdate, final String jsonNotifDelete) throws Exception {
557 final var handler = createHandler(pathYiid, "Casey", NotificationOutputType.JSON, false, skipData, false,
560 var writeTransaction = dataBroker.newWriteOnlyTransaction();
561 var builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
562 final var iid = InstanceIdentifier.create(PatchCont.class)
563 .child(MyList1.class, new MyList1Key("Althea"));
564 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
565 writeTransaction.commit();
566 handler.assertGot(getNotifJson(jsonNotifCreate));
568 writeTransaction = dataBroker.newWriteOnlyTransaction();
569 builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
570 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
571 writeTransaction.commit();
572 handler.assertGot(getNotifJson(jsonNotifUpdate));
574 writeTransaction = dataBroker.newWriteOnlyTransaction();
575 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
576 writeTransaction.commit();
577 handler.assertGot(getNotifJson(jsonNotifDelete));
580 private void xmlNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
581 final String xmlNotifCreate, final String xmlNotifUpdate, final String xmlNotifDelete) throws Exception {
582 final var handler = createHandler(pathYiid, "Casey", NotificationOutputType.XML, false, skipData, false, false);
584 var writeTransaction = dataBroker.newWriteOnlyTransaction();
585 var builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
586 final var iid = InstanceIdentifier.create(PatchCont.class)
587 .child(MyList1.class, new MyList1Key("Althea"));
588 writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
589 writeTransaction.commit();
590 handler.assertXmlSimilar(getResultXml(xmlNotifCreate));
592 writeTransaction = dataBroker.newWriteOnlyTransaction();
593 builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
594 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
595 writeTransaction.commit();
596 handler.assertXmlSimilar(getResultXml(xmlNotifUpdate));
598 writeTransaction = dataBroker.newWriteOnlyTransaction();
599 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
600 writeTransaction.commit();
601 handler.assertXmlSimilar(getResultXml(xmlNotifDelete));