Add validation methods for PAP REST API

Also made the identifier classes comparable.
Stupid tabs.

Change-Id: I54c0595c6a2c61a1b72b58fe1d667657f9d5d71e
Issue-ID: POLICY-1542
Signed-off-by: Jim Hahn <jrh3@att.com>
diff --git a/models-pdp/pom.xml b/models-pdp/pom.xml
index 8eb7231..a3146fa 100644
--- a/models-pdp/pom.xml
+++ b/models-pdp/pom.xml
@@ -45,6 +45,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+          <groupId>org.onap.policy.common</groupId>
+          <artifactId>common-parameters</artifactId>
+          <version>${policy.common.version}</version>
+        </dependency>
+        <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
             <scope>test</scope>
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java
index 8805ae9..53bf3c1 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroup.java
@@ -21,13 +21,17 @@
 
 package org.onap.policy.models.pdp.concepts;
 
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-
+import java.util.Set;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.ObjectValidationResult;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.common.parameters.ValidationStatus;
 import org.onap.policy.models.base.PfNameVersion;
 import org.onap.policy.models.base.PfUtils;
 import org.onap.policy.models.pdp.enums.PdpState;
@@ -78,4 +82,55 @@
     public int compareTo(final PdpGroup other) {
         return compareNameVersion(this, other);
     }
+
+    /**
+     * Validates that appropriate fields are populated for an incoming call to the PAP
+     * REST API.
+     *
+     * @return the validation result
+     */
+    public ValidationResult validatePapRest() {
+        BeanValidationResult result = new BeanValidationResult("group", this);
+
+        /*
+         * Don't care about version or state, because we override them. Ok if description
+         * is null.
+         */
+
+        result.validateNotNull("name", name);
+        result.validateNotNullList("pdpSubgroups", pdpSubgroups, PdpSubGroup::validatePapRest);
+
+        checkDuplicateSubgroups(result);
+
+        return result;
+    }
+
+    /**
+     * Checks for duplicate subgroups.
+     *
+     * @param result where to place validation results
+     */
+    private void checkDuplicateSubgroups(BeanValidationResult result) {
+        if (pdpSubgroups == null) {
+            return;
+        }
+
+        Set<String> set = new HashSet<>();
+
+        for (PdpSubGroup subgrp : pdpSubgroups) {
+            if (subgrp == null) {
+                continue;
+            }
+
+            String pdpType = subgrp.getPdpType();
+            if (pdpType == null) {
+                continue;
+            }
+
+            if (!set.add(pdpType)) {
+                result.addResult(new ObjectValidationResult("subgroups", pdpType, ValidationStatus.INVALID,
+                                "duplicate subgroup"));
+            }
+        }
+    }
 }
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java
index 32e0b1a..a7e05dd 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpGroups.java
@@ -24,14 +24,14 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.ValidationResult;
 
 /**
- * Request deploy or update a set of groups via the PDP Group deployment
- * REST API.
+ * Request deploy or update a set of groups via the PDP Group deployment REST API.
  */
 @Getter
 @Setter
@@ -41,14 +41,29 @@
 
     /**
      * Get the contents of this class as a list of PDP group maps.
+     *
      * @return the PDP groups in a list of maps
      */
     public List<Map<String, PdpGroup>> toMapList() {
         final Map<String, PdpGroup> pdpGroupMap = new LinkedHashMap<>();
         for (PdpGroup pdpGroup : groups) {
-            pdpGroupMap.put(pdpGroup.getName() + ':' + pdpGroup.getVersion() , pdpGroup);
+            pdpGroupMap.put(pdpGroup.getName() + ':' + pdpGroup.getVersion(), pdpGroup);
         }
 
         return Collections.singletonList(pdpGroupMap);
     }
+
+    /**
+     * Validates that appropriate fields are populated for an incoming call to the PAP
+     * REST API.
+     *
+     * @return the validation result
+     */
+    public ValidationResult validatePapRest() {
+        BeanValidationResult result = new BeanValidationResult("groups", this);
+
+        result.validateNotNullList("groups", groups, PdpGroup::validatePapRest);
+
+        return result;
+    }
 }
