diff options
author | 2023-03-07 20:35:14 +0000 | |
---|---|---|
committer | 2023-03-13 13:18:02 +0000 | |
commit | b8b29c97ee64b395bceb1e100eaf2a6bf115871f (patch) | |
tree | 104f7fc9cf9060091dc478c0f48a5dd3d7db517c /cps-ri/src/test | |
parent | 8e08dc797c89f6e85347c10cc7a2e877c126432a (diff) |
Reduce dataspace/anchor lookups in CpsDataPersistenceService
- Remove unneeded calls to DataspaceRepository::getByName
- Remove unneeded calls to AnchorRepository::getByDataspaceAndName
- Refactor FragmentRepository
Issue-ID: CPS-1536
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: I2121f6247070ee4a7c000e06ec66a6278b758540
Diffstat (limited to 'cps-ri/src/test')
-rwxr-xr-x | cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy | 43 | ||||
-rw-r--r-- | cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy | 35 |
2 files changed, 27 insertions, 51 deletions
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 71253278e..25f19f7a8 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 @@ -70,13 +70,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def static deleteTestChildXpath = "${deleteTestParentXPath}/child-with-slash[@key='a/b']" def static deleteTestGrandChildXPath = "${deleteTestChildXpath}/grandChild" - def expectedLeavesByXpathMap = [ - '/parent-207' : ['parent-leaf': 'parent-leaf value'], - '/parent-207/child-001' : ['first-child-leaf': 'first-child-leaf value'], - '/parent-207/child-002' : ['second-child-leaf': 'second-child-leaf value'], - '/parent-207/child-002/grand-child': ['grand-child-leaf': 'grand-child-leaf value'] - ] - @Sql([CLEAR_DATA, SET_DATA]) def 'Get all datanodes with descendants .'() { when: 'data nodes are retrieved by their xpath' @@ -187,7 +180,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { and: 'the (grand)child node of the new list entry is also present' def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3) - def grandChildFragmentEntity = fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, grandChild.xpath) + def grandChildFragmentEntity = fragmentRepository.findByAnchorAndXpath(anchorEntity, grandChild.xpath) assert grandChildFragmentEntity.isPresent() } @@ -212,7 +205,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { and: 'the new entity is inserted correctly' def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT) - fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, '/parent-200/child-new2').isPresent() + fragmentRepository.findByAnchorAndXpath(anchorEntity, '/parent-200/child-new2').isPresent() } @Sql([CLEAR_DATA, SET_DATA]) @@ -674,27 +667,27 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @Sql([CLEAR_DATA, SET_DATA]) def 'Delete data node for an anchor.'() { given: 'a data-node exists for an anchor' - assert fragmentsExistInDB(1001, 3003) + assert fragmentsExistInDB(3003) when: 'data nodes are deleted ' objectUnderTest.deleteDataNodes(DATASPACE_NAME, ANCHOR_NAME3) then: 'all data-nodes are deleted successfully' - assert !fragmentsExistInDB(1001, 3003) + assert !fragmentsExistInDB(3003) } @Sql([CLEAR_DATA, SET_DATA]) def 'Delete data node for multiple anchors.'() { given: 'a data-node exists for an anchor' - assert fragmentsExistInDB(1001, 3001) - assert fragmentsExistInDB(1001, 3003) + assert fragmentsExistInDB(3001) + assert fragmentsExistInDB(3003) when: 'data nodes are deleted ' objectUnderTest.deleteDataNodes(DATASPACE_NAME, ['ANCHOR-001', 'ANCHOR-003']) then: 'all data-nodes are deleted successfully' - assert !fragmentsExistInDB(1001, 3001) - assert !fragmentsExistInDB(1001, 3003) + assert !fragmentsExistInDB(3001) + assert !fragmentsExistInDB(3003) } - def fragmentsExistInDB(dataSpaceId, anchorId) { - !fragmentRepository.findRootsByDataspaceAndAnchor(dataSpaceId, anchorId).isEmpty() + def fragmentsExistInDB(anchorId) { + fragmentRepository.existsByAnchorId(anchorId) } static Collection<DataNode> toDataNodes(xpaths) { @@ -710,19 +703,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { return jsonObjectMapper.convertJsonString(fragmentEntity.attributes, Map<String, Object>.class) } - def static assertLeavesMaps(actualLeavesMap, expectedLeavesMap) { - expectedLeavesMap.forEach((key, value) -> { - def actualValue = actualLeavesMap[key] - if (value instanceof Collection<?> && actualValue instanceof Collection<?>) { - assert value.size() == actualValue.size() - assert value.containsAll(actualValue) - } else { - assert value == actualValue - } - }) - return true - } - def static treeToFlatMapByXpath(Map<String, DataNode> flatMap, DataNode dataNodeTree) { flatMap.put(dataNodeTree.xpath, dataNodeTree) dataNodeTree.childDataNodes @@ -756,10 +736,9 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def getFragmentByXpath(dataspaceName, anchorName, xpath) { def dataspace = dataspaceRepository.getByName(dataspaceName) def anchor = anchorRepository.getByDataspaceAndName(dataspace, anchorName) - return fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspace, anchor, xpath).orElseThrow() + return fragmentRepository.findByAnchorAndXpath(anchor, xpath).orElseThrow() } - def createChildListAllHavingAttributeValue(parentXpath, tag, Collection keys, boolean addGrandChild) { def listElementAsDataNodes = keysToXpaths(parentXpath, keys).collect { new DataNodeBuilder() 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 ba42a083e..e74b4a745 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 @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import org.hibernate.StaleStateException import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.entities.AnchorEntity +import org.onap.cps.spi.entities.DataspaceEntity import org.onap.cps.spi.entities.FragmentEntity import org.onap.cps.spi.exceptions.ConcurrencyException import org.onap.cps.spi.exceptions.DataValidationException @@ -48,6 +49,12 @@ class CpsDataPersistenceServiceSpec extends Specification { def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper, mockSessionManager)) + def anchorEntity = new AnchorEntity(id: 123, dataspace: new DataspaceEntity(id: 1)) + + def setup() { + mockAnchorRepository.getByDataspaceAndName(_, _) >> anchorEntity + } + def 'Storing data nodes individually when batch operation fails'(){ given: 'two data nodes and supporting repository mock behavior' def dataNode1 = createDataNodeAndMockRepositoryMethodSupportingIt('xpath1','OK') @@ -57,7 +64,7 @@ class CpsDataPersistenceServiceSpec extends Specification { when: 'trying to store data nodes' objectUnderTest.storeDataNodes('dataSpaceName', 'anchorName', [dataNode1, dataNode2]) then: 'the two data nodes are saved individually' - 2 * mockFragmentRepository.save(_); + 2 * mockFragmentRepository.save(_) } def 'Store single data node.'() { @@ -71,7 +78,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'Handling of StaleStateException (caused by concurrent updates) during update data node and descendants.'() { given: 'the fragment repository returns a fragment entity' - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(*_) >> { + mockFragmentRepository.getByAnchorAndXpath(*_) >> { def fragmentEntity = new FragmentEntity() fragmentEntity.setChildFragments([new FragmentEntity()] as Set<FragmentEntity>) return fragmentEntity @@ -93,8 +100,6 @@ class CpsDataPersistenceServiceSpec extends Specification { '/node1': 'OK', '/node2': 'EXCEPTION', '/node3': 'EXCEPTION']) - and: 'db contains an anchor' - mockAnchorRepository.getByDataspaceAndName(*_) >> new AnchorEntity(id:123) and: 'the batch update will therefore also fail' mockFragmentRepository.saveAll(*_) >> { throw new StaleStateException("concurrent updates") } when: 'attempt batch update data nodes' @@ -144,10 +149,7 @@ class CpsDataPersistenceServiceSpec extends Specification { } def 'Retrieving multiple data nodes.'() { - given: 'db contains an anchor' - def anchorEntity = new AnchorEntity(id:123) - mockAnchorRepository.getByDataspaceAndName(*_) >> anchorEntity - and: 'fragment repository returns a collection of fragments' + given: 'fragment repository returns a collection of fragments' def fragmentEntity1 = new FragmentEntity(xpath: '/xpath1', childFragments: []) def fragmentEntity2 = new FragmentEntity(xpath: '/xpath2', childFragments: []) mockFragmentRepository.findByAnchorAndMultipleCpsPaths(123, ['/xpath1', '/xpath2'] as Set<String>) >> [fragmentEntity1, fragmentEntity2] @@ -182,7 +184,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'update data node leaves: #scenario'(){ given: 'A node exists for the given xpath' - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, '/some/xpath') >> new FragmentEntity(xpath: '/some/xpath', attributes: existingAttributes) + mockFragmentRepository.getByAnchorAndXpath(_, '/some/xpath') >> new FragmentEntity(xpath: '/some/xpath', attributes: existingAttributes) when: 'the node leaves are updated' objectUnderTest.updateDataLeaves('some-dataspace', 'some-anchor', '/some/xpath', newAttributes as Map<String, Serializable>) then: 'the fragment entity saved has the original and new attributes' @@ -200,8 +202,7 @@ class CpsDataPersistenceServiceSpec extends Specification { } def 'update data node and descendants: #scenario'(){ - given: 'mocked responses' - mockAnchorRepository.getByDataspaceAndName(_, _) >> new AnchorEntity(id:123) + given: 'the fragment repository returns fragment entities related to the xpath inputs' mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, [] as Set) >> [] mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, ['/test/xpath'] as Set) >> [new FragmentEntity(xpath: '/test/xpath', childFragments: [])] when: 'replace data node tree' @@ -217,10 +218,8 @@ class CpsDataPersistenceServiceSpec extends Specification { def 'update data nodes and descendants'() { given: 'the fragment repository returns fragment entities related to the xpath inputs' mockFragmentRepository.findByAnchorAndMultipleCpsPaths(_, ['/test/xpath1', '/test/xpath2'] as Set) >> [ - new FragmentEntity(xpath: '/test/xpath1', childFragments: []), - new FragmentEntity(xpath: '/test/xpath2', childFragments: [])] - and: 'db contains an anchor' - mockAnchorRepository.getByDataspaceAndName(*_) >> new AnchorEntity(id:123) + new FragmentEntity(xpath: '/test/xpath1', childFragments: [], anchor: anchorEntity), + new FragmentEntity(xpath: '/test/xpath2', childFragments: [], anchor: anchorEntity)] and: 'some data nodes with descendants' def dataNode1 = new DataNode(xpath: '/test/xpath1', leaves: ['id': 'testId1'], childDataNodes: [new DataNode(xpath: '/test/xpath1/child', leaves: ['id': 'childTestId1'])]) def dataNode2 = new DataNode(xpath: '/test/xpath2', leaves: ['id': 'testId2'], childDataNodes: [new DataNode(xpath: '/test/xpath2/child', leaves: ['id': 'childTestId2'])]) @@ -239,7 +238,7 @@ class CpsDataPersistenceServiceSpec extends Specification { def createDataNodeAndMockRepositoryMethodSupportingIt(xpath, scenario) { def dataNode = new DataNodeBuilder().withXpath(xpath).build() def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: []) - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, xpath) >> fragmentEntity + mockFragmentRepository.getByAnchorAndXpath(_, xpath) >> fragmentEntity if ('EXCEPTION' == scenario) { mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") } } @@ -256,7 +255,7 @@ class CpsDataPersistenceServiceSpec extends Specification { dataNodes.add(dataNode) def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: []) fragmentEntities.add(fragmentEntity) - mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, xpath) >> fragmentEntity + mockFragmentRepository.getByAnchorAndXpath(_, xpath) >> fragmentEntity if ('EXCEPTION' == scenario) { mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") } } @@ -266,8 +265,6 @@ class CpsDataPersistenceServiceSpec extends Specification { } def mockFragmentWithJson(json) { - def anchorEntity = new AnchorEntity(id:123) - mockAnchorRepository.getByDataspaceAndName(*_) >> anchorEntity def fragmentEntity = new FragmentEntity(xpath: '/parent-01', childFragments: [], attributes: json) mockFragmentRepository.findByAnchorAndMultipleCpsPaths(123, ['/parent-01'] as Set<String>) >> [fragmentEntity] } |