Move persistence methods from NCMPproperty handler 82/129782/4
authoremaclee <lee.anjella.macabuhay@est.tech>
Mon, 4 Jul 2022 21:49:41 +0000 (22:49 +0100)
committeremaclee <lee.anjella.macabuhay@est.tech>
Tue, 5 Jul 2022 16:33:52 +0000 (17:33 +0100)
Issue-ID: CPS-1117
Signed-off-by: emaclee <lee.anjella.macabuhay@est.tech>
Change-Id: If6a0c620970a2a34a601267c6610f85e4bc07f60

.gitignore
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/constants/DmiRegistryConstants.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy

index 624bc1a..a377967 100755 (executable)
@@ -4,6 +4,7 @@
 *.log
 *.log.zip
 
+cps-ncmp-rest-stub/dependency-reduced-pom.xml
 cps-application/archunit_store
 cps-ri/src/main/resources/changelog/db/changes/data/dmi/generated-csv/generated_yang_resource_*
 target/
index aae2f20..d9aeaf2 100644 (file)
@@ -23,10 +23,6 @@ package org.onap.cps.ncmp.api.impl;
 
 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY;
 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_PARENT;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
 
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
@@ -39,11 +35,10 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsDataService;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.model.DataNode;
@@ -58,9 +53,7 @@ import org.springframework.stereotype.Service;
 @SuppressWarnings("squid:S5852")
 public class NetworkCmProxyDataServicePropertyHandler {
 
-    private static final String CM_HANDLE_XPATH_TEMPLATE = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='%s']";
-
-    private final CpsDataService cpsDataService;
+    private final InventoryPersistence inventoryPersistence;
 
     /**
      * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes.
@@ -72,31 +65,28 @@ public class NetworkCmProxyDataServicePropertyHandler {
         final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
-            final String cmHandle = ncmpServiceCmHandle.getCmHandleId();
+            final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
             try {
-                CpsValidator.validateNameCharacters(cmHandle);
-                final String cmHandleXpath = String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandle);
-                final DataNode existingCmHandleDataNode =
-                        cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandleXpath,
-                                FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+                CpsValidator.validateNameCharacters(cmHandleId);
+                final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId);
                 processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle);
-                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
+                cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
             } catch (final DataNodeNotFoundException e) {
                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
-                    cmHandle, e.getMessage());
+                    cmHandleId, e.getMessage());
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
-                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+                    .createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
             } catch (final DataValidationException e) {
                 log.error("Unable to update cm handle : {}, caused by : {}",
-                    cmHandle, e.getMessage());
+                    cmHandleId, e.getMessage());
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(cmHandle,
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandleId,
                         RegistrationError.CM_HANDLE_INVALID_ID));
             } catch (final Exception exception) {
                 log.error("Unable to update cmHandle : {} , caused by : {}",
-                    cmHandle, exception.getMessage());
+                    cmHandleId, exception.getMessage());
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception));
             }
         }
         return cmHandleRegistrationResponses;
@@ -120,8 +110,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
         if (replacementPropertyDataNodes.isEmpty()) {
             removeAllProperties(existingCmHandleDataNode, propertyType);
         } else {
-            cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                    existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes, NO_TIMESTAMP);
+            inventoryPersistence.replaceListContent(existingCmHandleDataNode.getXpath(), replacementPropertyDataNodes);
         }
     }
 
@@ -130,8 +119,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
             final Matcher matcher = propertyType.propertyXpathPattern.matcher(dataNode.getXpath());
             if (matcher.find()) {
                 log.info("Deleting dataNode with xpath : [{}]", dataNode.getXpath());
-                cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNode.getXpath(),
-                        NO_TIMESTAMP);
+                inventoryPersistence.deleteDataNode(dataNode.getXpath());
             }
         });
     }
index c29c725..a133cfb 100644 (file)
@@ -32,11 +32,5 @@ public final class DmiRegistryConstants {
 
     public static final String NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME = "NFP-Operational";
 
-    public static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
-
-    public static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
-
-    public static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry";
-
     public static final OffsetDateTime NO_TIMESTAMP = null;
 }
index 19cf225..af01fb4 100644 (file)
@@ -58,7 +58,7 @@ public class InventoryPersistence {
 
     private static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry";
 
-    private String xpathCmHandle = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
+    private static final String CM_HANDLE_XPATH_TEMPLATE = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
 
     private static final String ANCESTOR_CM_HANDLES = "\"]/ancestor::cm-handles";
 
@@ -80,7 +80,7 @@ public class InventoryPersistence {
      */
     public CompositeState getCmHandleState(final String cmHandleId) {
         final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            String.format(xpathCmHandle, cmHandleId) + "/state",
+            String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state",
             FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build();
     }
