From 760dd950e6f6fcb6f49c6f9d92a33f538adffd24 Mon Sep 17 00:00:00 2001 From: Rudrangi Anupriya Date: Thu, 11 Jul 2024 21:56:24 +0530 Subject: XML content support on replace a node with descendants Issue-ID: CPS-2282 Change-Id: Ibb7ffb65ccbb03703266712c6d5c2eade0e7ab4b Signed-off-by: Rudrangi Anupriya --- .../main/java/org/onap/cps/api/CpsDataService.java | 20 +++++----- .../org/onap/cps/api/impl/CpsDataServiceImpl.java | 19 ++++----- .../cps/api/impl/CpsDataServiceImplSpec.groovy | 45 +++++++++++++++++++--- 3 files changed, 60 insertions(+), 24 deletions(-) (limited to 'cps-service') diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java index f396b49e6b..cfa5f2de20 100644 --- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java +++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java @@ -55,7 +55,7 @@ public interface CpsDataService { * @param anchorName anchor name * @param nodeData node data * @param observedTimestamp observedTimestamp - * @param contentType node data content type + * @param contentType JSON/XML content type */ void saveData(String dataspaceName, String anchorName, String nodeData, OffsetDateTime observedTimestamp, ContentType contentType); @@ -80,7 +80,7 @@ public interface CpsDataService { * @param parentNodeXpath parent node xpath * @param nodeData node data * @param observedTimestamp observedTimestamp - * @param contentType node data content type + * @param contentType JSON/XML content type * */ void saveData(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, @@ -134,7 +134,7 @@ public interface CpsDataService { * @param parentNodeXpath xpath to parent node * @param nodeData node data * @param observedTimestamp observedTimestamp - * @param contentType node data content type + * @param contentType JSON/XML content type */ void updateNodeLeaves(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, OffsetDateTime observedTimestamp, ContentType contentType); @@ -145,22 +145,24 @@ public interface CpsDataService { * @param dataspaceName dataspace name * @param anchorName anchor name * @param parentNodeXpath xpath to parent node - * @param jsonData json data + * @param nodeData node data * @param observedTimestamp observedTimestamp + * @param contentType JSON/XML content type */ - void updateDataNodeAndDescendants(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData, - OffsetDateTime observedTimestamp); + void updateDataNodeAndDescendants(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData, + OffsetDateTime observedTimestamp, ContentType contentType); /** * Replaces multiple existing data nodes' content including descendants in a batch operation. * * @param dataspaceName dataspace name * @param anchorName anchor name - * @param nodesJsonData map of xpath and node JSON data + * @param nodeDataPerXPath map of xpath and node JSON/XML data * @param observedTimestamp observedTimestamp + * @param contentType JSON/XML content type */ - void updateDataNodesAndDescendants(String dataspaceName, String anchorName, Map nodesJsonData, - OffsetDateTime observedTimestamp); + void updateDataNodesAndDescendants(String dataspaceName, String anchorName, Map nodeDataPerXPath, + OffsetDateTime observedTimestamp, ContentType contentType); /** * Replaces list content by removing all existing elements and inserting the given new elements as json diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java index 5a48428772..c65bc5e0e9 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java @@ -251,12 +251,12 @@ public class CpsDataServiceImpl implements CpsDataService { @Timed(value = "cps.data.service.datanode.descendants.update", description = "Time taken to update a data node and descendants") public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final String jsonData, - final OffsetDateTime observedTimestamp) { + final String parentNodeXpath, final String nodeData, + final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); final Collection dataNodes = - buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON); + buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); sendDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp); } @@ -265,13 +265,13 @@ public class CpsDataServiceImpl implements CpsDataService { @Timed(value = "cps.data.service.datanode.descendants.batch.update", description = "Time taken to update a batch of data nodes and descendants") public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, - final Map nodesJsonData, - final OffsetDateTime observedTimestamp) { + final Map nodeDataPerXPath, + final OffsetDateTime observedTimestamp, final ContentType contentType) { cpsValidator.validateNameCharacters(dataspaceName, anchorName); final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); - final Collection dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodesJsonData); + final Collection dataNodes = buildDataNodesWithParentNodeXpath(anchor, nodeDataPerXPath, contentType); cpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, dataNodes); - nodesJsonData.keySet().forEach(nodeXpath -> + nodeDataPerXPath.keySet().forEach(nodeXpath -> sendDataUpdatedEvent(anchor, nodeXpath, Operation.UPDATE, observedTimestamp)); } @@ -408,11 +408,12 @@ public class CpsDataServiceImpl implements CpsDataService { } private Collection buildDataNodesWithParentNodeXpath(final Anchor anchor, - final Map nodesJsonData) { + final Map nodesJsonData, + final ContentType contentType) { final Collection dataNodes = new ArrayList<>(); for (final Map.Entry nodeJsonData : nodesJsonData.entrySet()) { dataNodes.addAll(buildDataNodesWithParentNodeXpath(anchor, nodeJsonData.getKey(), - nodeJsonData.getValue(), ContentType.JSON)); + nodeJsonData.getValue(), contentType)); } return dataNodes; } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index 4e5807ec84..2e38d797d9 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -362,11 +362,11 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) } - def 'Replace data node using singular data node: #scenario.'() { + def 'Replace data node using singular JSON data node: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'replace data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath' - objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp) + objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp, ContentType.JSON) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) @@ -379,30 +379,63 @@ class CpsDataServiceImplSpec extends Specification { 'json list' | '/test-tree' | '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] } - def 'Replace data node using multiple data nodes: #scenario.'() { + def 'Replace data node using singular XML data node: #scenario.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'replace data method is invoked with XML data #xmlData and parent node xpath #parentNodeXpath' + objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, xmlData, observedTimestamp, ContentType.XML) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, + { dataNode -> dataNode.xpath == expectedNodeXpath }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + where: 'following parameters were used' + scenario | parentNodeXpath | xmlData || expectedNodeXpath + 'level 2 node' | '/test-tree' | 'Name' || ['/test-tree/branch[@name=\'Name\']'] + 'xml list' | '/test-tree' | 'Name1' + 'Name2' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] + } + + def 'Replace data node using multiple JSON data nodes: #scenario.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') when: 'replace data method is invoked with a map of xpaths and json data' - objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodesJsonData, observedTimestamp) + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodeDataPerXPath, observedTimestamp, ContentType.JSON) then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath}) and: 'the CpsValidator is called on the dataspaceName and AnchorName' 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) where: 'following parameters were used' - scenario | nodesJsonData || expectedNodeXpath + scenario | nodeDataPerXPath || expectedNodeXpath 'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}'] || ["/test-tree", "/test-tree/branch[@name='Name']"] 'level 2 node' | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"] 'json list' | ['/test-tree' : '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] } + def 'Replace data node using multiple XML data nodes: #scenario.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'replace data method is invoked with a map of xpaths and XML data' + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, nodeXmlDataPerXPath, observedTimestamp, ContentType.XML) + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName, + { dataNode -> dataNode.xpath == expectedNodeXpath }) + and: 'the CpsValidator is called on the dataspaceName and AnchorName' + 1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName) + where: 'following parameters were used' + scenario | nodeXmlDataPerXPath || expectedNodeXpath + 'top level node' | ['/test-tree': 'Name'] || ["/test-tree/branch[@name='Name']"] + 'level 2 node' | ['/test-tree': 'Name', '/test-tree/branch[@name=\'Name\']': 'nestName'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"] + 'xml list' | ['/test-tree': 'Name1' + 'Name2'] || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"] + } + def 'Replace data node with concurrency exception in persistence layer.'() { given: 'the persistence layer throws an concurrency exception' def originalException = new ConcurrencyException('message', 'details') mockCpsDataPersistenceService.updateDataNodesAndDescendants(*_) >> { throw originalException } setupSchemaSetMocks('test-tree.yang') when: 'attempt to replace data node' - objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp) + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, ['/' : '{"test-tree": {}}'] , observedTimestamp, ContentType.JSON) then: 'the same exception is thrown up' def thrownUp = thrown(ConcurrencyException) assert thrownUp == originalException -- cgit 1.2.3-korg