Optimization improvements and test cases 35/97635/5
authorPamela Dragosh <pdragosh@research.att.com>
Mon, 28 Oct 2019 12:51:10 +0000 (08:51 -0400)
committerPamela Dragosh <pdragosh@research.att.com>
Mon, 28 Oct 2019 18:06:51 +0000 (14:06 -0400)
* StdBaseTranslator added helpful support methods and
can now add obligations to either rule, policy or policy sets.
* StdMatchablePolicyRequest improved to support optional
policy-type as part of the request to refine the output results.
* Added more tests to ensure that the decision is returning the
appropriate results.
* Added more Javadoc for code.
* Added some sonar fix for either log or throw exception.

Issue-ID: POLICY-2066
Change-Id: I90d6d90c2cdbb627e96cfce1d2632b2439a1e477
Signed-off-by: Pamela Dragosh <pdragosh@research.att.com>
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java
applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java
applications/optimization/src/test/java/org/onap/policy/xacml/pdp/application/optimization/OptimizationPdpApplicationTest.java
applications/optimization/src/test/resources/decision.optimization.input.json [moved from main/src/test/resources/decisions/decision.optimization.affinity.input.json with 77% similarity]
applications/optimization/src/test/resources/vCPE.policies.optimization.input.tosca.yaml

index 48da996..d2d383b 100644 (file)
@@ -28,16 +28,23 @@ import com.att.research.xacml.api.Obligation;
 import com.att.research.xacml.api.Request;
 import com.att.research.xacml.api.Response;
 import com.att.research.xacml.api.Result;
+import com.att.research.xacml.api.XACML3;
 import com.google.gson.Gson;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
 import org.onap.policy.models.decisions.concepts.DecisionRequest;
@@ -46,6 +53,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
+import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
 import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -99,6 +107,13 @@ public class StdBaseTranslator implements ToscaPolicyTranslator {
         return decisionResponse;
     }
 
+    /**
+     * scanObligations - scans the list of obligations and make appropriate method calls to process
+     * obligations.
+     *
+     * @param obligations Collection of obligation objects
+     * @param decisionResponse DecisionResponse object used to store any results from obligations.
+     */
     protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
         for (Obligation obligation : obligations) {
             LOGGER.info("Obligation: {}", obligation);
@@ -109,6 +124,12 @@ public class StdBaseTranslator implements ToscaPolicyTranslator {
         }
     }
 