@@ -95,7 +95,7 @@ public class InventoryPersistence {
         final String cmHandleJsonData = String.format("{\"state\":%s}",
             jsonObjectMapper.asJsonString(compositeState));
         cpsDataService.replaceNodeTree(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            String.format(xpathCmHandle, cmHandleId),
+            String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
             cmHandleJsonData, OffsetDateTime.now());
     }
 
@@ -241,14 +241,11 @@ public class InventoryPersistence {
     /**
      * Get data node of given cm handle.
      *
-     * @param cmHandle cm handle
+     * @param cmHandleId cmHandle ID
      * @return data node
      */
-    private DataNode getCmHandleDataNode(final String cmHandle) {
-        return cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
-            NCMP_DMI_REGISTRY_ANCHOR,
-            String.format(xpathCmHandle, cmHandle),
-            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+    public DataNode getCmHandleDataNode(final String cmHandleId) {
+        return this.getDataNode(String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId));
     }
 
     /**
@@ -269,4 +266,25 @@ public class InventoryPersistence {
     public Collection<Anchor> getAnchors() {
         return cpsAdminPersistenceService.getAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
     }
+
+    /**
+     * Replaces list content by removing all existing elements and inserting the given new elements as data nodes.
+     *
+     * @param parentNodeXpath   parent node xpath
+     * @param dataNodes         datanodes representing the updated data
+     */
+    public void replaceListContent(final String parentNodeXpath, final Collection<DataNode> dataNodes) {
+        cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                parentNodeXpath, dataNodes, NO_TIMESTAMP);
+    }
+
+    /**
+     * Deletes data node for given anchor and dataspace.
+     *
+     * @param dataNodeXpath data node xpath
+     */
+    public void deleteDataNode(final String dataNodeXpath) {
+        cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNodeXpath,
+                NO_TIMESTAMP);
+    }
 }
\ No newline at end of file
index 854c568..7cf572d 100644 (file)
@@ -22,8 +22,6 @@ package org.onap.cps.ncmp.api.impl
 
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
-import org.onap.cps.spi.CpsAdminPersistenceService
-import org.onap.cps.spi.CpsDataPersistenceService
 import org.onap.cps.spi.model.Anchor
 import org.onap.cps.spi.model.CmHandleQueryServiceParameters
 import org.onap.cps.spi.model.ConditionProperties
index 3efc9c4..64461fa 100644 (file)
@@ -21,6 +21,7 @@
 
 package org.onap.cps.ncmp.api.impl
 
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
 import org.onap.cps.spi.exceptions.DataValidationException
 
 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
@@ -28,9 +29,7 @@ import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Registra
 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
 import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 
-import org.onap.cps.api.CpsDataService
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
@@ -38,14 +37,11 @@ import spock.lang.Specification
 
 class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
-    def mockCpsDataService = Mock(CpsDataService)
+    def mockInventoryPersistence = Mock(InventoryPersistence)
 
-    def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockCpsDataService)
-    def dataspaceName = 'NCMP-Admin'
-    def anchorName = 'ncmp-dmi-registry'
+    def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence)
     def static cmHandleId = 'myHandle1'
     def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']"
