native controller generates invalid properties

Additional instrumetation and tests have been added
as well.

Issue-ID: POLICY-2489
Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
Change-Id: I65df586f3a44acf3d6f825ebfb8bd73107255a1f
Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java
index 9a11955..65ed350 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java
@@ -96,7 +96,7 @@
         try {
             controller.start();
         } catch (RuntimeException e) {
-            logger.warn("failed deploy (cannot start ontroller) for policy: {}", policy, e);
+            logger.warn("failed deploy (cannot start controller) for policy: {}", policy, e);
             PolicyEngineConstants.getManager().removePolicyController(controller);
             return false;
         }
@@ -199,7 +199,7 @@
 
     private void configTopic(
             String configCommPrefix, String topicName, List<ControllerEvent> events, Properties controllerProps) {
-        String configTopicPrefix = configCommPrefix + "." + topicName;
+        String configTopicPrefix = configCommPrefix + ".topics." + topicName;
         configTopics(configCommPrefix, topicName, controllerProps);
         for (ControllerEvent configEvent : events) {
             configEvent(configTopicPrefix, configEvent, controllerProps);
@@ -213,7 +213,6 @@
 
     private void configEvent(String propPrefix, ControllerEvent configEvent, Properties controllerProps) {
         String eventPropPrefix = propPrefix + ".events";
-        controllerProps.setProperty(eventPropPrefix, configEvent.getEventClass());
         if (configEvent.getEventFilter() != null) {
             controllerProps.setProperty(
                 eventPropPrefix + "." + configEvent.getEventClass() + ".filter", configEvent.getEventFilter());
@@ -225,10 +224,11 @@
     }
 
     private void configTopicItemList(String itemPrefix, String item, Properties controllerProps) {
-        if (controllerProps.getProperty(itemPrefix) == null) {
+        String itemValue = controllerProps.getProperty(itemPrefix);
+        if (itemValue == null) {
             controllerProps.setProperty(itemPrefix, item);
         } else {
-            controllerProps.setProperty(itemPrefix, "," + item);
+            controllerProps.setProperty(itemPrefix, itemValue + "," + item);
         }
     }
 
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/server/restful/RestLifecycleManager.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/server/restful/RestLifecycleManager.java
index 5c8f9dc..4093b51 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/server/restful/RestLifecycleManager.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/server/restful/RestLifecycleManager.java
@@ -18,9 +18,11 @@
 
 package org.onap.policy.drools.server.restful;
 
+import com.worldturner.medeia.api.ValidationFailedException;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 import java.util.stream.Collectors;
@@ -356,7 +358,7 @@
     @Path("policies/operations")
     @ApiOperation(value = "Gets Policy Operations", responseContainer = "List")
     public Response policiesOperations() {
-        return Response.status(Response.Status.OK).entity(List.of("deployment", "undeployment")).build();
+        return Response.status(Response.Status.OK).entity(List.of("deployment", "undeployment", "validation")).build();
     }
 
     /**
@@ -381,6 +383,29 @@
         return deployUndeployOperation(policy, false);
     }
 
+    /**
+     * POST a policy for validation.
+     */
+
+    @POST
+    @Path("policies/operations/validation")
+    @ApiOperation(value = "Validates a policy", responseContainer = "List")
+    public Response validateOperation(@ApiParam(value = "Tosca Policy", required = true) String policy) {
+        ToscaPolicy toscaPolicy = getToscaPolicy(policy);
+        if (toscaPolicy == null) {
+            return Response.status(Response.Status.NOT_ACCEPTABLE).build();
+        }
+
+        try {
+            LifecycleFeature.fsm.getDomainMaker().conformance(toscaPolicy);
+        } catch (ValidationFailedException v) {
+            logger.trace("policy {} validation errors: {}", toscaPolicy, v.getMessage(), v);
+            return Response.status(Response.Status.NOT_ACCEPTABLE).entity(v.getFailures()).build();
+        }
+
+        return Response.status(Response.Status.OK).entity(Collections.emptyList()).build();
+    }
+
     private Response deployUndeployOperation(String policy, boolean deploy) {
         ToscaPolicy toscaPolicy = getToscaPolicy(policy);
         if (toscaPolicy == null) {
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java
index 1647478..50f35e3 100644
--- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java
@@ -34,6 +34,7 @@
 import org.onap.policy.common.utils.time.PseudoScheduledExecutorService;
 import org.onap.policy.common.utils.time.TestTimeMulti;
 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
+import org.onap.policy.drools.system.PolicyControllerConstants;
 import org.onap.policy.drools.utils.logging.LoggerUtil;
 import org.onap.policy.models.pdp.concepts.PdpStatus;
 import org.onap.policy.models.pdp.enums.PdpMessageType;
@@ -69,6 +70,7 @@
         controllerSupport.destroyController();
         NoopTopicFactories.getSourceFactory().destroy();
         NoopTopicFactories.getSinkFactory().destroy();
+        PolicyControllerConstants.getFactory().destroy();
         try {
             Files.deleteIfExists(Paths.get(SystemPersistenceConstants.getManager().getConfigurationPath().toString(),
                                      CONTROLLER_NAME + "-controller.properties.bak"));
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsControllerTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsControllerTest.java
index a230ced..caab965 100644
--- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsControllerTest.java
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsControllerTest.java
@@ -21,7 +21,9 @@
 package org.onap.policy.drools.lifecycle;
 
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
@@ -32,60 +34,155 @@
 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
 import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
 import org.onap.policy.drools.domain.models.controller.ControllerPolicy;
 import org.onap.policy.drools.system.PolicyControllerConstants;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 
 /**
  * Native Controller Policy Test.
  */
 public class PolicyTypeNativeDroolsControllerTest extends LifecycleStateRunningTest {
-    // Native Drools Policy
     private static final String EXAMPLE_NATIVE_DROOLS_POLICY_NAME = "example.controller";
     private static final String EXAMPLE_NATIVE_DROOLS_POLICY_JSON =
             "src/test/resources/tosca-policy-native-controller-example.json";
 
-    private ToscaPolicy policy;
-    private ControllerPolicy controllerPolicy;
-    private PolicyTypeNativeDroolsController controller;
-
     /**
      * Test initialization.
      */
     @Before
     public void init() throws IOException, CoderException {
         fsm = makeFsmWithPseudoTime();
-        policy = getPolicyFromFile(EXAMPLE_NATIVE_DROOLS_POLICY_JSON, EXAMPLE_NATIVE_DROOLS_POLICY_NAME);
-        controllerPolicy = fsm.getDomainMaker().convertTo(policy, ControllerPolicy.class);
-        controller = new PolicyTypeNativeDroolsController(fsm, policy.getTypeIdentifier());
+    }
+
+    @Test
+    public void testDeployUndeploy() throws IOException, CoderException {
+        fsm = makeFsmWithPseudoTime();
 
         assertTrue(controllerSupport.getController().getDrools().isBrained());
         assertFalse(controllerSupport.getController().isAlive());
         assertFalse(controllerSupport.getController().getDrools().isAlive());
         assertSame(controllerSupport.getController(), PolicyControllerConstants.getFactory().get("lifecycle"));
 
-        /* start controller */
         assertTrue(controllerSupport.getController().start());
 
         assertTrue(controllerSupport.getController().isAlive());
         assertTrue(controllerSupport.getController().getDrools().isAlive());
         assertTrue(controllerSupport.getController().getDrools().isBrained());
         assertSame(controllerSupport.getController(), PolicyControllerConstants.getFactory().get("lifecycle"));
-    }
 
-    @Test
-    public void testUndeployDeploy() {
+        ToscaPolicy policy = getPolicyFromFile(EXAMPLE_NATIVE_DROOLS_POLICY_JSON, EXAMPLE_NATIVE_DROOLS_POLICY_NAME);
+        ControllerPolicy controllerPolicy = fsm.getDomainMaker().convertTo(policy, ControllerPolicy.class);
+        PolicyTypeNativeDroolsController controller =
+                new PolicyTypeNativeDroolsController(fsm, policy.getTypeIdentifier());
         assertTrue(controller.undeploy(policy));
         assertThatIllegalArgumentException().isThrownBy(
             () -> PolicyControllerConstants.getFactory().get(controllerPolicy.getName()));
 
-        assertFalse(controller.deploy(policy));
-
         Properties noopTopicProperties = new Properties();
         noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS, "DCAE_TOPIC");
         noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-CL");
         TopicEndpointManager.getManager().addTopics(noopTopicProperties);
 
         assertTrue(controller.deploy(policy));
+        /* this should be ok too */
+        assertTrue(controller.deploy(policy));
+    }
+
+    @Test
+    public void testControllerProperties() throws CoderException {
+        Properties noopTopicProperties = new Properties();
+        noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS,
+                "DCAE_TOPIC,APPC-CL,APPC-LCM-WRITE,SDNR-CL-RSP");
+        noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS,
+                "APPC-CL,APPC-LCM-READ,POLICY-CL-MGT,DCAE_CL_RSP");
+        TopicEndpointManager.getManager().addTopics(noopTopicProperties);
+
+        ToscaPolicy nativeControllerPolicy =
+                getExamplesPolicy("policies/usecases.native.controller.policy.input.tosca.json", "usecases");
+        PolicyTypeNativeDroolsController controller =
+                new PolicyTypeNativeDroolsController(fsm, nativeControllerPolicy.getTypeIdentifier());
+        assertTrue(controller.deploy(nativeControllerPolicy));
+        Properties properties = PolicyControllerConstants.getFactory().get("usecases").getProperties();
+
+        assertEquals("usecases", properties.getProperty("controller.name"));
+
+        assertNull(properties.getProperty("rules.groupId"));
+        assertNull(properties.getProperty("rules.artifactId"));
+        assertNull(properties.getProperty("rules.version"));
+
+        assertEquals("DCAE_TOPIC,APPC-CL,APPC-LCM-WRITE,SDNR-CL-RSP",
+                properties.getProperty("noop.source.topics"));
+        assertEquals("APPC-CL,APPC-LCM-READ,POLICY-CL-MGT,DCAE_CL_RSP",
+                properties.getProperty("noop.sink.topics"));
+
+        assertEquals("org.onap.policy.controlloop.CanonicalOnset,org.onap.policy.controlloop.CanonicalAbated",
+                properties.getProperty("noop.source.topics.DCAE_TOPIC.events"));
+        assertEquals("[?($.closedLoopEventStatus == 'ONSET')]",
+            properties
+                .getProperty("noop.source.topics.DCAE_TOPIC.events.org.onap.policy.controlloop.CanonicalOnset.filter"));
+        assertEquals("[?($.closedLoopEventStatus == 'ABATED')]",
+            properties
+                .getProperty("noop.source.topics.DCAE_TOPIC.events."
+                                     + "org.onap.policy.controlloop.CanonicalAbated.filter"));
+        assertEquals("org.onap.policy.controlloop.util.Serialization,gson",
+                properties.getProperty("noop.source.topics.DCAE_TOPIC.events.custom.gson"));
+
+        assertEquals("org.onap.policy.appc.Response", properties.getProperty("noop.source.topics.APPC-CL.events"));
+        assertEquals("[?($.CommonHeader && $.Status)]",
+                properties
+                        .getProperty("noop.source.topics.APPC-CL.events.org.onap.policy.appc.Response.filter"));
+        assertEquals("org.onap.policy.appc.util.Serialization,gsonPretty",
+                properties.getProperty("noop.source.topics.APPC-CL.events.custom.gson"));
+
+        assertEquals("org.onap.policy.appclcm.AppcLcmDmaapWrapper",
+                properties.getProperty("noop.source.topics.APPC-LCM-WRITE.events"));
+        assertEquals("[?($.type == 'response')]",
+            properties
+                .getProperty("noop.source.topics.APPC-LCM-WRITE.events."
+                        + "org.onap.policy.appclcm.AppcLcmDmaapWrapper.filter"));
+        assertEquals("org.onap.policy.appclcm.util.Serialization,gson",
+                properties.getProperty("noop.source.topics.APPC-LCM-WRITE.events.custom.gson"));
+
+        assertEquals("org.onap.policy.sdnr.PciResponseWrapper",
+                properties.getProperty("noop.source.topics.SDNR-CL-RSP.events"));
+        assertEquals("[?($.type == 'response')]",
+                properties
+                        .getProperty("noop.source.topics.SDNR-CL-RSP.events."
+                                             + "org.onap.policy.sdnr.PciResponseWrapper.filter"));
+        assertEquals("org.onap.policy.sdnr.util.Serialization,gson",
+                properties.getProperty("noop.source.topics.SDNR-CL-RSP.events.custom.gson"));
+
+        assertEquals("org.onap.policy.appc.Request", properties.getProperty("noop.sink.topics.APPC-CL.events"));
+        assertEquals("org.onap.policy.appc.util.Serialization,gsonPretty",
+                properties.getProperty("noop.sink.topics.APPC-CL.events.custom.gson"));
+
+        assertEquals("org.onap.policy.appclcm.AppcLcmDmaapWrapper",
+                properties.getProperty("noop.sink.topics.APPC-LCM-READ.events"));
+        assertEquals("org.onap.policy.appclcm.util.Serialization,gson",
+                properties.getProperty("noop.sink.topics.APPC-LCM-READ.events.custom.gson"));
+
+        assertEquals("org.onap.policy.controlloop.VirtualControlLoopNotification",
+                properties.getProperty("noop.sink.topics.POLICY-CL-MGT.events"));
+        assertEquals("org.onap.policy.controlloop.util.Serialization,gsonPretty",
+                properties.getProperty("noop.sink.topics.POLICY-CL-MGT.events.custom.gson"));
+
+        assertEquals("org.onap.policy.controlloop.ControlLoopResponse",
+                properties.getProperty("noop.sink.topics.DCAE_CL_RSP.events"));
+        assertEquals("org.onap.policy.controlloop.util.Serialization,gsonPretty",
+                properties.getProperty("noop.sink.topics.DCAE_CL_RSP.events.custom.gson"));
+
+        assertEquals("test", properties.getProperty("notes"));
+        assertEquals("auto", properties.getProperty("persistence.type"));
+
+        assertTrue(controller.undeploy(nativeControllerPolicy));
+    }
+
+    private ToscaPolicy getExamplesPolicy(String resourcePath, String policyName) throws CoderException {
+        String policyJson = ResourceUtils.getResourceAsString(resourcePath);
+        ToscaServiceTemplate serviceTemplate = new StandardCoder().decode(policyJson, ToscaServiceTemplate.class);
+        return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
     }
 }
\ No newline at end of file
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/server/restful/RestLifecycleManagerTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/server/restful/RestLifecycleManagerTest.java
index 2222399..d9e21b8 100644
--- a/feature-lifecycle/src/test/java/org/onap/policy/drools/server/restful/RestLifecycleManagerTest.java
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/server/restful/RestLifecycleManagerTest.java
@@ -119,8 +119,10 @@
         controllerSupport.installArtifact();
 
         Properties noopTopicProperties = new Properties();
-        noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS, "DCAE_TOPIC");
-        noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-CL");
+        noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS,
+                "DCAE_TOPIC,APPC-CL,APPC-LCM-WRITE,SDNR-CL-RSP");
+        noopTopicProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS,
+                "APPC-CL,APPC-LCM-READ,POLICY-CL-MGT,SDNR-CL,DCAE_CL_RSP");
         TopicEndpointManager.getManager().addTopics(noopTopicProperties);
 
         client = HttpClientFactoryInstance.getClientFactory().get("lifecycle");
@@ -227,6 +229,8 @@
         if (StringUtils.isBlank(opPolicy.getName())) {
             opPolicy.setName(opPolicy.getMetadata().get("policy-id"));
         }
+        assertTrue(
+            listPost("policies/operations/validation", toString(opPolicy), Status.OK.getStatusCode()).isEmpty());
 
         booleanPost("policies", toString(opPolicy), Status.OK.getStatusCode(), Boolean.TRUE);
         assertTrue(PolicyControllerConstants.getFactory().get("lifecycle").isAlive());
@@ -253,7 +257,7 @@
 
         /* individual deploy/undeploy operations */
 
-        resourceLists("policies/operations", 2);
+        resourceLists("policies/operations", 3);
 
         booleanPost("policies/operations/deployment", toString(opPolicy), Status.OK.getStatusCode(), Boolean.TRUE);
         assertEquals(1,
@@ -311,6 +315,10 @@
         get("policies/example.controller/1.0.0", Status.NOT_FOUND.getStatusCode());
 
         assertThatIllegalArgumentException().isThrownBy(() -> PolicyControllerConstants.getFactory().get("lifecycle"));
+        opPolicy.getMetadata().remove("policy-id");
+        assertFalse(
+            listPost("policies/operations/validation", toString(opPolicy),
+                    Status.NOT_ACCEPTABLE.getStatusCode()).isEmpty());
     }
 
     private Response get(String contextPath, int statusCode) {
@@ -329,6 +337,12 @@
         booleanResponse(response, statusCode, bool);
     }
 
+    private List<?> listPost(String contextPath, String body, int statusCode) {
+        Response response = client.post(contextPath, Entity.json(body), Collections.emptyMap());
+        assertEquals(statusCode, response.getStatus());
+        return HttpClient.getBody(response, List.class);
+    }
+
     private void booleanPost(String contextPath, String body, int statusCode, Boolean bool) {
         Response response = client.post(contextPath, Entity.json(body), Collections.emptyMap());
         booleanResponse(response, statusCode, bool);
diff --git a/feature-lifecycle/src/test/resources/tosca-policy-native-controller-example.json b/feature-lifecycle/src/test/resources/tosca-policy-native-controller-example.json
index 2b98a24..642ecc0 100644
--- a/feature-lifecycle/src/test/resources/tosca-policy-native-controller-example.json
+++ b/feature-lifecycle/src/test/resources/tosca-policy-native-controller-example.json
@@ -18,15 +18,11 @@
                                 "topicName": "DCAE_TOPIC",
                                 "events": [
                                     {
-                                        "eventClass": "org.onap.policy.controlloop.CanonicalOnset",
-                                        "eventFilter": "[?($.closedLoopEventStatus == 'ONSET')]",
-                                        "customSerialization": {
-                                            "customSerializerClass": "org.onap.policy.controlloop.util.Serialization",
-                                            "jsonParser": "gson"
-                                        }
+                                        "eventClass": "java.util.HashMap",
+                                        "eventFilter": "[?($.closedLoopEventStatus == 'ONSET')]"
                                     },
                                     {
-                                        "eventClass": "org.onap.policy.controlloop.CanonicalAbated",
+                                        "eventClass": "java.util.HashMap",
                                         "eventFilter": "[?($.closedLoopEventStatus == 'ABATED')]"
                                     }
                                 ]
@@ -37,12 +33,8 @@
                                 "topicName": "APPC-CL",
                                 "events": [
                                     {
-                                        "eventClass": "org.onap.policy.appc.Response",
-                                        "eventFilter": "[?($.CommonHeader && $.Status)]",
-                                        "customSerialization": {
-                                            "customSerializerClass": "org.onap.policy.appc.util.Serialization",
-                                            "jsonParser": "gsonPretty"
-                                        }
+                                        "eventClass": "java.util.HashMap",
+                                        "eventFilter": "[?($.CommonHeader && $.Status)]"
                                     }
                                 ]
                             }