+    /**
+     * processObligationAttribute - processes an individual obligation attribute assignment object.
+     *
+     * @param assignment AttributeAssignment object
+     * @param decisionResponse DecisionResponse object used to store any results from attribute assignment.
+     */
     @SuppressWarnings("unchecked")
     protected void processObligationAttribute(AttributeAssignment assignment, DecisionResponse decisionResponse) {
         //
@@ -169,11 +190,21 @@ public class StdBaseTranslator implements ToscaPolicyTranslator {
         return policy;
     }
 
-    protected RuleType addObligation(RuleType rule, String jsonPolicy) {
+    /**
+     * addObligation - general code to add a json policy as an obligation. Probably could just
+     * return the obligation only instead of adding it directly to a rule/policy/policyset.
+     * But this is fine for now.
+     *
+     * @param <T> RuleType, PolicyType, PolicySetType object
+     * @param ruleOrPolicy Incoming RuleType, PolicyType, PolicySetType object
+     * @param jsonPolicy JSON String representation of policy.
+     * @return Return the Incoming RuleType, PolicyType, PolicySetType object for convenience.
+     */
+    protected <T> T addObligation(T ruleOrPolicy, String jsonPolicy) {
         //
-        // Convert the YAML Policy to JSON Object
+        // Creating obligation for returning policy
         //
-        LOGGER.info("JSON Optimization Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy);
+        LOGGER.info("Obligation Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy);
         //
         // Create an AttributeValue for it
         //
@@ -196,12 +227,124 @@ public class StdBaseTranslator implements ToscaPolicyTranslator {
         obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
         obligation.getAttributeAssignmentExpression().add(expressionType);
         //
-        // Now we can add it into the rule
+        // Now we can add it into the rule/policy/policyset
         //
         ObligationExpressionsType obligations = new ObligationExpressionsType();
         obligations.getObligationExpression().add(obligation);
-        rule.setObligationExpressions(obligations);
-        return rule;
+        if (ruleOrPolicy instanceof RuleType) {
+            ((RuleType) ruleOrPolicy).setObligationExpressions(obligations);
+        } else if (ruleOrPolicy instanceof PolicyType) {
+            ((PolicyType) ruleOrPolicy).setObligationExpressions(obligations);
+        } else if (ruleOrPolicy instanceof PolicySetType) {
+            ((PolicySetType) ruleOrPolicy).setObligationExpressions(obligations);
+        }
+        //
+        // Return as a convenience
+        //
+        return ruleOrPolicy;
+    }
+
+    /**
+     * generateAnyOfForPolicyType - Creates a specific AnyOfType that includes the check
+     * to match on a specific TOSCA Policy Type.
+     *
+     * @param type String represenatation of TOSCA Policy Type (eg. "onap.policies.Foo")
+     * @return AnyOfType object
+     */
+    protected AnyOfType generateAnyOfForPolicyType(String type) {
+        //
+        // Create the match for the policy type
+        //
+        MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
+                XACML3.ID_FUNCTION_STRING_EQUAL,
+                type,
+                XACML3.ID_DATATYPE_STRING,
+                ToscaDictionary.ID_RESOURCE_POLICY_TYPE,
+                XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
+        //
+        // Add it to an AnyOfType object
+        //
+        AnyOfType anyOf = new AnyOfType();
+        anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
+        //
+        // Return new AnyOfType
+        //
+        return anyOf;
+    }
+
+    /**
+     * generateConditionForPolicyType - create a ConditionType XACML object
+     * that is able to determine if a request specifies a specific policy type
+     * that the policy is created from, only then is the rule applied. If the
+     * request doesn't even care about the policy type (eg it is missing) then
+     * return the rule should not apply.
+     *
+     * @param type PolicyType (eg. onap.policies.Foo
+     * @return ConditionType object
+     */
+    protected ConditionType generateConditionForPolicyType(String type) {
+        //
+        // Create an ApplyType that checks if the request contains the
+        // policy-type attribute
+        //
+        AttributeDesignatorType designator = new AttributeDesignatorType();
+        designator.setAttributeId(ToscaDictionary.ID_RESOURCE_POLICY_TYPE.stringValue());
+        designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue());
+        designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
+
+        ApplyType applyBagSize = new ApplyType();
+        applyBagSize.setDescription("Get the size of policy-type attributes");
+        applyBagSize.setFunctionId(XACML3.ID_FUNCTION_STRING_BAG_SIZE.stringValue());
+
+        AttributeValueType valueZero = new AttributeValueType();
+        valueZero.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue());
+        valueZero.getContent().add("0");    // Yes really - represent as a string
+
+        ObjectFactory factory = new ObjectFactory();
+        applyBagSize.getExpression().add(factory.createAttributeDesignator(designator));
+
+        ApplyType applyGreaterThan = new ApplyType();
+        applyGreaterThan.setDescription("Does the policy-type attribute exist?");
+        applyGreaterThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_EQUAL.stringValue());
+
+        applyGreaterThan.getExpression().add(factory.createApply(applyBagSize));
+        applyGreaterThan.getExpression().add(factory.createAttributeValue(valueZero));
+
+        //
+        // Create an apply type that checks the actual value
+        //
+        AttributeValueType value = new AttributeValueType();
+        value.setDataType(XACML3.ID_DATATYPE_STRING.stringValue());
+        value.getContent().add(type);
+
+        //
+        // Create string-is-in apply - which determines if the policy-type
+        // is in the request bag of resources for policy-type
+        //
+        ApplyType applyIsIn = new ApplyType();
+        applyIsIn.setDescription("Is this policy-type in the list?");
+        applyIsIn.setFunctionId(XACML3.ID_FUNCTION_STRING_IS_IN.stringValue());
+        applyIsIn.getExpression().add(factory.createAttributeValue(value));
+        applyIsIn.getExpression().add(factory.createAttributeDesignator(designator));
+
+        //
+        // Create our outer apply
+        //
+        ApplyType applyOr = new ApplyType();
+        applyOr.setDescription("IF exists and is equal");
+        applyOr.setFunctionId(XACML3.ID_FUNCTION_OR.stringValue());
+
+        applyOr.getExpression().add(factory.createApply(applyGreaterThan));
+        applyOr.getExpression().add(factory.createApply(applyIsIn));
+
+        //
+        // Finally create the condition
+        //
+        ConditionType condition = new ConditionType();
+
+        condition.setExpression(factory.createApply(applyOr));
+
+        return condition;
     }
 
 }
index 0f3a033..b478e8c 100644 (file)
@@ -59,6 +59,8 @@ public class StdMatchablePolicyRequest {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchablePolicyRequest.class);
 
+    public static final String POLICY_TYPE_KEY = "policy-type";
+
     @XACMLSubject(includeInResults = true)
     private String onapName;
 
@@ -120,7 +122,7 @@ public class StdMatchablePolicyRequest {
         try {
             xacmlRequest = RequestParser.parseRequest(request);
         } catch (IllegalAccessException | DataTypeException e) {
-            throw new XacmlApplicationException("Could not parse request " + e.getLocalizedMessage());
+            throw new XacmlApplicationException("Could not parse request ", e);
         }
         //
         // Create an object we can add to
@@ -133,6 +135,15 @@ public class StdMatchablePolicyRequest {
         //
         Map<String, Object> resources = decisionRequest.getResource();
         for (Entry<String, Object> entrySet : resources.entrySet()) {
+            //
+            // Check for special policy-type
+            //
+            String attributeId;
+            if (POLICY_TYPE_KEY.equals(entrySet.getKey())) {
+                attributeId = ToscaDictionary.ID_RESOURCE_POLICY_TYPE.stringValue();
+            } else {
+                attributeId = ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey();
+            }
             //
             // Making an assumption that these fields are matchable.
             // Its possible we may have to load the policy type model
@@ -140,12 +151,12 @@ public class StdMatchablePolicyRequest {
             //
             try {
                 if (entrySet.getValue() instanceof Collection) {
-                    addResources(resourceAttributes, (Collection) entrySet.getValue(), entrySet.getKey());
+                    addResources(resourceAttributes, (Collection) entrySet.getValue(), attributeId);
                 } else {
-                    addResources(resourceAttributes, Arrays.asList(entrySet.getValue().toString()), entrySet.getKey());
+                    addResources(resourceAttributes, Arrays.asList(entrySet.getValue().toString()), attributeId);
                 }
             } catch (DataTypeException e) {
-                throw new XacmlApplicationException("Failed to add resource " + e.getLocalizedMessage());
+                throw new XacmlApplicationException("Failed to add resource ", e);
             }
         }
         mutableRequest.add(resourceAttributes);
@@ -162,7 +173,7 @@ public class StdMatchablePolicyRequest {
         for (Object value : values) {
             StdMutableAttribute mutableAttribute    = new StdMutableAttribute();
             mutableAttribute.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
-            mutableAttribute.setAttributeId(new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + id));
+            mutableAttribute.setAttributeId(new IdentifierImpl(id));
             mutableAttribute.setIncludeInResults(true);
 
             DataType<?> dataTypeExtended    = factory.getDataType(XACML3.ID_DATATYPE_STRING);
@@ -175,5 +186,4 @@ public class StdMatchablePolicyRequest {
         }
         return attributes;
     }
-
 }
index 5908f53..7b47ad1 100644 (file)
@@ -109,12 +109,12 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         //
         // Get the TOSCA Policy Type for this policy
         //
-        Collection<ToscaPolicyType> policyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier());
+        Collection<ToscaPolicyType> toscaPolicyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier());
         //
-        // If we don't have any policy types, then we cannot know
+        // If we don't have any TOSCA policy types, then we cannot know
         // which properties are matchable.
         //
-        if (policyTypes.isEmpty()) {
+        if (toscaPolicyTypes.isEmpty()) {
             throw new ToscaPolicyConversionException(
                     "Cannot retrieve Policy Type definition for policy " + toscaPolicy.getName());
         }
@@ -134,7 +134,7 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         //
         // There should be a metadata section
         //
-        this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
+        fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
         //
         // Set the combining rule
         //
@@ -142,17 +142,7 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         //
         // Generate the TargetType
         //
-        newPolicyType.setTarget(generateTargetType(toscaPolicy.getProperties(), policyTypes));
-        //
-        // Now create the Permit Rule
-        // No target since the policy has a target
-        // With obligations.
-        //
-        RuleType rule = new RuleType();
-        rule.setDescription("Default is to PERMIT if the policy matches.");
-        rule.setRuleId(policyName + ":rule");
-        rule.setEffect(EffectType.PERMIT);
-        rule.setTarget(new TargetType());
+        newPolicyType.setTarget(new TargetType());
         //
         // Now represent the policy as Json
         //
@@ -163,13 +153,41 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         } catch (CoderException e) {
             throw new ToscaPolicyConversionException("Failed to encode policy to json", e);
         }
-        addObligation(rule, jsonPolicy);
+        //
+        // Add it as an obligation
+        //
+        addObligation(newPolicyType, jsonPolicy);
+        //
+        // Now create the Permit Rule for all the
+        // matchable properties.
+        //
+        RuleType rule = new RuleType();
+        rule.setDescription("Default is to PERMIT if the policy matches.");
+        rule.setRuleId(policyName + ":rule");
+        rule.setEffect(EffectType.PERMIT);
+        rule.setTarget(generateTargetType(toscaPolicy.getProperties(), toscaPolicyTypes));
+        rule.setCondition(generateConditionForPolicyType(toscaPolicy.getType()));
+        //
+        // Add the rule to the policy
+        //
+        newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
+        //
+        // If a Decision is for a specific policy-type, make sure
+        // there is a rule for it.
+        //
+        rule = new RuleType();
+        rule.setDescription("Match on policy-type " + toscaPolicy.getType());
+        rule.setRuleId(policyName + ":rule:policy-type");
+        rule.setEffect(EffectType.PERMIT);
+        TargetType target = new TargetType();
+        target.getAnyOf().add(this.generateAnyOfForPolicyType(toscaPolicy.getType()));
+        rule.setTarget(target);
         //
         // Add the rule to the policy
         //
         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
         //
-        // Return our new policy
+        // Log output of the policy
         //
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
             XACMLPolicyWriter.writePolicyFile(os, newPolicyType);
@@ -177,18 +195,20 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         } catch (IOException e) {
             LOGGER.error("Failed to create byte array stream", e);
         }
