Fix composition outProperties update issue 61/140561/1
authorFrancescoFioraEst <francesco.fiora@est.tech>
Mon, 24 Mar 2025 11:58:19 +0000 (11:58 +0000)
committerFrancesco Fiora <francesco.fiora@est.tech>
Mon, 24 Mar 2025 15:27:49 +0000 (15:27 +0000)
Add participantId filter in sync message in intermediary.
Add participantId filter in outProperties update in ACM-runtime.
Log ConstraintViolationException as warning not as error.

Issue-ID: POLICY-5328
Change-Id: Ib9a9f98646918c00e3885c975101f9521ad3600b
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
models/src/main/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProvider.java
models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/MessageProviderTest.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandler.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcDefinitionHandlerTest.java
runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionParticipantHandler.java
runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionParticipantHandlerTest.java

index c3e5543..55da8e6 100644 (file)
@@ -23,12 +23,15 @@ package org.onap.policy.clamp.models.acm.persistence.provider;
 import java.sql.Timestamp;
 import java.time.Instant;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
+import org.hibernate.exception.ConstraintViolationException;
+import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
 import org.onap.policy.clamp.models.acm.document.concepts.DocMessage;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantAckMessage;
@@ -39,6 +42,9 @@ import org.onap.policy.clamp.models.acm.persistence.concepts.JpaMessageJob;
 import org.onap.policy.clamp.models.acm.persistence.repository.MessageJobRepository;
 import org.onap.policy.clamp.models.acm.persistence.repository.MessageRepository;
 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -47,6 +53,8 @@ import org.springframework.transaction.annotation.Transactional;
 @AllArgsConstructor
 public class MessageProvider {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(MessageProvider.class);
+
     private final MessageRepository messageRepository;
     private final MessageJobRepository messageJobRepository;
 
@@ -85,20 +93,11 @@ public class MessageProvider {
     }
 
     /**
-     * Save ParticipantStatus message.
+     * Save instance OutProperties.
      *
      * @param message the ParticipantStatus message
      */
-    public void save(ParticipantStatus message) {
-        if (!message.getAutomationCompositionInfoList().isEmpty()) {
-            saveInstanceOutProperties(message);
-        }
-        if (!message.getParticipantDefinitionUpdates().isEmpty()) {
-            saveCompositionOutProperties(message);
-        }
-    }
-
-    private void saveInstanceOutProperties(ParticipantStatus message) {
+    public void saveInstanceOutProperties(ParticipantStatus message) {
         for (var instance : message.getAutomationCompositionInfoList()) {
             for (var element : instance.getElements()) {
                 var jpa = new JpaMessage();
@@ -116,17 +115,27 @@ public class MessageProvider {
         }
     }
 
-    private void saveCompositionOutProperties(ParticipantStatus message) {
+    /**
+     * Save composition OutProperties.
+     *
+     * @param message the ParticipantStatus message
+     * @param elementStateMap the NodeTemplateState map
+     */
+    public void saveCompositionOutProperties(ParticipantStatus message,
+            Map<ToscaConceptIdentifier, NodeTemplateState> elementStateMap) {
         for (var acDefinition : message.getParticipantDefinitionUpdates()) {
             for (var element : acDefinition.getAutomationCompositionElementDefinitionList()) {
-                var jpa = new JpaMessage();
-                jpa.setIdentificationId(message.getCompositionId().toString());
-                var doc = from(message);
-                doc.setOutProperties(element.getOutProperties());
-                doc.setAcElementDefinitionId(element.getAcElementDefinitionId());
-                jpa.fromAuthorative(doc);
-                ProviderUtils.validate(doc, jpa, "ParticipantStatus composition message");
-                messageRepository.save(jpa);
+                var elementState = elementStateMap.get(element.getAcElementDefinitionId());
+                if (elementState != null && elementState.getParticipantId().equals(message.getParticipantId())) {
+                    var jpa = new JpaMessage();
+                    jpa.setIdentificationId(message.getCompositionId().toString());
+                    var doc = from(message);
+                    doc.setOutProperties(element.getOutProperties());
+                    doc.setAcElementDefinitionId(element.getAcElementDefinitionId());
+                    jpa.fromAuthorative(doc);
+                    ProviderUtils.validate(doc, jpa, "ParticipantStatus composition message");
+                    messageRepository.save(jpa);
+                }
             }
         }
     }
@@ -223,8 +232,14 @@ public class MessageProvider {
             return Optional.empty();
         }
         var job = new JpaMessageJob(identificationId.toString());
-        var result = messageJobRepository.save(job);
-        return Optional.of(result.getJobId());
+        try {
+            var result = messageJobRepository.save(job);
+            return Optional.of(result.getJobId());
+        } catch (ConstraintViolationException ex) {
+            // already exist a job with this identificationId
+            LOGGER.warn(ex.getMessage());
+        }
+        return Optional.empty();
     }
 
     /**
index d850c54..b95ba95 100644 (file)
@@ -34,6 +34,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
+import org.hibernate.exception.ConstraintViolationException;
 import org.junit.jupiter.api.Test;
 import org.onap.policy.clamp.models.acm.concepts.AcElementDeployAck;
 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
@@ -41,6 +42,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementInf
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionInfo;
 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.concepts.NodeTemplateState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.document.concepts.DocMessage;
@@ -95,13 +97,30 @@ class MessageProviderTest {
         message.setReplicaId(UUID.randomUUID());
         var participantDefinition = new ParticipantDefinition();
         participantDefinition.setParticipantId(message.getParticipantId());
-        var element = CommonTestData.getAcElementDefinition(new ToscaConceptIdentifier("name", "1.0.0"));
-        element.setOutProperties(Map.of("compositionProperty", "value"));
-        participantDefinition.setAutomationCompositionElementDefinitionList(List.of(element));
+        var element1 = CommonTestData.getAcElementDefinition(new ToscaConceptIdentifier("name1", "1.0.0"));
+        element1.setOutProperties(Map.of("compositionProperty1", "value"));
+        var element2 = CommonTestData.getAcElementDefinition(new ToscaConceptIdentifier("name2", "1.0.0"));
+        element2.setOutProperties(Map.of("compositionProperty2", "value"));
+        participantDefinition.setAutomationCompositionElementDefinitionList(List.of(element1, element2));
         message.setParticipantDefinitionUpdates(List.of(participantDefinition));
+
+        var nodeTemplateState1 = new NodeTemplateState();
+        nodeTemplateState1.setParticipantId(message.getParticipantId());
+        nodeTemplateState1.setNodeTemplateId(element1.getAcElementDefinitionId());
+
+        var nodeTemplateState2 = new NodeTemplateState();
+        nodeTemplateState2.setParticipantId(UUID.randomUUID());
+        nodeTemplateState2.setNodeTemplateId(element2.getAcElementDefinitionId());
+
         var messageRepository = mock(MessageRepository.class);
         var messageProvider = new MessageProvider(messageRepository, mock(MessageJobRepository.class));
-        messageProvider.save(message);
+
+        messageProvider.saveCompositionOutProperties(message, Map.of());
+        verify(messageRepository, times(0)).save(any());
+
+        messageProvider.saveCompositionOutProperties(message,
+                Map.of(nodeTemplateState1.getNodeTemplateId(), nodeTemplateState1,
+                        nodeTemplateState2.getNodeTemplateId(), nodeTemplateState2));
         verify(messageRepository).save(any());
     }
 
@@ -120,7 +139,7 @@ class MessageProviderTest {
         message.setAutomationCompositionInfoList(List.of(automationCompositionInfo));
         var messageRepository = mock(MessageRepository.class);
         var messageProvider = new MessageProvider(messageRepository, mock(MessageJobRepository.class));
-        messageProvider.save(message);
+        messageProvider.saveInstanceOutProperties(message);
         verify(messageRepository).save(any());
     }
 
@@ -216,6 +235,16 @@ class MessageProviderTest {
         assertThat(opt).isEmpty();
     }
 
+    @Test
+    void testCreateJobFail() {
+        var messageJobRepository = mock(MessageJobRepository.class);
+        var identificationId = UUID.randomUUID();
+        when(messageJobRepository.save(any())).thenThrow(new ConstraintViolationException("", null, ""));
+        var messageProvider = new MessageProvider(mock(MessageRepository.class), messageJobRepository);
+        var opt = messageProvider.createJob(identificationId);
+        assertThat(opt).isEmpty();
+    }
+
     @Test
     void testRemoveJob() {
         var messageJobRepository = mock(MessageJobRepository.class);
index 9e3efce..d624677 100644 (file)
@@ -30,6 +30,7 @@ import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
+import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantPrime;
@@ -53,12 +54,7 @@ public class AcDefinitionHandler {
     public void handlePrime(ParticipantPrime participantPrimeMsg) {
         if (!participantPrimeMsg.getParticipantDefinitionUpdates().isEmpty()) {
             // prime
-            List<AutomationCompositionElementDefinition> list = new ArrayList<>();
-            for (var participantDefinition : participantPrimeMsg.getParticipantDefinitionUpdates()) {
-                if (participantDefinition.getParticipantId().equals(cacheProvider.getParticipantId())) {
-                    list.addAll(participantDefinition.getAutomationCompositionElementDefinitionList());
-                }
-            }
+            var list = collectAcElementDefinition(participantPrimeMsg.getParticipantDefinitionUpdates());
             if (!list.isEmpty()) {
                 cacheProvider.addElementDefinition(participantPrimeMsg.getCompositionId(), list);
                 prime(participantPrimeMsg.getMessageId(), participantPrimeMsg.getCompositionId(), list);
@@ -69,6 +65,17 @@ public class AcDefinitionHandler {
         }
     }
 
+    private List<AutomationCompositionElementDefinition> collectAcElementDefinition(
+            List<ParticipantDefinition> participantDefinitionList) {
+        List<AutomationCompositionElementDefinition> list = new ArrayList<>();
+        for (var participantDefinition : participantDefinitionList) {
+            if (participantDefinition.getParticipantId().equals(cacheProvider.getParticipantId())) {
+                list.addAll(participantDefinition.getAutomationCompositionElementDefinitionList());
+            }
+        }
+        return list;
+    }
+
     private void prime(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list) {
         var inPropertiesMap = list.stream().collect(Collectors.toMap(
                 AutomationCompositionElementDefinition::getAcElementDefinitionId,
@@ -123,11 +130,10 @@ public class AcDefinitionHandler {
                 listener.cleanExecution(participantSyncMsg.getCompositionId(), participantSyncMsg.getMessageId());
             }
 
-            List<AutomationCompositionElementDefinition> list = new ArrayList<>();
-            for (var participantDefinition : participantSyncMsg.getParticipantDefinitionUpdates()) {
-                list.addAll(participantDefinition.getAutomationCompositionElementDefinitionList());
+            var list = collectAcElementDefinition(participantSyncMsg.getParticipantDefinitionUpdates());
+            if (!list.isEmpty()) {
+                cacheProvider.addElementDefinition(participantSyncMsg.getCompositionId(), list);
             }
-            cacheProvider.addElementDefinition(participantSyncMsg.getCompositionId(), list);
         }
 
         for (var automationcomposition : participantSyncMsg.getAutomationcompositionList()) {
index d72f5de..a6fd103 100644 (file)
@@ -44,7 +44,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 class AcDefinitionHandlerTest {
 
     @Test
-    void handleComposiotPrimeTest() {
+    void handleCompositionPrimeTest() {
         var listener = mock(ThreadHandler.class);
         var cacheProvider = mock(CacheProvider.class);
         when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
@@ -103,6 +103,7 @@ class AcDefinitionHandlerTest {
         participantSyncMsg.setAutomationcompositionList(List.of(CommonTestData.createParticipantRestartAc()));
 
         var cacheProvider = mock(CacheProvider.class);
+        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
         var listener = mock(ThreadHandler.class);
         var ach = new AcDefinitionHandler(cacheProvider, mock(ParticipantMessagePublisher.class), listener);
         ach.handleParticipantSync(participantSyncMsg);
@@ -122,6 +123,7 @@ class AcDefinitionHandlerTest {
         participantSyncMsg.setAutomationcompositionList(List.of(participantRestartAc));
 
         var cacheProvider = mock(CacheProvider.class);
+        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
         var listener = mock(ThreadHandler.class);
         var ach = new AcDefinitionHandler(cacheProvider, mock(ParticipantMessagePublisher.class), listener);
         ach.handleParticipantSync(participantSyncMsg);
index d0b7b62..5627bea 100644 (file)
@@ -25,6 +25,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
 import lombok.AllArgsConstructor;
 import org.apache.commons.collections4.MapUtils;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantDeregisterAckPublisher;
@@ -33,6 +35,7 @@ import org.onap.policy.clamp.acm.runtime.supervision.comm.ParticipantSyncPublish
 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
+import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
 import org.onap.policy.clamp.models.acm.concepts.Participant;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantReplica;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
@@ -109,11 +112,19 @@ public class SupervisionParticipantHandler {
                 participantStatusMsg.getParticipantSupportedElementType(), false);
 
         if (!participantStatusMsg.getAutomationCompositionInfoList().isEmpty()) {
-            messageProvider.save(participantStatusMsg);
+            messageProvider.saveInstanceOutProperties(participantStatusMsg);
         }
         if (!participantStatusMsg.getParticipantDefinitionUpdates().isEmpty()
                 && participantStatusMsg.getCompositionId() != null) {
-            messageProvider.save(participantStatusMsg);
+            var acDefinition = acDefinitionProvider.findAcDefinition(participantStatusMsg.getCompositionId());
+            if (acDefinition.isPresent()) {
+                var map = acDefinition.get().getElementStateMap()
+                        .values().stream().collect(Collectors.toMap(NodeTemplateState::getNodeTemplateId,
+                                UnaryOperator.identity()));
+                messageProvider.saveCompositionOutProperties(participantStatusMsg, map);
+            } else {
+                LOGGER.error("Not valid ParticipantStatus message");
+            }
         }
     }
 
index 315fb8c..2cdcf21 100644 (file)
@@ -169,16 +169,11 @@ class SupervisionParticipantHandlerTest {
     }
 
     @Test
-    void testHandleParticipantStatus() {
+    void testHandleParticipantStatusWithInstanceOutProperties() {
         var participantStatusMessage = createParticipantStatus();
         participantStatusMessage.setAutomationCompositionInfoList(List.of(new AutomationCompositionInfo()));
         participantStatusMessage.setCompositionId(UUID.randomUUID());
 
-        var acDefinitionProvider = mock(AcDefinitionProvider.class);
-        var acDefinition = new AutomationCompositionDefinition();
-        acDefinition.setCompositionId(participantStatusMessage.getCompositionId());
-        when(acDefinitionProvider.getAcDefinition(acDefinition.getCompositionId())).thenReturn(acDefinition);
-
         var participantProvider = mock(ParticipantProvider.class);
         var participant = CommonTestData.createParticipant(CommonTestData.getParticipantId());
         when(participantProvider.findParticipant(CommonTestData.getParticipantId()))
@@ -190,17 +185,18 @@ class SupervisionParticipantHandlerTest {
 
         var automationCompositionProvider = mock(AutomationCompositionProvider.class);
         var messageProvider = mock(MessageProvider.class);
+        var acDefinitionProvider = mock(AcDefinitionProvider.class);
         var handler =
                 new SupervisionParticipantHandler(participantProvider, mock(ParticipantRegisterAckPublisher.class),
                         mock(ParticipantDeregisterAckPublisher.class), automationCompositionProvider,
                         acDefinitionProvider, mock(ParticipantSyncPublisher.class), messageProvider);
         handler.handleParticipantMessage(participantStatusMessage);
 
-        verify(messageProvider).save(any(ParticipantStatus.class));
+        verify(messageProvider).saveInstanceOutProperties(any(ParticipantStatus.class));
     }
 
     @Test
-    void testAcDefinitionOutProperties() {
+    void testHandleParticipantStatusWithCompositionOutProperties() {
         var participantStatusMessage = createParticipantStatus();
         var participantDefinition = new ParticipantDefinition();
         participantStatusMessage.setParticipantDefinitionUpdates(List.of(participantDefinition));
@@ -228,31 +224,30 @@ class SupervisionParticipantHandlerTest {
                         mock(ParticipantDeregisterAckPublisher.class), mock(AutomationCompositionProvider.class),
                         acDefinitionProvider, mock(ParticipantSyncPublisher.class), messageProvider);
         handler.handleParticipantMessage(participantStatusMessage);
-        verify(messageProvider).save(participantStatusMessage);
+        verify(messageProvider).saveCompositionOutProperties(any(), any());
     }
 
     @Test
-    void testAcOutProperties() {
+    void testAcOutPropertiesNotValid() {
         var participantStatusMessage = createParticipantStatus();
-        participantStatusMessage.setAutomationCompositionInfoList(List.of(new AutomationCompositionInfo()));
+        var participantDefinition = new ParticipantDefinition();
+        participantStatusMessage.setParticipantDefinitionUpdates(List.of(participantDefinition));
+        participantDefinition.setParticipantId(participantStatusMessage.getParticipantId());
+        var acElementDefinition = new AutomationCompositionElementDefinition();
+        acElementDefinition.setAcElementDefinitionId(new ToscaConceptIdentifier("code", "1.0.0"));
+        participantDefinition.setAutomationCompositionElementDefinitionList(List.of(acElementDefinition));
 
         var compositionId = UUID.randomUUID();
         participantStatusMessage.setCompositionId(compositionId);
-        var acDefinition = new AutomationCompositionDefinition();
-        acDefinition.setState(AcTypeState.COMMISSIONED);
-        acDefinition.setCompositionId(compositionId);
         var acDefinitionProvider = mock(AcDefinitionProvider.class);
         var messageProvider = mock(MessageProvider.class);
-        when(acDefinitionProvider.findAcDefinition(compositionId)).thenReturn(Optional.of(acDefinition));
-        when(acDefinitionProvider.getAcDefinition(compositionId)).thenReturn(acDefinition);
-
         var participantProvider = mock(ParticipantProvider.class);
         var handler =
                 new SupervisionParticipantHandler(participantProvider, mock(ParticipantRegisterAckPublisher.class),
                         mock(ParticipantDeregisterAckPublisher.class), mock(AutomationCompositionProvider.class),
                         acDefinitionProvider, mock(ParticipantSyncPublisher.class), messageProvider);
         handler.handleParticipantMessage(participantStatusMessage);
-        verify(messageProvider).save(participantStatusMessage);
+        verify(messageProvider, times(0)).saveCompositionOutProperties(any(), any());
     }
 
     @Test
@@ -275,7 +270,6 @@ class SupervisionParticipantHandlerTest {
     @Test
     void testHandleParticipantStatusCheckOnline() {
         var participantStatusMessage = createParticipantStatus();
-        participantStatusMessage.setAutomationCompositionInfoList(List.of(new AutomationCompositionInfo()));
         participantStatusMessage.setCompositionId(UUID.randomUUID());
 
         var acDefinitionProvider = mock(AcDefinitionProvider.class);
@@ -296,7 +290,6 @@ class SupervisionParticipantHandlerTest {
         handler.handleParticipantMessage(participantStatusMessage);
 
         verify(participantProvider).saveParticipant(any());
-        verify(messageProvider).save(any(ParticipantStatus.class));
     }
 
     private ParticipantStatus createParticipantStatus() {