summaryrefslogtreecommitdiffstats
path: root/cps-service
diff options
context:
space:
mode:
authorToine Siebelink <toine.siebelink@est.tech>2024-01-18 16:51:48 +0000
committerGerrit Code Review <gerrit@onap.org>2024-01-18 16:51:48 +0000
commit83433140bac9358aef50036081d1f7079ac10c01 (patch)
tree39cd88b32ee3f3b45f02f1b1e5ce28ceeb548365 /cps-service
parent5ee1836bd9a2336afad291623db5b265f27d801a (diff)
parent885980d8ebf6bc8be9efad7ce9094deeb602a076 (diff)
Merge "CPS Delta API: Update action for delta service"
Diffstat (limited to 'cps-service')
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java121
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java1
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java5
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy100
4 files changed, 185 insertions, 42 deletions
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java
index 683ddce3d1..1e1fe819ac 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java
@@ -28,7 +28,7 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import lombok.NoArgsConstructor;
+import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsDeltaService;
import org.onap.cps.spi.model.DataNode;
@@ -38,7 +38,6 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
-@NoArgsConstructor
public class CpsDeltaServiceImpl implements CpsDeltaService {
@Override
@@ -50,7 +49,7 @@ public class CpsDeltaServiceImpl implements CpsDeltaService {
final Map<String, DataNode> xpathToSourceDataNodes = convertToXPathToDataNodesMap(sourceDataNodes);
final Map<String, DataNode> xpathToTargetDataNodes = convertToXPathToDataNodesMap(targetDataNodes);
- deltaReport.addAll(getRemovedDeltaReports(xpathToSourceDataNodes, xpathToTargetDataNodes));
+ deltaReport.addAll(getRemovedAndUpdatedDeltaReports(xpathToSourceDataNodes, xpathToTargetDataNodes));
deltaReport.addAll(getAddedDeltaReports(xpathToSourceDataNodes, xpathToTargetDataNodes));
@@ -70,26 +69,122 @@ public class CpsDeltaServiceImpl implements CpsDeltaService {
return xpathToDataNode;
}
- private static List<DeltaReport> getRemovedDeltaReports(
- final Map<String, DataNode> xpathToSourceDataNodes,
- final Map<String, DataNode> xpathToTargetDataNodes) {
-
- final List<DeltaReport> removedDeltaReportEntries = new ArrayList<>();
+ private static List<DeltaReport> getRemovedAndUpdatedDeltaReports(
+ final Map<String, DataNode> xpathToSourceDataNodes,
+ final Map<String, DataNode> xpathToTargetDataNodes) {
+ final List<DeltaReport> removedAndUpdatedDeltaReportEntries = new ArrayList<>();
for (final Map.Entry<String, DataNode> entry: xpathToSourceDataNodes.entrySet()) {
final String xpath = entry.getKey();
final DataNode sourceDataNode = entry.getValue();
final DataNode targetDataNode = xpathToTargetDataNodes.get(xpath);
-
+ final List<DeltaReport> deltaReports;
if (targetDataNode == null) {
- final Map<String, Serializable> sourceDataNodeLeaves = sourceDataNode.getLeaves();
- final DeltaReport removedData = new DeltaReportBuilder().actionRemove().withXpath(xpath)
- .withSourceData(sourceDataNodeLeaves).build();
- removedDeltaReportEntries.add(removedData);
+ deltaReports = getRemovedDeltaReports(xpath, sourceDataNode);
+ } else {
+ deltaReports = getUpdatedDeltaReports(xpath, sourceDataNode, targetDataNode);
}
+ removedAndUpdatedDeltaReportEntries.addAll(deltaReports);
}
+ return removedAndUpdatedDeltaReportEntries;
+ }
+
+ private static List<DeltaReport> getRemovedDeltaReports(final String xpath, final DataNode sourceDataNode) {
+ final List<DeltaReport> removedDeltaReportEntries = new ArrayList<>();
+ final Map<String, Serializable> sourceDataNodeLeaves = sourceDataNode.getLeaves();
+ final DeltaReport removedDeltaReportEntry = new DeltaReportBuilder().actionRemove().withXpath(xpath)
+ .withSourceData(sourceDataNodeLeaves).build();
+ removedDeltaReportEntries.add(removedDeltaReportEntry);
return removedDeltaReportEntries;
}
+ private static List<DeltaReport> getUpdatedDeltaReports(final String xpath, final DataNode sourceDataNode,
+ final DataNode targetDataNode) {
+ final List<DeltaReport> updatedDeltaReportEntries = new ArrayList<>();
+ final Map<Map<String, Serializable>, Map<String, Serializable>> updatedLeavesAsSourceDataToTargetData =
+ getUpdatedLeavesBetweenSourceAndTargetDataNode(sourceDataNode.getLeaves(), targetDataNode.getLeaves());
+ addUpdatedLeavesToDeltaReport(xpath, updatedLeavesAsSourceDataToTargetData, updatedDeltaReportEntries);
+ return updatedDeltaReportEntries;
+ }
+
+ private static Map<Map<String, Serializable>,
+ Map<String, Serializable>> getUpdatedLeavesBetweenSourceAndTargetDataNode(
+ final Map<String, Serializable> leavesOfSourceDataNode,
+ final Map<String, Serializable> leavesOfTargetDataNode) {
+ final Map<Map<String, Serializable>, Map<String, Serializable>> updatedLeavesAsSourceDataToTargetData =
+ new LinkedHashMap<>();
+ final Map<String, Serializable> sourceDataInDeltaReport = new LinkedHashMap<>();
+ final Map<String, Serializable> targetDataInDeltaReport = new LinkedHashMap<>();
+ processLeavesPresentInSourceAndTargetDataNode(leavesOfSourceDataNode, leavesOfTargetDataNode,
+ sourceDataInDeltaReport, targetDataInDeltaReport);
+ processLeavesUniqueInTargetDataNode(leavesOfSourceDataNode, leavesOfTargetDataNode,
+ sourceDataInDeltaReport, targetDataInDeltaReport);
+ final boolean isUpdatedDataInDeltaReport =
+ !sourceDataInDeltaReport.isEmpty() || !targetDataInDeltaReport.isEmpty();
+ if (isUpdatedDataInDeltaReport) {
+ updatedLeavesAsSourceDataToTargetData.put(sourceDataInDeltaReport, targetDataInDeltaReport);
+ }
+ return updatedLeavesAsSourceDataToTargetData;
+ }
+
+ private static void processLeavesPresentInSourceAndTargetDataNode(
+ final Map<String, Serializable> leavesOfSourceDataNode,
+ final Map<String, Serializable> leavesOfTargetDataNode,
+ final Map<String, Serializable> sourceDataInDeltaReport,
+ final Map<String, Serializable> targetDataInDeltaReport) {
+ for (final Map.Entry<String, Serializable> entry: leavesOfSourceDataNode.entrySet()) {
+ final String key = entry.getKey();
+ final Serializable sourceLeaf = entry.getValue();
+ final Serializable targetLeaf = leavesOfTargetDataNode.get(key);
+ compareLeaves(key, sourceLeaf, targetLeaf, sourceDataInDeltaReport, targetDataInDeltaReport);
+ }
+ }
+
+ private static void processLeavesUniqueInTargetDataNode(
+ final Map<String, Serializable> leavesOfSourceDataNode,
+ final Map<String, Serializable> leavesOfTargetDataNode,
+ final Map<String, Serializable> sourceDataInDeltaReport,
+ final Map<String, Serializable> targetDataInDeltaReport) {
+ final Map<String, Serializable> uniqueLeavesOfTargetDataNode =
+ new LinkedHashMap<>(leavesOfTargetDataNode);
+ uniqueLeavesOfTargetDataNode.keySet().removeAll(leavesOfSourceDataNode.keySet());
+ for (final Map.Entry<String, Serializable> entry: uniqueLeavesOfTargetDataNode.entrySet()) {
+ final String key = entry.getKey();
+ final Serializable targetLeaf = entry.getValue();
+ final Serializable sourceLeaf = leavesOfSourceDataNode.get(key);
+ compareLeaves(key, sourceLeaf, targetLeaf, sourceDataInDeltaReport, targetDataInDeltaReport);
+ }
+ }
+
+ private static void compareLeaves(final String key,
+ final Serializable sourceLeaf,
+ final Serializable targetLeaf,
+ final Map<String, Serializable> sourceDataInDeltaReport,
+ final Map<String, Serializable> targetDataInDeltaReport) {
+ if (sourceLeaf != null && targetLeaf != null) {
+ if (!Objects.equals(sourceLeaf, targetLeaf)) {
+ sourceDataInDeltaReport.put(key, sourceLeaf);
+ targetDataInDeltaReport.put(key, targetLeaf);
+ }
+ } else if (sourceLeaf != null) {
+ sourceDataInDeltaReport.put(key, sourceLeaf);
+ } else if (targetLeaf != null) {
+ targetDataInDeltaReport.put(key, targetLeaf);
+ }
+ }
+
+ private static void addUpdatedLeavesToDeltaReport(final String xpath,
+ final Map<Map<String, Serializable>, Map<String,
+ Serializable>> updatedLeavesAsSourceDataToTargetData,
+ final List<DeltaReport> updatedDeltaReportEntries) {
+ for (final Map.Entry<Map<String, Serializable>, Map<String, Serializable>> entry:
+ updatedLeavesAsSourceDataToTargetData.entrySet()) {
+ final DeltaReport updatedDataForDeltaReport = new DeltaReportBuilder().actionUpdate()
+ .withXpath(xpath).withSourceData(entry.getKey()).withTargetData(entry.getValue()).build();
+ updatedDeltaReportEntries.add(updatedDataForDeltaReport);
+ }
+
+ }
+
private static List<DeltaReport> getAddedDeltaReports(final Map<String, DataNode> xpathToSourceDataNodes,
final Map<String, DataNode> xpathToTargetDataNodes) {
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java
index b9c05dcf02..fb9c1971b2 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReport.java
@@ -32,6 +32,7 @@ public class DeltaReport {
public static final String ADD_ACTION = "add";
public static final String REMOVE_ACTION = "remove";
+ public static final String UPDATE_ACTION = "update";
DeltaReport() {}
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java
index cef6ca3fa2..1e151eeb2d 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/model/DeltaReportBuilder.java
@@ -58,6 +58,11 @@ public class DeltaReportBuilder {
return this;
}
+ public DeltaReportBuilder actionUpdate() {
+ this.action = DeltaReport.UPDATE_ACTION;
+ return this;
+ }
+
/**
* To create a single entry of {@link DeltaReport}.
*
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy
index a4f4339737..e21c6f0e2f 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDeltaServiceImplSpec.groovy
@@ -21,7 +21,6 @@
package org.onap.cps.api.impl
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.model.DataNodeBuilder
import spock.lang.Shared
import spock.lang.Specification
@@ -29,38 +28,81 @@ class CpsDeltaServiceImplSpec extends Specification{
def objectUnderTest = new CpsDeltaServiceImpl()
- @Shared
- def dataNodeWithLeafAndChildDataNode = [new DataNodeBuilder().withXpath('/parent').withLeaves(['parent-leaf': 'parent-payload'])
- .withChildDataNodes([new DataNodeBuilder().withXpath("/parent/child").withLeaves('child-leaf': 'child-payload').build()]).build()]
- @Shared
- def dataNodeWithChildDataNode = [new DataNodeBuilder().withXpath('/parent').withLeaves(['parent-leaf': 'parent-payload'])
- .withChildDataNodes([new DataNodeBuilder().withXpath("/parent/child").build()]).build()]
- @Shared
- def emptyDataNode = [new DataNodeBuilder().withXpath('/parent').build()]
- def 'Get delta between data nodes for removed data where source data node has #scenario'() {
+ static def sourceDataNodeWithLeafData = [new DataNode(xpath: '/parent', leaves: ['parent-leaf': 'parent-payload-in-source'])]
+ static def sourceDataNodeWithoutLeafData = [new DataNode(xpath: '/parent')]
+ static def targetDataNodeWithLeafData = [new DataNode(xpath: '/parent', leaves: ['parent-leaf': 'parent-payload-in-target'])]
+ static def targetDataNodeWithoutLeafData = [new DataNode(xpath: '/parent')]
+ static def sourceDataNodeWithMultipleLeaves = [new DataNode(xpath: '/parent', leaves: ['leaf-1': 'leaf-1-in-source', 'leaf-2': 'leaf-2-in-source'])]
+ static def targetDataNodeWithMultipleLeaves = [new DataNode(xpath: '/parent', leaves: ['leaf-1': 'leaf-1-in-target', 'leaf-2': 'leaf-2-in-target'])]
+
+ def 'Get delta between data nodes for REMOVED data where source data node has #scenario'() {
+ when: 'attempt to get delta between 2 data nodes'
+ def result = objectUnderTest.getDeltaReports(sourceDataNodeWithLeafData, [])
+ then: 'the delta report contains expected "remove" action'
+ assert result[0].action.equals('remove')
+ and : 'the delta report contains the expected xpath'
+ assert result[0].xpath == '/parent'
+ and: 'the delta report contains expected source data'
+ assert result[0].sourceData == ['parent-leaf': 'parent-payload-in-source']
+ and: 'the delta report contains no target data'
+ assert result[0].targetData == null
+ }
+
+ def 'Get delta between data nodes with ADDED data where target data node has #scenario'() {
+ when: 'attempt to get delta between 2 data nodes'
+ def result = objectUnderTest.getDeltaReports([], targetDataNodeWithLeafData)
+ then: 'the delta report contains expected "add" action'
+ assert result[0].action.equals('add')
+ and: 'the delta report contains expected xpath'
+ assert result[0].xpath == '/parent'
+ and: 'the delta report contains no source data'
+ assert result[0].sourceData == null
+ and: 'the delta report contains expected target data'
+ assert result[0].targetData == ['parent-leaf': 'parent-payload-in-target']
+ }
+
+ def 'Delta Report between leaves for parent and child nodes, #scenario'() {
+ given: 'Two data nodes'
+ def sourceDataNode = [new DataNode(xpath: '/parent', leaves: ['parent-leaf': 'parent-payload'], childDataNodes: [new DataNode(xpath: '/parent/child', leaves: ['child-leaf': 'child-payload'])])]
+ def targetDataNode = [new DataNode(xpath: '/parent', leaves: ['parent-leaf': 'parent-payload-updated'], childDataNodes: [new DataNode(xpath: '/parent/child', leaves: ['child-leaf': 'child-payload-updated'])])]
+ when: 'attempt to get delta between 2 data nodes'
+ def result = objectUnderTest.getDeltaReports(sourceDataNode, targetDataNode)
+ then: 'the delta report contains expected "update" action'
+ assert result[index].action.equals('update')
+ and: 'the delta report contains expected xpath'
+ assert result[index].xpath == expectedXpath
+ and: 'the delta report contains expected source and target data'
+ assert result[index].sourceData == expectedSourceData
+ assert result[index].targetData == expectedTargetData
+ where: 'the following data was used'
+ scenario | index || expectedXpath | expectedSourceData | expectedTargetData
+ 'parent data node' | 0 || '/parent' | ['parent-leaf': 'parent-payload'] | ['parent-leaf': 'parent-payload-updated']
+ 'child data node' | 1 || '/parent/child' | ['child-leaf': 'child-payload'] | ['child-leaf': 'child-payload-updated']
+ }
+
+ def 'Delta report between leaves, #scenario'() {
when: 'attempt to get delta between 2 data nodes'
- def result = objectUnderTest.getDeltaReports(sourceDataNode as Collection<DataNode>, emptyDataNode)
- then: 'the delta report contains "remove" action with right data'
- assert result.first().action.equals("remove")
- assert result.first().xpath == "/parent/child"
- assert result.first().sourceData == expectedSourceData
- where: 'following data was used'
- scenario | sourceDataNode || expectedSourceData
- 'leaf data' | dataNodeWithLeafAndChildDataNode || ['child-leaf': 'child-payload']
- 'no leaf data' | dataNodeWithChildDataNode || null
+ def result = objectUnderTest.getDeltaReports(sourceDataNode, targetDataNode)
+ then: 'the delta report contains expected "update" action'
+ assert result[0].action.equals('update')
+ and: 'the delta report contains expected xpath'
+ assert result[0].xpath == '/parent'
+ and: 'the delta report contains expected source and target data'
+ assert result[0].sourceData == expectedSourceData
+ assert result[0].targetData == expectedTargetData
+ where: 'the following data was used'
+ scenario | sourceDataNode | targetDataNode || expectedSourceData | expectedTargetData
+ 'source and target data nodes have leaves' | sourceDataNodeWithLeafData | targetDataNodeWithLeafData || ['parent-leaf': 'parent-payload-in-source'] | ['parent-leaf': 'parent-payload-in-target']
+ 'only source data node has leaves' | sourceDataNodeWithLeafData | targetDataNodeWithoutLeafData || ['parent-leaf': 'parent-payload-in-source'] | null
+ 'only target data node has leaves' | sourceDataNodeWithoutLeafData | targetDataNodeWithLeafData || null | ['parent-leaf': 'parent-payload-in-target']
+ 'source and target dsta node with multiple leaves' | sourceDataNodeWithMultipleLeaves | targetDataNodeWithMultipleLeaves || ['leaf-1': 'leaf-1-in-source', 'leaf-2': 'leaf-2-in-source'] | ['leaf-1': 'leaf-1-in-target', 'leaf-2': 'leaf-2-in-target']
}
- def 'Get delta between data nodes with new data where target data node has #scenario'() {
+ def 'Get delta between data nodes for updated data, where source and target data nodes have no leaves '() {
when: 'attempt to get delta between 2 data nodes'
- def result = objectUnderTest.getDeltaReports(emptyDataNode, targetDataNode)
- then: 'the delta report contains "add" action with right data'
- assert result.first().action.equals("add")
- assert result.first().xpath == "/parent/child"
- assert result.first().targetData == expectedTargetData
- where: 'following data was used'
- scenario | targetDataNode || expectedTargetData
- 'leaf data' | dataNodeWithLeafAndChildDataNode || ['child-leaf': 'child-payload']
- 'no leaf data' | dataNodeWithChildDataNode || null
+ def result = objectUnderTest.getDeltaReports(sourceDataNodeWithoutLeafData, targetDataNodeWithoutLeafData)
+ then: 'the delta report contains "update" action with right data'
+ assert result.isEmpty()
}
}