Update data producer id and send LCM event 49/141849/3
authormpriyank <priyank.maheshwari@est.tech>
Wed, 13 Aug 2025 16:39:06 +0000 (17:39 +0100)
committerPriyank Maheshwari <priyank.maheshwari@est.tech>
Thu, 21 Aug 2025 14:40:16 +0000 (14:40 +0000)
- Logic updated for setting the data producer id
   * null to value is allowed
   * some-value to other-value allowed
   * other-value to null is not allowed and ignored
   * some-value to some-value is not allowed and ignored
- LCM event is raised only when the data producer identifier is updated
- added testware for the same

Issue-ID: CPS-2935
Change-Id: I0c194ed5da37affd3f82889520685bb9c1d4cdae
Signed-off-by: mpriyank <priyank.maheshwari@est.tech>
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsHelper.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java with 86% similarity]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy

index 0996263..31b0f4f 100644 (file)
@@ -55,6 +55,7 @@ import org.onap.cps.impl.DataNodeBuilder;
 import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse;
 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsHelper;
 import org.onap.cps.ncmp.impl.utils.YangDataConverter;
 import org.onap.cps.utils.ContentType;
 import org.onap.cps.utils.JsonObjectMapper;
@@ -74,6 +75,7 @@ public class CmHandleRegistrationServicePropertyHandler {
     private final AlternateIdChecker alternateIdChecker;
     @Qualifier("cmHandleIdPerAlternateId")
     private final IMap<String, String> cmHandleIdPerAlternateId;
+    private final LcmEventsHelper lcmEventsHelper;
 
     /**
      * Iterates over incoming updatedNcmpServiceCmHandles and update the dataNodes based on the updated attributes.
@@ -140,25 +142,40 @@ public class CmHandleRegistrationServicePropertyHandler {
     }
 
     private void updateDataProducerIdentifier(final DataNode cmHandleDataNode,
-                                              final NcmpServiceCmHandle ncmpServiceCmHandle) {
-        final String newDataProducerIdentifier = ncmpServiceCmHandle.getDataProducerIdentifier();
-        if (StringUtils.isNotBlank(newDataProducerIdentifier)) {
-            final YangModelCmHandle yangModelCmHandle = YangDataConverter.toYangModelCmHandle(cmHandleDataNode);
-            final String existingDataProducerIdentifier = yangModelCmHandle.getDataProducerIdentifier();
-            if (StringUtils.isNotBlank(existingDataProducerIdentifier)) {
-                if (!existingDataProducerIdentifier.equals(newDataProducerIdentifier)) {
-                    log.warn("Unable to update dataProducerIdentifier for cmHandle {}. "
-                            + "Value for dataProducerIdentifier has been set previously.",
-                        ncmpServiceCmHandle.getCmHandleId());
-                } else {
-                    log.debug("dataProducerIdentifier for cmHandle {} is already set to {}.",
-                        ncmpServiceCmHandle.getCmHandleId(), newDataProducerIdentifier);
-                }
-            } else {
-                setAndUpdateCmHandleField(
-                    yangModelCmHandle.getId(), "data-producer-identifier", newDataProducerIdentifier);
-            }
+            final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        final String targetDataProducerIdentifier = ncmpServiceCmHandle.getDataProducerIdentifier();
+        final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
+
+        if (StringUtils.isBlank(targetDataProducerIdentifier)) {
+            log.warn("Ignoring update for cmHandle {}: target dataProducerIdentifier is null or blank.", cmHandleId);
+            return;
         }
+
+        final YangModelCmHandle existingYangModelCmHandle = YangDataConverter.toYangModelCmHandle(cmHandleDataNode);
+        final String existingDataProducerIdentifier = existingYangModelCmHandle.getDataProducerIdentifier();
+
+        if (existingDataProducerIdentifier.equals(targetDataProducerIdentifier)) {
+            log.debug("Ignoring update as dataProducerIdentifier for cmHandle {} is already set to {}.", cmHandleId,
+                    targetDataProducerIdentifier);
+            return;
+        }
+
+        setAndUpdateCmHandleField(cmHandleId, "data-producer-identifier", targetDataProducerIdentifier);
+        log.debug("dataProducerIdentifier for cmHandle {} updated from {} to {}", cmHandleId,
+                existingDataProducerIdentifier, targetDataProducerIdentifier);
+        sendLcmEventForDataProducerIdentifier(cmHandleId, existingYangModelCmHandle);
+    }
+
+    private void sendLcmEventForDataProducerIdentifier(final String cmHandleId,
+            final YangModelCmHandle existingYangModelCmHandle) {
+        final YangModelCmHandle updatedYangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
+        final NcmpServiceCmHandle existingNcmpServiceCmHandle =
+                YangDataConverter.toNcmpServiceCmHandle(existingYangModelCmHandle);
+        final NcmpServiceCmHandle updatedNcmpServiceCmHandle =
+                YangDataConverter.toNcmpServiceCmHandle(updatedYangModelCmHandle);
+
+        lcmEventsHelper.sendLcmEventAsynchronously(updatedNcmpServiceCmHandle,
+                existingNcmpServiceCmHandle);
     }
 
     private void updateProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType,
index 4fed26c..e4df721 100644 (file)
@@ -50,7 +50,7 @@ import org.springframework.stereotype.Service;
 public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleStateHandler {
 
     private final InventoryPersistence inventoryPersistence;
-    private final LcmEventsCmHandleStateHandlerAsyncHelper lcmEventsCmHandleStateHandlerAsyncHelper;
+    private final LcmEventsHelper lcmEventsHelper;
     private final CmHandleStateMonitor cmHandleStateMonitor;
 
     @Override
@@ -60,7 +60,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
         final Collection<CmHandleTransitionPair> cmHandleTransitionPairs =
                 prepareCmHandleTransitionBatch(cmHandleStatePerCmHandle);
         persistCmHandleBatch(cmHandleTransitionPairs);
-        lcmEventsCmHandleStateHandlerAsyncHelper.sendLcmEventBatchAsynchronously(cmHandleTransitionPairs);
+        lcmEventsHelper.sendLcmEventBatchAsynchronously(cmHandleTransitionPairs);
         cmHandleStateMonitor.updateCmHandleStateMetrics(cmHandleTransitionPairs);
     }
 
@@ -33,7 +33,7 @@ import org.springframework.stereotype.Service;
 
 @Service
 @RequiredArgsConstructor
-public class LcmEventsCmHandleStateHandlerAsyncHelper {
+public class LcmEventsHelper {
 
     private final LcmEventsProducerHelper lcmEventsProducerHelper;
     private final LcmEventsProducer lcmEventsProducer;
@@ -50,8 +50,20 @@ public class LcmEventsCmHandleStateHandlerAsyncHelper {
                 toNcmpServiceCmHandle(cmHandleTransitionPair.getCurrentYangModelCmHandle())));
     }
 
-    private void sendLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+    /**
+     * Sends LcmEvent asynchronously.
+     *
+     * @param targetNcmpServiceCmHandle   target Ncmp Service Cm Handle
+     * @param existingNcmpServiceCmHandle existing Ncmp Service Cm Handle
+     */
+    @Async("notificationExecutor")
+    public void sendLcmEventAsynchronously(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
                               final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
+        sendLcmEvent(targetNcmpServiceCmHandle, existingNcmpServiceCmHandle);
+    }
+
+    private void sendLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+            final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
         final String cmHandleId = targetNcmpServiceCmHandle.getCmHandleId();
         final LcmEventHeader lcmEventHeader =
                 lcmEventsProducerHelper.populateLcmEventHeader(cmHandleId, targetNcmpServiceCmHandle,
index e3ebbcd..10f328d 100644 (file)
@@ -34,6 +34,8 @@ import org.onap.cps.api.exceptions.DataNodeNotFoundException
 import org.onap.cps.api.exceptions.DataValidationException
 import org.onap.cps.api.model.DataNode
 import org.onap.cps.impl.DataNodeBuilder
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsHelper
 import org.onap.cps.utils.ContentType
 import org.onap.cps.utils.JsonObjectMapper
 import org.slf4j.LoggerFactory
@@ -54,8 +56,9 @@ class CmHandleRegistrationServicePropertyHandlerSpec extends Specification {
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
     def mockAlternateIdChecker = Mock(AlternateIdChecker)
     def mockCmHandleIdPerAlternateId = Mock(IMap)
+    def mockLcmEventsHelper = Mock(LcmEventsHelper)
 
-    def objectUnderTest = new CmHandleRegistrationServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker, mockCmHandleIdPerAlternateId)
+    def objectUnderTest = new CmHandleRegistrationServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker, mockCmHandleIdPerAlternateId, mockLcmEventsHelper)
     def logger = Spy(ListAppender<ILoggingEvent>)
 
     void setup() {
@@ -242,50 +245,88 @@ class CmHandleRegistrationServicePropertyHandlerSpec extends Specification {
     }
 
     def 'Update CM Handle data producer identifier from #scenario'() {
-        given: 'an existing cm handle with no data producer identifier'
-            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId','data-producer-identifier': oldDataProducerIdentifier])
+        given: 'an existing cm handle with old data producer identifier'
+            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': oldDataProducerIdentifier])
         and: 'an update request with a new data producer identifier'
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'New Data Producer Identifier')
-        when: 'data producer identifier updated'
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'cmHandleId', dataProducerIdentifier: 'New Data Producer Identifier')
+        and: 'the inventory persistence returns updated yang model'
+            1 * mockInventoryPersistence.getYangModelCmHandle('cmHandleId') >> createYangModelCmHandle('cmHandleId', 'New Data Producer Identifier')
+        when: 'data producer identifier is updated'
             objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle)
