CM SUBSCRIPTION: Add delete use case #2 (DMI-NCMP) 03/138103/2
authoremaclee <lee.anjella.macabuhay@est.tech>
Thu, 30 May 2024 05:48:23 +0000 (06:48 +0100)
committeremaclee <lee.anjella.macabuhay@est.tech>
Fri, 31 May 2024 10:13:39 +0000 (11:13 +0100)
Issue-ID: CPS-2241
Change-Id: I218c46e9c16cc78025450494f5019bde1f351a40
Signed-off-by: emaclee <lee.anjella.macabuhay@est.tech>
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy

index b5370bf..368e27a 100644 (file)
@@ -123,12 +123,12 @@ public class DmiCmNotificationSubscriptionCacheHandler {
      *
      */
     public void updateDmiCmNotificationSubscriptionStatusPerDmi(final String subscriptionId,
-            final String dmiServiceName, final CmNotificationSubscriptionStatus status) {
+                                                                final String dmiServiceName,
+                                                                final CmNotificationSubscriptionStatus status) {
         final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi =
                 cmNotificationSubscriptionCache.get(subscriptionId);
         dmiCmNotificationSubscriptionDetailsPerDmi.get(dmiServiceName).setCmNotificationSubscriptionStatus(status);
         cmNotificationSubscriptionCache.put(subscriptionId, dmiCmNotificationSubscriptionDetailsPerDmi);
-
     }
 
     /**
@@ -157,6 +157,32 @@ public class DmiCmNotificationSubscriptionCacheHandler {
         }
     }
 
+    /**
+     *  Remove subscription from database per DMI service name.
+     *
+     * @param subscriptionId    String of subscription id
+     * @param dmiServiceName    String of dmiServiceName
+     *
+     */
+    public void removeFromDatabasePerDmi(final String subscriptionId, final String dmiServiceName) {
+        final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicateList =
+                cmNotificationSubscriptionCache.get(subscriptionId).get(dmiServiceName)
+                        .getDmiCmNotificationSubscriptionPredicates();
+        for (final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate:
+                dmiCmNotificationSubscriptionPredicateList) {
+            final DatastoreType datastoreType = dmiCmNotificationSubscriptionPredicate.getDatastoreType();
+            final Set<String> cmHandles = dmiCmNotificationSubscriptionPredicate.getTargetCmHandleIds();
+            final Set<String> xpaths = dmiCmNotificationSubscriptionPredicate.getXpaths();
+
+            for (final String cmHandle: cmHandles) {
+                for (final String xpath: xpaths) {
+                    cmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(datastoreType,
+                            cmHandle, xpath, subscriptionId);
+                }
+            }
+        }
+    }
+
     private void updateDmiCmNotificationSubscriptionDetailsPerDmi(
             final String dmiServiceName,
             final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate,
index 7615e7e..051949c 100644 (file)
@@ -65,26 +65,33 @@ public class CmNotificationSubscriptionDmiOutEventConsumer {
         final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent =
                 toTargetEvent(cloudEvent, CmNotificationSubscriptionDmiOutEvent.class);
         final String correlationId = String.valueOf(cloudEvent.getExtension("correlationid"));
-        if ("subscriptionCreateResponse".equals(cloudEvent.getType()) && cmNotificationSubscriptionDmiOutEvent != null
-                    && correlationId != null) {
-            handleCmSubscriptionCreate(correlationId, cmNotificationSubscriptionDmiOutEvent);
+        if (cmNotificationSubscriptionDmiOutEvent != null && correlationId != null) {
+            final String eventType = cloudEvent.getType();
+            handleCmSubscriptionDmiOutEvent(correlationId, eventType, cmNotificationSubscriptionDmiOutEvent);
         }
     }
 
-    private void handleCmSubscriptionCreate(final String correlationId,
-            final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent) {
+    private void handleCmSubscriptionDmiOutEvent(final String correlationId,
+                                                 final String eventType,
+                                                 final CmNotificationSubscriptionDmiOutEvent
+                                                         cmNotificationSubscriptionDmiOutEvent) {
         final String subscriptionId = correlationId.split(CM_DATA_SUBSCRIPTION_CORRELATION_ID_SEPARATOR)[0];
         final String dmiPluginName = correlationId.split(CM_DATA_SUBSCRIPTION_CORRELATION_ID_SEPARATOR)[1];
 
         if (checkStatusCodeAndMessage(CM_DATA_SUBSCRIPTION_ACCEPTED, cmNotificationSubscriptionDmiOutEvent.getData())) {
             handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmNotificationSubscriptionStatus.ACCEPTED);
-            dmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
-            handleEventsStatusPerDmi(subscriptionId);
+            if (eventType.equals("subscriptionCreateResponse")) {
+                dmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
+            }
+            if (eventType.equals("subscriptionDeleteResponse")) {
+                dmiCmNotificationSubscriptionCacheHandler.removeFromDatabasePerDmi(subscriptionId, dmiPluginName);
+            }
+            handleEventsStatusPerDmi(subscriptionId, eventType);
         }
 
         if (checkStatusCodeAndMessage(CM_DATA_SUBSCRIPTION_REJECTED, cmNotificationSubscriptionDmiOutEvent.getData())) {
             handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmNotificationSubscriptionStatus.REJECTED);
-            handleEventsStatusPerDmi(subscriptionId);
+            handleEventsStatusPerDmi(subscriptionId, eventType);
         }
 
         log.info("Cm Subscription with id : {} handled by the dmi-plugin : {} has the status : {}", subscriptionId,
@@ -92,25 +99,25 @@ public class CmNotificationSubscriptionDmiOutEventConsumer {
     }
 
     private void handleCacheStatusPerDmi(final String subscriptionId, final String dmiPluginName,
-            final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus) {
+                                         final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus) {
         dmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi(subscriptionId,
                 dmiPluginName, cmNotificationSubscriptionStatus);
     }
 
-    private void handleEventsStatusPerDmi(final String subscriptionId) {
+    private void handleEventsStatusPerDmi(final String subscriptionId, final String eventType) {
         final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi =
                 dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId);
         final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent =
                 cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEvent(subscriptionId,
                         dmiCmNotificationSubscriptionDetailsPerDmi);
         cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId,
-                "subscriptionCreateResponse", cmNotificationSubscriptionNcmpOutEvent, false);
+                eventType, cmNotificationSubscriptionNcmpOutEvent, false);
     }
 
     private boolean checkStatusCodeAndMessage(final NcmpResponseStatus ncmpResponseStatus,
-            final Data cmNotificationSubscriptionDmiOutData) {
+                                              final Data cmNotificationSubscriptionDmiOutData) {
         return ncmpResponseStatus.getCode().equals(cmNotificationSubscriptionDmiOutData.getStatusCode())
-                       && ncmpResponseStatus.getMessage()
-                                  .equals(cmNotificationSubscriptionDmiOutData.getStatusMessage());
+                && ncmpResponseStatus.getMessage()
+                .equals(cmNotificationSubscriptionDmiOutData.getStatusMessage());
     }
-}
+}
\ No newline at end of file
index 0adf225..f87d012 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.api.impl.events.cmsubscription.service;
 
