From 2736ac3cc3d4e733c2efcca9289f49c519068e97 Mon Sep 17 00:00:00 2001 From: mpriyank Date: Wed, 13 Aug 2025 17:39:06 +0100 Subject: [PATCH] Update data producer id and send LCM event - 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 --- ...CmHandleRegistrationServicePropertyHandler.java | 53 ++++++++---- .../lcm/LcmEventsCmHandleStateHandlerImpl.java | 4 +- ...andlerAsyncHelper.java => LcmEventsHelper.java} | 16 +++- ...leRegistrationServicePropertyHandlerSpec.groovy | 96 +++++++++++++++++----- .../LcmEventsCmHandleStateHandlerImplSpec.groovy | 4 +- 5 files changed, 127 insertions(+), 46 deletions(-) rename cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/{LcmEventsCmHandleStateHandlerAsyncHelper.java => LcmEventsHelper.java} (86%) diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java index 0996263db3..31b0f4ff2d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java @@ -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 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, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java index 4fed26cd6a..e4df721d91 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java @@ -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 cmHandleTransitionPairs = prepareCmHandleTransitionBatch(cmHandleStatePerCmHandle); persistCmHandleBatch(cmHandleTransitionPairs); - lcmEventsCmHandleStateHandlerAsyncHelper.sendLcmEventBatchAsynchronously(cmHandleTransitionPairs); + lcmEventsHelper.sendLcmEventBatchAsynchronously(cmHandleTransitionPairs); cmHandleStateMonitor.updateCmHandleStateMetrics(cmHandleTransitionPairs); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsHelper.java similarity index 86% rename from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java rename to cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsHelper.java index 6717f8f7c5..f94558a550 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsHelper.java @@ -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, diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandlerSpec.groovy index e3ebbcde2a..10f328d9f1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandlerSpec.groovy @@ -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) 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: [] + ) + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy index 8b7708392d..8df69d8766 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy @@ -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 -- 2.16.6