summaryrefslogtreecommitdiffstats
path: root/policy-management
diff options
context:
space:
mode:
authorJorge Hernandez <jorge.hernandez-herrero@att.com>2019-02-22 13:25:42 +0000
committerGerrit Code Review <gerrit@onap.org>2019-02-22 13:25:42 +0000
commitc150cf1a2327696e289c3a80ef28842f014b1866 (patch)
tree7cfa7224433a6841573e7942efdf199d007adebc /policy-management
parentd914823eb7a992cd3410072970a2643c7d0c432c (diff)
parentfa2a5a43c82cd35cca9e7d4b51f83ce70e1e3e59 (diff)
Merge "Add Nested JSON Filtering"
Diffstat (limited to 'policy-management')
-rw-r--r--policy-management/pom.xml5
-rw-r--r--policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java78
-rw-r--r--policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java388
-rw-r--r--policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java31
-rw-r--r--policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java314
-rw-r--r--policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java317
-rw-r--r--policy-management/src/test/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolsetTest.java45
7 files changed, 349 insertions, 829 deletions
diff --git a/policy-management/pom.xml b/policy-management/pom.xml
index 81edf7a0..d48d3ffb 100644
--- a/policy-management/pom.xml
+++ b/policy-management/pom.xml
@@ -241,6 +241,11 @@
<artifactId>gson-javatime-serialisers</artifactId>
<version>1.1.1</version>
</dependency>
+
+ <dependency>
+ <groupId>com.jayway.jsonpath</groupId>
+ <artifactId>json-path</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java
index 24fc6de9..16daa947 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java
@@ -7,9 +7,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
-
import org.onap.policy.common.endpoints.event.comm.Topic;
import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
import org.onap.policy.common.endpoints.event.comm.TopicSink;
@@ -38,7 +37,6 @@ import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
-import org.onap.policy.drools.utils.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,11 +48,11 @@ public interface DroolsControllerFactory {
/**
* Constructs a Drools Controller based on properties.
- *
+ *
* @param properties properties containing initialization parameters
* @param eventSources list of event sources
* @param eventSinks list of event sinks
- *
+ *
* @return the instantiated Drools Controller
* @throws IllegalArgumentException with invalid parameters
* @throws LinkageError Failure to link rules and models in Drools Libraries
@@ -64,13 +62,13 @@ public interface DroolsControllerFactory {
/**
* Explicit construction of a Drools Controller.
- *
+ *
* @param groupId maven group id of drools artifact
* @param artifactId maven artifact id of drools artifact
* @param version maven version id of drools artifact
* @param decoderConfigurations list of decoder configurations
* @param encoderConfigurations list of encoder configurations
- *
+ *
* @return the instantiated Drools Controller
* @throws IllegalArgumentException with invalid parameters
* @throws LinkageError Failure to link rules and models in Drools Libraries
@@ -81,7 +79,7 @@ public interface DroolsControllerFactory {
/**
* Releases the Drools Controller from operation.
- *
+ *
* @param controller the Drools Controller to shut down
*/
public void shutdown(DroolsController controller);
@@ -93,7 +91,7 @@ public interface DroolsControllerFactory {
/**
* Destroys and releases resources for a Drools Controller.
- *
+ *
* @param controller the Drools Controller to destroy
*/
public void destroy(DroolsController controller);
@@ -105,11 +103,11 @@ public interface DroolsControllerFactory {
/**
* Gets the Drools Controller associated with the maven group and artifact id.
- *
+ *
* @param groupId maven group id of drools artifact
* @param artifactId maven artifact id of drools artifact
* @param version maven version id of drools artifact
- *
+ *
* @return the Drools Controller
* @throws IllegalArgumentException with invalid parameters
*/
@@ -117,7 +115,7 @@ public interface DroolsControllerFactory {
/**
* returns the current inventory of Drools Controllers.
- *
+ *
* @return a list of Drools Controllers
*/
public List<DroolsController> inventory();
@@ -208,7 +206,7 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory {
/*
* The Null Drools Controller for no maven coordinates is always here so when no
* coordinates present, this is the return point
- *
+ *
* assert (controllerCopy instanceof NullDroolsController)
*/
if (droolsControllers.containsKey(controllerId)) {
@@ -245,7 +243,7 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory {
/**
* find out decoder classes and filters.
- *
+ *
* @param properties properties with information about decoders
* @param topicEntities topic sources
* @return list of topics, each with associated decoder classes, each with a list of associated
@@ -313,7 +311,7 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory {
// 3. second the list of classes associated with each topic
String eventClasses = properties
- .getProperty(propertyTopicEntityPrefix + firstTopic
+ .getProperty(propertyTopicEntityPrefix + firstTopic
+ PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX);
if (eventClasses == null || eventClasses.isEmpty()) {
@@ -328,54 +326,14 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory {
for (String theClass : topicClasses) {
- // 4. third, for each coder class, get the list of field filters
+ // 4. third, for each coder class, get the filter expression
String filter = properties
- .getProperty(propertyTopicEntityPrefix + firstTopic
+ .getProperty(propertyTopicEntityPrefix + firstTopic
+ PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX
+ "." + theClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX);
- List<Pair<String, String>> filters = new ArrayList<>();
-
- if (filter == null || filter.isEmpty()) {
- // 4. topic -> class -> with no filters
-
- JsonProtocolFilter protocolFilter = JsonProtocolFilter.fromRawFilters(filters);
- PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter);
- classes2Filters.add(class2Filters);
- continue;
- }
-
- // There are filters associated with the applicability of
- // this class for decoding.
- List<String> listOfFilters = new ArrayList<>(Arrays.asList(filter.split("\\s*,\\s*")));
-
- for (String nameValue : listOfFilters) {
- String fieldName;
- String regexValue;
-
- String[] nameValueSplit = nameValue.split("\\s*=\\s*");
- if (nameValueSplit.length <= 0 || nameValueSplit.length > 2) {
- // TODO warn
- // skip
- continue;
- }
-
- if (nameValueSplit.length == 2) {
- fieldName = nameValueSplit[0];
- regexValue = nameValueSplit[1];
- } else if (nameValueSplit.length == 1) {
- fieldName = nameValueSplit[0];
- regexValue = null;
- } else {
- // unreachable
- continue;
- }
-
- filters.add(new Pair<String, String>(fieldName, regexValue));
- }
-
- JsonProtocolFilter protocolFilter = JsonProtocolFilter.fromRawFilters(filters);
+ JsonProtocolFilter protocolFilter = new JsonProtocolFilter(filter);
PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter);
classes2Filters.add(class2Filters);
}
@@ -408,7 +366,7 @@ class IndexedDroolsControllerFactory implements DroolsControllerFactory {
/**
* unmanage the drools controller.
- *
+ *
* @param controller the controller
*/
protected void unmanage(DroolsController controller) {
diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java
index f65efaed..2ccc010f 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java
@@ -2,14 +2,14 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,378 +20,120 @@
package org.onap.policy.drools.protocol.coders;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
+import com.jayway.jsonpath.Configuration;
+import com.jayway.jsonpath.DocumentContext;
+import com.jayway.jsonpath.JsonPath;
+import com.jayway.jsonpath.Option;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import org.onap.policy.drools.utils.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * JSON Protocol Filter.
- */
+/** JSON Protocol Filter. */
public class JsonProtocolFilter {
- private static final String MISSING_RULE_NAME = "no rule name provided";
- /**
- * Logger.
- */
- private static final Logger logger = LoggerFactory.getLogger(JsonProtocolFilter.class);
-
- /**
- * Helper class to collect Filter information.
- */
- public static class FilterRule {
- /**
- * Field name.
- */
- private String name;
-
- /**
- * Field Value regex.
- */
- private String regex;
-
- /**
- * Filter Constructor.
- *
- * @param name field name
- * @param regex field regex value
- */
- public FilterRule(String name, String regex) {
- this.setName(name);
- this.setRegex(regex);
- }
-
- /**
- * Default constructor (for serialization only).
- */
- public FilterRule() {
- super();
- }
-
- /**
- * gets name.
- *
- * @return name
- */
- public String getName() {
- return name;
- }
-
- /**
- * gets regex.
- *
- * @return regular expression string
- */
- public String getRegex() {
- return regex;
- }
-
- /**
- * Sets field name.
- *
- * @param name field name
- */
- public void setName(String name) {
- if (name == null || name.isEmpty()) {
- throw new IllegalArgumentException("filter field name must be provided");
- }
-
- this.name = name;
- }
-
- /**
- * sets regex name.
- *
- * @param regex expression
- */
- public void setRegex(String regex) {
- if (regex == null || regex.isEmpty()) {
- this.regex = ".*";
- }
-
- this.regex = regex;
- }
+ /** Default filter to match anything. */
+ public static final String MATCH_ANY = "[?($ =~ /.*/)]";
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("Filter [name=").append(name).append(", regex=").append(regex).append("]");
- return builder.toString();
- }
- }
+ /** Logger. */
+ private static final Logger logger = LoggerFactory.getLogger(JsonProtocolFilter.class);
- /**
- * all the filters to be applied.
- */
- protected List<FilterRule> rules = new CopyOnWriteArrayList<>();
+ /** A rule based on a JsonPath expression that is used for filtering. */
+ private String rule;
/**
- * Create a Protocol Filter.
- *
- * @throws IllegalArgumentException an invalid input has been provided
+ * Default constructor (for serialization only).
*/
public JsonProtocolFilter() {
super();
+ this.setRule(null);
}
/**
* Constructor.
- *
- * @param filters filter list
- *
- * @throws IllegalArgumentException an invalid input has been provided
- */
- public JsonProtocolFilter(List<FilterRule> filters) {
- List<FilterRule> temp = new ArrayList<>();
- for (FilterRule rule : filters) {
- if (rule.getName() == null || rule.getName().isEmpty()) {
- continue;
- }
-
- if (rule.getRegex() == null || rule.getRegex().isEmpty()) {
- rule.setRegex(".*");
- }
-
- temp.add(rule);
- }
-
- this.rules.addAll(temp);
- }
-
- /**
- * From raw filters.
- *
- * @param rawFilters raw filter initialization
- *
+ *
+ * @param rule the JsonPath expression used for the filter rule
* @throws IllegalArgumentException an invalid input has been provided
*/
- public static JsonProtocolFilter fromRawFilters(List<Pair<String, String>> rawFilters) {
-
- if (rawFilters == null) {
- throw new IllegalArgumentException("No raw filters provided");
- }
-
- List<FilterRule> filters = new ArrayList<>();
- for (Pair<String, String> filterPair: rawFilters) {
- if (filterPair.first() == null || filterPair.first().isEmpty()) {
- continue;
- }
-
- filters.add(new FilterRule(filterPair.first(), filterPair.second()));
- }
- return new JsonProtocolFilter(filters);
+ public JsonProtocolFilter(String rule) {
+ this.setRule(rule);
}
/**
- * are there any filters.
- *
- * @return true if there are filters, false otherwise
+ * Gets the filter expression rule.
+ *
+ * @return the filter expression associated with this JsonProtocolFilter
*/
- public boolean isRules() {
- return !this.rules.isEmpty();
+ public String getRule() {
+ return this.rule;
}
/**
- * accept a JSON string as conformant it if passes all filters.
- *
- * @param json json is a JSON object
- * @return true if json string is conformant
- *
- * @throws IllegalArgumentException an invalid input has been provided
+ * Sets the filter expression rule.
+ *
+ * @param rule the JsonPath expression rule
*/
- public boolean accept(JsonElement json) {
- if (json == null) {
- throw new IllegalArgumentException("no JSON provided");
- }
-
- if (!json.isJsonObject()) {
- return false;
- }
-
- if (rules.isEmpty()) {
- return true;
- }
-
- try {
- JsonObject event = json.getAsJsonObject();
- for (FilterRule filter: rules) {
- if (filter.getRegex() == null
- || filter.getRegex().isEmpty()
- || ".*".equals(filter.getRegex())) {
-
- // Only check for presence
- if (!event.has(filter.getName())) {
- return false;
- }
- } else {
- JsonElement field = event.get(filter.getName());
- if (field == null) {
- return false;
- }
-
- String fieldValue = field.getAsString();
- if (!fieldValue.matches(filter.getRegex())) {
- return false;
- }
- }
- }
- return true;
- } catch (Exception e) {
- throw new IllegalArgumentException(e);
+ public void setRule(String rule) {
+ String ruleExpression = rule;
+ if (rule == null || rule.isEmpty()) {
+ ruleExpression = MATCH_ANY;
}
+ this.rule = ruleExpression;
}
/**
- * Accept a JSON string as conformant it if passes all filters.
- *
- * @param json json string
- * @return true if json string is conformant
- *
- * @throws IllegalArgumentException an invalid input has been provided
+ * Accepts a JSON message if there is a match on the filter expression.
+ *
+ * @return true if a match is found or the rule uses the match any policy, false otherwise
*/
public boolean accept(String json) {
- if (json == null || json.isEmpty()) {
- throw new IllegalArgumentException("no JSON provided");
- }
-
- if (rules.isEmpty()) {
+ if (MATCH_ANY.equals(this.rule)) {
return true;
}
-
- try {
- JsonElement element = new JsonParser().parse(json);
- if (element == null || !element.isJsonObject()) {
- return false;
- }
-
- return this.accept(element.getAsJsonObject());
- } catch (IllegalArgumentException ile) {
- throw ile;
- } catch (Exception e) {
- logger.info("{}: cannot accept {} because of {}",
- this, json, e.getMessage(), e);
- throw new IllegalArgumentException(e);
- }
- }
-
- public List<FilterRule> getRules() {
- return new ArrayList<>(this.rules);
+ return !filter(json).isEmpty();
}
/**
- * Get rules.
- *
- * @param name name
- * @return list of filter rules
+ * Finds a field based on a path or a subset of the JSON if using an expression.
+ *
+ * @param json the JSON string to be parsed
+ * @return a list of strings that match the expression
*/
- public List<FilterRule> getRules(String name) {
- if (name == null || name.isEmpty()) {
- throw new IllegalArgumentException(MISSING_RULE_NAME);
- }
-
- ArrayList<FilterRule> temp = new ArrayList<>();
- for (FilterRule rule : this.rules) {
- if (rule.getName().equals(name)) {
- temp.add(rule);
- }
- }
- return temp;
+ public List<String> filter(String json) {
+ return filter(json, this.rule);
}
/**
- * Set Rules.
- *
- * @param rulesFilters filters
+ * Finds all occurrences of a field in a JSON document based on the JsonPath
+ * expression.
+ *
+ * @param json the JSON string to be parsed
+ * @param expression the JsonPath expression
+ * @return a list of matches from the JSON document
*/
- public void setRules(List<FilterRule> rulesFilters) {
- if (rulesFilters == null) {
- throw new IllegalArgumentException("no rules provided");
- }
-
- this.rules.clear();
- this.rules.addAll(rulesFilters);
- }
-
- /**
- * Delete rules.
- *
- * @param name name
- */
- public void deleteRules(String name) {
- if (name == null || name.isEmpty()) {
- throw new IllegalArgumentException(MISSING_RULE_NAME);
- }
-
- List<FilterRule> temp = new ArrayList<>();
- for (FilterRule rule : this.rules) {
- if (rule.name.equals(name)) {
- temp.add(rule);
- }
- }
- this.rules.removeAll(temp);
- }
-
- /**
- * Delete rule.
- *
- * @param name name
- * @param regex regex
- */
- public void deleteRule(String name, String regex) {
- if (name == null || name.isEmpty()) {
- throw new IllegalArgumentException(MISSING_RULE_NAME);
- }
-
- String nonNullRegex = regex;
- if (regex == null || regex.isEmpty()) {
- nonNullRegex = ".*";
+ public static List<String> filter(String json, String expression) {
+ if (json == null || json.isEmpty()) {
+ throw new IllegalArgumentException("a json string must be provided");
}
- List<FilterRule> temp = new ArrayList<>();
- for (FilterRule rule : this.rules) {
- if (rule.name.equals(name) && rule.getRegex().equals(nonNullRegex)) {
- temp.add(rule);
- }
+ if (expression == null || expression.isEmpty()) {
+ throw new IllegalArgumentException("an expression must be provided");
}
- this.rules.removeAll(temp);
- }
-
- /**
- * Add rule.
- *
- * @param name name
- * @param regex regex
- */
- public void addRule(String name, String regex) {
- if (name == null || name.isEmpty()) {
- throw new IllegalArgumentException(MISSING_RULE_NAME);
- }
+ Configuration conf = Configuration.defaultConfiguration().addOptions(Option.ALWAYS_RETURN_LIST);
+ DocumentContext document = JsonPath.using(conf).parse(json);
- String nonNullRegex = regex;
- if (regex == null || regex.isEmpty()) {
- nonNullRegex = ".*";
+ List<String> matches = new ArrayList<>();
+ try {
+ matches = document.read(expression);
+ } catch (Exception e) {
+ logger.error("JsonPath couldn't read {} because of {}", expression, e.getMessage(), e);
}
- for (FilterRule rule : this.rules) {
- if (rule.getName().equals(name) && rule.getRegex().equals(regex)) {
- return;
- }
+ if (matches.isEmpty()) {
+ logger.warn("Could not find any matches for rule {} in json {}", expression, json);
}
- this.rules.add(new FilterRule(name, nonNullRegex));
+ return matches;
}
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("JsonProtocolFilter [rules=").append(rules).append("]");
- return builder.toString();
- }
-
}
diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java
index f125c134..394e73af 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java
@@ -31,7 +31,6 @@ import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
-
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
@@ -168,7 +167,7 @@ public abstract class ProtocolCoderToolset {
/**
* remove coder.
- *
+ *
* @param eventClass event class
*/
public void removeCoders(String eventClass) {
@@ -206,7 +205,7 @@ public abstract class ProtocolCoderToolset {
/**
* Get group id.
- *
+ *
* @return the groupId
*/
public String getGroupId() {
@@ -215,7 +214,7 @@ public abstract class ProtocolCoderToolset {
/**
* Get artifact id.
- *
+ *
* @return the artifactId
*/
public String getArtifactId() {
@@ -224,7 +223,7 @@ public abstract class ProtocolCoderToolset {
/**
* Get custom coder.
- *
+ *
* @return the customCoder
*/
public CustomCoder getCustomCoder() {
@@ -233,7 +232,7 @@ public abstract class ProtocolCoderToolset {
/**
* Set custom coder.
- *
+ *
* @param customCoder the customCoder to set.
*/
public void setCustomCoder(CustomCoder customCoder) {
@@ -262,28 +261,14 @@ public abstract class ProtocolCoderToolset {
throw new IllegalStateException("No coders available");
}
- if (this.coders.size() == 1) {
- final JsonProtocolFilter filter = this.coders.get(0).getFilter();
- if (!filter.isRules()) {
- return this.coders.get(0);
- }
- }
-
- JsonElement event;
- try {
- event = this.filteringParser.parse(json);
- } catch (final Exception e) {
- throw new UnsupportedOperationException(e);
- }
-
for (final CoderFilters decoder : this.coders) {
try {
- final boolean accepted = decoder.getFilter().accept(event);
+ boolean accepted = decoder.getFilter().accept(json);
if (accepted) {
return decoder;
}
} catch (final Exception e) {
- logger.info("{}: unexpected failure accepting {} because of {}", this, event,
+ logger.info("{}: unexpected failure accepting {} because of {}", this, json,
e.getMessage(), e);
// continue
}
@@ -325,8 +310,6 @@ public abstract class ProtocolCoderToolset {
}
}
-
-
/**
* Tools used for encoding/decoding using GSON.
*/
diff --git a/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java b/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java
index 64fd6823..cbe2b339 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* policy-management
* ================================================================================
- * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,8 +36,6 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
-import java.util.regex.Pattern;
-
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
@@ -51,7 +49,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
-
import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
import org.onap.policy.common.endpoints.event.comm.TopicSink;
import org.onap.policy.common.endpoints.event.comm.TopicSource;
@@ -67,7 +64,6 @@ import org.onap.policy.drools.properties.DroolsProperties;
import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
-import org.onap.policy.drools.protocol.coders.JsonProtocolFilter.FilterRule;
import org.onap.policy.drools.protocol.coders.ProtocolCoderToolset;
import org.onap.policy.drools.protocol.configuration.ControllerConfiguration;
import org.onap.policy.drools.protocol.configuration.PdpdConfiguration;
@@ -77,7 +73,6 @@ import org.onap.policy.drools.utils.logging.LoggerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
/**
* Telemetry JAX-RS Interface to the PDP-D.
*/
@@ -98,7 +93,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -111,7 +106,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -132,7 +127,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -154,7 +149,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -175,7 +170,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -187,7 +182,7 @@ public class RestManager {
/**
* POST.
- *
+ *
* @return response object
*/
@POST
@@ -216,7 +211,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -229,7 +224,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -242,7 +237,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -257,7 +252,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -274,7 +269,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -287,7 +282,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -315,7 +310,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -343,7 +338,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -364,7 +359,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -385,7 +380,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -398,7 +393,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -411,7 +406,7 @@ public class RestManager {
/**
* POST.
- *
+ *
* @return response object
*/
@POST
@@ -479,7 +474,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -492,7 +487,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -506,7 +501,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -528,7 +523,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -557,7 +552,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -604,7 +599,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -632,7 +627,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -644,7 +639,7 @@ public class RestManager {
/**
* POST.
- *
+ *
* @return response object
*/
@POST
@@ -688,7 +683,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -701,7 +696,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -724,7 +719,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -747,7 +742,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -775,7 +770,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -807,7 +802,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -836,7 +831,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -875,7 +870,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -928,7 +923,7 @@ public class RestManager {
/**
* POST.
- *
+ *
* @return response object
*/
@POST
@@ -985,7 +980,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -1027,7 +1022,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -1085,29 +1080,21 @@ public class RestManager {
/**
* POST.
- *
+ *
* @return response object
*/
@POST
- @Path("engine/controllers/tools/coders/decoders/filters/rules/{ruleName}")
+ @Path("engine/controllers/tools/coders/decoders/filters/rule")
@ApiOperation(value = "Produces a Decoder Rule Filter in a format that the Policy Controller can understand",
notes = "The result can be used with other APIs to attach a filter to a decoder")
public Response rules(
- @ApiParam(value = "Negate regex?",
- required = true) @DefaultValue("false") @QueryParam("negate") boolean negate,
- @ApiParam(value = "Rule Name", required = true) @PathParam("ruleName") String name,
- @ApiParam(value = "Regex expression", required = true) String regex) {
- String literalRegex = Pattern.quote(regex);
- if (negate) {
- literalRegex = "^(?!" + literalRegex + "$).*";
- }
-
- return Response.status(Status.OK).entity(new JsonProtocolFilter.FilterRule(name, literalRegex)).build();
+ @ApiParam(value = "JsonPath expression", required = true) String expression) {
+ return Response.status(Status.OK).entity(new JsonProtocolFilter(expression)).build();
}
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1142,7 +1129,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1178,7 +1165,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1215,7 +1202,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1258,7 +1245,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1303,7 +1290,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -1359,17 +1346,16 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
- @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rules")
- @ApiOperation(value = "Gets the filter rules attached to a topic decoder of a controller",
+ @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rule")
+ @ApiOperation(value = "Gets the filter rule attached to a topic decoder of a controller",
notes = "Decoders are associated with networked topics. A Policy Controller manages "
+ "multiple topics and therefore its attached decoders. "
+ "A Policy Controller uses filters to further specify the fact mapping. "
- + "Filters are applied on a per fact type and are composed of field matching rules. ",
- responseContainer = "List", response = FilterRule.class)
+ + "Filters are applied on a per fact type using a jsonpath expression rule. ")
@ApiResponses(value = {@ApiResponse(code = 404, message = "The controller, topic, or fact type cannot be found"),
@ApiResponse(code = 406,
message = "The system is an administrative state that prevents " + "this request to be fulfilled")})
@@ -1394,7 +1380,7 @@ public class RestManager {
.entity(new Error(controllerName + ":" + topic + ":" + factClass + " no filters")).build();
}
- return Response.status(Response.Status.OK).entity(filter.getRules()).build();
+ return Response.status(Response.Status.OK).entity(filter.getRule()).build();
} catch (final IllegalArgumentException e) {
logger.debug("{}: cannot get decoder filters for policy-controller {} topic {} type {} because of {}", this,
controllerName, topic, factClass, e.getMessage(), e);
@@ -1409,86 +1395,25 @@ public class RestManager {
}
/**
- * GET.
- *
- * @return response object
- */
- @GET
- @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rules/{ruleName}")
- @ApiOperation(value = "Gets a filter rule by name attached to a topic decoder of a controller",
- notes = "Decoders are associated with networked topics. A Policy Controller manages "
- + "multiple topics and therefore its attached decoders. "
- + "A Policy Controller uses filters to further specify the fact mapping. "
- + "Filters are applied on a per fact type and are composed of field matching rules. ",
- responseContainer = "List", response = FilterRule.class)
- @ApiResponses(value = {
- @ApiResponse(code = 404, message = "The controller, topic, fact type, or rule name cannot be found"),
- @ApiResponse(code = 406,
- message = "The system is an administrative state that prevents " + "this request to be fulfilled")})
- public Response decoderFilterRules(
- @ApiParam(value = "Policy Controller Name", required = true) @PathParam("controller") String controllerName,
- @ApiParam(value = "Topic Name", required = true) @PathParam("topic") String topic,
- @ApiParam(value = "Fact Type", required = true) @PathParam("factType") String factClass,
- @ApiParam(value = "Rule Name", required = true) @PathParam("ruleName") String ruleName) {
- try {
- final DroolsController drools = this.getDroolsController(controllerName);
- final ProtocolCoderToolset decoder =
- EventProtocolCoder.manager.getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
-
- final CoderFilters filters = decoder.getCoder(factClass);
- if (filters == null) {
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(new Error(controllerName + ":" + topic + ":" + factClass + " does not exist")).build();
- }
-
- final JsonProtocolFilter filter = filters.getFilter();
- if (filter == null) {
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(new Error(controllerName + ":" + topic + ":" + factClass + " no filters")).build();
- }
-
- return Response.status(Response.Status.OK).entity(filter.getRules(ruleName)).build();
- } catch (final IllegalArgumentException e) {
- logger.debug(
- "{}: cannot get decoder filters for policy-controller {} topic {} type {} rule {} because of {}",
- this, controllerName, topic, factClass, ruleName, e.getMessage(), e);
- return Response.status(Response.Status.NOT_FOUND)
- .entity(new Error(controllerName + ":" + topic + ":" + factClass + ": " + ruleName + " not found"))
- .build();
- } catch (final IllegalStateException e) {
- logger.debug(
- "{}: cannot get decoder filters for policy-controller {} topic {} type {} rule {} because of {}",
- this, controllerName, topic, factClass, ruleName, e.getMessage(), e);
- return Response.status(Response.Status.NOT_ACCEPTABLE)
- .entity(new Error(
- controllerName + ":" + topic + ":" + factClass + ":" + ruleName + " not acceptable"))
- .build();
- }
- }
-
- /**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
- @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rules/{ruleName}")
- @ApiOperation(value = "Deletes a filter rule by name attached to a topic decoder of a controller",
+ @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rule")
+ @ApiOperation(value = "Deletes the filter rule attached to a topic decoder of a controller",
notes = "Decoders are associated with networked topics. A Policy Controller manages "
+ "multiple topics and therefore its attached decoders. "
+ "A Policy Controller uses filters to further specify the fact mapping. "
- + "Filters are applied on a per fact type and are composed of field matching rules. ",
- responseContainer = "List", response = FilterRule.class)
+ + "Filters are applied on a per fact type using a jsonpath expression rule. ")
@ApiResponses(value = {
- @ApiResponse(code = 404, message = "The controller, topic, fact type, or rule name cannot be found"),
+ @ApiResponse(code = 404, message = "The controller, topic, or fact type cannot be found"),
@ApiResponse(code = 406,
message = "The system is an administrative state that prevents " + "this request to be fulfilled")})
public Response decoderFilterRuleDelete(
@ApiParam(value = "Policy Controller Name", required = true) @PathParam("controller") String controllerName,
@ApiParam(value = "Topic Name", required = true) @PathParam("topic") String topic,
- @ApiParam(value = "Fact Type", required = true) @PathParam("factType") String factClass,
- @ApiParam(value = "Rule Name", required = true) @PathParam("ruleName") String ruleName,
- @ApiParam(value = "Filter Rule", required = true) FilterRule rule) {
+ @ApiParam(value = "Fact Type", required = true) @PathParam("factType") String factClass) {
try {
final DroolsController drools = this.getDroolsController(controllerName);
@@ -1507,50 +1432,38 @@ public class RestManager {
.entity(new Error(controllerName + ":" + topic + ":" + factClass + " no filters")).build();
}
- if (rule == null) {
- filter.deleteRules(ruleName);
- return Response.status(Response.Status.OK).entity(filter.getRules()).build();
- }
-
- if (rule.getName() == null || !rule.getName().equals(ruleName)) {
- return Response.status(Response.Status.BAD_REQUEST).entity(new Error(controllerName + ":" + topic + ":"
- + factClass + ":" + ruleName + " rule name request inconsistencies (" + rule.getName() + ")"))
- .build();
- }
-
- filter.deleteRule(ruleName, rule.getRegex());
- return Response.status(Response.Status.OK).entity(filter.getRules()).build();
+ filter.setRule(null);
+ return Response.status(Response.Status.OK).entity(filter.getRule()).build();
} catch (final IllegalArgumentException e) {
logger.debug(
- "{}: cannot get decoder filters for policy-controller {} topic {} type {} rule {} because of {}",
- this, controllerName, topic, factClass, ruleName, e.getMessage(), e);
+ "{}: cannot get decoder filters for policy-controller {} topic {} type {} because of {}",
+ this, controllerName, topic, factClass, e.getMessage(), e);
return Response.status(Response.Status.NOT_FOUND)
- .entity(new Error(controllerName + ":" + topic + ":" + factClass + ": " + ruleName + " not found"))
+ .entity(new Error(controllerName + ":" + topic + ":" + factClass + " not found"))
.build();
} catch (final IllegalStateException e) {
logger.debug(
- "{}: cannot get decoder filters for policy-controller {} topic {} type {} rule {} because of {}",
- this, controllerName, topic, factClass, ruleName, e.getMessage(), e);
+ "{}: cannot get decoder filters for policy-controller {} topic {} type {} because of {}",
+ this, controllerName, topic, factClass, e.getMessage(), e);
return Response.status(Response.Status.NOT_ACCEPTABLE)
.entity(new Error(
- controllerName + ":" + topic + ":" + factClass + ":" + ruleName + " not acceptable"))
+ controllerName + ":" + topic + ":" + factClass + " not acceptable"))
.build();
}
}
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
- @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rules")
+ @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rule")
@ApiOperation(value = "Places a new filter rule in a topic decoder",
notes = "Decoders are associated with networked topics. A Policy Controller manages "
+ "multiple topics and therefore its attached decoders. "
+ "A Policy Controller uses filters to further specify the fact mapping. "
- + "Filters are applied on a per fact type and are composed of field matching rules. ",
- responseContainer = "List", response = FilterRule.class)
+ + "Filters are applied on a per fact type using a jsonpath expression rule. ")
@ApiResponses(value = {@ApiResponse(code = 404, message = "The controller, topic, or fact type cannot be found"),
@ApiResponse(code = 406,
message = "The system is an administrative state that prevents " + "this request to be fulfilled")})
@@ -1558,8 +1471,7 @@ public class RestManager {
@ApiParam(value = "Policy Controller Name", required = true) @PathParam("controller") String controllerName,
@ApiParam(value = "Topic Name", required = true) @PathParam("topic") String topic,
@ApiParam(value = "Fact Type", required = true) @PathParam("factType") String factClass,
- @ApiParam(value = "Rule Name", required = true) @PathParam("ruleName") String ruleName,
- @ApiParam(value = "Filter Rule", required = true) FilterRule rule) {
+ @ApiParam(value = "JsonPath filter expression", required = true) String rule) {
try {
final DroolsController drools = this.getDroolsController(controllerName);
@@ -1578,25 +1490,25 @@ public class RestManager {
.entity(new Error(controllerName + ":" + topic + ":" + factClass + " no filters")).build();
}
- if (rule.getName() == null) {
+ if (rule == null || rule.isEmpty()) {
return Response.status(Response.Status.BAD_REQUEST).entity(new Error(controllerName + ":" + topic + ":"
- + factClass + " rule name request inconsistencies (" + rule.getName() + ")")).build();
+ + factClass + " no filter rule provided")).build();
}
- filter.addRule(rule.getName(), rule.getRegex());
- return Response.status(Response.Status.OK).entity(filter.getRules()).build();
+ filter.setRule(rule);
+ return Response.status(Response.Status.OK).entity(filter.getRule()).build();
} catch (final IllegalArgumentException e) {
logger.debug(
"{}: cannot access decoder filter rules for policy-controller {} "
- + "topic {} type {} rule {} because of {}",
- this, controllerName, topic, factClass, ruleName, e.getMessage(), e);
+ + "topic {} type {} because of {}",
+ this, controllerName, topic, factClass, e.getMessage(), e);
return Response.status(Response.Status.NOT_FOUND)
- .entity(new Error(controllerName + ":" + topic + ":" + factClass + " not found")).build();
+ .entity(new Error(controllerName + ":" + topic + " not found")).build();
} catch (final IllegalStateException e) {
logger.debug(
"{}: cannot access decoder filter rules for policy-controller {} "
- + "topic {} type {} rule {} because of {}",
- this, controllerName, topic, factClass, ruleName, e.getMessage(), e);
+ + "topic {} type {} because of {}",
+ this, controllerName, topic, factClass, e.getMessage(), e);
return Response.status(Response.Status.NOT_ACCEPTABLE)
.entity(new Error(controllerName + ":" + topic + ":" + factClass + " not acceptable")).build();
}
@@ -1604,7 +1516,7 @@ public class RestManager {
/**
* POST.
- *
+ *
* @return response object
*/
@POST
@@ -1665,7 +1577,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1715,7 +1627,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -1735,7 +1647,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -1755,7 +1667,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1768,7 +1680,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1781,7 +1693,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1794,7 +1706,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1807,7 +1719,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1820,7 +1732,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1833,7 +1745,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1847,7 +1759,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1861,7 +1773,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1876,7 +1788,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1890,7 +1802,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1905,7 +1817,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1920,7 +1832,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1936,7 +1848,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1951,7 +1863,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1964,7 +1876,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1977,7 +1889,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -1991,7 +1903,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -2004,7 +1916,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -2024,7 +1936,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -2048,7 +1960,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -2061,7 +1973,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -2081,7 +1993,7 @@ public class RestManager {
/**
* DELETE.
- *
+ *
* @return response object
*/
@DELETE
@@ -2102,7 +2014,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -2146,7 +2058,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
@@ -2190,7 +2102,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -2203,7 +2115,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -2227,7 +2139,7 @@ public class RestManager {
/**
* GET.
- *
+ *
* @return response object
*/
@GET
@@ -2255,7 +2167,7 @@ public class RestManager {
/**
* PUT.
- *
+ *
* @return response object
*/
@PUT
diff --git a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java
index e83e5880..e0baeb6d 100644
--- a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java
+++ b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,205 +24,140 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import java.util.ArrayList;
-import java.util.List;
-
import org.junit.Test;
-import org.onap.policy.drools.protocol.coders.JsonProtocolFilter.FilterRule;
-import org.onap.policy.drools.utils.Pair;
-
public class JsonProtocolFilterTest {
- private static final String NAME1 = "name1";
- private static final String REGEX1 = "regex1";
+ private static final String JSON =
+ "{\"requestID\":\"38adde30-cc22-11e8-a8d5-f2801f1b9fd1\",\"entity\":\"controller\","
+ + "\"controllers\":[{\"name\":\"test-controller\","
+ + "\"drools\":{\"groupId\":\"org.onap.policy.drools.test\","
+ + "\"artifactId\":\"test\",\"version\":\"0.0.1\"},\"operation\":\"update\"}]}";
+
+ /**
+ * Tests getting the rule expression of the filter.
+ */
+ @Test
+ public void getRuleTest() {
+ assertEquals("$.test", new JsonProtocolFilter("$.test").getRule());
+ }
+
+ /**
+ * Tests setting the rule expression of the filter.
+ */
+ @Test
+ public void setRuleTest() {
+ JsonProtocolFilter filter = new JsonProtocolFilter();
+ assertEquals(JsonProtocolFilter.MATCH_ANY, filter.getRule());
+ filter.setRule("$.test");
+ assertEquals("$.test", filter.getRule());
+ }
+
+ /**
+ * Tests that the rule expression will be set to match anything if an empty string is passed.
+ */
+ @Test
+ public void setRuleEmptyTest() {
+ assertEquals(JsonProtocolFilter.MATCH_ANY, new JsonProtocolFilter("").getRule());
+ }
+
+ /**
+ * Tests that the rule expression will be set to match anything if a null string is passed.
+ */
+ @Test
+ public void setRuleNullTest() {
+ assertEquals(JsonProtocolFilter.MATCH_ANY, new JsonProtocolFilter(null).getRule());
+ }
+
+ /**
+ * Tests accepting a message if all filter rules pass.
+ */
+ @Test
+ public void acceptPassTest() {
+ assertTrue(new JsonProtocolFilter(
+ "$.controllers[?(@.drools.version =~ /\\d\\.\\d\\.\\d/ && @.operation == 'update')]")
+ .accept(JSON));
+ }
+
+ /**
+ * Tests accepting a message without having to filter if the rule is set to match anything.
+ */
+ @Test
+ public void acceptAnyTest() {
+ assertTrue(new JsonProtocolFilter(null).accept(JSON));
+ }
- private static final String NAME2 = "name2";
- private static final String REGEX2 = "regex2";
+ /**
+ * Tests rejecting a message if one or more of the filter rules fail.
+ */
+ @Test
+ public void acceptFailTest() {
+ assertFalse(
+ new JsonProtocolFilter("$.controllers[?(@.drools.version =~ /\\\\d\\\\.\\\\d\\\\.2/)]")
+ .accept(JSON));
+ }
- private static final String NAME3 = "name3";
- private static final String REGEX3 = "regex3";
+ /**
+ * Tests finding field matches for a filter rule corresponding to a topic.
+ */
+ @Test
+ public void filterPassTest() {
+ assertEquals("38adde30-cc22-11e8-a8d5-f2801f1b9fd1", new JsonProtocolFilter("$.requestID").filter(JSON).get(0));
+ }
+
+ /**
+ * Tests that an empty list is returned when no matches are found.
+ */
+ @Test
+ public void filterFailTest() {
+ assertTrue(new JsonProtocolFilter("$.test").filter(JSON).isEmpty());
+ }
- private static final String NAME4 = "name4";
- private static final String REGEX4a = "^regex4a.*";
- private static final String REGEX4b = ".*regex4b$";
-
+ /**
+ * Tests static method for filtering a JSON string with an arbitrary expression.
+ */
+ @Test
+ public void staticFilterPassTest() {
+ assertEquals("controller", JsonProtocolFilter.filter(JSON, "$.entity").get(0));
+ }
+ /**
+ * Tests that an empty list is returned when the static filter() method does not find any matches.
+ */
@Test
- public void test() {
-
- // ******************** D E F I N E f i l t e r R u l e O b j e c t s ***************************
- // DEFINE one (1) filterRule object (using constructor without parms passed; instead use set methods
- FilterRule filterRule1 = new FilterRule();
- filterRule1.setName(NAME1);
- filterRule1.setRegex(REGEX1);
- assertEquals(filterRule1.getName(), NAME1);
- assertEquals(filterRule1.getRegex(), REGEX1);
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // DEFINE four (4) filterRule objects (using constructor passing the field values
- FilterRule filterRule2 = new FilterRule(NAME2, REGEX2);
- assertEquals(filterRule2.getName(), NAME2);
- assertEquals(filterRule2.getRegex(), REGEX2);
-
- FilterRule filterRule3 = new FilterRule(NAME3, REGEX3);
- assertEquals(filterRule3.getName(), NAME3);
- assertEquals(filterRule3.getRegex(), REGEX3);
-
- FilterRule filterRule4a = new FilterRule(NAME4, REGEX4a);
- assertEquals(filterRule4a.getName(), NAME4);
- assertEquals(filterRule4a.getRegex(), REGEX4a);
-
- FilterRule filterRule4b = new FilterRule(NAME4, REGEX4b);
- assertEquals(filterRule4b.getName(), NAME4);
- assertEquals(filterRule4b.getRegex(), REGEX4b);
-
-
-
- // ************************ D E F I N E f i l t e r L i s t s ************************************
- // DEFINE rawFiltersA
- List<Pair<String,String>> rawFiltersA = new ArrayList<>();
- rawFiltersA.add(new Pair<String,String>(filterRule1.getName(), filterRule1.getRegex()));
- rawFiltersA.add(new Pair<String,String>(filterRule2.getName(), filterRule2.getRegex()));
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // DEFINE filtersA
- List<FilterRule> filtersA = new ArrayList<>();
- for (Pair<String, String> filterPair: rawFiltersA) {
- if (filterPair.first() == null || filterPair.first().isEmpty()) {
- continue;
- }
- filtersA.add(new FilterRule(filterPair.first(), filterPair.second()));
- }
-
-
-
- // *********** I N S T A N T I A T E J s o n P r o t o c o l F i l t e r O b j e c t s **********
- // INSTANTIATE protocolFilterA (passing raw filters to the 'fromRawFilters' constructor)
- JsonProtocolFilter protocolFilterA = JsonProtocolFilter.fromRawFilters(rawFiltersA);
- assertTrue(protocolFilterA.isRules());
- assertEquals(protocolFilterA.getRules().toString(), filtersA.toString());
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // INSTANTIATE protocolFilterB (passing filters list to constructor which accepts such)
- JsonProtocolFilter protocolFilterB = new JsonProtocolFilter(filtersA);
- assertTrue(protocolFilterB.isRules());
- assertEquals(protocolFilterB.getRules(), filtersA);
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // INSTANTIATE protocolFilterC (using constructor without parms; add instead using setRules() method
- JsonProtocolFilter protocolFilterC = new JsonProtocolFilter();
- protocolFilterC.setRules(filtersA);
- assertTrue(protocolFilterC.isRules());
- assertEquals(protocolFilterC.getRules(), filtersA);
-
-
-
- // *** D E F I N E o t h e r f i l t e r L i s t s f o r v a l i d a t i o n s ************
- // DEFINE rawFiltersB
- List<Pair<String,String>> rawFiltersB = new ArrayList<>();
- rawFiltersB.add(new Pair<String,String>(filterRule1.getName(), filterRule1.getRegex()));
- rawFiltersB.add(new Pair<String,String>(filterRule2.getName(), filterRule2.getRegex()));
- rawFiltersB.add(new Pair<String,String>(filterRule3.getName(), filterRule3.getRegex()));
- rawFiltersB.add(new Pair<String,String>(filterRule4a.getName(), filterRule4a.getRegex()));
- rawFiltersB.add(new Pair<String,String>(filterRule4b.getName(), filterRule4b.getRegex()));
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // DEFINE filtersB
- List<FilterRule> filtersB = new ArrayList<>();
- for (Pair<String, String> filterPair: rawFiltersB) {
- filtersB.add(new FilterRule(filterPair.first(), filterPair.second()));
- }
-
-
-
- // *********** A D D T O p r o t o c o l F i l t e r B 3 m o r e f i l t e r s ************
- protocolFilterB.addRule(filterRule3.getName(), filterRule3.getRegex());
- protocolFilterB.addRule(filterRule4a.getName(), filterRule4a.getRegex());
- protocolFilterB.addRule(filterRule4b.getName(), filterRule4b.getRegex());
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // VALIDATE that protocolFilterB now contains filters passed using filtersA and the new ones just added
- assertEquals(protocolFilterB.getRules().toString(), filtersB.toString());
-
-
-
- // ************ D E L E T E f i l t e r s f r o m p r o t o c o l F i l t e r B ***********
- // DELETE specific filter from protocolFilterB by passing both the name & regex values
-
- assertTrue(protocolFilterB.getRules(NAME3).size() == 1);
- assertTrue(protocolFilterB.getRules(NAME3).get(0).getName().equals(NAME3));
- assertTrue(protocolFilterB.getRules(NAME3).get(0).getRegex().equals(REGEX3));
-
- assertTrue(protocolFilterB.getRules(NAME4).size() == 2);
- assertTrue(protocolFilterB.getRules(NAME4).get(0).getName().equals(NAME4));
- assertTrue(protocolFilterB.getRules(NAME4).get(0).getRegex().equals(REGEX4a));
- assertTrue(protocolFilterB.getRules(NAME4).get(1).getName().equals(NAME4));
- assertTrue(protocolFilterB.getRules(NAME4).get(1).getRegex().equals(REGEX4b));
-
- final String jsonA = "{ \"name1\":\"regex1\",\"name2\":\"regex2\","
- + "\"name3\":\"regex3\",\"name4\":\"regex4a\",\"name4\":\"regex4b\"}";
- final String jsonB = "{ \"name1\":\"regex1\",\"name2\":\"regex2\","
- + "\"name3\":\"regex3\",\"name4\":\"regex4a-regex4b\"}";
- final String jsonC = "{ \"name1\":\"regex1\",\"name2\":\"regex2\","
- + "\"name3\":\"regex3\",\"name4\":\"regex4a\"}";
- final String jsonD = "{ \"name1\":\"regex1\",\"name2\":\"regex2\","
- + "\"name3\":\"regex3\",\"name4\":\"regex4b\"}";
-
- assertFalse(protocolFilterB.accept(jsonA));
- assertTrue(protocolFilterB.accept(jsonB));
- assertFalse(protocolFilterB.accept(jsonC));
- assertFalse(protocolFilterB.accept(jsonD));
-
- protocolFilterB.deleteRule(NAME4, REGEX4a);
-
- assertTrue(protocolFilterB.accept(jsonA));
- assertTrue(protocolFilterB.accept(jsonB));
- assertFalse(protocolFilterB.accept(jsonC));
- assertTrue(protocolFilterB.accept(jsonD));
-
- protocolFilterB.addRule(NAME4, REGEX4a);
-
- assertTrue(protocolFilterB.getRules(NAME4).size() == 2);
- assertTrue(protocolFilterB.getRules(NAME4).get(0).getName().equals(NAME4));
- assertTrue(protocolFilterB.getRules(NAME4).get(0).getRegex().equals(REGEX4b));
- assertTrue(protocolFilterB.getRules(NAME4).get(1).getName().equals(NAME4));
- assertTrue(protocolFilterB.getRules(NAME4).get(1).getRegex().equals(REGEX4a));
-
- assertFalse(protocolFilterB.accept(jsonA));
- assertTrue(protocolFilterB.accept(jsonB));
- assertFalse(protocolFilterB.accept(jsonC));
- assertFalse(protocolFilterB.accept(jsonD));
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // DELETE all filters from protocolFilterB that have a match to the same name value
- protocolFilterB.deleteRules(NAME4);
-
- assertTrue(protocolFilterB.getRules(NAME4).isEmpty());
- assertTrue(protocolFilterB.accept(jsonA));
- assertTrue(protocolFilterB.accept(jsonB));
- assertTrue(protocolFilterB.accept(jsonC));
- assertTrue(protocolFilterB.accept(jsonD));
-
- assertTrue(protocolFilterB.getRules(NAME3).size() == 1);
- protocolFilterB.addRule(NAME3, REGEX3);
- assertTrue(protocolFilterB.getRules(NAME3).size() == 1);
- protocolFilterB.deleteRule(NAME3, REGEX3);
- assertTrue(protocolFilterB.getRules(NAME3).isEmpty());
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // VALIDATE that protocolFilterB now only contains the filters that were originally passed using filtersA
- assertEquals(protocolFilterB.getRules(), filtersA);
-
- // ************ A C C E P T J S O N I F I T P A S S E S A L L F I L T E R S ***********
- // ACCEPT TRUE a JSON that passes all filters
- String jsonE = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"}";
- assertTrue(protocolFilterA.accept(jsonE));
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // ACCEPT FALSE a JSON that does NOT pass all filters
- String jsonF = "{ \"name1\":\"regex1\"}";
- assertFalse(protocolFilterA.accept(jsonF));
+ public void staticFilterFailTest() {
+ assertTrue(JsonProtocolFilter.filter(JSON, "$.test").isEmpty());
}
+ /**
+ * Tests that an exception is thrown if a null JSON string is passed.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void staticFilterNullJsonTest() {
+ JsonProtocolFilter.filter(null, "[?($ =~ /.*/");
+ }
+
+ /**
+ * Tests that an exception is thrown if an empty JSON string is passed.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void staticFilterEmptyJsonTest() {
+ JsonProtocolFilter.filter("", "[?($ =~ /.*/");
+ }
+
+ /**
+ * Tests that an exception is thrown if a null expression string is passed.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void staticFilterNullExpressionTest() {
+ JsonProtocolFilter.filter("{\"hello\":\"world\"}", null);
+ }
+
+ /**
+ * Tests that an exception is thrown if an empty expression string is passed.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void staticFilterEmptyExpressionTest() {
+ JsonProtocolFilter.filter("{\"hello\":\"world\"}", "");
+ }
} \ No newline at end of file
diff --git a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolsetTest.java b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolsetTest.java
index da491261..b7d72f31 100644
--- a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolsetTest.java
+++ b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolsetTest.java
@@ -25,7 +25,6 @@ import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.Assert;
@@ -39,7 +38,6 @@ import org.onap.policy.drools.controller.DroolsController;
import org.onap.policy.drools.controller.internal.MavenDroolsControllerTest;
import org.onap.policy.drools.properties.DroolsProperties;
import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
-import org.onap.policy.drools.protocol.coders.JsonProtocolFilter.FilterRule;
import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
import org.onap.policy.drools.util.KieUtils;
import org.onap.policy.drools.utils.Triple;
@@ -64,7 +62,7 @@ public class ProtocolCoderToolsetTest {
/**
* Setup.
- *
+ *
* @throws IOException throws IO Exception
*/
@Before
@@ -96,7 +94,7 @@ public class ProtocolCoderToolsetTest {
/**
* Test the Gson toolset.
- *
+ *
* @param protocolFilter protocol filter
*/
public void testGsonToolset(JsonProtocolFilter protocolFilter) {
@@ -157,11 +155,9 @@ public class ProtocolCoderToolsetTest {
CoderFilters coderFilters = coderToolset.getCoder(Triple.class.getCanonicalName());
Assert.assertTrue(coderFilters.getCodedClass() == Triple.class.getCanonicalName());
Assert.assertTrue(coderFilters.getFilter() == protocolFilter);
- Assert.assertTrue(coderFilters.getFilter().getRules("second").size() == 1);
- Assert.assertTrue(coderFilters.getFilter().getRules("third").size() == 1);
+ Assert.assertTrue(coderFilters.getFilter().getRule() != null);
- coderFilters.getFilter().getRules("second").get(0).setRegex("^v2$");
- coderFilters.getFilter().getRules("third").get(0).setRegex(".*v3.*");
+ coderFilters.getFilter().setRule("[?($.second =~ /^v2$/ && $.third =~ /.*v3.*/)]");
tripleDecoded = (Triple<String, String, String>) coderToolset.decode(tripleEncoded);
@@ -169,8 +165,8 @@ public class ProtocolCoderToolsetTest {
Assert.assertTrue(tripleDecoded.second().equals(triple.second()));
Assert.assertTrue(tripleDecoded.third().equals(triple.third()));
- coderFilters.getFilter().deleteRules("third");
- Assert.assertTrue(coderFilters.getFilter().getRules("third").isEmpty());
+ coderFilters.getFilter().setRule(null);
+ Assert.assertEquals("[?($ =~ /.*/)]", coderFilters.getFilter().getRule());
tripleDecoded = (Triple<String, String, String>) coderToolset.decode(tripleEncoded);
@@ -178,7 +174,7 @@ public class ProtocolCoderToolsetTest {
Assert.assertTrue(tripleDecoded.second().equals(triple.second()));
Assert.assertTrue(tripleDecoded.third().equals(triple.third()));
- coderFilters.getFilter().addRule("third", ".*v3.*");
+ coderFilters.getFilter().setRule("[?($.third =~ /.*v3.*/)]");
}
private String encode(ProtocolCoderToolset coderToolset, Triple<String, String, String> triple) {
@@ -188,10 +184,8 @@ public class ProtocolCoderToolsetTest {
}
private void addRemoveCoder(ProtocolCoderToolset coderToolset) {
- List<FilterRule> filters = new ArrayList<>();
- filters.add(new FilterRule("second", ".*"));
-
- coderToolset.addCoder(this.getClass().getCanonicalName(), new JsonProtocolFilter(filters), 654321);
+ coderToolset.addCoder(this.getClass().getCanonicalName(),
+ new JsonProtocolFilter("[?($.second =~ /.*/)]"), 654321);
Assert.assertTrue(coderToolset.getCoders().size() == 2);
coderToolset.removeCoders(this.getClass().getCanonicalName());
@@ -199,22 +193,18 @@ public class ProtocolCoderToolsetTest {
}
private void updateCoderFilterRule(ProtocolCoderToolset coderToolset) {
- List<FilterRule> filters = new ArrayList<>();
- filters.add(new FilterRule("third", ".*"));
- coderToolset.addCoder(Triple.class.getCanonicalName(), new JsonProtocolFilter(filters), 654321);
+ coderToolset.addCoder(Triple.class.getCanonicalName(), new JsonProtocolFilter("[?($.third =~ /.*/)]"), 654321);
Assert.assertTrue(coderToolset.getCoders().size() == 1);
Assert.assertTrue(coderToolset.getCoder(Triple.class.getCanonicalName()).getModelClassLoaderHash() == 654321);
Assert.assertTrue(
- coderToolset.getCoder(Triple.class.getCanonicalName()).getFilter().getRules("third").size() == 1);
-
- Assert.assertTrue(
- coderToolset.getCoder(Triple.class.getCanonicalName()).getFilter().getRules("third").size() == 1);
+ coderToolset.getCoder(
+ Triple.class.getCanonicalName()).getFilter().getRule() != null);
- Assert.assertTrue(".*".equals(coderToolset.getCoder(Triple.class.getCanonicalName()).getFilter()
- .getRules("third").get(0).getRegex()));
+ Assert.assertTrue("[?($.third =~ /.*/)]".equals(coderToolset.getCoder(Triple.class.getCanonicalName())
+ .getFilter().getRule()));
}
private void validateInitialization(JsonProtocolFilter protocolFilter, ProtocolCoderToolset coderToolset) {
@@ -254,11 +244,6 @@ public class ProtocolCoderToolsetTest {
}
private JsonProtocolFilter createFilterSet() {
- List<FilterRule> filters = new ArrayList<>();
- filters.add(new FilterRule("first", ".*"));
- filters.add(new FilterRule("second", "^blah.*"));
- filters.add(new FilterRule("third", "^hello$"));
-
- return new JsonProtocolFilter(filters);
+ return new JsonProtocolFilter("[?($.first =~ /.*/ && $.second =~ /^blah.*/ && $.third =~ /^hello$/)]");
}
}