diff options
Diffstat (limited to 'cps-service')
8 files changed, 115 insertions, 78 deletions
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 31a7517340..e6cb65fa78 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 @@ -56,17 +56,18 @@ public interface CpsDataService { @NonNull String jsonData, OffsetDateTime observedTimestamp); /** - * Persists child data fragment representing list-node (with one or more elements) under existing data node for the + * Persists child data fragment representing one or more list elements under existing data node for the * given anchor and dataspace. * - * @param dataspaceName dataspace name - * @param anchorName anchor name - * @param parentNodeXpath parent node xpath - * @param jsonData json data representing list element + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param parentNodeXpath parent node xpath + * @param jsonData json data representing list element(s) * @param observedTimestamp observedTimestamp */ - void saveListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, - @NonNull String jsonData, OffsetDateTime observedTimestamp); + void saveListElements(@NonNull String dataspaceName, @NonNull String anchorName, + @NonNull String parentNodeXpath, + @NonNull String jsonData, OffsetDateTime observedTimestamp); /** * Retrieves datanode by XPath for given dataspace and anchor. @@ -106,29 +107,28 @@ public interface CpsDataService { @NonNull String jsonData, OffsetDateTime observedTimestamp); /** - * Replaces (if exists) child data fragment representing list-node (with one or more elements) under existing data - * node for the given anchor and dataspace. + * Replaces list content by removing all existing elements and inserting the given new elements as json + * under given parent, anchor and dataspace. * * @param dataspaceName dataspace name * @param anchorName anchor name * @param parentNodeXpath parent node xpath - * @param jsonData json data representing list element + * @param jsonData json data representing the new list elements * @param observedTimestamp observedTimestamp */ - void replaceListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, - @NonNull String jsonData, OffsetDateTime observedTimestamp); + void replaceListContent(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, + @NonNull String jsonData, OffsetDateTime observedTimestamp); /** - * Deletes (if exists) child data fragment representing list-node (with one or more elements) under existing data - * node for the given anchor and dataspace. + * Deletes a list or a list-element under given anchor and dataspace. * * @param dataspaceName dataspace name * @param anchorName anchor name - * @param listNodeXpath list node xpath + * @param listElementXpath list element xpath * @param observedTimestamp observedTimestamp */ - void deleteListNodeData(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String listNodeXpath, - OffsetDateTime observedTimestamp); + void deleteListOrListElement(@NonNull String dataspaceName, @NonNull String anchorName, + @NonNull String listElementXpath, OffsetDateTime observedTimestamp); /** * Updates leaves of DataNode for given dataspace and anchor using xpath, along with the leaves of each Child Data 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 7b3567ed3c..44a17f89dd 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 @@ -64,7 +64,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void saveData(final String dataspaceName, final String anchorName, final String jsonData, final OffsetDateTime observedTimestamp) { - final var dataNode = buildDataNodeFromJson(dataspaceName, anchorName, ROOT_NODE_XPATH, jsonData); + final var dataNode = buildDataNode(dataspaceName, anchorName, ROOT_NODE_XPATH, jsonData); cpsDataPersistenceService.storeDataNode(dataspaceName, anchorName, dataNode); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); } @@ -72,17 +72,18 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void saveData(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - final var dataNode = buildDataNodeFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData); + final var dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, parentNodeXpath, dataNode); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); } @Override - public void saveListNodeData(final String dataspaceName, final String anchorName, + public void saveListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - final Collection<DataNode> dataNodesCollection = - buildDataNodeCollectionFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData); - cpsDataPersistenceService.addListDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodesCollection); + final Collection<DataNode> listElementDataNodeCollection = + buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData); + cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath, + listElementDataNodeCollection); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); } @@ -95,7 +96,7 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void updateNodeLeaves(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - final var dataNode = buildDataNodeFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData); + final var dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService .updateDataLeaves(dataspaceName, anchorName, dataNode.getXpath(), dataNode.getLeaves()); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); @@ -107,7 +108,8 @@ public class CpsDataServiceImpl implements CpsDataService { final String dataNodeUpdatesAsJson, final OffsetDateTime observedTimestamp) { final Collection<DataNode> dataNodeUpdates = - buildDataNodeCollectionFromJson(dataspaceName, anchorName, parentNodeXpath, dataNodeUpdatesAsJson); + buildDataNodes(dataspaceName, anchorName, + parentNodeXpath, dataNodeUpdatesAsJson); for (final DataNode dataNodeUpdate : dataNodeUpdates) { processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate); } @@ -117,30 +119,29 @@ public class CpsDataServiceImpl implements CpsDataService { @Override public void replaceNodeTree(final String dataspaceName, final String anchorName, final String parentNodeXpath, final String jsonData, final OffsetDateTime observedTimestamp) { - final var dataNode = buildDataNodeFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData); + final var dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.replaceDataNodeTree(dataspaceName, anchorName, dataNode); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); } @Override - public void replaceListNodeData(final String dataspaceName, final String anchorName, final String parentNodeXpath, - final String jsonData, final OffsetDateTime observedTimestamp) { - final Collection<DataNode> dataNodes = - buildDataNodeCollectionFromJson(dataspaceName, anchorName, parentNodeXpath, jsonData); - cpsDataPersistenceService.replaceListDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes); + public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath, + final String jsonData, final OffsetDateTime observedTimestamp) { + final Collection<DataNode> newListElements = + buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData); + cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); } @Override - public void deleteListNodeData(final String dataspaceName, final String anchorName, final String listNodeXpath, + public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath, final OffsetDateTime observedTimestamp) { - cpsDataPersistenceService.deleteListDataNodes(dataspaceName, anchorName, listNodeXpath); + cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath); processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); } - - private DataNode buildDataNodeFromJson(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final String jsonData) { + private DataNode buildDataNode(final String dataspaceName, final String anchorName, + final String parentNodeXpath, final String jsonData) { final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); final var schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName()); @@ -157,8 +158,10 @@ public class CpsDataServiceImpl implements CpsDataService { .build(); } - private Collection<DataNode> buildDataNodeCollectionFromJson(final String dataspaceName, final String anchorName, - final String parentNodeXpath, final String jsonData) { + private Collection<DataNode> buildDataNodes(final String dataspaceName, + final String anchorName, + final String parentNodeXpath, + final String jsonData) { final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); final var schemaContext = getSchemaContext(dataspaceName, anchor.getSchemaSetName()); @@ -169,7 +172,7 @@ public class CpsDataServiceImpl implements CpsDataService { .withNormalizedNodeTree(normalizedNode) .buildCollection(); if (dataNodes.isEmpty()) { - throw new DataValidationException("Invalid list data.", "List node is empty."); + throw new DataValidationException("Invalid data.", "No data nodes provided"); } return dataNodes; 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 bf8dd1a073..b8c472f277 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 @@ -55,16 +55,16 @@ public interface CpsDataPersistenceService { @NonNull DataNode dataNode); /** - * Adds list node child elements to a Fragment. + * Adds list child elements to a Fragment. * - * @param dataspaceName dataspace name - * @param anchorName anchor name - * @param parentNodeXpath parent node xpath - * @param dataNodes collection of data nodes representing list node elements + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param parentNodeXpath parent node xpath + * @param listElementsCollection collection of data nodes representing list elements */ - void addListDataNodes(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, - @NonNull Collection<DataNode> dataNodes); + void addListElements(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull String parentNodeXpath, + @NonNull Collection<DataNode> listElementsCollection); /** * Retrieves datanode by XPath for given dataspace and anchor. @@ -101,25 +101,36 @@ public interface CpsDataPersistenceService { void replaceDataNodeTree(@NonNull String dataspaceName, @NonNull String anchorName, @NonNull DataNode dataNode); /** - * Replaces existing list data node content including descendants. + * Replaces list content by removing all existing elements and inserting the given new elements + * under given parent, anchor and dataspace. * * @param dataspaceName dataspace name * @param anchorName anchor name * @param parentNodeXpath parent node xpath - * @param dataNodes collection of data nodes representing list node elements + * @param newListElements collection of data nodes representing the new list content + */ + void replaceListContent(@NonNull String dataspaceName, @NonNull String anchorName, + @NonNull String parentNodeXpath, @NonNull Collection<DataNode> newListElements); + + /** + * Deletes any dataNode, yang container or yang list or yang list element. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @param targetXpath xpath to list or list element (include [@key=value] to delete a single list element) */ - void replaceListDataNodes(@NonNull String dataspaceName, @NonNull String anchorName, - @NonNull String parentNodeXpath, @NonNull Collection<DataNode> dataNodes); + void deleteDataNode(@NonNull String dataspaceName, @NonNull String anchorName, + @NonNull String targetXpath); /** - * Deletes existing list data node content including descendants. + * Deletes existing a single list element or the whole list. * * @param dataspaceName dataspace name * @param anchorName anchor name - * @param listNodeXpath list node xpath + * @param targetXpath xpath to list or list element (include [@key=value] to delete a single list element) */ - void deleteListDataNodes(@NonNull String dataspaceName, @NonNull String anchorName, - @NonNull String listNodeXpath); + void deleteListDataNode(@NonNull String dataspaceName, @NonNull String anchorName, + @NonNull String targetXpath); /** * Get a datanode by cps path. diff --git a/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataNodeNotFoundException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataNodeNotFoundException.java index 848d5692c1..b717a2b183 100755 --- a/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataNodeNotFoundException.java +++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/DataNodeNotFoundException.java @@ -31,6 +31,21 @@ public class DataNodeNotFoundException extends DataValidationException { /** * Constructor. * + * @param dataspaceName the name of the dataspace + * @param anchorName the anchor name + * @param xpath datanode xpath + * @param additionalInformation additional information + */ + public DataNodeNotFoundException(final String dataspaceName, final String anchorName, final String xpath, + final String additionalInformation) { + super("DataNode not found", String + .format("DataNode with xpath %s was not found for anchor %s and dataspace %s, %s.", xpath, + anchorName, dataspaceName, additionalInformation)); + } + + /** + * Constructor. + * * @param dataspaceName the name of the dataspace * @param anchorName the anchor name * @param xpath datanode xpath diff --git a/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java b/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java index 3ee6afb719..71a95f1ca0 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java +++ b/cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java @@ -58,7 +58,7 @@ public class DataMapUtils { return ImmutableMap.<String, Object>builder() .putAll( dataNodes.stream() - .filter(dataNode -> isListNode(dataNode.getXpath())) + .filter(dataNode -> isListElement(dataNode.getXpath())) .collect(groupingBy( dataNode -> getNodeIdentifier(dataNode.getXpath()), mapping(DataMapUtils::toDataMap, toUnmodifiableList()) @@ -86,10 +86,10 @@ public class DataMapUtils { } private static boolean isContainerNode(final String xpath) { - return !isListNode(xpath); + return !isListElement(xpath); } - private static boolean isListNode(final String xpath) { + private static boolean isListElement(final String xpath) { return xpath.endsWith("]"); } } 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 6a0a4649a6..2bd44824a6 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 @@ -22,7 +22,6 @@ package org.onap.cps.api.impl -import java.time.OffsetDateTime import org.onap.cps.TestUtils import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsModuleService @@ -36,6 +35,8 @@ import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import spock.lang.Specification +import java.time.OffsetDateTime + class CpsDataServiceImplSpec extends Specification { def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) def mockCpsAdminService = Mock(CpsAdminService) @@ -84,14 +85,14 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) } - def 'Saving list-node data fragment under existing node.'() { + def 'Saving list element data fragment under existing node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') - when: 'save data method is invoked with list-node json data' + when: 'save data method is invoked with list element json data' def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}' - objectUnderTest.saveListNodeData(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) + objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) then: 'the persistence service method is invoked with correct parameters' - 1 * mockCpsDataPersistenceService.addListDataNodes(dataspaceName, anchorName, '/test-tree', + 1 * mockCpsDataPersistenceService.addListElements(dataspaceName, anchorName, '/test-tree', { dataNodeCollection -> { assert dataNodeCollection.size() == 2 @@ -104,12 +105,12 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) } - def 'Saving empty list-node data fragment.'() { + def 'Saving empty list element data fragment.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') - when: 'save data method is invoked with empty list-node data fragment' + when: 'save data method is invoked with an empty list' def jsonData = '{"branch": []}' - objectUnderTest.saveListNodeData(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) + objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) then: 'invalid data exception is thrown' thrown(DataValidationException) } @@ -185,14 +186,14 @@ class CpsDataServiceImplSpec extends Specification { 'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']' } - def 'Replace list-node data fragment under existing node.'() { + def 'Replace list content data fragment under parent node.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') - when: 'replace list data method is invoked with list-node json data' + when: 'replace list data method is invoked with list element json data' def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}' - objectUnderTest.replaceListNodeData(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) + objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) then: 'the persistence service method is invoked with correct parameters' - 1 * mockCpsDataPersistenceService.replaceListDataNodes(dataspaceName, anchorName, '/test-tree', + 1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree', { dataNodeCollection -> { assert dataNodeCollection.size() == 2 @@ -205,23 +206,23 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) } - def 'Replace with empty list-node data fragment.'() { + def 'Replace whole list content with empty list element.'() { given: 'schema set for given anchor and dataspace references test-tree model' setupSchemaSetMocks('test-tree.yang') - when: 'replace list data method is invoked with empty list-node data fragment' + when: 'replace list data method is invoked with empty list' def jsonData = '{"branch": []}' - objectUnderTest.replaceListNodeData(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) + objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp) then: 'invalid data exception is thrown' thrown(DataValidationException) } - def 'Delete list-node data fragment under existing node.'() { + def 'Delete list element 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', observedTimestamp) + when: 'delete list data method is invoked with list element json data' + objectUnderTest.deleteListOrListElement(dataspaceName, anchorName, '/test-tree/branch', observedTimestamp) then: 'the persistence service method is invoked with correct parameters' - 1 * mockCpsDataPersistenceService.deleteListDataNodes(dataspaceName, anchorName, '/test-tree/branch') + 1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch') and: 'data updated event is sent to notification service' 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) } diff --git a/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy index 2751d55075..7af1ed41c7 100644 --- a/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/model/DataNodeBuilderSpec.groovy @@ -140,9 +140,9 @@ class DataNodeBuilderSpec extends Specification { given: 'a schema context for expected model' def yangResourceNameToContent = TestUtils.getYangResourcesAsMap('test-tree.yang') def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent) getSchemaContext() - and: 'parent node xpath referencing parent of list-node element' + and: 'parent node xpath referencing parent of list element' def parentNodeXpath = "/test-tree" - and: 'the json data fragment (list-node element) parsed into normalized node object' + and: 'the json data fragment (list element) parsed into normalized node object' def normalizedNode = YangUtils.parseJsonData(jsonData, schemaContext, parentNodeXpath) when: 'the normalized node is converted to a data node collection' def result = new DataNodeBuilder().withNormalizedNodeTree(normalizedNode) diff --git a/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy index 8a0e0c82e7..4243c18c24 100755 --- a/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/spi/exceptions/CpsExceptionsSpec.groovy @@ -30,6 +30,7 @@ class CpsExceptionsSpec extends Specification { def providedMessage = 'some message' def providedDetails = 'some details' def xpath = 'some xpath' + def additionalInformation = 'some information' def 'Creating an exception that the Anchor already exist.'() { given: 'an exception dat the Anchor already exist is created' @@ -133,6 +134,12 @@ class CpsExceptionsSpec extends Specification { == "DataNode not found for anchor ${anchorName} and dataspace ${dataspaceName}." } + def 'Creating a exception that a datanode with a specified xpath with additional information does not exist.'() { + expect: 'the exception details contains the correct message with dataspace name and anchor.' + (new DataNodeNotFoundException(dataspaceName, anchorName, xpath, additionalInformation)).details + == "DataNode with xpath ${xpath} was not found for anchor ${anchorName} and dataspace ${dataspaceName}, ${additionalInformation}." + } + def 'Creating a exception that a dataspace already exists.'() { expect: 'the exception details contains the correct message with dataspace name.' (AlreadyDefinedException.forDataspace(dataspaceName, rootCause)).details |