+        //
+        // Done
+        //
         return newPolicyType;
     }
 
     /**
-     * For generating target type, we are scan for matchable properties
+     * For generating target type, we scan for matchable properties
      * and use those to build the policy.
      *
      * @param properties Properties section of policy
      * @param policyTypes Collection of policy Type to find matchable metadata
      * @return TargetType object
      */
-    @SuppressWarnings("unchecked")
     protected TargetType generateTargetType(Map<String, Object> properties, Collection<ToscaPolicyType> policyTypes) {
         TargetType targetType = new TargetType();
         //
@@ -200,25 +220,21 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
             //
             if (isMatchable(entrySet.getKey(), policyTypes)) {
                 LOGGER.info("Found matchable property {}", entrySet.getValue());
-                if (entrySet.getValue() instanceof Collection) {
-                    AnyOfType anyOf = generateMatches((Collection<Object>) entrySet.getValue(),
-                            new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey()));
-                    if (! anyOf.getAllOf().isEmpty()) {
-                        targetType.getAnyOf().add(anyOf);
-                    }
-                } else {
-                    AnyOfType anyOf = generateMatches(Arrays.asList(entrySet.getValue()),
-                            new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey()));
-                    if (! anyOf.getAllOf().isEmpty()) {
-                        targetType.getAnyOf().add(anyOf);
-                    }
-                }
+                generateMatchable(targetType, entrySet.getKey(), entrySet.getValue());
             }
         }
 
         return targetType;
     }
 
