From: Renu Kumari Date: Sun, 9 Jan 2022 13:38:31 +0000 (-0500) Subject: Send operation information in existing notification events X-Git-Tag: mr/823/126723/7~5^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?p=cps.git;a=commitdiff_plain;h=4bee6edd2dcf1b2ada3a808ae19b003c684025cd Send operation information in existing notification events Issue-ID: CPS-791 Signed-off-by: Renu Kumari Change-Id: Ic68dd476942291cf03afed99e95a245dafde5be9 --- 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 1445ccadf..a23bc95f3 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 @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation - * Modifications Copyright (C) 2020-2021 Bell Canada. + * Modifications Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,8 +27,8 @@ import java.util.Collection; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsDataService; -import org.onap.cps.api.CpsModuleService; import org.onap.cps.notification.NotificationService; +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; @@ -52,9 +52,6 @@ public class CpsDataServiceImpl implements CpsDataService { @Autowired private CpsAdminService cpsAdminService; - @Autowired - private CpsModuleService cpsModuleService; - @Autowired private YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; @@ -66,7 +63,7 @@ public class CpsDataServiceImpl implements CpsDataService { final OffsetDateTime observedTimestamp) { final var dataNode = buildDataNode(dataspaceName, anchorName, ROOT_NODE_XPATH, jsonData); cpsDataPersistenceService.storeDataNode(dataspaceName, anchorName, dataNode); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, ROOT_NODE_XPATH, Operation.CREATE); } @Override @@ -74,7 +71,7 @@ public class CpsDataServiceImpl implements CpsDataService { final String jsonData, final OffsetDateTime observedTimestamp) { final var dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, parentNodeXpath, dataNode); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.CREATE); } @Override @@ -84,7 +81,7 @@ public class CpsDataServiceImpl implements CpsDataService { buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath, listElementDataNodeCollection); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE); } @Override @@ -99,7 +96,7 @@ public class CpsDataServiceImpl implements CpsDataService { final var dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService .updateDataLeaves(dataspaceName, anchorName, dataNode.getXpath(), dataNode.getLeaves()); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE); } @Override @@ -113,7 +110,7 @@ public class CpsDataServiceImpl implements CpsDataService { for (final DataNode dataNodeUpdate : dataNodeUpdates) { processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate); } - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE); } @Override @@ -121,7 +118,7 @@ public class CpsDataServiceImpl implements CpsDataService { final String jsonData, final OffsetDateTime observedTimestamp) { final var dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.replaceDataNodeTree(dataspaceName, anchorName, dataNode); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE); } @Override @@ -130,21 +127,21 @@ public class CpsDataServiceImpl implements CpsDataService { final Collection newListElements = buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData); cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE); } @Override public void deleteDataNode(final String dataspaceName, final String anchorName, final String dataNodeXpath, final OffsetDateTime observedTimestamp) { cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, dataNodeXpath, Operation.DELETE); } @Override public void deleteListOrListElement(final String dataspaceName, final String anchorName, final String listNodeXpath, final OffsetDateTime observedTimestamp) { cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath); - processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp); + processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, listNodeXpath, Operation.DELETE); } private DataNode buildDataNode(final String dataspaceName, final String anchorName, @@ -186,9 +183,10 @@ public class CpsDataServiceImpl implements CpsDataService { } private void processDataUpdatedEventAsync(final String dataspaceName, final String anchorName, - final OffsetDateTime observedTimestamp) { + final OffsetDateTime observedTimestamp, final String xpath, + final Operation operation) { try { - notificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp); + notificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, xpath, operation); } catch (final Exception exception) { log.error("Failed to send message to notification service", exception); } @@ -210,4 +208,5 @@ public class CpsDataServiceImpl implements CpsDataService { processDataNodeUpdate(dataspaceName, anchorName, childDataNodeUpdate); } } + } diff --git a/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java b/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java index 2985ed53b..6054ce5d7 100644 --- a/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java +++ b/cps-service/src/main/java/org/onap/cps/notification/CpsDataUpdatedEventFactory.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. + * Copyright (c) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,20 +71,21 @@ public class CpsDataUpdatedEventFactory { * @param dataspaceName dataspaceName * @param anchorName anchorName * @param observedTimestamp observedTimestamp + * @param operation operation * @return CpsDataUpdatedEvent */ public CpsDataUpdatedEvent createCpsDataUpdatedEvent(final String dataspaceName, final String anchorName, - final OffsetDateTime observedTimestamp) { + final OffsetDateTime observedTimestamp, final Operation operation) { final var dataNode = cpsDataService .getDataNode(dataspaceName, anchorName, "/", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName); - return toCpsDataUpdatedEvent(anchor, dataNode, observedTimestamp); + return toCpsDataUpdatedEvent(anchor, dataNode, observedTimestamp, operation); } private CpsDataUpdatedEvent toCpsDataUpdatedEvent(final Anchor anchor, final DataNode dataNode, - final OffsetDateTime observedTimestamp) { + final OffsetDateTime observedTimestamp, final Operation operation) { final var cpsDataUpdatedEvent = new CpsDataUpdatedEvent(); - cpsDataUpdatedEvent.withContent(createContent(anchor, dataNode, observedTimestamp)); + cpsDataUpdatedEvent.withContent(createContent(anchor, dataNode, observedTimestamp, operation)); cpsDataUpdatedEvent.withId(UUID.randomUUID().toString()); cpsDataUpdatedEvent.withSchema(EVENT_SCHEMA); cpsDataUpdatedEvent.withSource(EVENT_SOURCE); @@ -99,12 +100,13 @@ public class CpsDataUpdatedEventFactory { } private Content createContent(final Anchor anchor, final DataNode dataNode, - final OffsetDateTime observedTimestamp) { + final OffsetDateTime observedTimestamp, final Operation operation) { final var content = new Content(); 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)); return content; diff --git a/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java b/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java index 029efbe79..97a14797b 100644 --- a/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java +++ b/cps-service/src/main/java/org/onap/cps/notification/NotificationService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. + * Copyright (c) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ import org.springframework.stereotype.Service; @Slf4j public class NotificationService { + private static final String ROOT_NODE_XPATH = "/"; + private NotificationProperties notificationProperties; private NotificationPublisher notificationPublisher; private CpsDataUpdatedEventFactory cpsDataUpdatedEventFactory; @@ -78,19 +80,23 @@ public class NotificationService { /** * Process Data Updated Event and publishes the notification. * - * @param dataspaceName dataspace name - * @param anchorName anchor name + * @param dataspaceName dataspace name + * @param anchorName anchor name * @param observedTimestamp observedTimestamp + * @param xpath xpath of changed data node + * @param operation operation * @return future */ @Async("notificationExecutor") public Future processDataUpdatedEvent(final String dataspaceName, final String anchorName, - final OffsetDateTime observedTimestamp) { + final OffsetDateTime observedTimestamp, + final String xpath, final Operation operation) { log.debug("process data updated event for dataspace '{}' & anchor '{}'", dataspaceName, anchorName); try { if (shouldSendNotification(dataspaceName)) { final var cpsDataUpdatedEvent = - cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp); + cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, anchorName, + observedTimestamp, getRootNodeOperation(xpath, operation)); log.debug("data updated event to be published {}", cpsDataUpdatedEvent); notificationPublisher.sendNotification(cpsDataUpdatedEvent); } @@ -114,4 +120,8 @@ public class NotificationService { .anyMatch(pattern -> pattern.matcher(dataspaceName).find()); } + private Operation getRootNodeOperation(final String xpath, final Operation operation) { + return ROOT_NODE_XPATH.equals(xpath) ? operation : Operation.UPDATE; + } + } diff --git a/cps-service/src/main/java/org/onap/cps/notification/Operation.java b/cps-service/src/main/java/org/onap/cps/notification/Operation.java new file mode 100644 index 000000000..83e1ccf79 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/notification/Operation.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2022 Bell Canada. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.notification; + +public enum Operation { + CREATE, + UPDATE, + DELETE +} 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 ba9c156d7..6c899c793 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 @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021 Bell Canada. + * Modifications Copyright (C) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ 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 import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.DataValidationException @@ -40,7 +41,6 @@ import java.time.OffsetDateTime class CpsDataServiceImplSpec extends Specification { def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) def mockCpsAdminService = Mock(CpsAdminService) - def mockCpsModuleService = Mock(CpsModuleService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) def mockNotificationService = Mock(NotificationService) @@ -49,7 +49,6 @@ class CpsDataServiceImplSpec extends Specification { def setup() { objectUnderTest.cpsDataPersistenceService = mockCpsDataPersistenceService objectUnderTest.cpsAdminService = mockCpsAdminService - objectUnderTest.cpsModuleService = mockCpsModuleService objectUnderTest.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache objectUnderTest.notificationService = mockNotificationService } @@ -69,7 +68,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) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/', Operation.CREATE) } def 'Saving child data fragment under existing node.'() { @@ -82,7 +81,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) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.CREATE) } def 'Saving list element data fragment under existing node.'() { @@ -102,7 +101,7 @@ class CpsDataServiceImplSpec extends Specification { } ) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.UPDATE) } def 'Saving empty list element data fragment.'() { @@ -134,7 +133,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) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE) where: 'following parameters were used' scenario | parentNodeXpath | jsonData || expectedNodeXpath | leaves 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' | Collections.emptyMap() @@ -167,7 +166,7 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, "/dmi-registry/cm-handles[@id='cmHandle001']", ['id': 'cmHandle001']) and: 'the data updated event is sent to the notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/dmi-registry', Operation.UPDATE) } def 'Replace data node: #scenario.'() { @@ -179,7 +178,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) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE) where: 'following parameters were used' scenario | parentNodeXpath | jsonData || expectedNodeXpath 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' @@ -203,7 +202,7 @@ class CpsDataServiceImplSpec extends Specification { } ) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.UPDATE) } def 'Replace whole list content with empty list element.'() { @@ -224,7 +223,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) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree/branch', Operation.DELETE) } def 'Delete data node under anchor and dataspace.'() { @@ -235,7 +234,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) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/data-node', Operation.DELETE) } def setupSchemaSetMocks(String... yangResources) { diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy index aa0c7c0b3..67ed3d90f 100644 --- a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. + * Copyright (c) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import java.time.format.DateTimeFormatter import org.onap.cps.utils.DateTimeUtility import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsDataService +import org.onap.cps.event.model.Content import org.onap.cps.event.model.Data import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.Anchor @@ -45,7 +46,6 @@ class CpsDataUpdateEventFactorySpec extends Specification { 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) @@ -54,13 +54,10 @@ class CpsDataUpdateEventFactorySpec extends Specification { def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(['leafName': 'leafValue']).build() mockCpsDataService.getDataNode( myDataspaceName, myAnchorName, xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode - when: 'CPS data updated event is created' def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(myDataspaceName, - myAnchorName, DateTimeUtility.toOffsetDateTime(inputObservedTimestamp)) - + myAnchorName, DateTimeUtility.toOffsetDateTime(inputObservedTimestamp), Operation.CREATE) 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') @@ -79,6 +76,7 @@ class CpsDataUpdateEventFactorySpec extends Specification { assert anchorName == myAnchorName assert dataspaceName == myDataspaceName assert schemaSetName == mySchemasetName + assert operation == Content.Operation.CREATE assert data == new Data().withAdditionalProperty('leafName', 'leafValue') } where: diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy index ca704edb4..306e187a0 100644 --- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. + * Copyright (c) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ class NotificationServiceSpec extends Specification { given: 'notification is disabled' spyNotificationProperties.isEnabled() >> false when: 'dataUpdatedEvent is received' - objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp) + objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/', Operation.CREATE) then: 'the notification is not sent' 0 * mockNotificationPublisher.sendNotification(_) } @@ -71,10 +71,12 @@ class NotificationServiceSpec extends Specification { spyNotificationProperties.isEnabled() >> true and: 'event factory can create event successfully' def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp) >> - cpsDataUpdatedEvent + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp, + Operation.CREATE) >> + cpsDataUpdatedEvent when: 'dataUpdatedEvent is received' - def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp) + def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp, + '/', Operation.CREATE) and: 'wait for async processing to complete' future.get(10, TimeUnit.SECONDS) then: 'async process completed successfully' @@ -87,14 +89,57 @@ class NotificationServiceSpec extends Specification { 'dataspace name matches filter' | myDataspacePublishedName || 1 } + def 'Send UPDATE operation when non-root data nodes are changed.'() { + given: 'notification is enabled' + spyNotificationProperties.isEnabled() >> true + and: 'event factory creates event if operation is UPDATE' + def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, + Operation.UPDATE) >> cpsDataUpdatedEvent + when: 'dataUpdatedEvent is received for non-root xpath' + def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/non-root-node', + operation) + and: 'wait for async processing to complete' + future.get(10, TimeUnit.SECONDS) + then: 'async process completed successfully' + future.isDone() + and: 'notification is sent' + 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) + where: + operation << [Operation.CREATE, Operation.UPDATE, Operation.DELETE] + } + + def 'Send same operation when root nodes are changed.'() { + given: 'notification is enabled' + spyNotificationProperties.isEnabled() >> true + and: 'event factory creates event if operation is #operation' + def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, + operation) >> cpsDataUpdatedEvent + when: 'dataUpdatedEvent is received for root xpath' + def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/', + operation) + and: 'wait for async processing to complete' + future.get(10, TimeUnit.SECONDS) + then: 'async process completed successfully' + future.isDone() + and: 'notification is sent' + 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) + where: + operation << [Operation.CREATE, Operation.UPDATE, Operation.DELETE] + } + + def 'Error handling in notification service.'() { given: 'notification is enabled' spyNotificationProperties.isEnabled() >> true and: 'event factory can not create event successfully' - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp) >> - { throw new Exception("Could not create event") } + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, + myObservedTimestamp, Operation.CREATE) >> + { throw new Exception("Could not create event") } when: 'event is sent for processing' - def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp) + def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, + myObservedTimestamp, '/', Operation.CREATE) and: 'wait for async processing to complete' future.get(10, TimeUnit.SECONDS) then: 'async process completed successfully'