-        then: 'the update node leaves method is invoked once'
+        then: 'the update node leaves method is invoked once with correct parameters'
             1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> { args ->
                 assert args[3].contains('New Data Producer Identifier')
             }
-        and: 'correct information is logged'
-            def lastLoggingEvent = logger.list[0]
-            assert lastLoggingEvent.level == Level.DEBUG
-            assert lastLoggingEvent.formattedMessage.contains('Updating data-producer-identifier')
-        where: 'the following scenarios are attempted'
+        and: 'LCM event is sent'
+            1 * mockLcmEventsHelper.sendLcmEventAsynchronously(_, _) >> { args ->
+                assert args[0].dataProducerIdentifier == 'New Data Producer Identifier'
+            }
+        where: 'the following scenarios are used'
             scenario             | oldDataProducerIdentifier
             'null to something'  | null
             'blank to something' | ''
     }
 
     def 'Update CM Handle data producer identifier with same value'() {
-        given: 'an existing cm handle with no data producer identifier'
-            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId','data-producer-identifier': 'same id'])
-        and: 'an update request with a new data producer identifier'
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'same id')
-        when: 'data producer identifier updated'
+        given: 'an existing cm handle with existing data producer identifier'
+            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': 'same-data-producer-id'])
+        and: 'an update request with the same data producer identifier'
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'cmHandleId', dataProducerIdentifier: 'same-data-producer-id')
+        when: 'data producer identifier is updated'
             objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle)
         then: 'the update node leaves method is not invoked'
             0 * mockCpsDataService.updateNodeLeaves(*_)
