diff options
Diffstat (limited to 'cps-service')
5 files changed, 126 insertions, 42 deletions
diff --git a/cps-service/pom.xml b/cps-service/pom.xml index 6856f7c671..c69ead09e9 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -91,6 +91,10 @@ <artifactId>spring-context</artifactId>
</dependency>
<dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-validation</artifactId>
+ </dependency>
+ <dependency>
<!-- For parsing JSON object -->
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
diff --git a/cps-service/src/main/java/org/onap/cps/notification/NotificationProperties.java b/cps-service/src/main/java/org/onap/cps/notification/NotificationProperties.java new file mode 100644 index 0000000000..eb75e3f75e --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/notification/NotificationProperties.java @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Bell Canada. All rights reserved. + * ================================================================================ + * 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; + +import java.util.Collections; +import java.util.Map; +import javax.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +@ConfigurationProperties(prefix = "notification.data-updated") +@Component +@Data +@Validated +public class NotificationProperties { + + @NotNull + private String topic; + private Map<String, String> filters = Collections.emptyMap(); + @NotNull + private boolean enabled = false; +} 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 67fc54ba2b..9cb2c52e01 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 @@ -18,39 +18,56 @@ package org.onap.cps.notification; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service @Slf4j public class NotificationService { - private boolean dataUpdatedEventNotificationEnabled; + private NotificationProperties notificationProperties; private NotificationPublisher notificationPublisher; private CpsDataUpdatedEventFactory cpsDataUpdatedEventFactory; private NotificationErrorHandler notificationErrorHandler; + private List<Pattern> dataspacePatterns; /** * Create an instance of Notification Subscriber. * - * @param dataUpdatedEventNotificationEnabled notification can be enabled by setting - * 'notification.data-updated.enabled=true' in application properties - * @param notificationPublisher notification Publisher - * @param cpsDataUpdatedEventFactory to create CPSDataUpdatedEvent - * @param notificationErrorHandler error handler + * @param notificationProperties properties for notification + * @param notificationPublisher notification Publisher + * @param cpsDataUpdatedEventFactory to create CPSDataUpdatedEvent + * @param notificationErrorHandler error handler */ @Autowired public NotificationService( - @Value("${notification.data-updated.enabled}") final boolean dataUpdatedEventNotificationEnabled, + final NotificationProperties notificationProperties, final NotificationPublisher notificationPublisher, final CpsDataUpdatedEventFactory cpsDataUpdatedEventFactory, final NotificationErrorHandler notificationErrorHandler) { - this.dataUpdatedEventNotificationEnabled = dataUpdatedEventNotificationEnabled; + this.notificationProperties = notificationProperties; this.notificationPublisher = notificationPublisher; this.cpsDataUpdatedEventFactory = cpsDataUpdatedEventFactory; this.notificationErrorHandler = notificationErrorHandler; + this.dataspacePatterns = getDataspaceFilterPatterns(notificationProperties); + } + + private List<Pattern> getDataspaceFilterPatterns(final NotificationProperties notificationProperties) { + if (notificationProperties.isEnabled()) { + return Arrays.stream(notificationProperties.getFilters() + .getOrDefault("enabled-dataspaces", "") + .split(",")) + .map(filterPattern -> Pattern.compile(filterPattern, Pattern.CASE_INSENSITIVE)) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } } /** @@ -62,7 +79,7 @@ public class NotificationService { public void processDataUpdatedEvent(final String dataspaceName, final String anchorName) { log.debug("process data updated event for dataspace '{}' & anchor '{}'", dataspaceName, anchorName); try { - if (shouldSendNotification()) { + if (shouldSendNotification(dataspaceName)) { final var cpsDataUpdatedEvent = cpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, anchorName); log.debug("data updated event to be published {}", cpsDataUpdatedEvent); @@ -80,8 +97,11 @@ public class NotificationService { /* Add more complex rules based on dataspace and anchor later */ - private boolean shouldSendNotification() { - return dataUpdatedEventNotificationEnabled; + private boolean shouldSendNotification(final String dataspaceName) { + + return notificationProperties.isEnabled() + && dataspacePatterns.stream() + .anyMatch(pattern -> pattern.matcher(dataspaceName).find()); } } 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 a74279548e..b60d093236 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 @@ -20,59 +20,76 @@ package org.onap.cps.notification import org.onap.cps.event.model.CpsDataUpdatedEvent +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ContextConfiguration +import spock.lang.Shared import spock.lang.Specification +@SpringBootTest +@EnableConfigurationProperties +@ContextConfiguration(classes = [NotificationProperties]) class NotificationServiceSpec extends Specification { - def mockNotificationPublisher = Mock(NotificationPublisher) - def spyNotificationErrorHandler = Spy(new NotificationErrorHandler()) - def mockCpsDataUpdatedEventFactory = Mock(CpsDataUpdatedEventFactory) + @SpringBean + NotificationPublisher mockNotificationPublisher = Mock() + @SpringBean + NotificationErrorHandler spyNotificationErrorHandler = Spy(new NotificationErrorHandler()) + @SpringBean + CpsDataUpdatedEventFactory mockCpsDataUpdatedEventFactory = Mock() - def objectUnderTest = new NotificationService(true, mockNotificationPublisher, - mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler) + @Autowired + NotificationProperties notificationProperties + NotificationProperties spyNotificationProperties - def myDataspaceName = 'my-dataspace' + @Shared + def myDataspacePublishedName = 'my-dataspace-published' def myAnchorName = 'my-anchorname' def 'Skip sending notification when disabled.'() { - given: 'notification is disabled' - objectUnderTest.dataUpdatedEventNotificationEnabled = false - + def objectUnderTest = createNotificationService(false) when: 'dataUpdatedEvent is received' - objectUnderTest.processDataUpdatedEvent(myDataspaceName, myAnchorName) - + objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName) then: 'the notification is not sent' 0 * mockNotificationPublisher.sendNotification(_) } - def 'Send notification when enabled.'() { - + def 'Send notification when enabled: #scenario.'() { given: 'notification is enabled' - objectUnderTest.dataUpdatedEventNotificationEnabled = true + def objectUnderTest = createNotificationService(true) and: 'event factory can create event successfully' def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspaceName, myAnchorName) >> cpsDataUpdatedEvent - + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, myAnchorName) >> cpsDataUpdatedEvent when: 'dataUpdatedEvent is received' - objectUnderTest.processDataUpdatedEvent(myDataspaceName, myAnchorName) - - then: 'notification is sent with correct event' - 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) + objectUnderTest.processDataUpdatedEvent(dataspaceName, myAnchorName) + then: 'notification is sent' + expectedSendNotificationCount * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) + where: + scenario | dataspaceName || expectedSendNotificationCount + 'dataspace name does not match filter' | 'does-not-match-pattern' || 0 + 'dataspace name matches filter' | myDataspacePublishedName || 1 } - def 'Error handling in notification service.'(){ - given: 'event factory can not create event successfully' - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspaceName, myAnchorName) >> - { throw new Exception("Could not create event") } - + def 'Error handling in notification service.'() { + given: 'notification is enabled' + def objectUnderTest = createNotificationService(true) + and: 'event factory can not create event successfully' + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName) >> + { throw new Exception("Could not create event") } when: 'event is sent for processing' - objectUnderTest.processDataUpdatedEvent(myDataspaceName, myAnchorName) - + objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName) then: 'error is handled and not thrown to caller' notThrown Exception - 1 * spyNotificationErrorHandler.onException(_,_,_,_) - + 1 * spyNotificationErrorHandler.onException(_, _, _, _) } + NotificationService createNotificationService(boolean notificationEnabled) { + spyNotificationProperties = Spy(notificationProperties) + spyNotificationProperties.isEnabled() >> notificationEnabled + return new NotificationService(spyNotificationProperties, mockNotificationPublisher, + mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler) + } } diff --git a/cps-service/src/test/resources/application.yml b/cps-service/src/test/resources/application.yml index c934486fc9..94f7e818f4 100644 --- a/cps-service/src/test/resources/application.yml +++ b/cps-service/src/test/resources/application.yml @@ -17,8 +17,10 @@ notification: data-updated: - topic: cps-event + filters: + enabled-dataspaces: ".*-published,.*-important" enabled: true + topic: cps-event spring: kafka: |