diff --git a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpSubGroup.java b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpSubGroup.java
index 4054089..2618a50 100644
--- a/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpSubGroup.java
+++ b/models-pdp/src/main/java/org/onap/policy/models/pdp/concepts/PdpSubGroup.java
@@ -24,16 +24,19 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-
 import lombok.Data;
 import lombok.NonNull;
-
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.ObjectValidationResult;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.common.parameters.ValidationStatus;
 import org.onap.policy.models.base.PfUtils;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 
 /**
- * Class to represent a group of all PDP's of the same pdp type running for a particular domain.
+ * Class to represent a group of all PDP's of the same pdp type running for a particular
+ * domain.
  *
  * @author Ram Krishna Verma (ram.krishna.verma@est.tech)
  */
@@ -68,4 +71,31 @@
         this.properties = (source.properties == null ? null : new LinkedHashMap<>(source.properties));
         this.pdpInstances = PfUtils.mapList(source.pdpInstances, Pdp::new);
     }
+
+    /**
+     * Validates that appropriate fields are populated for an incoming call to the PAP
+     * REST API.
+     *
+     * @return the validation result
+     */
+    public ValidationResult validatePapRest() {
+        BeanValidationResult result = new BeanValidationResult("group", this);
+
+        result.validateNotNull("pdpType", pdpType);
+        result.validateNotNullList("supportedPolicyTypes", supportedPolicyTypes,
+                        ToscaPolicyTypeIdentifier::validatePapRest);
+        result.validateNotNullList("policies", policies, ToscaPolicyIdentifier::validatePapRest);
+
+        if (supportedPolicyTypes != null && supportedPolicyTypes.isEmpty()) {
+            result.addResult(new ObjectValidationResult("supportedPolicyTypes", supportedPolicyTypes,
+                            ValidationStatus.INVALID, "empty list"));
+        }
+
+        if (desiredInstanceCount <= 0) {
+            result.addResult(new ObjectValidationResult("desiredInstanceCount", desiredInstanceCount,
+                            ValidationStatus.INVALID, "non-positive"));
+        }
+
+        return result;
+    }
 }
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/ModelsTest.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/ModelsTest.java
index 541e00b..7a6c03c 100644
--- a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/ModelsTest.java
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/ModelsTest.java
@@ -21,12 +21,12 @@
 
 package org.onap.policy.models.pdp.concepts;
 
+import com.openpojo.reflection.filters.FilterClassName;
 import com.openpojo.reflection.filters.FilterPackageInfo;
 import com.openpojo.validation.Validator;
 import com.openpojo.validation.ValidatorBuilder;
 import com.openpojo.validation.test.impl.GetterTester;
 import com.openpojo.validation.test.impl.SetterTester;
-
 import org.junit.Test;
 import org.onap.policy.common.utils.validation.ToStringTester;
 