+        and: 'LCM event is not sent'
+            0 * mockLcmEventsHelper.sendLcmEventAsynchronously(*_)
+        and: 'debug information is logged'
+            def loggingEvent = logger.list[0]
+            assert loggingEvent.level == Level.DEBUG
+            assert loggingEvent.formattedMessage.contains('dataProducerIdentifier for cmHandle cmHandleId is already set to same-data-producer-id')
     }
 
-    def 'Update CM Handle data producer identifier from some data producer identifier to another data producer identifier'() {
+    def 'Update CM Handle data producer identifier from existing to new value'() {
         given: 'an existing cm handle with a data producer identifier'
-            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': 'someDataProducerIdentifier'])
+            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': 'oldDataProducerIdentifier'])
         and: 'an update request with a new data producer identifier'
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'someNewDataProducerIdentifier')
-        when: 'update data producer identifier is called with the update request'
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'cmHandleId', dataProducerIdentifier: 'newDataProducerIdentifier')
+        and: 'the inventory persistence returns updated yang model'
+            mockInventoryPersistence.getYangModelCmHandle('cmHandleId') >> createYangModelCmHandle('cmHandleId', 'newDataProducerIdentifier')
+        when: 'update data producer identifier is called'
+            objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle)
+        then: 'the update node leaves method is invoked once with correct parameters'
+            1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> { args ->
+                assert args[3].contains('newDataProducerIdentifier')
+            }
+        and: 'LCM event is sent'
+            1 * mockLcmEventsHelper.sendLcmEventAsynchronously(_, _) >> { args ->
+                assert args[0].dataProducerIdentifier == 'newDataProducerIdentifier'
+                assert args[1].dataProducerIdentifier == 'oldDataProducerIdentifier'
+            }
+        and: 'correct information is logged'
+            def loggingEvent = logger.list[1]
+            assert loggingEvent.level == Level.DEBUG
+            assert loggingEvent.formattedMessage.contains('updated from oldDataProducerIdentifier to newDataProducerIdentifier')
+    }
+
+    def 'Update CM Handle data producer identifier with null or blank target identifier'() {
+        given: 'an existing cm handle'
+            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': 'cmHandleId', 'data-producer-identifier': 'some existing id'])
+        and: 'an update request with null/blank data producer identifier'
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'cmHandleId', dataProducerIdentifier: targetDataProducerIdentifier)
+        when: 'data producer identifier update is attempted'
             objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle)
         then: 'the update node leaves method is not invoked'
             0 * mockCpsDataService.updateNodeLeaves(*_)