+    /**
+     * isMatchable - Iterates through available TOSCA Policy Types to determine if a property
+     * should be treated as matchable.
+     *
+     * @param propertyName Name of property
+     * @param policyTypes Collection of TOSCA Policy Types to scan
+     * @return true if matchable
+     */
     protected boolean isMatchable(String propertyName, Collection<ToscaPolicyType> policyTypes) {
         for (ToscaPolicyType policyType : policyTypes) {
             for (Entry<String, ToscaProperty> propertiesEntry : policyType.getProperties().entrySet()) {
@@ -236,6 +252,42 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         return false;
     }
 
+    /**
+     * generateMatchable - Given the object, generates list of MatchType objects and add them
+     * to the TargetType object.
+     *
+     * @param targetType TargetType object to add matches to
+     * @param key Property key
+     * @param value Object is the value - which can be a Collection or single Object
+     * @return TargetType incoming TargetType returned as a convenience
+     */
+    @SuppressWarnings("unchecked")
+    protected TargetType generateMatchable(TargetType targetType, String key, Object value) {
+        if (value instanceof Collection) {
+            AnyOfType anyOf = generateMatches((Collection<Object>) value,
+                    new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
+            if (! anyOf.getAllOf().isEmpty()) {
+                targetType.getAnyOf().add(anyOf);
+            }
+        } else {
+            AnyOfType anyOf = generateMatches(Arrays.asList(value),
+                    new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
+            if (! anyOf.getAllOf().isEmpty()) {
+                targetType.getAnyOf().add(anyOf);
+            }
+        }
+        return targetType;
+    }
+
+    /**
+     * generateMatches - Goes through the collection of objects, creates a MatchType object
+     * for each object and associates it with the given attribute Id. Returns the AnyOfType
+     * object that contains all the generated MatchType objects.
+     *
+     * @param matchables Collection of object to generate MatchType from
+     * @param attributeId Given attribute Id for each MatchType
+     * @return AnyOfType object
+     */
     protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
         //
         // This is our outer AnyOf - which is an OR
@@ -346,11 +398,17 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
             //
             childPolicyType = parentPolicyType;
         }
-
-
         return listTypes;
     }
 
+    /**
+     * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or
+     * then tries to find it either locally on disk or pull it from the Policy
+     * Lifecycle API the given TOSCA Policy Type.
+     *
+     * @param policyTypeId ToscaPolicyTypeIdentifier to find
+     * @return ToscaPolicyType object. Can be null if failure.
+     */
     private ToscaPolicyType findPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
         //
         // Is it loaded in memory?
@@ -368,6 +426,14 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         return policyType;
     }
 
+    /**
+     * loadPolicyType - Tries to load the given ToscaPolicyTypeIdentifier from local
+     * storage. If it does not exist, will then attempt to pull from Policy Lifecycle
+     * API.
+     *
+     * @param policyTypeId ToscaPolicyTypeIdentifier input
+     * @return ToscaPolicyType object. Null if failure.
+     */
     private ToscaPolicyType loadPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
         //
         // Construct what the file name should be
@@ -392,6 +458,10 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
             //
             return this.pullPolicyType(policyTypeId, policyTypePath);
         }