@@ -41,7 +41,7 @@
     @Test
     public void testPdpModels() {
         final Validator validator = ValidatorBuilder.create().with(new ToStringTester()).with(new SetterTester())
-                .with(new GetterTester()).build();
-        validator.validate(POJO_PACKAGE, new FilterPackageInfo());
+                        .with(new GetterTester()).build();
+        validator.validate(POJO_PACKAGE, new FilterPackageInfo(), new FilterClassName(PdpMessage.class.getName()));
     }
 }
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupTest.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupTest.java
index 77666b2..3082bb2 100644
--- a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupTest.java
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupTest.java
@@ -23,20 +23,30 @@
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
 import org.onap.policy.models.pdp.enums.PdpState;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 
 /**
- * Test the copy constructor, as {@link ModelsTest} tests the other methods.
+ * Test methods not tested by {@link ModelsTest}.
  */
 public class PdpGroupTest {
+    private static final String NAME = "my-name";
+    private static final String PDP_TYPE1 = "type-1";
+    private static final String PDP_TYPE2 = "type-2";
+    private static final String PDP_TYPE3 = "type-3";
 
     @Test
     public void testCopyConstructor() {
@@ -50,7 +60,7 @@
 
         // verify with all values
         orig.setDescription("my-descript");
-        orig.setName("my-name");
+        orig.setName(NAME);
         orig.setVersion("1.2.3");
         orig.setDescription("my-description");
         orig.setPdpGroupState(PdpState.SAFE);
@@ -108,4 +118,64 @@
         assertEquals(1, mapList.size());
         assertEquals(1, mapList.get(0).size());
     }
+
+    @Test
+    public void testValidatePapRest() {
+        PdpGroup group = new PdpGroup();
+        group.setName(NAME);
+
+        PdpSubGroup subgroup1 = new PdpSubGroup();
+        subgroup1.setDesiredInstanceCount(1);
+        subgroup1.setPdpType(PDP_TYPE1);
+        subgroup1.setSupportedPolicyTypes(Arrays.asList(new ToscaPolicyTypeIdentifier("a-type-name", "3.2.1")));
+        subgroup1.setPolicies(Collections.emptyList());
+
+        PdpSubGroup subgroup2 = new PdpSubGroup(subgroup1);
+        subgroup2.setPdpType(PDP_TYPE2);
+
+        PdpSubGroup subgroup3 = new PdpSubGroup(subgroup1);
+        subgroup3.setPdpType(PDP_TYPE3);
+
+        group.setPdpSubgroups(Arrays.asList(subgroup1, subgroup2, subgroup3));
+
+        // valid
+        ValidationResult result = group.validatePapRest();
+        assertNotNull(result);
+        assertTrue(result.isValid());
+        assertNull(result.getResult());
+
+        // null name
+        PdpGroup group2 = new PdpGroup(group);
+        group2.setName(null);
+        assertInvalid(group2);
+
+        // null subgroup list
+        group2 = new PdpGroup(group);
+        group2.setPdpSubgroups(null);
+        assertInvalid(group2);
+
+        // null subgroup
+        group2 = new PdpGroup(group);
+        group2.setPdpSubgroups(Arrays.asList(subgroup1, null));
+        assertInvalid(group2);
+
+        // invalid subgroup
+        group2 = new PdpGroup(group);
+        PdpSubGroup subgroupX = new PdpSubGroup(subgroup1);
+        subgroupX.setPdpType(null);
+        group2.setPdpSubgroups(Arrays.asList(subgroupX));
+        assertInvalid(group2);
+
+        // duplicate PDP type
+        group2 = new PdpGroup(group);
+        group2.setPdpSubgroups(Arrays.asList(subgroup1, subgroup2, subgroup1));
+        assertInvalid(group2);
+    }
+
+    private void assertInvalid(PdpGroup group) {
+        ValidationResult result = group.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+    }
 }
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupsTest.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupsTest.java
new file mode 100644
index 0000000..22e178d
--- /dev/null
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpGroupsTest.java
@@ -0,0 +1,93 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP Policy Models
+ * ================================================================================
+ * Copyright (C) 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.models.pdp.concepts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
+
+public class PdpGroupsTest {
+
+    @Test
+    public void testValidatePapRest_toMapList() {
+        PdpGroup group1 = new PdpGroup();
+        group1.setName("group-1");
+        group1.setPdpSubgroups(Collections.emptyList());
+
+        PdpGroup group2 = new PdpGroup();
+        group2.setName("group-2");
+        group2.setPdpSubgroups(Collections.emptyList());
+
+        PdpGroups groups = new PdpGroups();
+        groups.setGroups(Arrays.asList(group1, group2));
+
+        // valid
+        ValidationResult result = groups.validatePapRest();
+        assertNotNull(result);
+        assertTrue(result.isValid());
+        assertNull(result.getResult());
+
+        // check toMapList()
+        List<Map<String, PdpGroup>> lst = groups.toMapList();
+        assertEquals(1, lst.size());
+
+        Map<String, PdpGroup> map = lst.get(0);
+        assertEquals(2, map.size());
+
+        Iterator<PdpGroup> iter = map.values().iterator();
+        assertSame(group1, iter.next());
+        assertSame(group2, iter.next());
+
+        // null group list
+        groups = new PdpGroups();
+        groups.setGroups(null);
+        assertInvalid(groups);
+
+        // null group
+        groups = new PdpGroups();
+        groups.setGroups(Arrays.asList(group1, null));
+        assertInvalid(groups);
+
+        // invalid group
+        PdpGroup groupX = new PdpGroup(group1);
+        groupX.setName(null);
+        groups.setGroups(Arrays.asList(group1, groupX));
+        assertInvalid(groups);
+    }
+
+    private void assertInvalid(PdpGroups groups) {
+        ValidationResult result = groups.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+    }
+}
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpMessageTest.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpMessageTest.java
index bc90f64..10f3131 100644
--- a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpMessageTest.java
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpMessageTest.java
@@ -29,9 +29,6 @@
 import org.junit.Test;
 import org.onap.policy.models.pdp.enums.PdpMessageType;
 