+import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY;
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
 
 import java.io.Serializable;
@@ -45,10 +46,14 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
 
     private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions";
     private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE = """
-            /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters
+            /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']
             """.trim();
+    private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE =
+            CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filters";
+
     private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH =
-            CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']";
+            CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']";
+
 
     private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID = """
             //filter/subscriptionIds[text()='%s']
@@ -106,12 +111,11 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
         final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType,
                 cmHandleId, xpath);
         if (subscriptionIds.remove(subscriptionId)) {
-            if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)) {
-                saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds);
-                log.info("There are subscribers left for the following cps path {} :",
-                        CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
-                                datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)));
-            } else {
+            saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds);
+            log.info("There are subscribers left for the following cps path {} :",
+                    CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
+                            datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)));
+            if (subscriptionIds.isEmpty()) {
                 log.info("No subscribers left for the following cps path {} :",
                         CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
                                 datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)));
@@ -126,11 +130,25 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
                 CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
                         datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)),
                 OffsetDateTime.now());
+        final Collection<DataNode> existingFiltersForCmHandle =
+                cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
+                        CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
+                                datastoreType.getDatastoreName(), cmHandleId),
+                        DIRECT_CHILDREN_ONLY).iterator().next().getChildDataNodes();
+        if (existingFiltersForCmHandle.isEmpty()) {
+            removeCmHandleFromDatastore(datastoreType.getDatastoreName(), cmHandleId);
+        }
+    }
+
+    private void removeCmHandleFromDatastore(final String datastoreName, final String cmHandleId) {
+        cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+                        datastoreName, cmHandleId), OffsetDateTime.now());
     }
 
     private boolean isFirstSubscriptionForCmHandle(final DatastoreType datastoreType, final String cmHandleId) {
         return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
-                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
                         datastoreType.getDatastoreName(), cmHandleId),
                 OMIT_DESCENDANTS).isEmpty();
     }
@@ -150,7 +168,7 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
                     OffsetDateTime.now(), ContentType.JSON);
         } else {
             cpsDataService.saveListElements(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
-                    CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+                    CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
                             datastoreType.getDatastoreName(), cmHandleId),
                     subscriptionDetailsAsJson, OffsetDateTime.now());
         }
@@ -161,7 +179,7 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
                                          final  Collection<String> subscriptionIds) {
         final String subscriptionDetailsAsJson = getSubscriptionDetailsAsJson(xpath, subscriptionIds);
         cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
-                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
                         datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson, OffsetDateTime.now());
     }
 
index 43568be..8d7a4b9 100644 (file)
@@ -133,10 +133,10 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
             assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].targetCmHandleIds == ['ch4'].toSet()
         and: 'the list of xpath for each is correct'
             assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[0].xpaths
-                    && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].xpaths == ['/x1/y1','x2/y2'].toSet()
+                && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].xpaths == ['/x1/y1','x2/y2'].toSet()
 
             assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[1].xpaths
-                    && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].xpaths == ['/x3/y3','x4/y4'].toSet()
+                && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].xpaths == ['/x3/y3','x4/y4'].toSet()
     }
 
     def 'Get map for cm handle IDs by DMI service name'() {
@@ -164,7 +164,7 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
     }
 
     def 'Persist Cache into database per dmi'() {
-        given: 'populate cache'
+        given: 'populated cache'
             def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates()
             def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()
             objectUnderTest.add(subscriptionId, predicates)
@@ -174,15 +174,26 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
             4 * mockCmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(_,_,_,subscriptionId)
     }
 
+    def 'Remove subscription from database per dmi'() {
+        given: 'populated cache'
+            def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates()
+            def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()
+            objectUnderTest.add(subscriptionId, predicates)
+        when: 'subscription is persisted in database'
+            objectUnderTest.removeFromDatabasePerDmi(subscriptionId,'dmi-1')
+        then: 'persistence service is called the correct number of times per dmi'
+            4 * mockCmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(_,_,_,subscriptionId)
+    }
+
     def setUpTestEvent(){
         def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
         def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
         def testCloudEventSent = CloudEventBuilder.v1()
-                .withData(objectMapper.writeValueAsBytes(testEventSent))
-                .withId('subscriptionCreated')
-                .withType('subscriptionCreated')
-                .withSource(URI.create('some-resource'))
-                .withExtension('correlationid', 'test-cmhandle1').build()
+            .withData(objectMapper.writeValueAsBytes(testEventSent))
+            .withId('subscriptionCreated')
+            .withType('subscriptionCreated')
+            .withSource(URI.create('some-resource'))
+            .withExtension('correlationid', 'test-cmhandle1').build()
         def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
         def cloudEvent = consumerRecord.value()
 
@@ -191,10 +202,10 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
 
     def initialiseMockInventoryPersistenceResponses(){
         mockInventoryPersistence.getYangModelCmHandles(['ch1','ch2'])
-                >> [yangModelCmHandle1, yangModelCmHandle2]
+            >> [yangModelCmHandle1, yangModelCmHandle2]
 
         mockInventoryPersistence.getYangModelCmHandles(['ch3','ch4'])
-                >> [yangModelCmHandle3, yangModelCmHandle4]
+            >> [yangModelCmHandle3, yangModelCmHandle4]
     }
 
 }
\ No newline at end of file
index b51ecb0..60cec00 100644 (file)
@@ -23,9 +23,8 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.service
 import org.onap.cps.utils.ContentType
 
 import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID;
-import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE;
+import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE;
 import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH;
-
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsQueryService
 import org.onap.cps.ncmp.api.impl.operations.DatastoreType
@@ -48,7 +47,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
             def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']";
         and: 'datanodes optionally returned'
             1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
-                    cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode
+                cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode
         when: 'we check for an ongoing cm subscription'
             def response = objectUnderTest.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/cps/path')
         then: 'we get expected response'
@@ -64,7 +63,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
             def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted('some-sub')
         and: 'relevant datanodes are returned'
             1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >>
-                    dataNodes
+                dataNodes
         when: 'a subscription ID is tested for uniqueness'
             def result = objectUnderTest.isUniqueSubscriptionId('some-sub')
         then: 'result is as expected'
@@ -79,7 +78,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
         given: 'a valid cm subscription path query'
             def cpsPathQuery =CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y')
         and: 'a dataNode exists for the given cps path query'
-             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
+            mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
                 cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
         when: 'the method to add/update cm notification subscription is called'
             objectUnderTest.addCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1','/x/y', 'newSubId')
@@ -94,12 +93,12 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
     def 'Add new cm notification subscription for #datastoreType'() {
         given: 'a valid cm subscription path query'
             def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y')
-            def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
+            def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
         and: 'a parent node xpath for the cm subscription path above'
             def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles'
         and: 'a datanode does not exist for cm subscription path query'
             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
-               cmSubscriptionCpsPathQuery,
+                cmSubscriptionCpsPathQuery,
                 FetchDescendantsOption.OMIT_DESCENDANTS) >> []
         and: 'a datanode does not exist for the given cm handle subscription path query'
             mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
@@ -124,7 +123,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
     def 'Add new cm notification subscription when xpath does not exist for existing subscription cm handle'() {
         given: 'a valid cm subscription path query'
             def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y')
-            def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
+            def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
         and: 'a parent node xpath for given cm handle for subscription path above'
             def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles/cm-handle[@id=\'%s\']/filters'
         and: 'a datanode does not exist for cm subscription path query'
@@ -160,16 +159,33 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
                 objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _)
     }
 
-    def 'Removing last ongoing subscription for datastore, cmhandle and xpath'(){
+    def 'Removing last ongoing subscription for datastore and cmhandle and xpath'(){
         given: 'a subscription exists when queried but has only 1 subscriber'
-            def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y')
-            mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
-                cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
+            mockCpsQueryService.queryDataNodes(
+                'NCMP-Admin',
+                'cm-data-subscriptions',
+                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y'),
+                FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
+        and: 'the #scenario'
+            mockCpsQueryService.queryDataNodes(
+                'NCMP-Admin',
+                'cm-data-subscriptions',
+                CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted('ncmp-datastore:passthrough-running', 'ch-1'),
+                FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [new DataNode(childDataNodes: listOfChildNodes)]
         when: 'that last ongoing subscription is removed'
             objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1')
         then: 'the subscription with empty subscriber list is removed'
             1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions',
                 '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters/filter[@xpath=\'/x/y\']',
                 _)
+        and: 'method call to delete the cm handle is called the correct number of times'
+            numberOfCallsToDeleteCmHandle * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions',
+                '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']',
+                _)
+        where:
+            scenario                                                            | listOfChildNodes  || numberOfCallsToDeleteCmHandle
+            'cm handle in same datastore is used for other subscriptions'       | [new DataNode()]  || 0
+            'cm handle in same datastore is NOT used for other subscriptions'   | []                || 1
     }
+
 }
\ No newline at end of file