From: Jakub Toth Date: Fri, 14 Oct 2016 08:43:36 +0000 (+0200) Subject: Bug 4883 - implement query parameter - filter X-Git-Tag: release/carbon~125 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=ddc71443d58ac78f8a593be88e181310f1e4b9c8;p=netconf.git Bug 4883 - implement query parameter - filter * added test Change-Id: I75ace69452c31b29460727f36c66cf397f44e26e Signed-off-by: Jakub Toth --- diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java index a7e2161702..eff4885f63 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java @@ -1051,8 +1051,10 @@ public class RestconfImpl implements RestconfService { public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { boolean startTime_used = false; boolean stopTime_used = false; + boolean filter_used = false; Date start = null; Date stop = null; + String filter = null; for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { switch (entry.getKey()) { @@ -1072,6 +1074,14 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Stop-time parameter can be used only once."); } break; + case "filter": + if (!filter_used) { + filter_used = true; + filter = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Filter parameter can be used only once."); + } + break; default: throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey()); } @@ -1081,9 +1091,9 @@ public class RestconfImpl implements RestconfService { } URI response = null; if (identifier.contains(DATA_SUBSCR)) { - response = dataSubs(identifier, uriInfo, start, stop); + response = dataSubs(identifier, uriInfo, start, stop, filter); } else if (identifier.contains(NOTIFICATION_STREAM)) { - response = notifStream(identifier, uriInfo, start, stop); + response = notifStream(identifier, uriInfo, start, stop, filter); } if(response != null){ @@ -1161,9 +1171,12 @@ public class RestconfImpl implements RestconfService { * - stop-time of getting notification * @param start * - start-time of getting notification - * @return {@link Response} + * @param filter + * - indicate wh ich subset of allpossible events are of interest + * @return {@link URI} of location */ - private URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop) { + private URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, + final String filter) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -1176,7 +1189,7 @@ public class RestconfImpl implements RestconfService { for (final NotificationListenerAdapter listener : listeners) { this.broker.registerToListenNotification(listener); - listener.setTime(start, stop); + listener.setQueryParams(start, stop, filter); } final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); @@ -1204,9 +1217,12 @@ public class RestconfImpl implements RestconfService { * - start-time of getting notification * @param start * - stop-time of getting notification - * @return {@link Response} + * @param filter + * - indicate which subset of all possible events are of interest + * @return {@link URI} of location */ - private URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop) { + private URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, + final String filter) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -1216,7 +1232,7 @@ public class RestconfImpl implements RestconfService { if (listener == null) { throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } - listener.setTimer(start, stop); + listener.setQueryParams(start, stop, filter); final Map paramToValues = resolveValuesFromUri(identifier); final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class, diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java index 0f784d11b4..ccc30a5616 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java @@ -17,12 +17,12 @@ import io.netty.util.internal.ConcurrentSet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Random; @@ -42,6 +42,9 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; import org.json.JSONObject; import org.json.XML; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; @@ -63,6 +66,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWrit import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; @@ -70,6 +74,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.xml.sax.InputSource; /** * {@link ListenerAdapter} is responsible to track events, which occurred by changing data in data source. @@ -92,6 +97,7 @@ public class ListenerAdapter implements DOMDataChangeListener { private final NotificationOutputType outputType; private Date start = null; private Date stop = null; + private String filter = null; /** * Creates new {@link ListenerAdapter} listener specified by path and stream @@ -121,7 +127,7 @@ public class ListenerAdapter implements DOMDataChangeListener { final Date now = new Date(); if (this.stop != null) { if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) { - prepareAndPostData(change); + checkFilter(change); } if (this.stop.compareTo(now) < 0) { try { @@ -133,17 +139,57 @@ public class ListenerAdapter implements DOMDataChangeListener { } else if (this.start != null) { if (this.start.compareTo(now) < 0) { this.start = null; - prepareAndPostData(change); + checkFilter(change); } } else { - prepareAndPostData(change); + checkFilter(change); } } - private void prepareAndPostData(final AsyncDataChangeEvent> change) { - if (!change.getCreatedData().isEmpty() || !change.getUpdatedData().isEmpty() - || !change.getRemovedPaths().isEmpty()) { - final String xml = prepareXmlFrom(change); + /** + * Check if is filter used and then prepare and post data do client + * + * @param change + * - data of notification + */ + private void checkFilter(final AsyncDataChangeEvent> change) { + final String xml = prepareXmlFrom(change); + if (this.filter == null) { + prepareAndPostData(xml); + } else { + try { + if (parseFilterParam(xml)) { + prepareAndPostData(xml); + } + } catch (final Exception e) { + throw new RestconfDocumentedException("Problem while parsing filter.", e); + } + } + } + + /** + * Parse and evaluate filter value by xml + * + * @param xml + * - notification data in xml + * @return true or false - depends on filter expression and data of + * notifiaction + * @throws Exception + */ + private boolean parseFilterParam(final String xml) throws Exception { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder builder = factory.newDocumentBuilder(); + final Document docOfXml = builder.parse(new InputSource(new StringReader(xml))); + final XPath xPath = XPathFactory.newInstance().newXPath(); + return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN); + } + + /** + * Prepare data of notification and data to client + * + * @param xml + */ + private void prepareAndPostData(final String xml) { final Event event = new Event(EventType.NOTIFY); if (this.outputType.equals(NotificationOutputType.JSON)) { final JSONObject jsonObject = XML.toJSONObject(xml); @@ -152,7 +198,6 @@ public class ListenerAdapter implements DOMDataChangeListener { event.setData(xml); } this.eventBus.post(event); - } } /** @@ -499,24 +544,21 @@ public class ListenerAdapter implements DOMDataChangeListener { * {@link Element} */ private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element) { - // Map< key = namespace, value = prefix> - final Map prefixes = new HashMap<>(); final YangInstanceIdentifier normalizedPath = ControllerContext.getInstance().toXpathRepresentation(path); final StringBuilder textContent = new StringBuilder(); - // FIXME: BUG-1281: this is duplicated code from yangtools (BUG-1275) for (final PathArgument pathArgument : normalizedPath.getPathArguments()) { if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) { continue; } textContent.append("/"); - writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes); + writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType()); if (pathArgument instanceof NodeIdentifierWithPredicates) { final Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); for (final QName keyValue : predicates.keySet()) { final String predicateValue = String.valueOf(predicates.get(keyValue)); textContent.append("["); - writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes); + writeIdentifierWithNamespacePrefix(element, textContent, keyValue); textContent.append("='"); textContent.append(predicateValue); textContent.append("'"); @@ -541,21 +583,13 @@ public class ListenerAdapter implements DOMDataChangeListener { * StringBuilder * @param qName * QName - * @param prefixes - * Map of namespaces and prefixes. */ private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, - final QName qName, final Map prefixes) { - final String namespace = qName.getNamespace().toString(); - String prefix = prefixes.get(namespace); - if (prefix == null) { - prefix = generateNewPrefix(prefixes.values()); - } - - element.setAttribute("xmlns:" + prefix, namespace); - textContent.append(prefix); - prefixes.put(namespace, prefix); + final QName qName) { + final Module module = ControllerContext.getInstance().getGlobalSchema() + .findModuleByNamespaceAndRevision(qName.getNamespace(), qName.getRevision()); + textContent.append(module.getName()); textContent.append(":"); textContent.append(qName.getLocalName()); } @@ -701,10 +735,13 @@ public class ListenerAdapter implements DOMDataChangeListener { * - start-time of getting notification * @param stop * - stop-time of getting notification + * @param filter + * - indicate which subset of all possible events are of interest */ - public void setTimer(final Date start, final Date stop) { + public void setQueryParams(final Date start, final Date stop, final String filter) { this.start = start; this.stop = stop; + this.filter = filter; } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java index 303bfb30b5..1d32b39e23 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java @@ -18,11 +18,14 @@ import io.netty.util.internal.ConcurrentSet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Date; import java.util.Set; import java.util.concurrent.Executors; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -33,6 +36,9 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; import org.json.JSONObject; import org.json.XML; import org.opendaylight.controller.md.sal.dom.api.DOMNotification; @@ -56,6 +62,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.xml.sax.InputSource; /** * {@link NotificationListenerAdapter} is responsible to track events on @@ -77,6 +84,7 @@ public class NotificationListenerAdapter implements DOMNotificationListener { private final String outputType; private Date start = null; private Date stop = null; + private String filter; /** * Set path of listener and stream name, register event bus. @@ -104,7 +112,7 @@ public class NotificationListenerAdapter implements DOMNotificationListener { final Date now = new Date(); if (this.stop != null) { if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) { - prepareAndPostData(notification); + checkFilter(notification); } if (this.stop.compareTo(now) < 0) { try { @@ -116,18 +124,57 @@ public class NotificationListenerAdapter implements DOMNotificationListener { } else if (this.start != null) { if (this.start.compareTo(now) < 0) { this.start = null; - prepareAndPostData(notification); + checkFilter(notification); } } else { - prepareAndPostData(notification); + checkFilter(notification); } } /** + * Check if is filter used and then prepare and post data do client + * * @param notification + * - data of notification */ - private void prepareAndPostData(final DOMNotification notification) { + private void checkFilter(final DOMNotification notification) { final String xml = prepareXmlFrom(notification); + if (this.filter == null) { + prepareAndPostData(xml); + } else { + try { + if (parseFilterParam(xml)) { + prepareAndPostData(xml); + } + } catch (final Exception e) { + throw new RestconfDocumentedException("Problem while parsing filter.", e); + } + } + } + + /** + * Parse and evaluate filter value by xml + * + * @param xml + * - notification data in xml + * @return true or false - depends on filter expression and data of + * notifiaction + * @throws Exception + */ + private boolean parseFilterParam(final String xml) throws Exception { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder builder = factory.newDocumentBuilder(); + final Document docOfXml = builder.parse(new InputSource(new StringReader(xml))); + final XPath xPath = XPathFactory.newInstance().newXPath(); + return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN); + } + + /** + * Prepare data of notification and data to client + * + * @param xml + */ + private void prepareAndPostData(final String xml) { final Event event = new Event(EventType.NOTIFY); if (this.outputType.equals("JSON")) { final JSONObject jsonObject = XML.toJSONObject(xml); @@ -423,9 +470,12 @@ public class NotificationListenerAdapter implements DOMNotificationListener { * - start-time of getting notification * @param stop * - stop-time of getting notification + * @param filter + * - indicate which subset of all possible events are of interest */ - public void setTime(final Date start, final Date stop) { + public void setQueryParams(final Date start, final Date stop, final String filter) { this.start = start; this.stop = stop; + this.filter = filter; } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java index 6b0cbe3c2b..9852330a0e 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java @@ -56,8 +56,10 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { boolean startTime_used = false; boolean stopTime_used = false; + boolean filter_used = false; Date start = null; Date stop = null; + String filter = null; for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { switch (entry.getKey()) { @@ -77,6 +79,12 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu throw new RestconfDocumentedException("Stop-time parameter can be used only once."); } break; + case "filter": + if (!filter_used) { + filter_used = true; + filter = entry.getValue().iterator().next(); + } + break; default: throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey()); } @@ -86,10 +94,11 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu } URI response = null; if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) { - response = SubscribeToStreamUtil.dataSubs(identifier, uriInfo, start, stop, this.domDataBrokerHandler); + response = + SubscribeToStreamUtil.dataSubs(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter); } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) { response = SubscribeToStreamUtil.notifStream(identifier, uriInfo, start, stop, - this.notificationServiceHandler); + this.notificationServiceHandler, filter); } if (response != null) { diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java index 0904b02e46..f6d45e5625 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java @@ -153,10 +153,12 @@ public final class SubscribeToStreamUtil { * - stop-time query parameter * @param notifiServiceHandler * - DOMNotificationService handler for register listeners + * @param filter + * - indicate which subset of all possible events are of interest * @return location for listening */ public static URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, - final NotificationServiceHandler notifiServiceHandler) { + final NotificationServiceHandler notifiServiceHandler, final String filter) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -169,7 +171,7 @@ public final class SubscribeToStreamUtil { for (final NotificationListenerAdapter listener : listeners) { registerToListenNotification(listener, notifiServiceHandler); - listener.setTime(start, stop); + listener.setQueryParams(start, stop, filter); } final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); @@ -232,10 +234,12 @@ public final class SubscribeToStreamUtil { * - stop-time query parameter * @param domDataBrokerHandler * - DOMDataBroker handler for register listener + * @param filter + * - indicate which subset of all possible events are of interest * @return location for listening */ public static URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, - final DOMDataBrokerHandler domDataBrokerHandler) { + final DOMDataBrokerHandler domDataBrokerHandler, final String filter) { final Map mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier); final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class, @@ -259,7 +263,7 @@ public final class SubscribeToStreamUtil { final ListenerAdapter listener = Notificator.getListenerFor(streamName); Preconditions.checkNotNull(listener, "Listener doesn't exist : " + streamName); - listener.setTimer(start, stop); + listener.setQueryParams(start, stop, filter); SubscribeToStreamUtil.registration(ds, scope, listener, domDataBrokerHandler.get()); diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java new file mode 100644 index 0000000000..61cd81d9e2 --- /dev/null +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.lang.reflect.Method; +import java.util.Collection; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class ExpressionParserTest { + + private Collection xmls; + + @Before + public void setup() throws Exception { + this.xmls = TestRestconfUtils.loadFiles("/notifications/xml/output/"); + } + + @Test + public void trueDownFilterTest() throws Exception { + final boolean parser = + parser("notification/data-changed-notification/data-change-event/data/toasterStatus='down'", + "data_change_notification_toaster_status_DOWN.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseDownFilterTest() throws Exception { + final boolean parser = + parser("notification/data-changed-notification/data-change-event/data/toasterStatus='up'", + "data_change_notification_toaster_status_DOWN.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberEqualsFilterTest() throws Exception { + final boolean parser = parser( + "notification/data-changed-notification/data-change-event/data/toasterStatus=1", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus=0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberLessFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<2", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberLessFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberLessEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=2", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberLessEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=-1", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberGreaterFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberGreaterFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>5", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberGreaterEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberGreaterEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=5", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + private boolean parser(final String filter, final String fileName) throws Exception { + File xml = null; + for (final File file : this.xmls) { + if (file.getName().equals(fileName)) { + xml = file; + } + } + final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class); + final ListenerAdapter listener = Notificator.createListener(path, "streamName", NotificationOutputType.JSON); + listener.setQueryParams(null, null, filter); + final Method m = listener.getClass().getDeclaredMethod("parseFilterParam", String.class); + m.setAccessible(true); + + return (boolean) m.invoke(listener, readFile(xml)); + } + + private String readFile(final File xml) throws Exception { + final BufferedReader br = new BufferedReader(new FileReader(xml)); + try { + final StringBuilder sb = new StringBuilder(); + String line = br.readLine(); + + while (line != null) { + sb.append(line); + sb.append("\n"); + line = br.readLine(); + } + return sb.toString(); + } finally { + br.close(); + } + } + +} diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml b/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml new file mode 100644 index 0000000000..43babd2c39 --- /dev/null +++ b/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml @@ -0,0 +1,13 @@ + + + 2016-11-10T04:45:31+01:00 + + + /toaster:toaster/toaster:toasterStatus + updated + + down + + + + \ No newline at end of file diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml b/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml new file mode 100644 index 0000000000..8a4a866055 --- /dev/null +++ b/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml @@ -0,0 +1,13 @@ + + + 2016-11-10T04:45:31+01:00 + + + /toaster:toaster/toaster:toasterStatus + updated + + 1 + + + + \ No newline at end of file