-/**
- * Tests methods not already tested by {@link TestModels}.
- */
 public class PdpMessageTest {
     private static final String PDP_NAME = "pdpA";
     private static final String PDP_GROUP = "groupA";
diff --git a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpSubGroupTest.java b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpSubGroupTest.java
index 4284f71..7a7babe 100644
--- a/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpSubGroupTest.java
+++ b/models-pdp/src/test/java/org/onap/policy/models/pdp/concepts/PdpSubGroupTest.java
@@ -23,19 +23,28 @@
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Map;
 import java.util.TreeMap;
-
 import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 
 /**
- * Test the copy constructor, as {@link ModelsTest} tests the other methods.
+ * Test methods not tested by {@link ModelsTest}.
  */
 public class PdpSubGroupTest {
+    private static final Coder coder = new StandardCoder();
 
     @Test
     public void testCopyConstructor() {
@@ -44,10 +53,9 @@
         final PdpSubGroup orig = new PdpSubGroup();
 
         // verify with null values
-        assertEquals(
-                "PdpSubGroup(pdpType=null, supportedPolicyTypes=[], policies=[], "
+        assertEquals("PdpSubGroup(pdpType=null, supportedPolicyTypes=[], policies=[], "
                         + "currentInstanceCount=0, desiredInstanceCount=0, properties=null, pdpInstances=[])",
-                new PdpSubGroup(orig).toString());
+                        new PdpSubGroup(orig).toString());
 
         // verify with all values
         orig.setCurrentInstanceCount(10);
@@ -80,4 +88,113 @@
 
         assertEquals(orig.toString(), new PdpSubGroup(orig).toString());
     }
+
+    @Test
+    public void testValidatePapRest() throws Exception {
+        PdpSubGroup subgrp = new PdpSubGroup();
+
+        subgrp.setDesiredInstanceCount(1);
+        subgrp.setPdpType("pdp-type");
+        subgrp.setSupportedPolicyTypes(Arrays.asList(makeIdent("type-X", "3.0.0", ToscaPolicyTypeIdentifier.class)));
+        subgrp.setPolicies(Arrays.asList(makeIdent("policy-X", "4.0.0", ToscaPolicyIdentifier.class)));
+
+        // valid
+        ValidationResult result = subgrp.validatePapRest();
+        assertNotNull(result);
+        assertTrue(result.isValid());
+        assertNull(result.getResult());
+
+        // zero count
+        PdpSubGroup sub2 = new PdpSubGroup(subgrp);
+        sub2.setDesiredInstanceCount(0);
+        assertInvalid(sub2);
+
+        // negative count
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.setDesiredInstanceCount(-1);
+        assertInvalid(sub2);
+
+        // null pdp type
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.setPdpType(null);
+        assertInvalid(sub2);
+
+        // null policy types
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.setSupportedPolicyTypes(null);
+        assertInvalid(sub2);
+
+        // empty policy types
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.setSupportedPolicyTypes(Collections.emptyList());
+        assertInvalid(sub2);
+
+        // null policy type item
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.getSupportedPolicyTypes().set(0, null);
+        assertInvalid(sub2);
+
+        // invalid policy type item
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.getSupportedPolicyTypes().set(0, makeIdent(null, "3.0.0", ToscaPolicyTypeIdentifier.class));
+        assertInvalid(sub2);
+
+        // null policies
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.setPolicies(null);
+        assertInvalid(sub2);
+
+        // null policy item
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.getPolicies().set(0, null);
+        assertInvalid(sub2);
+
+        // invalid policy item
+        sub2 = new PdpSubGroup(subgrp);
+        sub2.getPolicies().set(0, makeIdent(null, "3.0.0", ToscaPolicyIdentifier.class));
+        assertInvalid(sub2);
+    }
+
+    private void assertInvalid(PdpSubGroup sub2) {
+        ValidationResult result = sub2.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+    }
+
+    /**
+     * Makes an identifier. Uses JSON which does no error checking.
+     *
+     * @param name name to put into the identifier
+     * @param version version to put into the identifier
+     * @param clazz type of identifier to create
+     * @return a new identifier
+     * @throws CoderException if the JSON cannot be decoded
+     */
+    public <T> T makeIdent(String name, String version, Class<T> clazz) throws CoderException {
+        StringBuilder bldr = new StringBuilder();
+        bldr.append("{");
+
+        if (name != null) {
+            bldr.append("'name':'");
+            bldr.append(name);
+            bldr.append("'");
+        }
+
+        if (version != null) {
+            if (name != null) {
+                bldr.append(',');
+            }
+
+            bldr.append("'version':'");
+            bldr.append(version);
+            bldr.append("'");
+        }
+
+        bldr.append("}");
+
+        String json = bldr.toString().replace('\'', '"');
+
+        return coder.decode(json, clazz);
+    }
 }
diff --git a/models-tosca/pom.xml b/models-tosca/pom.xml
index 36ee69b..18b1e1e 100644
--- a/models-tosca/pom.xml
+++ b/models-tosca/pom.xml
@@ -58,6 +58,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.onap.policy.common</groupId>
+            <artifactId>common-parameters</artifactId>
+            <version>${policy.common.version}</version>
+        </dependency>
+
+        <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
             <scope>test</scope>
diff --git a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifier.java b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifier.java
index e55c6bd..f98a238 100644
--- a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifier.java
+++ b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifier.java
@@ -23,13 +23,16 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NonNull;
+import org.apache.commons.lang3.ObjectUtils;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.ValidationResult;
 
 /**
  * Identifies a policy. Both the name and version must be non-null.
  */
 @Data
 @NoArgsConstructor
-public class ToscaPolicyIdentifier {
+public class ToscaPolicyIdentifier implements Comparable<ToscaPolicyIdentifier> {
 
     @NonNull
     private String name;
@@ -47,4 +50,37 @@
         this.name = source.name;
         this.version = source.version;
     }
+
+    /**
+     * Validates that appropriate fields are populated for an incoming call to the PAP
+     * REST API.
+     *
+     * @return the validation result
+     */
+    public ValidationResult validatePapRest() {
+        BeanValidationResult result = new BeanValidationResult("group", this);
+
+        result.validateNotNull("name", name);
+        result.validateNotNull("version", version);
+
+        return result;
+    }
+
+    @Override
+    public int compareTo(ToscaPolicyIdentifier other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (other == null) {
+            return 1;
+        }
+
+        int result = ObjectUtils.compare(getName(), other.getName());
+        if (result != 0) {
+            return result;
+        }
+
+        return ObjectUtils.compare(getVersion(), other.getVersion());
+    }
 }
diff --git a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersion.java b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersion.java
index 9296780..d5ddb05 100644
--- a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersion.java
+++ b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersion.java
@@ -24,13 +24,14 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NonNull;
+import org.apache.commons.lang3.ObjectUtils;
 
 /**
  * Policy identifier with an optional version; only the "name" is required.
  */
 @Data
 @NoArgsConstructor
-public class ToscaPolicyIdentifierOptVersion {
+public class ToscaPolicyIdentifierOptVersion implements Comparable<ToscaPolicyIdentifierOptVersion> {
 
     @NonNull
     @SerializedName("policy-id")
@@ -58,4 +59,22 @@
     public boolean isNullVersion() {
         return (version == null);
     }
+
+    @Override
+    public int compareTo(ToscaPolicyIdentifierOptVersion other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (other == null) {
+            return 1;
+        }
+
+        int result = ObjectUtils.compare(getName(), other.getName());
+        if (result != 0) {
+            return result;
+        }
+
+        return ObjectUtils.compare(getVersion(), other.getVersion());
+    }
 }
diff --git a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifier.java b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifier.java
index a10c3eb..4cd1764 100644
--- a/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifier.java
+++ b/models-tosca/src/main/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifier.java
@@ -23,13 +23,16 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NonNull;
+import org.apache.commons.lang3.ObjectUtils;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.ValidationResult;
 
 /**
  * Identifies a policy type. Both the name and version must be non-null.
  */
 @Data
 @NoArgsConstructor
-public class ToscaPolicyTypeIdentifier {
+public class ToscaPolicyTypeIdentifier implements Comparable<ToscaPolicyTypeIdentifier> {
 
     @NonNull
     private String name;
@@ -47,4 +50,37 @@
         this.name = source.name;
         this.version = source.version;
     }
+
+    /**
+     * Validates that appropriate fields are populated for an incoming call to the PAP
+     * REST API.
+     *
+     * @return the validation result
+     */
+    public ValidationResult validatePapRest() {
+        BeanValidationResult result = new BeanValidationResult("group", this);
+
+        result.validateNotNull("name", name);
+        result.validateNotNull("version", version);
+
+        return result;
+    }
+
+    @Override
+    public int compareTo(ToscaPolicyTypeIdentifier other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (other == null) {
+            return 1;
+        }
+
+        int result = ObjectUtils.compare(getName(), other.getName());
+        if (result != 0) {
+            return result;
+        }
+
+        return ObjectUtils.compare(getVersion(), other.getVersion());
+    }
 }
diff --git a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaIdentifierTestBase.java b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaIdentifierTestBase.java
index 5c93539..d7bca28 100644
--- a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaIdentifierTestBase.java
+++ b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaIdentifierTestBase.java
@@ -20,6 +20,9 @@
 
 package org.onap.policy.models.tosca.authorative.concepts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import org.onap.policy.common.utils.coder.Coder;
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.common.utils.coder.StandardCoder;
@@ -29,19 +32,49 @@
  *
  * @param <T> type of key being tested
  */
-public class ToscaIdentifierTestBase<T> {
+public class ToscaIdentifierTestBase<T extends Comparable<T>> {
+    public static final String NAME = "my-name";
+    public static final String VERSION = "1.2.3";
 
     private static final Coder coder = new StandardCoder();
 
     private final Class<T> clazz;
+    private final String nameField;
+    private final String versionField;
 
 
     /**
      * Constructs the object.
+     *
      * @param clazz the type of class being tested
+     * @param nameField name of the field containing the "name"
+     * @param versionField name of the field containing the "version"
      */
-    public ToscaIdentifierTestBase(Class<T> clazz) {
+    public ToscaIdentifierTestBase(Class<T> clazz, String nameField, String versionField) {
         this.clazz = clazz;
+        this.nameField = nameField;
+        this.versionField = versionField;
+    }
+
+    /**
+     * Tests the compareTo() method.
+     *
+     * @throws Exception if an error occurs
+     */
+    public void testCompareTo() throws Exception {
+        T ident = makeIdent(NAME, VERSION);
+        assertEquals(0, ident.compareTo(ident));
+
+        assertTrue(ident.compareTo(null) > 0);
+
+        assertTrue(ident.compareTo(makeIdent(NAME, VERSION)) == 0);
+        assertTrue(ident.compareTo(makeIdent(NAME, null)) > 0);
+        assertTrue(ident.compareTo(makeIdent(null, VERSION)) > 0);
+        assertTrue(ident.compareTo(makeIdent(NAME, VERSION + "a")) < 0);
+        assertTrue(ident.compareTo(makeIdent(NAME + "a", VERSION)) < 0);
+
+        // name takes precedence over version
+        assertTrue(makeIdent(NAME, VERSION + "a").compareTo(makeIdent(NAME + "a", VERSION)) < 0);
     }
 
     /**
@@ -57,7 +90,9 @@
         bldr.append("{");
 
         if (name != null) {
-            bldr.append("'name':'");
+            bldr.append("'");
+            bldr.append(nameField);
+            bldr.append("':'");
             bldr.append(name);
             bldr.append("'");
         }
@@ -67,7 +102,9 @@
                 bldr.append(',');
             }
 
-            bldr.append("'version':'");
+            bldr.append("'");
+            bldr.append(versionField);
+            bldr.append("':'");
             bldr.append(version);
             bldr.append("'");
         }
diff --git a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersionTest.java b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersionTest.java
index 561b4fb..3f0a7b0 100644
--- a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersionTest.java
+++ b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierOptVersionTest.java
@@ -31,11 +31,9 @@
  * Test the other constructors, as {@link PojosTest} tests the other methods.
  */
 public class ToscaPolicyIdentifierOptVersionTest extends ToscaIdentifierTestBase<ToscaPolicyIdentifierOptVersion> {
-    private static final String NAME = "my-name";
-    private static final String VERSION = "1.2.3";
 
     public ToscaPolicyIdentifierOptVersionTest() {
-        super(ToscaPolicyIdentifierOptVersion.class);
+        super(ToscaPolicyIdentifierOptVersion.class, "policy-id", "policy-version");
     }
 
     @Test
@@ -68,4 +66,9 @@
         orig = makeIdent(NAME, VERSION);
         assertEquals(orig.toString(), new ToscaPolicyIdentifierOptVersion(orig).toString());
     }
+
+    @Test
+    public void testCompareTo() throws Exception {
+        super.testCompareTo();
+    }
 }
diff --git a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierTest.java b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierTest.java
index a53af7b..f31abf8 100644
--- a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierTest.java
+++ b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyIdentifierTest.java
@@ -22,18 +22,21 @@
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
 
 /**
- * Test the other constructors, as {@link PojosTest} tests the other methods.
+ * Test methods not tested by {@link PojosTest}.
  */
 public class ToscaPolicyIdentifierTest extends ToscaIdentifierTestBase<ToscaPolicyIdentifier> {
-    private static final String NAME = "my-name";
-    private static final String VERSION = "1.2.3";
 
     public ToscaPolicyIdentifierTest() {
-        super(ToscaPolicyIdentifier.class);
+        super(ToscaPolicyIdentifier.class, "name", "version");
     }
 
     @Test
@@ -59,4 +62,30 @@
         orig = new ToscaPolicyIdentifier(NAME, VERSION);
         assertEquals(orig.toString(), new ToscaPolicyIdentifier(orig).toString());
     }
+
+    @Test
+    public void testValidatePapRest() throws Exception {
+        ToscaPolicyIdentifier ident = new ToscaPolicyIdentifier(NAME, VERSION);
+        ValidationResult result = ident.validatePapRest();
+        assertNotNull(result);
+        assertTrue(result.isValid());
+        assertNull(result.getResult());
+
+        ident = makeIdent(NAME, null);
+        result = ident.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+
+        ident = makeIdent(null, VERSION);
+        result = ident.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+    }
+
+    @Test
+    public void testCompareTo() throws Exception {
+        super.testCompareTo();
+    }
 }
diff --git a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifierTest.java b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifierTest.java
index 8388f10..e440dd6 100644
--- a/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifierTest.java
+++ b/models-tosca/src/test/java/org/onap/policy/models/tosca/authorative/concepts/ToscaPolicyTypeIdentifierTest.java
@@ -22,18 +22,21 @@
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
+import org.onap.policy.common.parameters.ValidationResult;
 
 /**
- * Test the other constructors, as {@link PojosTest} tests the other methods.
+ * Test methods not tested by {@link PojosTest}.
  */
 public class ToscaPolicyTypeIdentifierTest extends ToscaIdentifierTestBase<ToscaPolicyTypeIdentifier> {
-    private static final String NAME = "my-name";
-    private static final String VERSION = "1.2.3";
 
     public ToscaPolicyTypeIdentifierTest() {
-        super(ToscaPolicyTypeIdentifier.class);
+        super(ToscaPolicyTypeIdentifier.class, "name", "version");
     }
 
     @Test
@@ -60,4 +63,29 @@
         assertEquals(orig.toString(), new ToscaPolicyTypeIdentifier(orig).toString());
     }
 
+    @Test
+    public void testValidatePapRest() throws Exception {
+        ToscaPolicyTypeIdentifier ident = new ToscaPolicyTypeIdentifier(NAME, VERSION);
+        ValidationResult result = ident.validatePapRest();
+        assertNotNull(result);
+        assertTrue(result.isValid());
+        assertNull(result.getResult());
+
+        ident = makeIdent(NAME, null);
+        result = ident.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+
+        ident = makeIdent(null, VERSION);
+        result = ident.validatePapRest();
+        assertNotNull(result);
+        assertFalse(result.isValid());
+        assertNotNull(result.getResult());
+    }
+
+    @Test
+    public void testCompareTo() throws Exception {
+        super.testCompareTo();
+    }
 }