From: Renu Kumari Date: Tue, 17 Aug 2021 18:06:53 +0000 (-0400) Subject: Filter data updated events based on configured pattern X-Git-Tag: mr/526/123402/5~1^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F37%2F123337%2F4;p=cps.git Filter data updated events based on configured pattern Issue-ID: CPS-469 Signed-off-by: Renu Kumari Change-Id: I7810990b54c3140677184ea671164b8835a6afbb --- diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index ac620f6cb..91c421bb6 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -72,12 +72,14 @@ notification: data-updated: enabled: false topic: ${CPS_CHANGE_EVENT_TOPIC:cps.cfg-state-events} + filters: + enabled-dataspaces: ${DATASPACE_FILTER_PATTERNS:""} springdoc: swagger-ui: url: /openapi.yml path: /swagger-ui/index.html - + security: # comma-separated uri patterns which do not require authorization permit-uri: /manage/**,/swagger-ui/**,/swagger-resources/**,/v3/api-docs diff --git a/cps-service/pom.xml b/cps-service/pom.xml index 6856f7c67..c69ead09e 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -90,6 +90,10 @@ org.springframework spring-context + + org.springframework.boot + spring-boot-starter-validation + com.google.code.gson 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 000000000..eb75e3f75 --- /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 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 67fc54ba2..9cb2c52e0 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 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 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 a74279548..b60d09323 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 c934486fc..94f7e818f 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: