diff options
7 files changed, 133 insertions, 26 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 c638b9113e..af010f4fcd 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 @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.transaction.Transactional; @@ -48,6 +49,7 @@ import org.onap.cps.spi.entities.FragmentEntity; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.ConcurrencyException; import org.onap.cps.spi.exceptions.CpsPathException; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; import org.onap.cps.spi.repository.AnchorRepository; @@ -82,6 +84,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private static final Gson GSON = new GsonBuilder().create(); private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})"; + private static final String REG_EX_FOR_LIST_NODE_KEY = "\\[(\\@([^/]*?))+( and)*\\]$"; @Override public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath, @@ -315,8 +318,33 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService fragmentRepository.save(parentEntity); } + @Override + @Transactional + public void deleteListDataNodes(final String dataspaceName, final String anchorName, final String listNodeXpath) { + final var parentNodeXpath = listNodeXpath.substring(0, listNodeXpath.lastIndexOf('/')); + final var parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath); + final var descendantNode = listNodeXpath.substring(listNodeXpath.lastIndexOf('/')); + final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(descendantNode); + + final boolean xpathPointsToAValidChildNodeWithKey = parentEntity.getChildFragments().stream().anyMatch( + (fragment) -> fragment.getXpath().equals(listNodeXpath)); + + final boolean xpathPointsToAValidChildNodeWithoutKey = parentEntity.getChildFragments().stream().anyMatch( + (fragment) -> fragment.getXpath().replaceAll(REG_EX_FOR_LIST_NODE_KEY, "").equals(listNodeXpath)); + + if ((descendantNodeHasListNodeKey.find() && xpathPointsToAValidChildNodeWithKey) + || + (!descendantNodeHasListNodeKey.find() && xpathPointsToAValidChildNodeWithoutKey)) { + removeListNodeDescendants(parentEntity, listNodeXpath); + } else { + throw new DataNodeNotFoundException(parentEntity.getDataspace().getName(), + parentEntity.getAnchor().getName(), listNodeXpath); + } + } + private void removeListNodeDescendants(final FragmentEntity parentFragmentEntity, final String listNodeXpath) { - final String listNodeXpathPrefix = listNodeXpath + "["; + final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(listNodeXpath); + final String listNodeXpathPrefix = listNodeXpath + (descendantNodeHasListNodeKey.find() ? "" : "["); if (parentFragmentEntity.getChildFragments() .removeIf(fragment -> fragment.getXpath().startsWith(listNodeXpathPrefix))) { fragmentRepository.save(parentFragmentEntity); 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 ad8db766f8..8217a4fb0d 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 @@ -21,7 +21,7 @@ */ package org.onap.cps.spi.impl -import org.onap.cps.spi.exceptions.ConcurrencyException +import org.onap.cps.spi.exceptions.DataValidationException import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS @@ -55,7 +55,10 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { static final String XPATH_DATA_NODE_WITH_LEAVES = '/parent-100' static final long UPDATE_DATA_NODE_FRAGMENT_ID = 4202L static final long UPDATE_DATA_NODE_SUB_FRAGMENT_ID = 4203L - static final long LIST_DATA_NODE_PARENT_FRAGMENT_ID = 4206L + static final long LIST_DATA_NODE_PARENT201_FRAGMENT_ID = 4206L + static final long LIST_DATA_NODE_PARENT203_FRAGMENT_ID = 4214L + static final long LIST_DATA_NODE_CHILD202_FRAGMENT_ID = 4204L + static final long LIST_DATA_NODE_PARENT202_FRAGMENT_ID = 4211L static final DataNode newDataNode = new DataNodeBuilder().build() static DataNode existingDataNode @@ -159,7 +162,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { when: 'list-node elements added to existing parent node' objectUnderTest.addListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', listNodeCollection) then: 'new entries successfully persisted, parent node now contains 5 children (2 new + 3 existing before)' - def parentFragment = fragmentRepository.getOne(LIST_DATA_NODE_PARENT_FRAGMENT_ID) + def parentFragment = fragmentRepository.getById(LIST_DATA_NODE_PARENT201_FRAGMENT_ID) def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() } assert allChildXpaths.size() == 5 assert allChildXpaths.containsAll(listNodeXpaths) @@ -253,7 +256,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { objectUnderTest.updateDataLeaves(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, "/parent-200/child-201", ['leaf-value': 'new']) then: 'leaves are updated for selected data node' - def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID) + def updatedFragment = fragmentRepository.getById(UPDATE_DATA_NODE_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) assert updatedLeaves.size() == 1 assert updatedLeaves.'leaf-value' == 'new' @@ -284,7 +287,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { when: 'replace data node tree is performed' objectUnderTest.replaceDataNodeTree(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) then: 'leaves have been updated for selected data node' - def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID) + def updatedFragment = fragmentRepository.getById(UPDATE_DATA_NODE_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) assert updatedLeaves.size() == 1 assert updatedLeaves.'leaf-value' == 'new' @@ -298,12 +301,12 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def 'Replace data node tree with descendants.'() { given: 'data node object with leaves updated, having child with old content' def submittedDataNode = buildDataNode("/parent-200/child-201", ['leaf-value': 'new'], [ - buildDataNode("/parent-200/child-201/grand-child", ['leaf-value': 'original'], []) + buildDataNode("/parent-200/child-201/grand-child", ['leaf-value': 'original'], []) ]) when: 'update is performed including descendants' objectUnderTest.replaceDataNodeTree(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) then: 'leaves have been updated for selected data node' - def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID) + def updatedFragment = fragmentRepository.getById(UPDATE_DATA_NODE_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) assert updatedLeaves.size() == 1 assert updatedLeaves.'leaf-value' == 'new' @@ -323,7 +326,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { when: 'update is performed including descendants' objectUnderTest.replaceDataNodeTree(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) then: 'leaves have been updated for selected data node' - def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID) + def updatedFragment = fragmentRepository.getById(UPDATE_DATA_NODE_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) assert updatedLeaves.size() == 1 assert updatedLeaves.'leaf-value' == 'new' @@ -343,7 +346,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { when: 'update is performed including descendants' objectUnderTest.replaceDataNodeTree(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) then: 'leaves have been updated for selected data node' - def updatedFragment = fragmentRepository.getOne(UPDATE_DATA_NODE_FRAGMENT_ID) + def updatedFragment = fragmentRepository.getById(UPDATE_DATA_NODE_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) assert updatedLeaves.size() == 1 assert updatedLeaves.'leaf-value' == 'new' @@ -362,7 +365,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { def submittedDataNode = buildDataNode(xpath, ['leaf-name': 'leaf-value'], []) when: 'attempt to update data node for #scenario' objectUnderTest.replaceDataNodeTree(dataspaceName, anchorName, submittedDataNode) - then: 'a #expectedException is thrown' + then: 'a #expectedException is thrown' thrown(expectedException) where: 'the following data is used' scenario | dataspaceName | anchorName | xpath || expectedException @@ -378,7 +381,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { when: 'list-node elements replaced within the existing parent node' objectUnderTest.replaceListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', listNodeCollection) then: 'child list elements are updated as expected, non-list element remains as is' - def parentFragment = fragmentRepository.getOne(LIST_DATA_NODE_PARENT_FRAGMENT_ID) + def parentFragment = fragmentRepository.getById(LIST_DATA_NODE_PARENT201_FRAGMENT_ID) def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() } assert allChildXpaths.size() == expectedChildXpaths.size() assert allChildXpaths.containsAll(expectedChildXpaths) @@ -401,6 +404,47 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { 'parent node does not exist' | '/unknown' | ['irrelevant'] || DataNodeNotFoundException } + @Sql([CLEAR_DATA, SET_DATA]) + def 'Delete list-node content of #scenario.'() { + given: 'list node data fragments are present in database' + when: 'list-node elements deleted within the existing parent node' + objectUnderTest.deleteListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, listNodeXpaths) + then: 'child list elements are removed as expected, non-list element remains as is' + def parentFragment = fragmentRepository.getById(listNodeFragmentID) + def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() } + assert allChildXpaths.size() == expectedChildXpaths.size() + assert allChildXpaths.containsAll(expectedChildXpaths) + where: 'following parameters were used' + scenario | listNodeXpaths | listNodeFragmentID || expectedChildXpaths + 'existing list-node with key' | '/parent-203/child-204[@key="A"]' | LIST_DATA_NODE_PARENT203_FRAGMENT_ID || ['/parent-203/child-203', '/parent-203/child-204[@key="X"]'] + 'existing list-node with key' | '/parent-203/child-204[@key="X"]' | LIST_DATA_NODE_PARENT203_FRAGMENT_ID || ['/parent-203/child-203', '/parent-203/child-204[@key="A"]'] + 'existing grand-child list node with keys' | '/parent-203/child-204[@key="X"]/grand-child-204[@key2="Y"]' | LIST_DATA_NODE_PARENT203_FRAGMENT_ID || ['/parent-203/child-203', '/parent-203/child-204[@key="X"]', '/parent-203/child-204[@key="A"]'] + 'existing list-node with combined keys' | '/parent-202/child-205[@key="A" and @key2="B"]' | LIST_DATA_NODE_PARENT202_FRAGMENT_ID || ['/parent-202/child-206[@key="A"]'] + 'existing node with list node variants to delete' | '/parent-203/child-204' | LIST_DATA_NODE_PARENT203_FRAGMENT_ID || ['/parent-203/child-203'] + 'existing grandchild list-node' | '/parent-200/child-202/grand-child-202[@key="D"]' | LIST_DATA_NODE_CHILD202_FRAGMENT_ID || [] + } + + @Sql([CLEAR_DATA, SET_DATA]) + def 'Delete list-node fragment error scenario: #scenario.'() { + given: 'list node data fragments are present in database' + when: 'list-node elements are deleted under existing parent node' + objectUnderTest.deleteListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, listNodeXpaths) + then: 'a #expectedException is thrown' + thrown(expectedException) + where: 'following parameters were used' + scenario | listNodeXpaths || expectedException + 'list parent node does not exist' | '/unknown/unknown' || DataNodeNotFoundException + 'list child nodes do not exist' | '/parent-200/unknown' || DataNodeNotFoundException + 'list child nodes with key does not exist' | '/parent-200/unknown[@key="C"]' || DataNodeNotFoundException + 'list grandchild nodes parent does not exist' | '/parent-200/unknown/unknown' || DataNodeNotFoundException + 'non-existing parent with existing list-node' | '/unknown/child-204' || DataNodeNotFoundException + 'non-existing parent with existing list-node & key' | '/unknown/child-204[@key="A"]' || DataNodeNotFoundException + 'valid with non existing key' | '/parent-200/child-202/grand-child-202[@key="A"]' || DataNodeNotFoundException + 'child list node without key' | '/parent-200/child-204/grand-child-204' || DataNodeNotFoundException + 'valid list node with invalid key' | '/parent-203/child-204[@key="C"]' || DataNodeNotFoundException + + } + static Collection<DataNode> buildDataNodeCollection(xpaths) { return xpaths.collect { new DataNodeBuilder().withXpath(it).build() } } diff --git a/cps-ri/src/test/resources/data/fragment.sql b/cps-ri/src/test/resources/data/fragment.sql index d7109f20b2..886e6e1751 100755 --- a/cps-ri/src/test/resources/data/fragment.sql +++ b/cps-ri/src/test/resources/data/fragment.sql @@ -49,8 +49,16 @@ INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) (4202, 1001, 3003, 4201, '/parent-200/child-201', '{"leaf-value": "original"}'), (4203, 1001, 3003, 4202, '/parent-200/child-201/grand-child', '{"leaf-value": "original"}'), (4204, 1001, 3003, 4201, '/parent-200/child-202', '{"common-leaf-name": "common-leaf value", "common-leaf-name-int" : 5}'), - (4205, 1001, 3003, 4204, '/parent-200/child-202/grand-child-202', '{"common-leaf-name": "common-leaf value", "common-leaf-name-int" : 5}'), + (4205, 1001, 3003, 4204, '/parent-200/child-202/grand-child-202[@key="D"]', '{"common-leaf-name": "common-leaf value", "common-leaf-name-int" : 5}'), (4206, 1001, 3003, null, '/parent-201', '{"leaf-value": "original"}'), (4207, 1001, 3003, 4206, '/parent-201/child-203', '{}'), (4208, 1001, 3003, 4206, '/parent-201/child-204[@key="A"]', '{"key": "A"}'), - (4209, 1001, 3003, 4206, '/parent-201/child-204[@key="X"]', '{"key": "X"}'); + (4209, 1001, 3003, 4206, '/parent-201/child-204[@key="X"]', '{"key": "X"}'), + (4211, 1001, 3003, null, '/parent-202', '{"leaf-value": "original"}'), + (4212, 1001, 3003, 4211, '/parent-202/child-205[@key="A" and @key2="B"]', '{"key": "A", "key2": "B"}'), + (4213, 1001, 3003, 4211, '/parent-202/child-206[@key="A"]', '{"key": "A"}'), + (4214, 1001, 3003, null, '/parent-203', '{"leaf-value": "original"}'), + (4215, 1001, 3003, 4214, '/parent-203/child-203', '{}'), + (4216, 1001, 3003, 4214, '/parent-203/child-204[@key="A"]', '{"key": "A"}'), + (4217, 1001, 3003, 4214, '/parent-203/child-204[@key="X"]', '{"key": "X"}'), + (4218, 1001, 3003, 4217, '/parent-203/child-204[@key="X"]/grand-child-204[@key2="Y"]', '{"key": "X", "key2": "Y"}');
\ No newline at end of file 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 7187810e4a..6036f92225 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 @@ -23,9 +23,6 @@ package org.onap.cps.api; import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.exceptions.AlreadyDefinedException; -import org.onap.cps.spi.exceptions.DataNodeNotFoundException; -import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; /* @@ -39,7 +36,6 @@ public interface CpsDataService { * @param dataspaceName dataspace name * @param anchorName anchor name * @param jsonData json data - * @throws DataValidationException when json data is invalid */ void saveData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String jsonData); @@ -50,9 +46,6 @@ public interface CpsDataService { * @param anchorName anchor name * @param parentNodeXpath parent node xpath * @param jsonData json data - * @throws DataValidationException when json data is invalid - * @throws DataNodeNotFoundException when parent node cannot be found by parent node xpath - * @throws AlreadyDefinedException when child data node with same xpath already exists */ void saveData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, @NonNull String jsonData); @@ -65,9 +58,6 @@ public interface CpsDataService { * @param anchorName anchor name * @param parentNodeXpath parent node xpath * @param jsonData json data representing list element - * @throws DataValidationException when json data is invalid (incl. list-node being empty) - * @throws DataNodeNotFoundException when parent node cannot be found by parent node xpath - * @throws AlreadyDefinedException when any of child data nodes is having xpath of already existing node */ void saveListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, @NonNull String jsonData); @@ -115,9 +105,17 @@ public interface CpsDataService { * @param anchorName anchor name * @param parentNodeXpath parent node xpath * @param jsonData json data representing list element - * @throws DataValidationException when json data is invalid (incl. list-node being empty) - * @throws DataNodeNotFoundException when parent node cannot be found by parent node xpath */ void replaceListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, @NonNull String jsonData); + + /** + * Deletes (if exists) child data fragment representing list-node (with one or more elements) + * under existing data node for the given anchor and dataspace. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param listNodeXpath list node xpath + */ + void deleteListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String listNodeXpath); } 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 717df16a5f..5e6e1a2687 100755 --- 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 @@ -114,6 +114,13 @@ public class CpsDataServiceImpl implements CpsDataService { notificationService.processDataUpdatedEvent(dataspaceName, anchorName); } + @Override + public void deleteListNodeData(final String dataspaceName, final String anchorName, final String listNodeXpath) { + cpsDataPersistenceService.deleteListDataNodes(dataspaceName, anchorName, listNodeXpath); + notificationService.processDataUpdatedEvent(dataspaceName, anchorName); + } + + private DataNode buildDataNodeFromJson(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData) { diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java index cfb39f5951..bf8dd1a073 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java @@ -112,6 +112,16 @@ public interface CpsDataPersistenceService { @NonNull String parentNodeXpath, @NonNull Collection<DataNode> dataNodes); /** + * Deletes existing list data node content including descendants. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param listNodeXpath list node xpath + */ + void deleteListDataNodes(@NonNull String dataspaceName, @NonNull String anchorName, + @NonNull String listNodeXpath); + + /** * Get a datanode by cps path. * * @param dataspaceName dataspace name 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 19ccee3675..122039728a 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 @@ -28,6 +28,7 @@ import org.onap.cps.api.CpsModuleService import org.onap.cps.notification.NotificationService import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption +import org.onap.cps.spi.exceptions.CpsPathException import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.DataNodeBuilder @@ -197,6 +198,17 @@ class CpsDataServiceImplSpec extends Specification { thrown(DataValidationException) } + def 'Delete list-node data fragment under existing node.'() { + given: 'schema set for given anchor and dataspace references test-tree model' + setupSchemaSetMocks('test-tree.yang') + when: 'delete list data method is invoked with list-node json data' + objectUnderTest.deleteListNodeData(dataspaceName, anchorName, '/test-tree/branch') + then: 'the persistence service method is invoked with correct parameters' + 1 * mockCpsDataPersistenceService.deleteListDataNodes(dataspaceName, anchorName, '/test-tree/branch') + and: 'data updated event is sent to notification service' + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName) + } + def setupSchemaSetMocks(String... yangResources) { def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build() mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor |