Add support for delete data-node event 08/126908/5
authorRenu Kumari <renu.kumari@bell.ca>
Mon, 31 Jan 2022 19:01:42 +0000 (14:01 -0500)
committerRenu Kumari <renu.kumari@bell.ca>
Mon, 7 Feb 2022 13:48:25 +0000 (13:48 +0000)
- Changed process event input to address delete
- Send null data in the event if the operation is DELETE

Issue-ID: CPS-791
Signed-off-by: Renu Kumari <renu.kumari@bell.ca>
Change-Id: If851f7132e94bcbcaf4324d07a2a00c90d1882b7

cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java
cps-service/src/main/java/org/onap/cps/notification/NotificationService.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy
cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy

index e292bbe..af06e5f 100755 (executable)
@@ -33,6 +33,7 @@ import org.onap.cps.notification.Operation;
 import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.DataValidationException;
+import org.onap.cps.spi.model.Anchor;
 import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.DataNodeBuilder;
 import org.onap.cps.utils.YangUtils;
@@ -134,7 +135,9 @@ public class CpsDataServiceImpl implements CpsDataService {
     @Override
     public void deleteDataNodes(final String dataspaceName, final String anchorName,
         final OffsetDateTime observedTimestamp) {
+        final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
         cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
+        processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, Operation.DELETE, observedTimestamp);
     }
 
     @Override
@@ -185,9 +188,16 @@ public class CpsDataServiceImpl implements CpsDataService {
     private void processDataUpdatedEventAsync(final String dataspaceName, final String anchorName,
                                               final OffsetDateTime observedTimestamp, final String xpath,
                                               final Operation operation) {
+        final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+        this.processDataUpdatedEventAsync(anchor, xpath, operation, observedTimestamp);
+    }
+
+    private void processDataUpdatedEventAsync(final Anchor anchor, final String xpath, final Operation operation,
+        final OffsetDateTime observedTimestamp) {
         try {
-            notificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, xpath, operation);
+            notificationService.processDataUpdatedEvent(anchor, observedTimestamp, xpath, operation);
         } catch (final Exception exception) {
+            //If async message can't be queued for notification service, the initial request should not failed.
             log.error("Failed to send message to notification service", exception);
         }
     }
index 6054ce5..e7b639d 100644 (file)
@@ -25,7 +25,7 @@ import java.net.URISyntaxException;
 import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.UUID;
-import org.onap.cps.api.CpsAdminService;
+import lombok.AllArgsConstructor;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.event.model.Content;
 import org.onap.cps.event.model.CpsDataUpdatedEvent;
@@ -38,6 +38,7 @@ import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 @Component
+@AllArgsConstructor(onConstructor = @__(@Lazy))
 public class CpsDataUpdatedEventFactory {
 
     private static final URI EVENT_SCHEMA;
@@ -56,29 +57,22 @@ public class CpsDataUpdatedEventFactory {
         }
     }
 
+    @Lazy
     private final CpsDataService cpsDataService;
-    private final CpsAdminService cpsAdminService;
-
-    public CpsDataUpdatedEventFactory(@Lazy final CpsDataService cpsDataService,
-        final CpsAdminService cpsAdminService) {
-        this.cpsDataService = cpsDataService;
-        this.cpsAdminService = cpsAdminService;
-    }
 
     /**
      * Generates CPS Data Updated event. If observedTimestamp is not provided, then current timestamp is used.
      *
-     * @param dataspaceName     dataspaceName
-     * @param anchorName        anchorName
+     * @param anchor            anchor
      * @param observedTimestamp observedTimestamp
      * @param operation         operation
      * @return CpsDataUpdatedEvent
      */
