summaryrefslogtreecommitdiffstats
path: root/cps-ri
diff options
context:
space:
mode:
authorarpitsingh <as00745003@techmahindra.com>2023-01-09 19:53:10 +0530
committerArpit Singh <as00745003@techmahindra.com>2023-02-17 11:20:35 +0000
commit0bd192ca12ac2f768e44d0d3482785c79a881904 (patch)
treea91a96325ecaa38adaa4b9420e4655b6cbea85a3 /cps-ri
parent9575b84ab4e2db885d8761a98eaae9ff3a06aa81 (diff)
CPS-1401 Implement V2 of GET Data Node API
- Modified the GET Data Node API so it returns all the data nodes when xpath set to root "/" - Fragment Repository now returns a collection of Fragment Entities - Instead of returning only the first Fragment Entity now all fragment entities are returned when xpath is set to root - The Fragemnt Entities are further processed to a Collection of Data Nodes. As opposed to singular Data Node in current implementation. - Finally the DataRestController also returns a Collection of Data Nodes when xpath is set to root and valid data is present - Response body changed from JSON object to JSON Array. - Exception handling for invalid xpath and non-existing xpath is now done separately at persistence layer. - Refactored code against CPS-1422 - Deprecated getDataNode method from Service and Persistence layer - Modified V1 of Get Data Node API to use the getDataNodes method and get the first data node from the collection returned. - Modified NCMP to use getDataNodes method - NCMP still does not support multiple data nodes. It retrieves the first data node from the collection returned by getDataNodes Signed-off-by: arpitsingh <as00745003@techmahindra.com> Change-Id: I494a5740a53f65376d135fcb9f1e2e8900a2803e
Diffstat (limited to 'cps-ri')
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java25
-rwxr-xr-xcps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy126
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy15
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy20
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy6
-rwxr-xr-xcps-ri/src/test/resources/data/fragment.sql13
6 files changed, 114 insertions, 91 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index 5b310efd5..46439fdb3 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -3,7 +3,7 @@
* Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2020-2022 Bell Canada.
- * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -249,17 +249,22 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
}
@Override
- public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
- final FetchDescendantsOption fetchDescendantsOption) {
- final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath,
- fetchDescendantsOption);
- return toDataNode(fragmentEntity, fetchDescendantsOption);
+ public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
+ final String xpath,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final String targetXpath = isRootXpath(xpath) ? xpath : CpsPathUtil.getNormalizedXpath(xpath);
+ final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName,
+ Collections.singletonList(targetXpath), fetchDescendantsOption);
+ if (dataNodes.isEmpty()) {
+ throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath);
+ }
+ return dataNodes;
}
@Override
- public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
- final Collection<String> xpaths,
- final FetchDescendantsOption fetchDescendantsOption) {
+ public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName,
+ final Collection<String> xpaths,
+ final FetchDescendantsOption fetchDescendantsOption) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
@@ -271,7 +276,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
try {
normalizedXpaths.add(CpsPathUtil.getNormalizedXpath(xpath));
} catch (final PathParsingException e) {
- log.warn("Error parsing xpath \"{}\" in getDataNodes: {}", xpath, e.getMessage());
+ log.warn("Error parsing xpath \"{}\" in getDataNodesForMultipleXpaths: {}", xpath, e.getMessage());
}
}
final Collection<FragmentEntity> fragmentEntities =
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
index e4c552978..336d6035a 100755
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
@@ -3,7 +3,7 @@
* Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021-2022 Bell Canada.
- * Modifications Copyright (C) 2022 TechMahindra Ltd.
+ * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
import javax.validation.ConstraintViolationException
+import java.nio.file.Path
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
@@ -82,15 +83,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
]
@Sql([CLEAR_DATA, SET_DATA])
- def 'Get existing datanode with descendants.'() {
- when: 'the node is retrieved by its xpath'
- def dataNode = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME1, '/parent-1', INCLUDE_ALL_DESCENDANTS)
- then: 'the path and prefix are populated correctly'
- assert dataNode.xpath == '/parent-1'
- and: 'dataNode has no prefix (to be addressed by CPS-1301'
- assert dataNode.moduleNamePrefix == null
- and: 'the child node has the correct path'
- assert dataNode.childDataNodes[0].xpath == '/parent-1/child-1'
+ def 'Get all datanodes with descendants .'() {
+ when: 'data nodes are retrieved by their xpath'
+ def dataNodes = objectUnderTest.getDataNodesForMultipleXpaths(DATASPACE_NAME, ANCHOR_NAME1, ['/parent-1'], INCLUDE_ALL_DESCENDANTS)
+ then: 'same data nodes are returned by getDataNodesForMultipleXpaths method'
+ assert objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME1, '/parent-1', INCLUDE_ALL_DESCENDANTS) == dataNodes
+ and: 'the dataNodes have no prefix (to be addressed by CPS-1301)'
+ assert dataNodes[0].moduleNamePrefix == null
}
@Sql([CLEAR_DATA, SET_DATA])
@@ -102,11 +101,11 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
def dataNodes = [createDataNodeTree(parentXpath, childXpath, grandChildXpath)]
objectUnderTest.storeDataNodes(DATASPACE_NAME, ANCHOR_NAME1, dataNodes)
then: 'it can be retrieved by its xpath'
- def dataNode = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME1, parentXpath, INCLUDE_ALL_DESCENDANTS)
- assert dataNode.xpath == parentXpath
+ def dataNode = objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME1, parentXpath, INCLUDE_ALL_DESCENDANTS)
+ assert dataNode[0].xpath == parentXpath
and: 'it has the correct child'
- assert dataNode.childDataNodes.size() == 1
- def childDataNode = dataNode.childDataNodes[0]
+ assert dataNode[0].childDataNodes.size() == 1
+ def childDataNode = dataNode[0].childDataNodes[0]
assert childDataNode.xpath == childXpath
and: 'and its grandchild'
assert childDataNode.childDataNodes.size() == 1
@@ -236,18 +235,19 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Get data node by xpath without descendants.'() {
- when: 'data node is requested'
- def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT,
- inputXPath, OMIT_DESCENDANTS)
- then: 'data node is returned with no descendants'
- assert result.xpath == XPATH_DATA_NODE_WITH_LEAVES
- and: 'expected leaves'
- assert result.childDataNodes.size() == 0
- assertLeavesMaps(result.leaves, expectedLeavesByXpathMap[XPATH_DATA_NODE_WITH_LEAVES])
- where: 'the following data is used'
+ def 'Get all data nodes by single xpath without descendants : #scenario'() {
+ when: 'data nodes are requested'
+ def result = objectUnderTest.getDataNodesForMultipleXpaths(DATASPACE_NAME, ANCHOR_WITH_MULTIPLE_TOP_LEVEL_FRAGMENTS,
+ [inputXPath], OMIT_DESCENDANTS)
+ then: 'data nodes under root are returned'
+ assert result.childDataNodes.size() == 2
+ and: 'no descendants of parent nodes are returned'
+ result.each {assert it.childDataNodes.size() == 0}
+ and: 'same data nodes are returned when V2 of get Data Nodes API is executed'
+ assert objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_WITH_MULTIPLE_TOP_LEVEL_FRAGMENTS,
+ inputXPath, OMIT_DESCENDANTS) == result
+ where: 'the following xpath is used'
scenario | inputXPath
- 'some xpath' | '/parent-207'
'root xpath' | '/'
'empty xpath' | ''
}
@@ -255,51 +255,50 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
@Sql([CLEAR_DATA, SET_DATA])
def 'Cps Path query with syntax error throws a CPS Path Exception.'() {
when: 'trying to execute a query with a syntax (parsing) error'
- objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, 'invalid-cps-path/child' , OMIT_DESCENDANTS)
+ objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, 'invalid-cps-path/child' , OMIT_DESCENDANTS)
then: 'exception is thrown'
- def exceptionThrown = thrown(CpsPathException)
- assert exceptionThrown.getDetails().contains('failed to parse at line 1 due to extraneous input \'invalid-cps-path\' expecting \'/\'')
+ def exceptionThrown = thrown(PathParsingException)
+ assert exceptionThrown.getMessage().contains('failed to parse at line 1 due to extraneous input \'invalid-cps-path\' expecting \'/\'')
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Get data node by xpath with all descendants.'() {
- when: 'data node is requested with all descendants'
- def result = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT,
- inputXPath, INCLUDE_ALL_DESCENDANTS)
- def mappedResult = treeToFlatMapByXpath(new HashMap<>(), result)
- then: 'data node is returned with all the descendants populated'
- assert mappedResult.size() == 4
+ def 'Get all data nodes by single xpath with all descendants : #scenario'() {
+ when: 'data nodes are requested with all descendants'
+ def result = objectUnderTest.getDataNodesForMultipleXpaths(DATASPACE_NAME, ANCHOR_WITH_MULTIPLE_TOP_LEVEL_FRAGMENTS,
+ [inputXPath], INCLUDE_ALL_DESCENDANTS)
+ def mappedResult = multipleTreesToFlatMapByXpath(new HashMap<>(), result)
+ then: 'data nodes are returned with all the descendants populated'
+ assert mappedResult.size() == 8
assert result.childDataNodes.size() == 2
- assert mappedResult.get('/parent-207/child-001').childDataNodes.size() == 0
- assert mappedResult.get('/parent-207/child-002').childDataNodes.size() == 1
- and: 'extracted leaves maps are matching expected'
- mappedResult.forEach(
- (xPath, dataNode) -> assertLeavesMaps(dataNode.leaves, expectedLeavesByXpathMap[xPath]))
+ assert mappedResult.get('/parent-208/child-001').childDataNodes.size() == 0
+ assert mappedResult.get('/parent-208/child-002').childDataNodes.size() == 1
+ assert mappedResult.get('/parent-209/child-001').childDataNodes.size() == 0
+ assert mappedResult.get('/parent-209/child-002').childDataNodes.size() == 1
+ and: 'same data nodes are returned when V2 of Get Data Nodes API is executed'
+ assert objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_WITH_MULTIPLE_TOP_LEVEL_FRAGMENTS,
+ inputXPath, INCLUDE_ALL_DESCENDANTS) == result
where: 'the following data is used'
scenario | inputXPath
- 'some xpath' | '/parent-207'
'root xpath' | '/'
'empty xpath' | ''
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Get data node error scenario: #scenario.'() {
- when: 'attempt to get data node with #scenario'
- objectUnderTest.getDataNode(dataspaceName, anchorName, xpath, OMIT_DESCENDANTS)
- then: 'a #expectedException is thrown'
+ def 'Get data nodes error scenario : #scenario.'() {
+ when: 'attempt to get data nodes with #scenario'
+ objectUnderTest.getDataNodes(dataspaceName, anchorName, xpath, OMIT_DESCENDANTS)
+ then: 'an #expectedException is thrown'
thrown(expectedException)
where: 'the following data is used'
- scenario | dataspaceName | anchorName | xpath || expectedException
- 'non-existing dataspace' | 'NO DATASPACE' | 'not relevant' | '/not relevant' || DataspaceNotFoundException
- 'non-existing anchor' | DATASPACE_NAME | 'NO ANCHOR' | '/not relevant' || AnchorNotFoundException
- 'non-existing xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | '/NO-XPATH' || DataNodeNotFoundException
- 'invalid xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH' || CpsPathException
+ scenario | dataspaceName | anchorName | xpath || expectedException
+ 'non existing xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | '/NO-XPATH' || DataNodeNotFoundException
+ 'invalid Xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH' || PathParsingException
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Get multiple data nodes by xpath.'() {
+ def 'Get data nodes for multiple xpaths.'() {
when: 'fetch #scenario.'
- def results = objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME3, inputXpaths, OMIT_DESCENDANTS)
+ def results = objectUnderTest.getDataNodesForMultipleXpaths(DATASPACE_NAME, ANCHOR_NAME3, inputXpaths, OMIT_DESCENDANTS)
then: 'the expected number of data nodes are returned'
assert results.size() == expectedResultSize
where: 'following parameters were used'
@@ -323,9 +322,9 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
}
@Sql([CLEAR_DATA, SET_DATA])
- def 'Get multiple data nodes error scenario: #scenario.'() {
+ def 'Get data nodes for collection of xpath error scenario : #scenario.'() {
when: 'attempt to get data nodes with #scenario'
- objectUnderTest.getDataNodes(dataspaceName, anchorName, ['/not-relevant'], OMIT_DESCENDANTS)
+ objectUnderTest.getDataNodesForMultipleXpaths(dataspaceName, anchorName, ['/not-relevant'], OMIT_DESCENDANTS)
then: 'a #expectedException is thrown'
thrown(expectedException)
where: 'the following data is used'
@@ -599,11 +598,11 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
def child = new DataNodeBuilder().withXpath(deleteTestChildXpath).withChildDataNodes([grandChild]).build()
objectUnderTest.addChildDataNode(DATASPACE_NAME, ANCHOR_NAME3, deleteTestParentXPath, child)
and: 'number of children before delete is stored'
- def numberOfChildrenBeforeDelete = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, pathToParentOfDeletedNode, INCLUDE_ALL_DESCENDANTS).childDataNodes.size()
+ def numberOfChildrenBeforeDelete = objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME3, pathToParentOfDeletedNode, INCLUDE_ALL_DESCENDANTS)[0].childDataNodes.size()
when: 'target node is deleted'
objectUnderTest.deleteDataNode(DATASPACE_NAME, ANCHOR_NAME3, deleteTarget)
then: 'one child has been deleted'
- def numberOfChildrenAfterDelete = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, pathToParentOfDeletedNode, INCLUDE_ALL_DESCENDANTS).childDataNodes.size()
+ def numberOfChildrenAfterDelete = objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME3, pathToParentOfDeletedNode, INCLUDE_ALL_DESCENDANTS)[0].childDataNodes.size()
assert numberOfChildrenAfterDelete == numberOfChildrenBeforeDelete - 1
where:
scenario | deleteTarget | pathToParentOfDeletedNode
@@ -634,13 +633,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
and: 'data nodes are deleted'
objectUnderTest.deleteDataNode(DATASPACE_NAME, ANCHOR_NAME3, xpathForDeletion)
when: 'verify data nodes are removed'
- objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, xpathForDeletion, INCLUDE_ALL_DESCENDANTS)
+ objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME3, xpathForDeletion, INCLUDE_ALL_DESCENDANTS)
then:
thrown(DataNodeNotFoundException)
and: 'some related object is not deleted'
if (xpathSurvivor!=null) {
- dataNode = objectUnderTest.getDataNode(DATASPACE_NAME, ANCHOR_NAME3, xpathSurvivor, INCLUDE_ALL_DESCENDANTS)
- assert dataNode.xpath == xpathSurvivor
+ dataNode = objectUnderTest.getDataNodes(DATASPACE_NAME, ANCHOR_NAME3, xpathSurvivor, INCLUDE_ALL_DESCENDANTS)
+ assert dataNode[0].xpath == xpathSurvivor
}
where: 'following parameters were used'
scenario | xpathForDeletion || xpathSurvivor
@@ -711,6 +710,15 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
return flatMap
}
+ def static multipleTreesToFlatMapByXpath(Map<String, DataNode> flatMap, Collection<DataNode> dataNodeTrees) {
+ for (DataNode dataNodeTree: dataNodeTrees){
+ flatMap.put(dataNodeTree.xpath, dataNodeTree)
+ dataNodeTree.childDataNodes
+ .forEach(childDataNode -> multipleTreesToFlatMapByXpath(flatMap, [childDataNode]))
+ }
+ return flatMap
+ }
+
def keysToXpaths(parent, Collection keys) {
return keys.collect { "${parent}/child-list[@key='${it}']".toString() }
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
index 5cabc85b3..ac66e8c02 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
@@ -25,7 +25,6 @@ import org.hibernate.StaleStateException
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.entities.AnchorEntity
import org.onap.cps.spi.entities.FragmentEntity
-import org.onap.cps.spi.entities.FragmentExtract
import org.onap.cps.spi.exceptions.ConcurrencyException
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.DataNode
@@ -112,10 +111,10 @@ class CpsDataPersistenceServiceSpec extends Specification {
given: 'the db has a fragment with an attribute property JSON value of #scenario'
mockFragmentWithJson("{\"some attribute\": ${dataString}}")
when: 'getting the data node represented by this fragment'
- def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
+ def dataNode = objectUnderTest.getDataNodes('my-dataspace', 'my-anchor',
'/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
then: 'the leaf is of the correct value and data type'
- def attributeValue = dataNode.leaves.get('some attribute')
+ def attributeValue = dataNode[0].leaves.get('some attribute')
assert attributeValue == expectedValue
assert attributeValue.class == expectedDataClass
where: 'the following Data Type is passed'
@@ -136,7 +135,7 @@ class CpsDataPersistenceServiceSpec extends Specification {
given: 'a fragment with invalid JSON'
mockFragmentWithJson('{invalid json')
when: 'getting the data node represented by this fragment'
- objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
+ objectUnderTest.getDataNodes('my-dataspace', 'my-anchor',
'/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
then: 'a data validation exception is thrown'
thrown(DataValidationException)
@@ -151,7 +150,7 @@ class CpsDataPersistenceServiceSpec extends Specification {
def fragmentEntity2 = new FragmentEntity(xpath: '/xpath2', childFragments: [])
mockFragmentRepository.findByAnchorAndMultipleCpsPaths(123, ['/xpath1', '/xpath2'] as Set<String>) >> [fragmentEntity1, fragmentEntity2]
when: 'getting data nodes for 2 xpaths'
- def result = objectUnderTest.getDataNodes('some-dataspace', 'some-anchor', ['/xpath1', '/xpath2'], FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+ def result = objectUnderTest.getDataNodesForMultipleXpaths('some-dataspace', 'some-anchor', ['/xpath1', '/xpath2'], FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
then: '2 data nodes are returned'
assert result.size() == 2
}
@@ -243,10 +242,8 @@ class CpsDataPersistenceServiceSpec extends Specification {
def mockFragmentWithJson(json) {
def anchorEntity = new AnchorEntity(id:123)
mockAnchorRepository.getByDataspaceAndName(*_) >> anchorEntity
- def mockFragmentExtract = Mock(FragmentExtract)
- mockFragmentExtract.getId() >> 456
- mockFragmentExtract.getAttributes() >> json
- mockFragmentRepository.findByAnchorIdAndParentXpath(*_) >> [mockFragmentExtract]
+ def fragmentEntity = new FragmentEntity(xpath: '/parent-01', childFragments: [], attributes: json)
+ mockFragmentRepository.findByAnchorAndMultipleCpsPaths(123, ['/parent-01'] as Set<String>) >> [fragmentEntity]
}
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy
index 1ecad4e68..30ff11b45 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy
@@ -3,6 +3,7 @@
* Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021 Bell Canada.
+ * Modifications Copyright (C) 2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -60,13 +61,14 @@ class CpsPersistenceSpecBase extends Specification {
static final String CLEAR_DATA = '/data/clear-all.sql'
- static final String DATASPACE_NAME = 'DATASPACE-001'
- static final String SCHEMA_SET_NAME1 = 'SCHEMA-SET-001'
- static final String SCHEMA_SET_NAME2 = 'SCHEMA-SET-002'
- static final String ANCHOR_NAME1 = 'ANCHOR-001'
- static final String ANCHOR_NAME2 = 'ANCHOR-002'
- static final String ANCHOR_NAME3 = 'ANCHOR-003'
- static final String ANCHOR_FOR_DATA_NODES_WITH_LEAVES = 'ANCHOR-003'
- static final String ANCHOR_FOR_SHOP_EXAMPLE = 'ANCHOR-004'
- static final String ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT = 'ANCHOR-005'
+ static def DATASPACE_NAME = 'DATASPACE-001'
+ static def SCHEMA_SET_NAME1 = 'SCHEMA-SET-001'
+ static def SCHEMA_SET_NAME2 = 'SCHEMA-SET-002'
+ static def ANCHOR_NAME1 = 'ANCHOR-001'
+ static def ANCHOR_NAME2 = 'ANCHOR-002'
+ static def ANCHOR_NAME3 = 'ANCHOR-003'
+ static def ANCHOR_FOR_DATA_NODES_WITH_LEAVES = 'ANCHOR-003'
+ static def ANCHOR_FOR_SHOP_EXAMPLE = 'ANCHOR-004'
+ static def ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT = 'ANCHOR-005'
+ static def ANCHOR_WITH_MULTIPLE_TOP_LEVEL_FRAGMENTS = 'ANCHOR-006'
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
index 0c4f5ec41..246fd80bb 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
@@ -64,13 +64,13 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase {
def 'Get data node with many descendants by xpath #scenario'() {
when: 'get parent is executed with all descendants'
stopWatch.start()
- def result = objectUnderTest.getDataNode(PERF_DATASPACE, PERF_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS)
+ def result = objectUnderTest.getDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS)
stopWatch.stop()
def readDurationInMillis = stopWatch.getTotalTimeMillis()
then: 'read duration is under 500 milliseconds'
recordAndAssertPerformance("Get ${scenario}", 500, readDurationInMillis)
and: 'data node is returned with all the descendants populated'
- assert countDataNodes(result) == TOTAL_NUMBER_OF_NODES
+ assert countDataNodes(result[0]) == TOTAL_NUMBER_OF_NODES
where: 'the following xPaths are used'
scenario || xpath
'parent' || PERF_TEST_PARENT
@@ -93,7 +93,7 @@ class CpsDataPersistenceServicePerfTest extends CpsPersistencePerfSpecBase {
when: 'we query for all grandchildren (except 1 for fun) with the new native method'
xpathsToAllGrandChildren.remove(0)
stopWatch.start()
- def result = objectUnderTest.getDataNodes(PERF_DATASPACE, PERF_ANCHOR, xpathsToAllGrandChildren, INCLUDE_ALL_DESCENDANTS)
+ def result = objectUnderTest.getDataNodesForMultipleXpaths(PERF_DATASPACE, PERF_ANCHOR, xpathsToAllGrandChildren, INCLUDE_ALL_DESCENDANTS)
stopWatch.stop()
def readDurationInMillis = stopWatch.getTotalTimeMillis()
then: 'the returned number of entities equal to the number of children * number of grandchildren'
diff --git a/cps-ri/src/test/resources/data/fragment.sql b/cps-ri/src/test/resources/data/fragment.sql
index ad463cffd..caafcd320 100755
--- a/cps-ri/src/test/resources/data/fragment.sql
+++ b/cps-ri/src/test/resources/data/fragment.sql
@@ -52,7 +52,8 @@ INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
(3001, 'ANCHOR-001', 1001, 2001),
(3003, 'ANCHOR-003', 1001, 2001),
(3004, 'ncmp-dmi-registry', 1002, 2001),
- (3005, 'ANCHOR-005', 1001, 2001);
+ (3005, 'ANCHOR-005', 1001, 2001),
+ (3006, 'ANCHOR-006', 1001, 2001);
INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH) VALUES
(4001, 1001, 3001, null, '/parent-1'),
@@ -69,6 +70,16 @@ INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES)
(5012, 1001, 3005, 5011, '/parent-207/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}');
INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
+ (5013, 1001, 3006, null, '/parent-208', '{"parent-leaf-1": "parent-leaf value-1"}'),
+ (5014, 1001, 3006, 5013, '/parent-208/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
+ (5015, 1001, 3006, 5013, '/parent-208/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
+ (5016, 1001, 3006, 5015, '/parent-208/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}'),
+ (5017, 1001, 3006, null, '/parent-209', '{"parent-leaf-2": "parent-leaf value-2"}'),
+ (5018, 1001, 3006, 5017, '/parent-209/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
+ (5019, 1001, 3006, 5017, '/parent-209/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
+ (5020, 1001, 3006, 5019, '/parent-209/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}');
+
+INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
(4201, 1001, 3003, null, '/parent-200', '{"leaf-value": "original"}'),
(4202, 1001, 3003, 4201, '/parent-200/child-201', '{"leaf-value": "original"}'),
(4203, 1001, 3003, 4202, '/parent-200/child-201/grand-child', '{"leaf-value": "original"}'),