Merge "Fix UNDEPLOY transition in policyParticipant in ACM"
diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java
index a86576e..bf1d24a 100644
--- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java
+++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Nordix Foundation.
+ *  Copyright (C) 2021,2023 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.onap.policy.clamp.acm.participant.policy.main.parameters.ParticipantPolicyParameters;
-import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 import org.springframework.stereotype.Component;
 
@@ -47,9 +46,8 @@
      *
      * @param toscaServiceTemplate the whole ToscaServiceTemplate
      * @return Response
-     * @throws PfModelException on errors creating the policy type
      */
-    public Response createPolicyType(ToscaServiceTemplate toscaServiceTemplate) throws PfModelException {
+    public Response createPolicyType(ToscaServiceTemplate toscaServiceTemplate) {
         return executePost(POLICY_URI + "policytypes", Entity.entity(toscaServiceTemplate, MediaType.APPLICATION_JSON));
     }
 
diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java
index 0b03e23..3e74019 100644
--- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java
+++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java
@@ -22,9 +22,13 @@
 
 package org.onap.policy.clamp.acm.participant.policy.main.handler;
 
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import javax.ws.rs.core.Response.Status;
+import lombok.RequiredArgsConstructor;
 import lombok.Setter;
 import org.apache.http.HttpStatus;
 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
@@ -35,9 +39,9 @@
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.models.base.PfModelException;
-import org.onap.policy.models.base.PfModelRuntimeException;
 import org.onap.policy.models.pdp.concepts.DeploymentSubGroup;
-import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -46,11 +50,12 @@
  * This class handles implementation of automationCompositionElement updates.
  */
 @Component
+@RequiredArgsConstructor
 public class AutomationCompositionElementHandler implements AutomationCompositionElementListener {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionElementHandler.class);
-    private final Map<String, String> policyTypeMap = new LinkedHashMap<>();
-    private final Map<String, String> policyMap = new LinkedHashMap<>();
+
+    private final Map<UUID, ToscaServiceTemplate> serviceTemplateMap = new LinkedHashMap<>();
 
     private final PolicyApiHttpClient apiHttpClient;
     private final PolicyPapHttpClient papHttpClient;
@@ -59,53 +64,48 @@
     private ParticipantIntermediaryApi intermediaryApi;
 
     /**
-     * constructor.
-     *
-     * @param apiHttpClient the Policy Api Http Client
-     * @param papHttpClient the Policy Pap Http Client
-     */
-    public AutomationCompositionElementHandler(PolicyApiHttpClient apiHttpClient, PolicyPapHttpClient papHttpClient) {
-        this.papHttpClient = papHttpClient;
-        this.apiHttpClient = apiHttpClient;
-    }
-
-    /**
      * Callback method to handle a automation composition element state change.
      *
      * @param automationCompositionId the ID of the automation composition
      * @param automationCompositionElementId the ID of the automation composition element
      */
     @Override
-    public void undeploy(UUID automationCompositionId, UUID automationCompositionElementId) {
-        try {
-            undeployPolicies(automationCompositionElementId);
-            deletePolicyData(automationCompositionId, automationCompositionElementId);
+    public void undeploy(UUID automationCompositionId, UUID automationCompositionElementId) throws PfModelException {
+        var automationCompositionDefinition = serviceTemplateMap.get(automationCompositionElementId);
+        if (automationCompositionDefinition == null) {
+            LOGGER.debug("No policies to undeploy to {}", automationCompositionElementId);
             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
                     automationCompositionElementId, DeployState.UNDEPLOYED, LockState.NONE);
-        } catch (PfModelRuntimeException e) {
-            LOGGER.error("Undeploying/Deleting policy failed {}", automationCompositionElementId, e);
+            return;
         }
+        var policyList = getPolicyList(automationCompositionDefinition);
+        undeployPolicies(policyList, automationCompositionElementId);
+        var policyTypeList = getPolicyTypeList(automationCompositionDefinition);
+        deletePolicyData(policyTypeList, policyList);
+        serviceTemplateMap.remove(automationCompositionElementId);
+        intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
+                automationCompositionElementId, DeployState.UNDEPLOYED, LockState.NONE);
     }
 
-    private void deletePolicyData(UUID automationCompositionId, UUID automationCompositionElementId) {
+    private void deletePolicyData(List<ToscaConceptIdentifier> policyTypeList,
+            List<ToscaConceptIdentifier> policyList) {
         // Delete all policies of this automationComposition from policy framework
-        for (var policy : policyMap.entrySet()) {
-            apiHttpClient.deletePolicy(policy.getKey(), policy.getValue());
+        for (var policy : policyList) {
+            apiHttpClient.deletePolicy(policy.getName(), policy.getVersion());
         }
-        policyMap.clear();
         // Delete all policy types of this automation composition from policy framework
-        for (var policyType : policyTypeMap.entrySet()) {
-            apiHttpClient.deletePolicyType(policyType.getKey(), policyType.getValue());
+        for (var policyType : policyTypeList) {
+            apiHttpClient.deletePolicyType(policyType.getName(), policyType.getVersion());
         }
-        policyTypeMap.clear();
     }
 
-    private void deployPolicies(UUID automationCompositionId, UUID automationCompositionElementId) {
+    private void deployPolicies(List<ToscaConceptIdentifier> policyList, UUID automationCompositionId,
+            UUID automationCompositionElementId) throws PfModelException {
         var deployFailure = false;
         // Deploy all policies of this automationComposition from Policy Framework
-        if (!policyMap.entrySet().isEmpty()) {
-            for (var policy : policyMap.entrySet()) {
-                var deployPolicyResp = papHttpClient.handlePolicyDeployOrUndeploy(policy.getKey(), policy.getValue(),
+        if (!policyList.isEmpty()) {
+            for (var policy : policyList) {
+                var deployPolicyResp = papHttpClient.handlePolicyDeployOrUndeploy(policy.getName(), policy.getVersion(),
                         DeploymentSubGroup.Action.POST).getStatus();
                 if (deployPolicyResp != HttpStatus.SC_ACCEPTED) {
                     deployFailure = true;
@@ -119,14 +119,16 @@
             // Update the AC element state
             intermediaryApi.updateAutomationCompositionElementState(automationCompositionId,
                     automationCompositionElementId, DeployState.DEPLOYED, LockState.LOCKED);
+        } else {
+            throw new PfModelException(Status.BAD_REQUEST, "Deploy of Policy failed.");
         }
     }
 
-    private void undeployPolicies(UUID automationCompositionElementId) {
+    private void undeployPolicies(List<ToscaConceptIdentifier> policyList, UUID automationCompositionElementId) {
         // Undeploy all policies of this automation composition from Policy Framework
-        if (!policyMap.entrySet().isEmpty()) {
-            for (var policy : policyMap.entrySet()) {
-                papHttpClient.handlePolicyDeployOrUndeploy(policy.getKey(), policy.getValue(),
+        if (!policyList.isEmpty()) {
+            for (var policy : policyList) {
+                papHttpClient.handlePolicyDeployOrUndeploy(policy.getName(), policy.getVersion(),
                         DeploymentSubGroup.Action.DELETE);
             }
             LOGGER.debug("Undeployed policies from {} successfully", automationCompositionElementId);
@@ -150,32 +152,53 @@
         var createPolicyResp = HttpStatus.SC_OK;
 
         var automationCompositionDefinition = element.getToscaServiceTemplateFragment();
-        if (automationCompositionDefinition.getToscaTopologyTemplate() != null) {
-            if (automationCompositionDefinition.getPolicyTypes() != null) {
-                for (var policyType : automationCompositionDefinition.getPolicyTypes().values()) {
-                    policyTypeMap.put(policyType.getName(), policyType.getVersion());
-                }
-                LOGGER.info("Found Policy Types in automation composition definition: {} , Creating Policy Types",
-                        automationCompositionDefinition.getName());
-                createPolicyTypeResp = apiHttpClient.createPolicyType(automationCompositionDefinition).getStatus();
-            }
-            if (automationCompositionDefinition.getToscaTopologyTemplate().getPolicies() != null) {
-                for (var gotPolicyMap : automationCompositionDefinition.getToscaTopologyTemplate().getPolicies()) {
-                    for (ToscaPolicy policy : gotPolicyMap.values()) {
-                        policyMap.put(policy.getName(), policy.getVersion());
-                    }
-                }
-                LOGGER.info("Found Policies in automation composition definition: {} , Creating Policies",
-                        automationCompositionDefinition.getName());
-                createPolicyResp = apiHttpClient.createPolicy(automationCompositionDefinition).getStatus();
-            }
-            if (createPolicyTypeResp == HttpStatus.SC_OK && createPolicyResp == HttpStatus.SC_OK) {
-                LOGGER.info("PolicyTypes/Policies for the automation composition element : {} are created "
-                        + "successfully", element.getId());
-                deployPolicies(automationCompositionId, element.getId());
-            } else {
-                LOGGER.error("Creation of PolicyTypes/Policies failed. Policies will not be deployed.");
+        if (automationCompositionDefinition.getToscaTopologyTemplate() == null) {
+            throw new PfModelException(Status.BAD_REQUEST, "ToscaTopologyTemplate not defined");
+        }
+        serviceTemplateMap.put(element.getId(), automationCompositionDefinition);
+        if (automationCompositionDefinition.getPolicyTypes() != null) {
+            LOGGER.info("Found Policy Types in automation composition definition: {} , Creating Policy Types",
+                    automationCompositionDefinition.getName());
+            createPolicyTypeResp = apiHttpClient.createPolicyType(automationCompositionDefinition).getStatus();
+        }
+        if (automationCompositionDefinition.getToscaTopologyTemplate().getPolicies() != null) {
+            LOGGER.info("Found Policies in automation composition definition: {} , Creating Policies",
+                    automationCompositionDefinition.getName());
+            createPolicyResp = apiHttpClient.createPolicy(automationCompositionDefinition).getStatus();
+        }
+        if (createPolicyTypeResp == HttpStatus.SC_OK && createPolicyResp == HttpStatus.SC_OK) {
+            LOGGER.info(
+                    "PolicyTypes/Policies for the automation composition element : {} are created " + "successfully",
+                    element.getId());
+            var policyList = getPolicyList(automationCompositionDefinition);
+            deployPolicies(policyList, automationCompositionId, element.getId());
+        } else {
+            throw new PfModelException(Status.BAD_REQUEST,
+                    "Creation of PolicyTypes/Policies failed. Policies will not be deployed.");
+        }
+    }
+
+    private List<ToscaConceptIdentifier> getPolicyTypeList(ToscaServiceTemplate serviceTemplate) {
+        List<ToscaConceptIdentifier> policyTypeList = new ArrayList<>();
+        if (serviceTemplate.getPolicyTypes() != null) {
+            for (var policyType : serviceTemplate.getPolicyTypes().values()) {
+                policyTypeList.add(policyType.getKey().asIdentifier());
             }
         }
+
+        return policyTypeList;
+    }
+
+    private List<ToscaConceptIdentifier> getPolicyList(ToscaServiceTemplate serviceTemplate) {
+        List<ToscaConceptIdentifier> policyList = new ArrayList<>();
+        if (serviceTemplate.getToscaTopologyTemplate().getPolicies() != null) {
+            for (var gotPolicyMap : serviceTemplate.getToscaTopologyTemplate().getPolicies()) {
+                for (var policy : gotPolicyMap.values()) {
+                    policyList.add(policy.getKey().asIdentifier());
+                }
+            }
+        }
+
+        return policyList;
     }
 }
diff --git a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java
index e0b4a69..47c2799 100644
--- a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java
+++ b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java
@@ -20,20 +20,23 @@
 
 package org.onap.policy.clamp.acm.participant.policy.main.handler;
 
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import javax.ws.rs.core.Response;
 import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
 import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
 import org.onap.policy.clamp.acm.participant.policy.client.PolicyApiHttpClient;
 import org.onap.policy.clamp.acm.participant.policy.client.PolicyPapHttpClient;
 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
 import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
@@ -50,21 +53,16 @@
     public static final UUID AC_ID = UUID.randomUUID();
     private static final ToscaConceptIdentifier DEFINITION = new ToscaConceptIdentifier(ID_NAME, ID_VERSION);
 
-    private PolicyApiHttpClient api = Mockito.mock(PolicyApiHttpClient.class);
-    private PolicyPapHttpClient pap = Mockito.mock(PolicyPapHttpClient.class);
-
     @Test
-    void testHandlerDoesNotThrowExceptions() {
-        var handler = getTestingHandler();
-
-        assertDoesNotThrow(() -> handler.undeploy(AC_ID, automationCompositionElementId));
-    }
-
-    private AutomationCompositionElementHandler getTestingHandler() {
-        var handler = new AutomationCompositionElementHandler(api, pap);
-        var intermediaryApi = Mockito.mock(ParticipantIntermediaryApi.class);
+    void testHandlerUndeploy() throws PfModelException {
+        var handler = new AutomationCompositionElementHandler(mock(PolicyApiHttpClient.class),
+                mock(PolicyPapHttpClient.class));
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         handler.setIntermediaryApi(intermediaryApi);
-        return handler;
+
+        handler.undeploy(AC_ID, automationCompositionElementId);
+        verify(intermediaryApi).updateAutomationCompositionElementState(AC_ID, automationCompositionElementId,
+                DeployState.UNDEPLOYED, LockState.NONE);
     }
 
     private AcElementDeploy getTestingAcElement() {
@@ -81,25 +79,58 @@
     }
 
     @Test
-    void testAcElementUpdate() throws PfModelException {
+    void testDeploy() throws PfModelException {
         // Mock success scenario for policy creation and deployment
+        var api = mock(PolicyApiHttpClient.class);
         doReturn(Response.ok().build()).when(api).createPolicyType(any());
         doReturn(Response.ok().build()).when(api).createPolicy(any());
+
+        var pap = mock(PolicyPapHttpClient.class);
         doReturn(Response.accepted().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any());
 
-        var handler = getTestingHandler();
+        var handler = new AutomationCompositionElementHandler(api, pap);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        handler.setIntermediaryApi(intermediaryApi);
+
+        handler.deploy(AC_ID, getTestingAcElement(), Map.of());
+        handler.undeploy(AC_ID, automationCompositionElementId);
+        verify(intermediaryApi).updateAutomationCompositionElementState(AC_ID, automationCompositionElementId,
+                DeployState.UNDEPLOYED, LockState.NONE);
+    }
+
+    @Test
+    void testApiException() throws PfModelException {
+        var api = mock(PolicyApiHttpClient.class);
+        doReturn(Response.serverError().build()).when(api).createPolicyType(any());
+        doReturn(Response.ok().build()).when(api).createPolicy(any());
+
+        var pap = mock(PolicyPapHttpClient.class);
+        doReturn(Response.accepted().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any());
+
+        var handler = new AutomationCompositionElementHandler(api, pap);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        handler.setIntermediaryApi(intermediaryApi);
         var element = getTestingAcElement();
 
-        assertDoesNotThrow(() -> handler.deploy(AC_ID, element, Map.of()));
-
-        assertDoesNotThrow(() -> handler.undeploy(AC_ID, automationCompositionElementId));
-
-        // Mock failure in policy deployment
-        doReturn(Response.serverError().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any());
-        assertDoesNotThrow(() -> handler.deploy(AC_ID, element, Map.of()));
-
         // Mock failure in policy type creation
-        doReturn(Response.serverError().build()).when(api).createPolicyType(any());
-        assertDoesNotThrow(() -> handler.deploy(AC_ID, element, Map.of()));
+        assertThatThrownBy(() -> handler.deploy(AC_ID, element, Map.of()))
+                .hasMessageMatching("Creation of PolicyTypes/Policies failed. Policies will not be deployed.");
+    }
+
+    @Test
+    void testDeployPapException() throws PfModelException {
+        var api = mock(PolicyApiHttpClient.class);
+        doReturn(Response.ok().build()).when(api).createPolicyType(any());
+        doReturn(Response.ok().build()).when(api).createPolicy(any());
+
+        var pap = mock(PolicyPapHttpClient.class);
+        doReturn(Response.serverError().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any());
+
+        var handler = new AutomationCompositionElementHandler(api, pap);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        handler.setIntermediaryApi(intermediaryApi);
+        var element = getTestingAcElement();
+        assertThatThrownBy(() -> handler.deploy(AC_ID, element, Map.of()))
+                .hasMessageMatching("Deploy of Policy failed.");
     }
 }