-    public CpsDataUpdatedEvent createCpsDataUpdatedEvent(final String dataspaceName, final String anchorName,
+    public CpsDataUpdatedEvent createCpsDataUpdatedEvent(final Anchor anchor,
         final OffsetDateTime observedTimestamp, final Operation operation) {
-        final var dataNode = cpsDataService
-            .getDataNode(dataspaceName, anchorName, "/", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
-        final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
+        final var dataNode = (operation == Operation.DELETE) ? null :
+            cpsDataService.getDataNode(anchor.getDataspaceName(), anchor.getName(),
+                "/", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return toCpsDataUpdatedEvent(anchor, dataNode, observedTimestamp, operation);
     }
 
@@ -105,10 +99,12 @@ public class CpsDataUpdatedEventFactory {
         content.withAnchorName(anchor.getName());
         content.withDataspaceName(anchor.getDataspaceName());
         content.withSchemaSetName(anchor.getSchemaSetName());
-        content.withData(createData(dataNode));
         content.withOperation(Content.Operation.fromValue(operation.name()));
         content.withObservedTimestamp(
             DATE_TIME_FORMATTER.format(observedTimestamp == null ? OffsetDateTime.now() : observedTimestamp));
+        if (dataNode != null) {
+            content.withData(createData(dataNode));
+        }
         return content;
     }
 }
index 97a1479..5ad59df 100644 (file)
@@ -29,6 +29,7 @@ import java.util.concurrent.Future;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.spi.model.Anchor;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
@@ -80,22 +81,20 @@ public class NotificationService {
     /**
      * Process Data Updated Event and publishes the notification.
      *
-     * @param dataspaceName     dataspace name
-     * @param anchorName        anchor name
+     * @param anchor            anchor
      * @param observedTimestamp observedTimestamp
      * @param xpath             xpath of changed data node
      * @param operation         operation
      * @return future
      */
     @Async("notificationExecutor")
-    public Future<Void> processDataUpdatedEvent(final String dataspaceName, final String anchorName,
-                                                final OffsetDateTime observedTimestamp,
+    public Future<Void> processDataUpdatedEvent(final Anchor anchor, final OffsetDateTime observedTimestamp,
                                                 final String xpath, final Operation operation) {
-        log.debug("process data updated event for dataspace '{}' & anchor '{}'", dataspaceName, anchorName);
+        log.debug("process data updated event for anchor '{}'", anchor);
         try {
-            if (shouldSendNotification(dataspaceName)) {
+            if (shouldSendNotification(anchor.getDataspaceName())) {
                 final var cpsDataUpdatedEvent =
-                    cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, anchorName,
+                    cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor,
                         observedTimestamp, getRootNodeOperation(xpath, operation));
                 log.debug("data updated event to be published {}", cpsDataUpdatedEvent);
                 notificationPublisher.sendNotification(cpsDataUpdatedEvent);
@@ -105,7 +104,7 @@ public class NotificationService {
                CPS operation should not fail if sending event fails for any reason.
              */
             notificationErrorHandler.onException("Failed to process cps-data-updated-event.",
-                exception, dataspaceName, anchorName);
+                exception, anchor, xpath, operation);
         }
         return CompletableFuture.completedFuture(null);
     }
index 56b0e2d..785788b 100644 (file)
@@ -24,7 +24,6 @@ package org.onap.cps.api.impl
 
 import org.onap.cps.TestUtils
 import org.onap.cps.api.CpsAdminService
-import org.onap.cps.api.CpsModuleService
 import org.onap.cps.notification.NotificationService
 import org.onap.cps.notification.Operation
 import org.onap.cps.spi.CpsDataPersistenceService
@@ -47,9 +46,14 @@ class CpsDataServiceImplSpec extends Specification {
     def objectUnderTest = new CpsDataServiceImpl(mockCpsDataPersistenceService, mockCpsAdminService,
             mockYangTextSchemaSourceSetCache, mockNotificationService)
 
+    def setup() {
+        mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
+    }
+
     def dataspaceName = 'some dataspace'
     def anchorName = 'some anchor'
     def schemaSetName = 'some schema set'
+    def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build()
     def observedTimestamp = OffsetDateTime.now()
 
     def 'Saving json data.'() {
@@ -62,7 +66,7 @@ class CpsDataServiceImplSpec extends Specification {
             1 * mockCpsDataPersistenceService.storeDataNode(dataspaceName, anchorName,
                 { dataNode -> dataNode.xpath == '/test-tree' })
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/', Operation.CREATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/', Operation.CREATE)
     }
 
     def 'Saving child data fragment under existing node.'() {
@@ -75,7 +79,7 @@ class CpsDataServiceImplSpec extends Specification {
             1 * mockCpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, '/test-tree',
                 { dataNode -> dataNode.xpath == '/test-tree/branch[@name=\'New\']' })
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.CREATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/test-tree', Operation.CREATE)
     }
 
     def 'Saving list element data fragment under existing node.'() {
@@ -95,7 +99,7 @@ class CpsDataServiceImplSpec extends Specification {
                 }
             )
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.UPDATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/test-tree', Operation.UPDATE)
     }
 
     def 'Saving empty list element data fragment.'() {
@@ -127,7 +131,7 @@ class CpsDataServiceImplSpec extends Specification {
         then: 'the persistence service method is invoked with correct parameters'
             1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, expectedNodeXpath, leaves)
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, parentNodeXpath, Operation.UPDATE)
         where: 'following parameters were used'
             scenario         | parentNodeXpath | jsonData                        || expectedNodeXpath                   | leaves
             'top level node' | '/'             | '{"test-tree": {"branch": []}}' || '/test-tree'                        | Collections.emptyMap()
@@ -160,7 +164,7 @@ class CpsDataServiceImplSpec extends Specification {
             1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName,
                 "/bookstore/categories[@code='01']", ['name':'Romance', 'code': '01'])
         and: 'the data updated event is sent to the notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/bookstore', Operation.UPDATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/bookstore', Operation.UPDATE)
     }
 
     def 'Replace data node: #scenario.'() {
@@ -172,7 +176,7 @@ class CpsDataServiceImplSpec extends Specification {
             1 * mockCpsDataPersistenceService.replaceDataNodeTree(dataspaceName, anchorName,
                 { dataNode -> dataNode.xpath == expectedNodeXpath })
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, parentNodeXpath, Operation.UPDATE)
         where: 'following parameters were used'
             scenario         | parentNodeXpath | jsonData                        || expectedNodeXpath
             'top level node' | '/'             | '{"test-tree": {"branch": []}}' || '/test-tree'
@@ -196,7 +200,7 @@ class CpsDataServiceImplSpec extends Specification {
                 }
             )
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.UPDATE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/test-tree', Operation.UPDATE)
     }
 
     def 'Replace whole list content with empty list element.'() {
@@ -217,7 +221,7 @@ class CpsDataServiceImplSpec extends Specification {
         then: 'the persistence service method is invoked with correct parameters'
             1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch')
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree/branch', Operation.DELETE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/test-tree/branch', Operation.DELETE)
     }
 
     def 'Delete data node under anchor and dataspace.'() {
@@ -228,7 +232,7 @@ class CpsDataServiceImplSpec extends Specification {
         then: 'the persistence service method is invoked with the correct parameters'
             1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node')
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/data-node', Operation.DELETE)
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/data-node', Operation.DELETE)
     }
 
     def 'Delete all data nodes for a given anchor and dataspace.'() {
@@ -238,11 +242,12 @@ class CpsDataServiceImplSpec extends Specification {
             objectUnderTest.deleteDataNodes(dataspaceName, anchorName, observedTimestamp)
         then: 'the persistence service method is invoked with the correct parameters'
             1 * mockCpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName)
+        and: 'data updated event is sent to notification service'
+            1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/', Operation.DELETE)
+
     }
 
     def setupSchemaSetMocks(String... yangResources) {
-        def anchor = Anchor.builder().name(anchorName).schemaSetName(schemaSetName).build()
-        mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> anchor
         def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet)
         mockYangTextSchemaSourceSetCache.get(dataspaceName, schemaSetName) >> mockYangTextSchemaSourceSet
         def yangResourceNameToContent = TestUtils.getYangResourcesAsMap(yangResources)
index 67ed3d9..5b13fa5 100644 (file)
@@ -36,27 +36,22 @@ import spock.lang.Specification
 class CpsDataUpdateEventFactorySpec extends Specification {
 
     def mockCpsDataService = Mock(CpsDataService)
-    def mockCpsAdminService = Mock(CpsAdminService)
 
-    def objectUnderTest = new CpsDataUpdatedEventFactory(mockCpsDataService, mockCpsAdminService)
+    def objectUnderTest = new CpsDataUpdatedEventFactory(mockCpsDataService)
 
-    def myDataspaceName = 'my-dataspace'
-    def myAnchorName = 'my-anchorname'
-    def mySchemasetName = 'my-schemaset-name'
     def dateTimeFormat = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ'
 
     def 'Create a CPS data updated event successfully: #scenario'() {
-        given: 'cps admin service is able to return anchor details'
-            mockCpsAdminService.getAnchor(myDataspaceName, myAnchorName) >>
-                new Anchor(myAnchorName, myDataspaceName, mySchemasetName)
+        given: 'an anchor which has been updated'
+            def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name')
         and: 'cps data service returns the data node details'
             def xpath = '/'
             def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(['leafName': 'leafValue']).build()
             mockCpsDataService.getDataNode(
-                myDataspaceName, myAnchorName, xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+                'my-dataspace', 'my-anchorname', xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
         when: 'CPS data updated event is created'
-            def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(myDataspaceName,
-                myAnchorName, DateTimeUtility.toOffsetDateTime(inputObservedTimestamp), Operation.CREATE)
+            def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(anchor,
+                    DateTimeUtility.toOffsetDateTime(inputObservedTimestamp), Operation.CREATE)
         then: 'CPS data updated event is created with correct envelope'
             with(cpsDataUpdatedEvent) {
                 type == 'org.onap.cps.data-updated-event'
@@ -72,10 +67,10 @@ class CpsDataUpdateEventFactorySpec extends Specification {
                     assert observedTimestamp == inputObservedTimestamp
                 else
                     assert OffsetDateTime.now().minusSeconds(20).isBefore(
-                        DateTimeUtility.toOffsetDateTime(observedTimestamp))
-                assert anchorName == myAnchorName
-                assert dataspaceName == myDataspaceName
-                assert schemaSetName == mySchemasetName
+                            DateTimeUtility.toOffsetDateTime(observedTimestamp))
+                assert anchorName == 'my-anchorname'
+                assert dataspaceName == 'my-dataspace'
+                assert schemaSetName == 'my-schemaset-name'
                 assert operation == Content.Operation.CREATE
                 assert data == new Data().withAdditionalProperty('leafName', 'leafValue')
             }
@@ -86,6 +81,33 @@ class CpsDataUpdateEventFactorySpec extends Specification {
             'missing observed timestamp'    | null
     }
 
+    def 'Create a delete CPS data updated event successfully'() {
+        given: 'an anchor which has been deleted'
+            def anchor = new Anchor('my-anchorname', 'my-dataspace', 'my-schemaset-name')
+            def deletionTimestamp = '2021-01-01T23:00:00.345-0400'
+        when: 'a delete root data node event is created'
+            def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(anchor,
+                    DateTimeUtility.toOffsetDateTime(deletionTimestamp), Operation.DELETE)
+        then: 'CPS data updated event is created with correct envelope'
+            with(cpsDataUpdatedEvent) {
+                type == 'org.onap.cps.data-updated-event'
+                source == new URI('urn:cps:org.onap.cps')
+                schema == new URI('urn:cps:org.onap.cps:data-updated-event-schema:v1')
+                StringUtils.hasText(id)
+                content != null
+            }
+        and: 'correct content'
+            with(cpsDataUpdatedEvent.content) {
+                assert isExpectedDateTimeFormat(observedTimestamp): "$observedTimestamp is not in $dateTimeFormat format"
+                assert observedTimestamp == deletionTimestamp
+                assert anchorName == 'my-anchorname'
+                assert dataspaceName == 'my-dataspace'
+                assert schemaSetName == 'my-schemaset-name'
+                assert operation == Content.Operation.DELETE
+                assert data == null
+            }
+    }
+
     def isExpectedDateTimeFormat(String observedTimestamp) {
         try {
             DateTimeFormatter.ofPattern(dateTimeFormat).parse(observedTimestamp)
index 306e187..c20bdee 100644 (file)
@@ -23,6 +23,7 @@ package org.onap.cps.notification
 import java.time.OffsetDateTime
 import org.onap.cps.config.AsyncConfig
 import org.onap.cps.event.model.CpsDataUpdatedEvent
+import org.onap.cps.spi.model.Anchor
 import org.spockframework.spring.SpringBean
 import org.spockframework.spring.SpringSpy
 import org.springframework.beans.factory.annotation.Autowired
@@ -53,15 +54,14 @@ class NotificationServiceSpec extends Specification {
     NotificationService objectUnderTest
 
     @Shared
-    def myDataspacePublishedName = 'my-dataspace-published'
-    def myAnchorName = 'my-anchorname'
+    def anchor = new Anchor('my-anchorname', 'my-dataspace-published', 'my-schemaset-name')
     def myObservedTimestamp = OffsetDateTime.now()
 
     def 'Skip sending notification when disabled.'() {
         given: 'notification is disabled'
             spyNotificationProperties.isEnabled() >> false
         when: 'dataUpdatedEvent is received'
-            objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/', Operation.CREATE)
+            objectUnderTest.processDataUpdatedEvent(anchor, myObservedTimestamp, '/', Operation.CREATE)
         then: 'the notification is not sent'
             0 * mockNotificationPublisher.sendNotification(_)
     }
@@ -69,13 +69,14 @@ class NotificationServiceSpec extends Specification {
     def 'Send notification when enabled: #scenario.'() {
         given: 'notification is enabled'
             spyNotificationProperties.isEnabled() >> true
+        and: 'an anchor is in dataspace where #scenario'
+            def anchor = new Anchor('my-anchorname', dataspaceName, 'my-schemaset-name')
         and: 'event factory can create event successfully'
             def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
-            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp,
-                    Operation.CREATE) >>
+            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >>
                     cpsDataUpdatedEvent
         when: 'dataUpdatedEvent is received'
-            def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp,
+            def future = objectUnderTest.processDataUpdatedEvent(anchor, myObservedTimestamp,
                     '/', Operation.CREATE)
         and: 'wait for async processing to complete'
             future.get(10, TimeUnit.SECONDS)
@@ -86,7 +87,7 @@ class NotificationServiceSpec extends Specification {
         where:
             scenario                               | dataspaceName            || expectedSendNotificationCount
             'dataspace name does not match filter' | 'does-not-match-pattern' || 0
-            'dataspace name matches filter'        | myDataspacePublishedName || 1
+            'dataspace name matches filter'        | 'my-dataspace-published' || 1
     }
 
     def 'Send UPDATE operation when non-root data nodes are changed.'() {
@@ -94,10 +95,10 @@ class NotificationServiceSpec extends Specification {
             spyNotificationProperties.isEnabled() >> true
         and: 'event factory creates event if operation is UPDATE'
             def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
-            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp,
+            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp,
                     Operation.UPDATE) >> cpsDataUpdatedEvent
         when: 'dataUpdatedEvent is received for non-root xpath'
-            def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/non-root-node',
+            def future = objectUnderTest.processDataUpdatedEvent(anchor, myObservedTimestamp, '/non-root-node',
                     operation)
         and: 'wait for async processing to complete'
             future.get(10, TimeUnit.SECONDS)
@@ -114,11 +115,10 @@ class NotificationServiceSpec extends Specification {
             spyNotificationProperties.isEnabled() >> true
         and: 'event factory creates event if operation is #operation'
             def cpsDataUpdatedEvent = new CpsDataUpdatedEvent()
-            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp,
-                    operation) >> cpsDataUpdatedEvent
+            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, operation) >>
+                    cpsDataUpdatedEvent
         when: 'dataUpdatedEvent is received for root xpath'
-            def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/',
-                    operation)
+            def future = objectUnderTest.processDataUpdatedEvent(anchor, myObservedTimestamp, '/', operation)
         and: 'wait for async processing to complete'
             future.get(10, TimeUnit.SECONDS)
         then: 'async process completed successfully'
@@ -134,19 +134,17 @@ class NotificationServiceSpec extends Specification {
         given: 'notification is enabled'
             spyNotificationProperties.isEnabled() >> true
         and: 'event factory can not create event successfully'
-            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName,
-                    myObservedTimestamp, Operation.CREATE) >>
+            mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(anchor, myObservedTimestamp, Operation.CREATE) >>
                     { throw new Exception("Could not create event") }
         when: 'event is sent for processing'
-            def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName,
-                    myObservedTimestamp, '/', Operation.CREATE)
+            def future = objectUnderTest.processDataUpdatedEvent(anchor, myObservedTimestamp, '/', Operation.CREATE)
         and: 'wait for async processing to complete'
             future.get(10, TimeUnit.SECONDS)
         then: 'async process completed successfully'
             future.isDone()
         and: 'error is handled and not thrown to caller'
             notThrown Exception
-            1 * spyNotificationErrorHandler.onException(_, _, _, _)
+            1 * spyNotificationErrorHandler.onException(_, _, _, '/', Operation.CREATE)
     }
 
 }