-        and: 'correct information is logged'
+        and: 'LCM event is not sent'
+            0 * mockLcmEventsHelper.sendLcmEventAsynchronously(*_)
+        and: 'warning is logged'
             def lastLoggingEvent = logger.list[0]
             assert lastLoggingEvent.level == Level.WARN
-            assert lastLoggingEvent.formattedMessage.contains('Unable to update dataProducerIdentifier')
+            assert lastLoggingEvent.formattedMessage.contains('Ignoring update for cmHandle cmHandleId: target dataProducerIdentifier is null or blank')
+        where: 'the following invalid scenarios are used'
+            scenario      | targetDataProducerIdentifier
+            'null value'  | null
+            'blank value' | ''
     }
 
     def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
@@ -296,4 +337,15 @@ class CmHandleRegistrationServicePropertyHandlerSpec extends Specification {
             }))
         return properties
     }
+
+
+    def createYangModelCmHandle(cmHandleId, dataProducerIdentifier) {
+        new YangModelCmHandle(
+            id: cmHandleId,
+            dmiDataServiceName: 'some-dmi-plugin',
+            dataProducerIdentifier: dataProducerIdentifier,
+            additionalProperties: [],
+            publicProperties: []
+        )
+    }
 }
index 8b77083..8df69d8 100644 (file)
@@ -60,8 +60,8 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
     def mockLcmEventsProducer = Mock(LcmEventsProducer)
     def mockCmHandleStateMonitor = Mock(CmHandleStateMonitor)
 
-    def lcmEventsCmHandleStateHandlerAsyncHelper = new LcmEventsCmHandleStateHandlerAsyncHelper(mockLcmEventsCreator, mockLcmEventsProducer)
-    def objectUnderTest = new LcmEventsCmHandleStateHandlerImpl(mockInventoryPersistence, lcmEventsCmHandleStateHandlerAsyncHelper, mockCmHandleStateMonitor)
+    def lcmEventsHelper = new LcmEventsHelper(mockLcmEventsCreator, mockLcmEventsProducer)
+    def objectUnderTest = new LcmEventsCmHandleStateHandlerImpl(mockInventoryPersistence, lcmEventsHelper, mockCmHandleStateMonitor)
 
     def cmHandleId = 'cmhandle-id-1'
     def compositeState