From: Arpit Singh Date: Thu, 3 Jul 2025 06:33:08 +0000 (+0530) Subject: Part 3: Refactor CPS Delta code to utility class X-Git-Tag: 3.7.0~12^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=e7201da45060316eb7ec730f7e63901955909484;p=cps.git Part 3: Refactor CPS Delta code to utility class - Condensed Delta Report format refactored to a utils class Issue-ID:CPS-2838 Change-Id: I0ffaf26701f9ab297ca66c5bd1c52dfeefff077e Signed-off-by: Arpit Singh --- diff --git a/cps-service/src/main/java/org/onap/cps/impl/CpsDeltaServiceImpl.java b/cps-service/src/main/java/org/onap/cps/impl/CpsDeltaServiceImpl.java index 8ca49a3e65..9dcc9cd3b9 100644 --- a/cps-service/src/main/java/org/onap/cps/impl/CpsDeltaServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/impl/CpsDeltaServiceImpl.java @@ -23,13 +23,11 @@ package org.onap.cps.impl; import static org.onap.cps.utils.ContentType.JSON; import io.micrometer.core.annotation.Timed; -import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAnchorService; @@ -40,12 +38,10 @@ import org.onap.cps.api.model.Anchor; import org.onap.cps.api.model.DataNode; import org.onap.cps.api.model.DeltaReport; import org.onap.cps.api.parameters.FetchDescendantsOption; -import org.onap.cps.cpspath.parser.CpsPathUtil; -import org.onap.cps.utils.DataMapUtils; import org.onap.cps.utils.DataMapper; import org.onap.cps.utils.JsonObjectMapper; import org.onap.cps.utils.deltareport.DeltaReportGenerator; -import org.onap.cps.utils.deltareport.DeltaReportHelper; +import org.onap.cps.utils.deltareport.GroupedDeltaReportGenerator; import org.springframework.stereotype.Service; @Slf4j @@ -58,8 +54,8 @@ public class CpsDeltaServiceImpl implements CpsDeltaService { private final DataNodeFactory dataNodeFactory; private final DataMapper dataMapper; private final JsonObjectMapper jsonObjectMapper; - private final DeltaReportHelper deltaReportHelper; private final DeltaReportGenerator deltaReportGenerator; + private final GroupedDeltaReportGenerator groupedDeltaReportGenerator; @Override @Timed(value = "cps.delta.service.get.delta", @@ -105,7 +101,8 @@ public class CpsDeltaServiceImpl implements CpsDeltaService { final List deltaReport = new ArrayList<>(); if (groupDataNodes) { - deltaReport.addAll(getCondensedDeltaReports(sourceDataNodes, targetDataNodes)); + deltaReport + .addAll(groupedDeltaReportGenerator.createCondensedDeltaReports(sourceDataNodes, targetDataNodes)); } else { deltaReport.addAll(deltaReportGenerator.createDeltaReports(sourceDataNodes, targetDataNodes)); } @@ -137,91 +134,4 @@ public class CpsDeltaServiceImpl implements CpsDeltaService { .createDataNodesWithYangResourceXpathAndNodeData(yangResourceContentPerName, xpath, targetData, JSON); } } - - private List getCondensedDeltaReports(final Collection sourceDataNodes, - final Collection targetDataNodes) { - - final List deltaReportEntries = new ArrayList<>(); - final Map xpathToTargetDataNodes = flattenToXpathToFirstLevelDataNodeMap(targetDataNodes); - deltaReportEntries.addAll(getCondensedRemovedDeltaReports(sourceDataNodes, xpathToTargetDataNodes)); - deltaReportEntries.addAll(getCondensedUpdatedDeltaReports(sourceDataNodes, xpathToTargetDataNodes)); - deltaReportEntries.addAll(getCondensedAddedDeltaReports(sourceDataNodes, targetDataNodes)); - return deltaReportEntries; - } - - private static List getCondensedRemovedDeltaReports(final Collection sourceDataNodes, - final Map xpathToTargetDataNodes) { - - final List deltaReportEntriesForRemove = new ArrayList<>(); - final Collection removedDataNodes = - getDataNodesForDeltaReport(sourceDataNodes, xpathToTargetDataNodes); - if (!removedDataNodes.isEmpty()) { - final String xpath = getXpathForDeltaReport(removedDataNodes); - deltaReportEntriesForRemove.add(new DeltaReportBuilder().actionRemove().withXpath(xpath) - .withSourceData(getCondensedDataForDeltaReport(removedDataNodes)).build()); - } - return deltaReportEntriesForRemove; - } - - private List getCondensedUpdatedDeltaReports(final Collection sourceDataNodes, - final Map xpathToTargetDataNodes) { - final List deltaReportEntriesForUpdates = new ArrayList<>(); - for (final DataNode sourceDataNode : sourceDataNodes) { - final String xpath = sourceDataNode.getXpath(); - if (xpathToTargetDataNodes.containsKey(xpath)) { - final DataNode targetDataNode = xpathToTargetDataNodes.get(xpath); - final List updatedDataForDeltaReport = - deltaReportHelper.createDeltaReportsForUpdates(xpath, sourceDataNode, targetDataNode); - deltaReportEntriesForUpdates.addAll(updatedDataForDeltaReport); - getCondensedDeltaReportsForChildDataNodes(sourceDataNode, targetDataNode, deltaReportEntriesForUpdates); - } - } - return deltaReportEntriesForUpdates; - } - - private void getCondensedDeltaReportsForChildDataNodes(final DataNode sourceDataNode, - final DataNode targetDataNode, - final List deltaReportEntries) { - final Collection childrenOfSourceDataNodes = sourceDataNode.getChildDataNodes(); - final Collection childrenOfTargetDataNodes = targetDataNode.getChildDataNodes(); - if (!childrenOfSourceDataNodes.isEmpty() || !childrenOfTargetDataNodes.isEmpty()) { - deltaReportEntries.addAll(getCondensedDeltaReports(childrenOfSourceDataNodes, childrenOfTargetDataNodes)); - } - } - - private static List getCondensedAddedDeltaReports(final Collection sourceDataNodes, - final Collection targetDataNodes) { - - final List addedDeltaReportEntries = new ArrayList<>(); - final Collection addedDataNodes = - getDataNodesForDeltaReport(targetDataNodes, flattenToXpathToFirstLevelDataNodeMap(sourceDataNodes)); - if (!addedDataNodes.isEmpty()) { - final String xpath = getXpathForDeltaReport(addedDataNodes); - addedDeltaReportEntries.add(new DeltaReportBuilder().actionCreate().withXpath(xpath) - .withTargetData(getCondensedDataForDeltaReport(addedDataNodes)).build()); - } - return addedDeltaReportEntries; - } - - private static String getXpathForDeltaReport(final Collection dataNodes) { - final String firstNodeXpath = dataNodes.iterator().next().getXpath(); - final String parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(firstNodeXpath); - return parentNodeXpath.isEmpty() ? firstNodeXpath : parentNodeXpath; - } - - private static Collection getDataNodesForDeltaReport(final Collection dataNodes, - final Map xpathToDataNodes) { - return dataNodes.stream().filter(dataNode -> !xpathToDataNodes.containsKey(dataNode.getXpath())).toList(); - } - - private static Map getCondensedDataForDeltaReport(final Collection dataNodes) { - final DataNode containerNode = new DataNodeBuilder().withChildDataNodes(dataNodes).build(); - final Map condensedData = DataMapUtils.toDataMap(containerNode); - return condensedData.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, - entry -> (Serializable) entry.getValue())); - } - - private static Map flattenToXpathToFirstLevelDataNodeMap(final Collection dataNodes) { - return dataNodes.stream().collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode)); - } } diff --git a/cps-service/src/main/java/org/onap/cps/utils/deltareport/GroupedDeltaReportGenerator.java b/cps-service/src/main/java/org/onap/cps/utils/deltareport/GroupedDeltaReportGenerator.java new file mode 100644 index 0000000000..d0ab418f97 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/utils/deltareport/GroupedDeltaReportGenerator.java @@ -0,0 +1,140 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2025 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.utils.deltareport; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.api.model.DataNode; +import org.onap.cps.api.model.DeltaReport; +import org.onap.cps.cpspath.parser.CpsPathUtil; +import org.onap.cps.impl.DataNodeBuilder; +import org.onap.cps.impl.DeltaReportBuilder; +import org.onap.cps.utils.DataMapUtils; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GroupedDeltaReportGenerator { + + private final DeltaReportHelper deltaReportHelper; + + /** + * Get condensed delta reports for the given source and target data nodes. + * + * @param sourceDataNodes the source data nodes + * @param targetDataNodes the target data nodes + * @return a list of condensed delta reports + */ + public List createCondensedDeltaReports(final Collection sourceDataNodes, + final Collection targetDataNodes) { + + final List deltaReportEntries = new ArrayList<>(); + final Map xpathToTargetDataNodes = flattenToXpathToFirstLevelDataNodeMap(targetDataNodes); + deltaReportEntries.addAll(getCondensedRemovedDeltaReports(sourceDataNodes, xpathToTargetDataNodes)); + deltaReportEntries.addAll(getCondensedUpdatedDeltaReports(sourceDataNodes, xpathToTargetDataNodes)); + deltaReportEntries.addAll(getCondensedAddedDeltaReports(sourceDataNodes, targetDataNodes)); + return deltaReportEntries; + } + + private static List getCondensedRemovedDeltaReports(final Collection sourceDataNodes, + final Map xpathToTargetDataNodes) { + + final List deltaReportEntriesForRemove = new ArrayList<>(); + final Collection removedDataNodes = + getDataNodesForDeltaReport(sourceDataNodes, xpathToTargetDataNodes); + if (!removedDataNodes.isEmpty()) { + final String xpath = getXpathForDeltaReport(removedDataNodes); + deltaReportEntriesForRemove.add(new DeltaReportBuilder().actionRemove().withXpath(xpath) + .withSourceData(getCondensedDataForDeltaReport(removedDataNodes)).build()); + } + return deltaReportEntriesForRemove; + } + + private List getCondensedUpdatedDeltaReports(final Collection sourceDataNodes, + final Map xpathToTargetDataNodes) { + final List deltaReportEntriesForUpdates = new ArrayList<>(); + for (final DataNode sourceDataNode : sourceDataNodes) { + final String xpath = sourceDataNode.getXpath(); + if (xpathToTargetDataNodes.containsKey(xpath)) { + final DataNode targetDataNode = xpathToTargetDataNodes.get(xpath); + final List updatedDataForDeltaReport = + deltaReportHelper.createDeltaReportsForUpdates(xpath, sourceDataNode, targetDataNode); + deltaReportEntriesForUpdates.addAll(updatedDataForDeltaReport); + getCondensedDeltaReportsForChildDataNodes(sourceDataNode, targetDataNode, deltaReportEntriesForUpdates); + } + } + return deltaReportEntriesForUpdates; + } + + private void getCondensedDeltaReportsForChildDataNodes(final DataNode sourceDataNode, + final DataNode targetDataNode, + final List deltaReportEntries) { + final Collection childrenOfSourceDataNodes = sourceDataNode.getChildDataNodes(); + final Collection childrenOfTargetDataNodes = targetDataNode.getChildDataNodes(); + if (!childrenOfSourceDataNodes.isEmpty() || !childrenOfTargetDataNodes.isEmpty()) { + deltaReportEntries + .addAll(createCondensedDeltaReports(childrenOfSourceDataNodes, childrenOfTargetDataNodes)); + } + } + + private static List getCondensedAddedDeltaReports(final Collection sourceDataNodes, + final Collection targetDataNodes) { + + final List addedDeltaReportEntries = new ArrayList<>(); + final Collection addedDataNodes = + getDataNodesForDeltaReport(targetDataNodes, flattenToXpathToFirstLevelDataNodeMap(sourceDataNodes)); + if (!addedDataNodes.isEmpty()) { + final String xpath = getXpathForDeltaReport(addedDataNodes); + addedDeltaReportEntries.add(new DeltaReportBuilder().actionCreate().withXpath(xpath) + .withTargetData(getCondensedDataForDeltaReport(addedDataNodes)).build()); + } + return addedDeltaReportEntries; + } + + private static String getXpathForDeltaReport(final Collection dataNodes) { + final String firstNodeXpath = dataNodes.iterator().next().getXpath(); + final String parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(firstNodeXpath); + return parentNodeXpath.isEmpty() ? firstNodeXpath : parentNodeXpath; + } + + private static Collection getDataNodesForDeltaReport(final Collection dataNodes, + final Map xpathToDataNodes) { + return dataNodes.stream().filter(dataNode -> !xpathToDataNodes.containsKey(dataNode.getXpath())).toList(); + } + + private static Map getCondensedDataForDeltaReport(final Collection dataNodes) { + final DataNode containerNode = new DataNodeBuilder().withChildDataNodes(dataNodes).build(); + final Map condensedData = DataMapUtils.toDataMap(containerNode); + return condensedData.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + entry -> (Serializable) entry.getValue())); + } + + private static Map flattenToXpathToFirstLevelDataNodeMap(final Collection dataNodes) { + return dataNodes.stream().collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode)); + } +} diff --git a/cps-service/src/test/groovy/org/onap/cps/impl/CpsDeltaServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/impl/CpsDeltaServiceImplSpec.groovy index dc48ec9f07..089c19ef6b 100644 --- a/cps-service/src/test/groovy/org/onap/cps/impl/CpsDeltaServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/impl/CpsDeltaServiceImplSpec.groovy @@ -38,6 +38,7 @@ import org.onap.cps.utils.YangParser import org.onap.cps.utils.YangParserHelper import org.onap.cps.utils.deltareport.DeltaReportGenerator import org.onap.cps.utils.deltareport.DeltaReportHelper +import org.onap.cps.utils.deltareport.GroupedDeltaReportGenerator import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder @@ -62,7 +63,8 @@ class CpsDeltaServiceImplSpec extends Specification { def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) def deltaReportHelper = new DeltaReportHelper() def deltaReportGenerator = new DeltaReportGenerator(deltaReportHelper) - def objectUnderTest = new CpsDeltaServiceImpl(mockCpsAnchorService, mockCpsDataService, dataNodeFactory, dataMapper, jsonObjectMapper, deltaReportHelper, deltaReportGenerator) + def groupedDeltaReportGenerator = new GroupedDeltaReportGenerator(deltaReportHelper) + def objectUnderTest = new CpsDeltaServiceImpl(mockCpsAnchorService, mockCpsDataService, dataNodeFactory, dataMapper, jsonObjectMapper, deltaReportGenerator, groupedDeltaReportGenerator) static def bookstoreDataNodeWithParentXpath = [new DataNode(xpath: '/bookstore', leaves: ['bookstore-name': 'Easons'])] static def bookstoreDataNodeWithChildXpath = [new DataNode(xpath: '/bookstore/categories[@code=\'02\']', leaves: ['code': '02', 'name': 'Kids'])]