From 7e1af5a4eb8e21d6a766b7f5ad9014ef2b5ee6ff Mon Sep 17 00:00:00 2001 From: Arpit Singh Date: Thu, 19 Jun 2025 17:27:01 +0530 Subject: [PATCH] Make original delta report format consistent with delta report new format The old delta report only provides information about the leaf nodes that changed. The details about the data node are not provided as part of the delta report. The change adds node name and JSON sructure of the data node to the original delta report format. Issue-ID: CPS-2860 Change-Id: Iac51925f0d0a8ddf1958b47ad372090fbf0d75d9 Signed-off-by: Arpit Singh --- .../utils/deltareport/DeltaReportGenerator.java | 22 ++++++++++++++++----- .../cps/utils/deltareport/DeltaReportHelper.java | 10 ++++++++-- .../deltareport/GroupedDeltaReportGenerator.java | 16 ++++----------- .../onap/cps/impl/CpsDeltaServiceImplSpec.groovy | 23 ++++++++++++++++------ 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportGenerator.java b/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportGenerator.java index 44b7ba63ae..840807a607 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportGenerator.java +++ b/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportGenerator.java @@ -20,9 +20,12 @@ package org.onap.cps.utils.deltareport; +import static org.onap.cps.utils.deltareport.DeltaReportHelper.getNodeNameToDataForDeltaReport; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -65,7 +68,14 @@ public class DeltaReportGenerator { xpathToDataNode.putAll(convertToXPathToDataNodesMap(childDataNodes)); } } - return xpathToDataNode; + return clearChildDataNodes(xpathToDataNode); + } + + private static Map clearChildDataNodes(final Map xpathToDataNodes) { + for (final DataNode dataNode : xpathToDataNodes.values()) { + dataNode.setChildDataNodes(Collections.emptyList()); + } + return xpathToDataNodes; } private List getRemovedAndUpdatedDeltaReports(final Map xpathToSourceDataNodes, @@ -88,9 +98,10 @@ public class DeltaReportGenerator { private static List getDeltaReportsForRemove(final String xpath, final DataNode sourceDataNode) { final List deltaReportEntriesForRemove = new ArrayList<>(); - final Map sourceDataNodeLeaves = sourceDataNode.getLeaves(); + final Map sourceDataNodeRemoved = + getNodeNameToDataForDeltaReport(Collections.singletonList(sourceDataNode)); final DeltaReport removedDeltaReportEntry = new DeltaReportBuilder().actionRemove().withXpath(xpath) - .withSourceData(sourceDataNodeLeaves).build(); + .withSourceData(sourceDataNodeRemoved).build(); deltaReportEntriesForRemove.add(removedDeltaReportEntry); return deltaReportEntriesForRemove; } @@ -104,12 +115,13 @@ public class DeltaReportGenerator { for (final Map.Entry entry: xpathToAddedNodes.entrySet()) { final String xpath = entry.getKey(); final DataNode dataNode = entry.getValue(); + final Map targetData = + getNodeNameToDataForDeltaReport(Collections.singletonList(dataNode)); final DeltaReport addedDataForDeltaReport = new DeltaReportBuilder().actionCreate().withXpath(xpath) - .withTargetData(dataNode.getLeaves()).build(); + .withTargetData(targetData).build(); addedDeltaReportEntries.add(addedDataForDeltaReport); } return addedDeltaReportEntries; } - } diff --git a/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportHelper.java b/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportHelper.java index 01127183aa..5e29ffa502 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportHelper.java +++ b/cps-service/src/main/java/org/onap/cps/utils/deltareport/DeltaReportHelper.java @@ -142,7 +142,7 @@ public class DeltaReportHelper { addKeyLeavesToUpdatedData(xpath, updatedLeaves); } final Collection updatedDataNode = buildUpdatedDataNode(dataNode, updatedLeaves); - updatedSourceData.putAll(getCondensedDataForDeltaReport(updatedDataNode)); + updatedSourceData.putAll(getNodeNameToDataForDeltaReport(updatedDataNode)); } return updatedSourceData; } @@ -169,7 +169,13 @@ public class DeltaReportHelper { return Collections.singletonList(updatedDataNode); } - private static Map getCondensedDataForDeltaReport(final Collection dataNodes) { + /** + * Converts a collection of DataNodes to a map where the keys are the node names and the values are the node data. + * + * @param dataNodes the collection of DataNodes + * @return a map with node names as keys and their corresponding data as values + */ + public static Map getNodeNameToDataForDeltaReport(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, 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 index d0ab418f97..839ebf7932 100644 --- 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 @@ -20,7 +20,8 @@ package org.onap.cps.utils.deltareport; -import java.io.Serializable; +import static org.onap.cps.utils.deltareport.DeltaReportHelper.getNodeNameToDataForDeltaReport; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -31,9 +32,7 @@ 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 @@ -70,7 +69,7 @@ public class GroupedDeltaReportGenerator { if (!removedDataNodes.isEmpty()) { final String xpath = getXpathForDeltaReport(removedDataNodes); deltaReportEntriesForRemove.add(new DeltaReportBuilder().actionRemove().withXpath(xpath) - .withSourceData(getCondensedDataForDeltaReport(removedDataNodes)).build()); + .withSourceData(getNodeNameToDataForDeltaReport(removedDataNodes)).build()); } return deltaReportEntriesForRemove; } @@ -111,7 +110,7 @@ public class GroupedDeltaReportGenerator { if (!addedDataNodes.isEmpty()) { final String xpath = getXpathForDeltaReport(addedDataNodes); addedDeltaReportEntries.add(new DeltaReportBuilder().actionCreate().withXpath(xpath) - .withTargetData(getCondensedDataForDeltaReport(addedDataNodes)).build()); + .withTargetData(getNodeNameToDataForDeltaReport(addedDataNodes)).build()); } return addedDeltaReportEntries; } @@ -127,13 +126,6 @@ public class GroupedDeltaReportGenerator { 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 7aaeedb5ab..2144732b35 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 @@ -132,8 +132,8 @@ class CpsDeltaServiceImplSpec extends Specification { deltaReport[0].targetData == expectedTargetData where: 'following data was used' scenario | sourceDataNodes | targetDataNodes | groupDataNodes || expectedAction | expectedSourceData | expectedTargetData - 'added with grouping disabled' | [] | targetDataNode | GROUPING_DISABLED || 'create' | null | ['parent-leaf':'parent-leaf-as-target-data'] - 'removed with grouping disabled' | sourceDataNode | [] | GROUPING_DISABLED || 'remove' | ['parent-leaf':'parent-leaf-as-source-data'] | null + 'added with grouping disabled' | [] | targetDataNode | GROUPING_DISABLED || 'create' | null | ['parent':['parent-leaf':'parent-leaf-as-target-data']] + 'removed with grouping disabled' | sourceDataNode | [] | GROUPING_DISABLED || 'remove' | ['parent':['parent-leaf':'parent-leaf-as-source-data']] | null 'updated with grouping disabled' | sourceDataNode | targetDataNode | GROUPING_DISABLED || 'replace' | ['parent':['parent-leaf':'parent-leaf-as-source-data']] | ['parent':['parent-leaf':'parent-leaf-as-target-data']] 'added with grouping enabled' | [] | targetDataNodeWithChild | GROUPING_ENABLED || 'create' | null | ['parent':['parent-leaf': 'parent-leaf-as-target-data', 'child':['child-leaf': 'child-leaf-as-target-data']]] 'removed with grouping enabled' | sourceDataNodeWithChild | [] | GROUPING_ENABLED || 'remove' | ['parent':['parent-leaf': 'parent-leaf-as-source-data', 'child':['child-leaf': 'child-leaf-as-source-data']]] | null @@ -162,10 +162,10 @@ class CpsDeltaServiceImplSpec extends Specification { assert deltaReport[1].sourceData == expectedSourceDataForChild assert deltaReport[1].targetData == expectedTargetDataForChild where: 'the following data is used' - scenario | sourceDataNodes | targetDataNodes || expectedAction | expectedSourceDataForParent | expectedTargetDataForParent | expectedSourceDataForChild | expectedTargetDataForChild - 'added' | [] | targetDataNodeWithChild || 'create' | null | ['parent-leaf': 'parent-leaf-as-target-data'] | null | ['child-leaf': 'child-leaf-as-target-data'] - 'removed' | sourceDataNodeWithChild | [] || 'remove' | ['parent-leaf': 'parent-leaf-as-source-data'] | null | ['child-leaf': 'child-leaf-as-source-data'] | null - 'updated' | sourceDataNodeWithChild | targetDataNodeWithChild || 'replace' | expectedParentSourceData | expectedParentTargetData | ['child':['child-leaf': 'child-leaf-as-source-data']] | ['child':['child-leaf': 'child-leaf-as-target-data']] + scenario | sourceDataNodes | targetDataNodes || expectedAction | expectedSourceDataForParent | expectedTargetDataForParent | expectedSourceDataForChild | expectedTargetDataForChild + 'added' | [] | targetDataNodeWithChild() || 'create' | null | expectedParentTargetData | null | ['child':['child-leaf': 'child-leaf-as-target-data']] + 'removed' | sourceDataNodeWithChild() | [] || 'remove' | expectedParentSourceData | null | ['child':['child-leaf': 'child-leaf-as-source-data']] | null + 'updated' | sourceDataNodeWithChild() | targetDataNodeWithChild() || 'replace' | expectedParentSourceData | expectedParentTargetData | ['child':['child-leaf': 'child-leaf-as-source-data']] | ['child':['child-leaf': 'child-leaf-as-target-data']] } def 'Delta Report between parent nodes with children where parent is updated and child node is #scenario with grouping of data nodes'() { @@ -360,4 +360,15 @@ class CpsDeltaServiceImplSpec extends Specification { def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceContentPerName).getSchemaContext() mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext } + + def sourceDataNodeWithChild() { + [new DataNode(xpath: '/parent', leaves: ['parent-leaf': 'parent-leaf-as-source-data'], + childDataNodes: [new DataNode(xpath: '/parent/child', leaves: ['child-leaf': 'child-leaf-as-source-data'])])] + } + + def targetDataNodeWithChild() { + [new DataNode(xpath: '/parent', leaves: ['parent-leaf': 'parent-leaf-as-target-data'], + childDataNodes: [new DataNode(xpath: '/parent/child', leaves: ['child-leaf': 'child-leaf-as-target-data'])])] + } + } -- 2.16.6