+        //
+        // Success - we have read locally the policy type. Now bring it into our
+        // return object.
+        //
         LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath());
         try {
             ToscaServiceTemplate serviceTemplate = standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8),
@@ -432,6 +502,14 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         return null;
     }
 
+    /**
+     * pullPolicyType - pulls the given ToscaPolicyTypeIdentifier from the Policy Lifecycle API.
+     * If successful, will store it locally given the policyTypePath.
+     *
+     * @param policyTypeId ToscaPolicyTypeIdentifier
+     * @param policyTypePath Path object to store locally
+     * @return ToscaPolicyType object. Null if failure.
+     */
     private synchronized ToscaPolicyType pullPolicyType(ToscaPolicyTypeIdentifier policyTypeId, Path policyTypePath) {
         //
         // This is what we return
@@ -460,6 +538,13 @@ public class StdMatchableTranslator  extends StdBaseTranslator {
         return policyType;
     }
 
+    /**
+     * constructLocalFilePath - common method to ensure the name of the local file for the
+     * policy type is the same.
+     *
+     * @param policyTypeId ToscaPolicyTypeIdentifier
+     * @return Path object
+     */
     private Path constructLocalFilePath(ToscaPolicyTypeIdentifier policyTypeId) {
         return Paths.get(this.pathForData.toAbsolutePath().toString(), policyTypeId.getName() + "-"
                 + policyTypeId.getVersion() + ".yaml");
index b84ec07..be553cf 100644 (file)
@@ -32,10 +32,16 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.ServiceLoader;
+import jersey.repackaged.com.google.common.collect.Lists;
 import org.apache.commons.lang3.tuple.Pair;
+import org.assertj.core.api.Condition;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.FixMethodOrder;
@@ -65,7 +71,7 @@ public class OptimizationPdpApplicationTest {
     private static File propertiesFile;
     private static XacmlApplicationServiceProvider service;
     private static StandardCoder gson = new StandardCoder();
-    private static DecisionRequest requestAffinity;
+    private static DecisionRequest baseRequest;
     private static RestServerParameters clientParams;
     private static String[] listPolicyTypeFiles = {
         "onap.policies.Optimization",
@@ -95,10 +101,10 @@ public class OptimizationPdpApplicationTest {
         //
         // Load Single Decision Request
         //
-        requestAffinity = gson.decode(
+        baseRequest = gson.decode(
                 TextFileUtils
                     .getTextFileAsString(
-                            "../../main/src/test/resources/decisions/decision.optimization.affinity.input.json"),
+                            "src/test/resources/decision.optimization.input.json"),
                     DecisionRequest.class);
         //
         // Setup our temporary folder
@@ -154,7 +160,7 @@ public class OptimizationPdpApplicationTest {
     }
 
     @Test
-    public void test1Basics() {
+    public void test01Basics() {
         //
         // Make sure there's an application name
         //
@@ -175,11 +181,11 @@ public class OptimizationPdpApplicationTest {
     }
 
     @Test
-    public void test2NoPolicies() {
+    public void test02NoPolicies() {
         //
-        // Ask for a decision
+        // Ask for a decision when there are no policies loaded
         //
-        Pair<DecisionResponse, Response> decision = service.makeDecision(requestAffinity, null);
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
         LOGGER.info("Decision {}", decision.getKey());
 
         assertThat(decision.getKey()).isNotNull();
@@ -187,23 +193,275 @@ public class OptimizationPdpApplicationTest {
     }
 
     @Test
-    public void test3AddOptimizationPolicies() throws CoderException, FileNotFoundException, IOException,
+    public void test03OptimizationDefault() throws CoderException, FileNotFoundException, IOException,
         XacmlApplicationException {
         //
-        // Now load the optimization policies
+        // Now load all the optimization policies
         //
         TestUtils.loadPolicies("src/test/resources/vCPE.policies.optimization.input.tosca.yaml", service);
         //
-        // Ask for a decision
+        // Ask for a decision for default
         //
-        Pair<DecisionResponse, Response> decision = service.makeDecision(requestAffinity, null);
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(1);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test04OptimizationDefaultGeography() throws CoderException {
+        //
+        // Add US to the geography list
+        //
+        ((List<String>)baseRequest.getResource().get("geography")).add("US");
+        //
+        // Ask for a decision for default US Policy
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(2);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test05OptimizationDefaultGeographyAndService() throws CoderException {
+        //
+        // Add vCPE to the service list
+        //
+        ((List<String>)baseRequest.getResource().get("services")).add("vCPE");
+        //
+        // Ask for a decision for default US policy for vCPE service
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(5);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test06OptimizationDefaultGeographyAndServiceAndResource() throws CoderException {
+        //
+        // Add vCPE to the service list
+        //
+        ((List<String>)baseRequest.getResource().get("resources")).add("vG");
+        //
+        // Ask for a decision for default US service vCPE resource vG policy
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(9);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test07OptimizationGeographyAndServiceAndResourceAndScope() throws CoderException {
+        //
+        // Add gold as a scope
+        //
+        ((List<String>)baseRequest.getResource().get("scope")).add("gold");
+        //
+        // Ask for a decision for specific US vCPE vG gold
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(10);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test08OptimizationGeographyAndServiceAndResourceAndScopeIsGoldOrPlatinum() throws CoderException {
+        //
+        // Add platinum to the scope list: this is now gold OR platinum
+        //
+        ((List<String>)baseRequest.getResource().get("scope")).add("platinum");
+        //
+        // Ask for a decision for specific US vCPE vG (gold or platinum)
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(11);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test09OptimizationGeographyAndServiceAndResourceAndScopeNotGold() throws CoderException {
+        //
+        // Add gold as a scope
+        //
+        ((List<String>)baseRequest.getResource().get("scope")).remove("gold");
+        //
+        // Ask for a decision for specific US vCPE vG gold
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(11);
+        //
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+        //
+        // Validate it
+        //
+        validateDecision(decision.getKey(), baseRequest);
+    }
+
+    @Test
+    public void test10OptimizationPolicyTypeDefault() throws CoderException {
+        //
+        // Remove all the other resources from the request
+        //
+        cleanOutResources();
+        //
+        // Add in policy type
+        //
+        List<String> policyTypes = Lists.newArrayList("onap.policies.optimization.AffinityPolicy");
+        baseRequest.getResource().put("policy-type", policyTypes);
+        //
+        // Ask for a decision for default
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
         LOGGER.info("Decision {}", decision.getKey());
 
         assertThat(decision.getKey()).isNotNull();
         assertThat(decision.getKey().getPolicies().size()).isEqualTo(4);
         //
-        // Dump it out as Json
+        // Double check that the contents are what we expect
+        //
+        LOGGER.info(gson.encode(decision.getKey()));
+    }
+
+    @Test
+    public void test20OptimizationPolicyTypeDefault() throws CoderException {
+        //
+        // Remove all the other resources from the request
+        //
+        cleanOutResources();
+        //
+        // Add in policy type
+        //
+        List<String> policyTypes = Lists.newArrayList("onap.policies.optimization.HpaPolicy");
+        baseRequest.getResource().put("policy-type", policyTypes);
+        //
+        // Ask for a decision for default
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(baseRequest, null);
+        LOGGER.info("Decision {}", decision.getKey());
+
+        assertThat(decision.getKey()).isNotNull();
+        assertThat(decision.getKey().getPolicies().size()).isEqualTo(1);
+        //
+        // Double check that the contents are what we expect
         //
         LOGGER.info(gson.encode(decision.getKey()));
     }
+
+    @SuppressWarnings("unchecked")
+    private void validateDecision(DecisionResponse decision, DecisionRequest request) {
+        for (Entry<String, Object> entrySet : decision.getPolicies().entrySet()) {
+            LOGGER.info("Decision Returned Policy {}", entrySet.getKey());
+            assertThat(entrySet.getValue()).isInstanceOf(Map.class);
+            Map<String, Object> policyContents = (Map<String, Object>) entrySet.getValue();
+            assertThat(policyContents.containsKey("properties")).isTrue();
+            assertThat(policyContents.get("properties")).isInstanceOf(Map.class);
+            Map<String, Object> policyProperties = (Map<String, Object>) policyContents.get("properties");
+
+            validateMatchable((Collection<String>) request.getResource().get("scope"),
+                    (Collection<String>) policyProperties.get("scope"));
+
+            validateMatchable((Collection<String>) request.getResource().get("services"),
+                    (Collection<String>) policyProperties.get("services"));
+
+            validateMatchable((Collection<String>) request.getResource().get("resources"),
+                    (Collection<String>) policyProperties.get("resources"));
+
+            validateMatchable((Collection<String>) request.getResource().get("geography"),
+                    (Collection<String>) policyProperties.get("geography"));
+        }
+    }
+
+    private void validateMatchable(Collection<String> requestList, Collection<String> policyProperties) {
+        LOGGER.info("Validating matchable: {} with {}", policyProperties, requestList);
+        //
+        // Null or empty implies '*' - that is any value is acceptable
+        // for this policy.
+        //
+        if (policyProperties == null || policyProperties.isEmpty()) {
+            return;
+        }
+        Condition<String> condition = new Condition<>(
+                requestList::contains,
+                "Request list is contained");
+        assertThat(policyProperties).haveAtLeast(1, condition);
+
+    }
+
+    @SuppressWarnings("unchecked")
+    private void cleanOutResources() {
+        ((List<String>)baseRequest.getResource().get("scope")).clear();
+        ((List<String>)baseRequest.getResource().get("services")).clear();
+        ((List<String>)baseRequest.getResource().get("resources")).clear();
+        ((List<String>)baseRequest.getResource().get("geography")).clear();
+    }
 }
@@ -5,8 +5,8 @@
   "action": "optimize",
   "resource": {
       "scope": [],
-      "services": ["vCPE"],
+      "services": [],
       "resources": [],
-      "geography": ["US"]
+      "geography": []
   }
 }
\ No newline at end of file
index 8088814..919a2f8 100644 (file)
@@ -9,6 +9,24 @@ topology_template:
             metadata:
                 policy-id: OSDF_CASABLANCA.Affinity_Default
                 policy-version: 1
+            properties:
+                scope: []
+                services: []
+                resources: []
+                geography: []
+                identity: affinity_vCPE
+                applicableResources: any
+                affinityProperties:
+                    qualifier: same
+                    category: complex
+    -
+        OSDF_CASABLANCA.Affinity_Default_US:
+            type: onap.policies.optimization.AffinityPolicy
+            version: 1.0.0
+            type_version: 1.0.0
+            metadata:
+                policy-id: OSDF_CASABLANCA.Affinity_Default_US
+                policy-version: 1
             properties:
                 scope: []
                 services: []
@@ -20,12 +38,12 @@ topology_template:
                     qualifier: same
                     category: complex
     -
-        OSDF_CASABLANCA.Affinity_vCPE_0:
+        OSDF_CASABLANCA.Affinity_Default_vCPE_0:
             type: onap.policies.optimization.AffinityPolicy
             version: 1.0.0
             type_version: 1.0.0
             metadata:
-                policy-id: OSDF_CASABLANCA.Affinity_vCPE_0
+                policy-id: OSDF_CASABLANCA.Affinity_Default_vCPE_0
                 policy-version: 1
             properties:
                 scope: []