diff options
author | ToineSiebelink <toine.siebelink@est.tech> | 2022-09-13 12:51:21 +0100 |
---|---|---|
committer | ToineSiebelink <toine.siebelink@est.tech> | 2022-09-13 15:26:30 +0100 |
commit | ae5a47388dae38c51b437e448ae6a23fa9a77591 (patch) | |
tree | 2a5fce300e78717de7f9a7309ccdeea3cec51d55 /cps-ri | |
parent | 5b977b6607d696d347de08ecba638ffbf4d57d16 (diff) |
Handle partial failure (improvements)
- catching of failures on retry of individual nodes
- extract cm handle id from xpaths (can only report xpaths in cps core)
- add test for same
Issue-ID: CPS-1232
Issue-ID: CPS-1126
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
Change-Id: Ice2032c8b15fea97ae0aaa4d1ed642b3499228fa
Diffstat (limited to 'cps-ri')
-rw-r--r-- | cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java | 24 | ||||
-rwxr-xr-x | cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy | 40 |
2 files changed, 40 insertions, 24 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 e02fb7355b..d62421c5af 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 @@ -103,17 +103,17 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<Collection<DataNode>> newLists) { - final Collection<String> failedCmHandleIds = new HashSet<>(); + final Collection<String> failedXpaths = new HashSet<>(); newLists.forEach(newList -> { try { addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newList); - } catch (final AlreadyDefinedException e) { - newList.forEach(listElement -> failedCmHandleIds.add((String) listElement.getLeaves().get("id"))); + } catch (final AlreadyDefinedExceptionBatch e) { + failedXpaths.addAll(e.getAlreadyDefinedXpaths()); } }); - if (!failedCmHandleIds.isEmpty()) { - throw new AlreadyDefinedExceptionBatch(failedCmHandleIds); + if (!failedXpaths.isEmpty()) { + throw new AlreadyDefinedExceptionBatch(failedXpaths); } } @@ -147,7 +147,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService }); fragmentRepository.saveAll(fragmentEntities); } catch (final DataIntegrityViolationException e) { - log.warn("Exception occurred : {} , Batch with size : {} will be retried using individual save operations", + log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations", e, fragmentEntities.size()); retrySavingEachChildIndividually(dataspaceName, anchorName, parentNodeXpath, newChildren); } @@ -155,7 +155,17 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName, final String parentNodeXpath, final Collection<DataNode> newChildren) { - newChildren.forEach(newChild -> addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChild)); + final Collection<String> failedXpaths = new HashSet<>(); + for (final DataNode newChild : newChildren) { + try { + addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChild); + } catch (final AlreadyDefinedException e) { + failedXpaths.add(newChild.getXpath()); + } + } + if (!failedXpaths.isEmpty()) { + throw new AlreadyDefinedExceptionBatch(failedXpaths); + } } @Override 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 ba9bd6f95a..5e15ca795f 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 @@ -49,6 +49,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { CpsDataPersistenceService objectUnderTest static final JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + static final DataNodeBuilder dataNodeBuilder = new DataNodeBuilder() static final String SET_DATA = '/data/fragment.sql' static final int DATASPACE_1001_ID = 1001L @@ -180,33 +181,41 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Add already existing list elements'() { + def 'Add multiple list with a mix of existing and new elements'() { given: 'two new child list elements for an existing parent' - def listElementXpaths1 = ['/parent-100', '/parent-100/child-001'] - def listElementXpaths2 = ['/parent-200', '/parent-200/child-201'] - def listElements1 = toDataNodesWithId(listElementXpaths1, 'cmhandle1') - def listElements2 = toDataNodesWithId(listElementXpaths2, 'cmhandle2') + def existingDataNode = dataNodeBuilder.withXpath('/parent-100/child-001').withLeaves(['id': '001']).build() + def newDataNode1 = dataNodeBuilder.withXpath('/parent-100/child-new1').withLeaves(['id': 'new1']).build() + def newDataNode2 = dataNodeBuilder.withXpath('/parent-200/child-new2').withLeaves(['id': 'new2']).build() + def dataNodeList1 = [existingDataNode, newDataNode1] + def dataNodeList2 = [newDataNode2] when: 'duplicate data node is requested to be added' - objectUnderTest.addMultipleLists(DATASPACE_NAME, ANCHOR_NAME3, '/', [listElements1,listElements2]) + objectUnderTest.addMultipleLists(DATASPACE_NAME, ANCHOR_NAME3, '/', [dataNodeList1,dataNodeList2]) then: 'already defined batch exception is thrown' - def e = thrown(AlreadyDefinedExceptionBatch) - and: 'it contains both cmhandle ids' - assert e.alreadyDefinedCmHandleIds.size() == 2 - assert e.alreadyDefinedCmHandleIds.containsAll(['cmhandle1', 'cmhandle2']) + def thrown = thrown(AlreadyDefinedExceptionBatch) + and: 'it only contains the xpath(s) of the duplicated elements' + assert thrown.alreadyDefinedXpaths.size() == 1 + assert thrown.alreadyDefinedXpaths.contains('/parent-100/child-001') + and: 'it does NOT contains the xpaths of the new element that were not combined with existing elements' + assert !thrown.alreadyDefinedXpaths.contains('/parent-100/child-new1') + assert !thrown.alreadyDefinedXpaths.contains('/parent-100/child-new1') + and: 'the new entity is inserted correctly' + def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) + def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3) + fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, '/parent-200/child-new2').isPresent() } @Sql([CLEAR_DATA, SET_DATA]) def 'Add list element error scenario: #scenario.'() { given: 'list element as a collection of data nodes' - def listElementCollection = toDataNodes(listElementXpaths) + def listElements = toDataNodes(listElementXpaths) when: 'attempt to add list elements to parent node' - objectUnderTest.addListElements(DATASPACE_NAME, ANCHOR_NAME3, parentNodeXpath, listElementCollection) + objectUnderTest.addListElements(DATASPACE_NAME, ANCHOR_NAME3, parentNodeXpath, listElements) then: 'a #expectedException is thrown' thrown(expectedException) where: 'following parameters were used' scenario | parentNodeXpath | listElementXpaths || expectedException 'parent node does not exist' | '/unknown' | ['irrelevant'] || DataNodeNotFoundException - 'data fragment already exists' | '/parent-201' | ["/parent-201/child-204[@key='A']"] || AlreadyDefinedException + 'data fragment already exists' | '/parent-201' | ["/parent-201/child-204[@key='A']"] || AlreadyDefinedExceptionBatch } @Sql([CLEAR_DATA, SET_DATA]) @@ -576,12 +585,9 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { return xpaths.collect { new DataNodeBuilder().withXpath(it).build() } } - static Collection<DataNode> toDataNodesWithId(xpaths, id) { - return xpaths.collect { new DataNodeBuilder().withXpath(it).withLeaves(['id': id]).build() } - } static DataNode buildDataNode(xpath, leaves, childDataNodes) { - return new DataNodeBuilder().withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build() + return dataNodeBuilder.withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build() } static Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) { |