-    def noTimeStamp = null
 
     def static propertyDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp1']").withLeaves(['name': 'additionalProp1', 'value': 'additionalValue1']).build(),
                                     new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(),
@@ -55,16 +51,16 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update CM Handle Public Properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode
         and: 'an update cm handle request with public properties updates'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)]
         when: 'update data node leaves is called with the update request'
             objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'the replace list method is called with correct params'
-            1 * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) >> { args ->
+            1 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) >> { args ->
                 {
-                    assert args[3].leaves.size() == expectedPropertiesAfterUpdate.size()
-                    assert args[3].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
+                    assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size()
+                    assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
                 }
             }
         where: 'following public properties updates are made'
@@ -77,16 +73,16 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update DMI Properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode
         and: 'an update cm handle request with DMI properties updates'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)]
         when: 'update data node leaves is called with the update request'
             objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'replace list method should is called with correct params'
-            expectedCallsToReplaceMethod * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp) >> { args ->
+            expectedCallsToReplaceMethod * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args ->
                 {
-                    assert args[3].leaves.size() == expectedPropertiesAfterUpdate.size()
-                    assert args[3].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
+                    assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size()
+                    assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
                 }
             }
         where: 'following DMI properties updates are made'
@@ -101,17 +97,17 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
     def 'Update CM Handle Properties, remove all properties: #scenario'() {
         given: 'the CPS service return a CM handle'
             def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: originalPropertyDataNodes)
-            mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNode
         and: 'an update cm handle request that removes all public properties(existing and non-existing)'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])]
         when: 'update data node leaves is called with the update request'
             objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'the replace list method is not called'
-            0 * mockCpsDataService.replaceListContent(*_)
+            0 * mockInventoryPersistence.replaceListContent(*_)
         then: 'delete data node will be called for any existing property'
-            expectedCallsToDeleteDataNode * mockCpsDataService.deleteDataNode(dataspaceName, anchorName, _, noTimeStamp) >> { arg ->
+            expectedCallsToDeleteDataNode * mockInventoryPersistence.deleteDataNode(_) >> { arg ->
                 {
-                    assert arg[2].contains("@name='publicProp")
+                    assert arg[0].contains("@name='publicProp")
                 }
             }
         where: 'following public properties updates are made'
@@ -124,7 +120,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
         given: 'cm handles request'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])]
         and: 'data node cannot be found'
-            mockCpsDataService.getDataNode(*_) >> { throw exception }
+            mockInventoryPersistence.getCmHandleDataNode(*_) >> { throw exception }
         when: 'update data node leaves is called using correct parameters'
             def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'one failed registration response'
@@ -149,7 +145,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                                          new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
                                          new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])]
         and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle'
-            mockCpsDataService.getDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode
+            mockInventoryPersistence.getCmHandleDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode
         when: 'update data node leaves is called using correct parameters'
             def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'response has 3 values'
@@ -171,7 +167,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                 assert it.errorText == "cm-handle does not exist"
             }
         then: 'the replace list method is called twice'
-            2 * mockCpsDataService.replaceListContent(dataspaceName, anchorName, cmHandleXpath, _, noTimeStamp)
+            2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_)
     }
 
     def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
