From 76b642f5ceecee90d87fdfecd265158440fe7620 Mon Sep 17 00:00:00 2001 From: leventecsanyi Date: Thu, 4 Jan 2024 10:06:00 +0100 Subject: [PATCH] Allow updating of cmHandles with an alternateId - added methods to update alternate id - fixed unit tests - added new unit test for coverage Issue-ID: CPS-1986 Change-Id: I9ef717c5f2c405482e0a5bb70f94f465ba1ff64f Signed-off-by: leventecsanyi --- .../NetworkCmProxyDataServicePropertyHandler.java | 46 +++++++++++++++- ...orkCmProxyDataServicePropertyHandlerSpec.groovy | 62 ++++++++++++++++++++-- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java index be6a40198..15209328f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ @@ -26,10 +26,15 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY; import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import com.google.common.collect.ImmutableMap; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -38,13 +43,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.onap.cps.api.CpsDataService; import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; @Slf4j @@ -55,6 +65,8 @@ import org.springframework.stereotype.Service; public class NetworkCmProxyDataServicePropertyHandler { private final InventoryPersistence inventoryPersistence; + private final CpsDataService cpsDataService; + private final JsonObjectMapper jsonObjectMapper; /** * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes. @@ -70,6 +82,7 @@ public class NetworkCmProxyDataServicePropertyHandler { try { final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId) .iterator().next(); + updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle); processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle); cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)); } catch (final DataNodeNotFoundException e) { @@ -89,6 +102,26 @@ public class NetworkCmProxyDataServicePropertyHandler { return cmHandleRegistrationResponses; } + private void updateAlternateId(final DataNode existingCmHandleDataNode, + final NcmpServiceCmHandle ncmpServiceCmHandle) { + final String newAlternateId = ncmpServiceCmHandle.getAlternateId(); + if (!StringUtils.isEmpty(newAlternateId)) { + final String existingAlternateId = (String) existingCmHandleDataNode.getLeaves().get("alternate-id"); + if (StringUtils.isEmpty(existingAlternateId)) { + final YangModelCmHandle yangModelCmHandle = + YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode, + ncmpServiceCmHandle.getCmHandleId()); + setAndUpdateAlternateId(yangModelCmHandle, newAlternateId); + } else { + if (!newAlternateId.equals(existingAlternateId)) { + log.warn("Unable to update alternateId for cmHandle {}. " + + "Value for alternateId has been set previously.", + ncmpServiceCmHandle.getCmHandleId()); + } + } + } + } + private void processUpdates(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle incomingCmHandle) { if (!incomingCmHandle.getPublicProperties().isEmpty()) { updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY, incomingCmHandle.getPublicProperties()); @@ -165,6 +198,17 @@ public class NetworkCmProxyDataServicePropertyHandler { return new DataNodeBuilder().withXpath(xpath).withLeaves(ImmutableMap.copyOf(updatedLeaves)).build(); } + private void setAndUpdateAlternateId(final YangModelCmHandle upgradedCmHandle, final String alternateId) { + final Map> dmiRegistryProperties = new HashMap<>(1); + final Map cmHandleProperties = new HashMap<>(2); + cmHandleProperties.put("id", upgradedCmHandle.getId()); + cmHandleProperties.put("alternate-id", alternateId); + dmiRegistryProperties.put("cm-handles", cmHandleProperties); + cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, + jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now()); + log.info("Updating alternateId for cmHandle {} with value : {})", upgradedCmHandle.getId(), alternateId); + } + enum PropertyType { DMI_PROPERTY("additional-properties"), PUBLIC_PROPERTY("public-properties"); diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy index 6439f0b58..2a15e6c1a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * Modifications Copyright (C) 2023 TechMahindra Ltd. * ================================================================================ @@ -22,6 +22,14 @@ package org.onap.cps.ncmp.api.impl +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import com.fasterxml.jackson.databind.ObjectMapper +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.slf4j.LoggerFactory + import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND @@ -29,6 +37,9 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status +import ch.qos.logback.core.read.ListAppender +import org.onap.cps.api.CpsDataService +import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle @@ -40,8 +51,22 @@ import spock.lang.Specification class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { def mockInventoryPersistence = Mock(InventoryPersistence) + def mockCpsDataService = Mock(CpsDataService) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def logger = Spy(ListAppender) + + @BeforeEach + void setup() { + ((Logger) LoggerFactory.getLogger(NetworkCmProxyDataServicePropertyHandler.class)).addAppender(logger); + logger.start(); + } - def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence) + @AfterEach + void teardown() { + ((Logger) LoggerFactory.getLogger(NetworkCmProxyDataServicePropertyHandler.class)).detachAndStopAllAppenders(); + } + + def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper) def static cmHandleId = 'myHandle1' def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']" @@ -173,6 +198,38 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) } + def 'Update CM Handle Alternate ID when #scenario'() { + given: 'an existing cm handle with alternate id #existingAlternateId' + DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['alternate-id': oldAlternateId]) + and: 'an update request with an alternate id #newAlternateId' + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: newAlternateId) + when: 'update data node leaves is called with the update request' + objectUnderTest.updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle) + then: 'the update node leaves method is invoked as many times as expected' + numberOfInvocations * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >> { args -> + assert args[3].contains(newAlternateId) + } + and: 'correct information is logged' + def lastLoggingEvent = logger.list[0] + if (expectLogWarning) { + assert lastLoggingEvent.level == Level.WARN + assert lastLoggingEvent.formattedMessage.contains('Unable') + } else if (numberOfInvocations == 1) { + assert lastLoggingEvent.level == Level.INFO + assert lastLoggingEvent.formattedMessage.contains('Updating alternateId') + } else { + assert lastLoggingEvent == null + } + where: 'following updates are attempted' + scenario | oldAlternateId | newAlternateId || numberOfInvocations | expectLogWarning + 'old alternate id null' | null | 'new' || 1 | false + 'old alternate id empty' | '' | 'new' || 1 | false + 'old alternate id not empty' | 'old' | 'new' || 0 | true + 'same alternate id' | 'old' | 'old' || 0 | false + 'empty new alternate id' | 'old' | '' || 0 | false + 'null new alternate id' | 'old' | null || 0 | false + } + def convertToProperties(expectedPropertiesAfterUpdateAsMap) { def properties = [].withDefault { [:] } expectedPropertiesAfterUpdateAsMap.forEach(property -> @@ -181,5 +238,4 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification { })) return properties } - } -- 2.16.6