index 9b50c5a..50494c0 100644 (file)
@@ -41,6 +41,7 @@ import java.time.OffsetDateTime
 import java.time.ZoneOffset
 import java.time.format.DateTimeFormatter
 
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 
@@ -85,7 +86,7 @@ class InventoryPersistenceSpec extends Specification {
     def "Retrieve CmHandle using datanode with #scenario."() {
         given: 'the cps data service returns a data node from the DMI registry'
             def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
-            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
         when: 'retrieving the yang modelled cm handle'
             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
         then: 'the result has the correct id and service names'
@@ -119,7 +120,7 @@ class InventoryPersistenceSpec extends Specification {
     def "Handling missing service names as null CPS-1043."() {
         given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
             def dataNode = new DataNode(childDataNodes:[], leaves: [:])
-            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockCpsDataPersistenceService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
         when: 'retrieving the yang modelled cm handle'
             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
         then: 'the service names ae returned as null'
@@ -161,7 +162,7 @@ class InventoryPersistenceSpec extends Specification {
             def cmHandleState = CmHandleState.ADVISED
         and: 'cps data service returns a list of data nodes'
             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+                    '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
         when: 'get cm handles by state is invoked'
             def result = objectUnderTest.getCmHandlesByState(cmHandleState)
         then: 'the returned result is a list of data nodes returned by cps data service'
@@ -173,7 +174,7 @@ class InventoryPersistenceSpec extends Specification {
             def cmHandleState = CmHandleState.READY
         and: 'cps data service returns a list of data nodes'
             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+                    '//cm-handles[@id=\'some-cm-handle\']/state[@cm-handle-state="'+ 'READY'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
         when: 'get cm handles by state and id is invoked'
             def result = objectUnderTest.getCmHandlesByIdAndState(cmHandleId, cmHandleState)
         then: 'the returned result is a list of data nodes returned by cps data service'
@@ -185,7 +186,7 @@ class InventoryPersistenceSpec extends Specification {
             def cmHandleState = CmHandleState.READY
         and: 'cps data service returns a list of data nodes'
             mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
+                    '//state/datastores/operational[@sync-state="'+'UNSYNCHRONIZED'+'"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> sampleDataNodes
         when: 'get cm handles by operational sync state as UNSYNCHRONIZED is invoked'
             def result = objectUnderTest.getCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED)
         then: 'the returned result is a list of data nodes returned by cps data service'
@@ -270,6 +271,15 @@ class InventoryPersistenceSpec extends Specification {
             1 * mockCpsDataPersistenceService.getDataNode('NCMP-Admin','ncmp-dmi-registry','sample xPath', INCLUDE_ALL_DESCENDANTS)
     }
 
+    def 'Get cmHandle data node'() {
+        given: 'expected xPath to get cmHandle data node'
+            def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']';
+        when: 'the method to get data nodes is called'
+            objectUnderTest.getCmHandleDataNode('sample cmHandleId')
+        then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath'
+            1 * mockCpsDataPersistenceService.getDataNode('NCMP-Admin','ncmp-dmi-registry',expectedXPath, INCLUDE_ALL_DESCENDANTS)
+    }
+
     def 'Query anchors'() {
         when: 'the method to query anchors is called'
             objectUnderTest.queryAnchors(['sample-module-name'])
@@ -279,8 +289,25 @@ class InventoryPersistenceSpec extends Specification {
 
     def 'Get anchors'() {
         when: 'the method to get anchors with no parameters is called'
-          objectUnderTest.getAnchors()
+            objectUnderTest.getAnchors()
         then: 'the admin persistence service method to query anchors is invoked once with a specific dataspace name'
             1 * mockCpsAdminPersistenceService.getAnchors('NFP-Operational')
     }
+
+    def 'Replace list content'() {
+        when: 'replace list content method is called with xpath and data nodes collection'
+            objectUnderTest.replaceListContent('sample xpath', [new DataNode()])
+        then: 'the cps data service method to replace list content is invoked once with same parameters'
+            1 * mockCpsDataService.replaceListContent('NCMP-Admin', 'ncmp-dmi-registry',
+                    'sample xpath', [new DataNode()], NO_TIMESTAMP);
+    }
+
+    def 'Delete data node via xPath'() {
+        when: 'Delete data node method is called with xpath as parameter'
+            objectUnderTest.deleteDataNode('sample dataNode xpath')
+        then: 'the cps data service method to delete data node is invoked once with the same xPath'
+            1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'ncmp-dmi-registry',
+                    'sample dataNode xpath', NO_TIMESTAMP);
+    }
+
 }