From 2a1e5aac09411523e38fc7126b523ea4f6ac5f78 Mon Sep 17 00:00:00 2001 From: emaclee Date: Tue, 16 Apr 2024 09:56:59 +0100 Subject: Reject create request with duplicated subscriptionId Issue-ID: CPS-2184 Change-Id: I1bddb02239e6a2d3f47aa731eb24a9f1f64e63b5 Signed-off-by: emaclee Signed-off-by: seanbeirne --- ...otificationSubscriptionNcmpInEventConsumer.java | 15 +++-- ...NotificationSubscriptionNcmpOutEventMapper.java | 18 ++++++ .../CmNotificationSubscriptionHandlerService.java | 35 +++++++++++ ...NotificationSubscriptionHandlerServiceImpl.java | 70 ++++++++++++++++++++++ ...ationSubscriptionNcmpInEventConsumerSpec.groovy | 19 +++--- ...cationSubscriptionNcmpOutEventMapperSpec.groovy | 9 +++ ...cationSubscriptionHandlerServiceImplSpec.groovy | 63 +++++++++++++++++++ 7 files changed, 212 insertions(+), 17 deletions(-) create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java create mode 100644 cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java index 362fbeb9e..377e15531 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java @@ -23,12 +23,11 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription; import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent; import io.cloudevents.CloudEvent; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionHandlerService; import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate; import org.springframework.beans.factory.annotation.Value; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @@ -38,7 +37,7 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class CmNotificationSubscriptionNcmpInEventConsumer { - private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; + private final CmNotificationSubscriptionHandlerService cmNotificationSubscriptionHandlerService; @Value("${notification.enabled:true}") private boolean notificationFeatureEnabled; @@ -56,12 +55,12 @@ public class CmNotificationSubscriptionNcmpInEventConsumer { toTargetEvent(cloudEvent, CmNotificationSubscriptionNcmpInEvent.class); log.info("Subscription with name {} to be mapped to hazelcast object...", cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()); + final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId(); - final List predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates(); - dmiCmNotificationSubscriptionCacheHandler.add(subscriptionId, predicates); - if ("subscriptionCreated".equals(cloudEvent.getType()) && cmNotificationSubscriptionNcmpInEvent != null) { - log.info("Subscription for ClientID {} with name {} ...", cloudEvent.getSource(), - cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()); + if ("subscriptionCreateRequest".equals(cloudEvent.getType())) { + log.info("Subscription for source {} with subscription id {} ...", cloudEvent.getSource(), subscriptionId); + cmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest( + cmNotificationSubscriptionNcmpInEvent); } } } \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java index 668f4517e..ea2175169 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java @@ -56,6 +56,24 @@ public class CmNotificationSubscriptionNcmpOutEventMapper { return cmNotificationSubscriptionNcmpOutEvent; } + /** + * Mapper to form a rejected response for the client for the Cm Notification Subscription Request. + * + * @param subscriptionId subscription id + * @param rejectedTargetFilters list of rejected target filters for the subscription request + * @return to sent back to the client + */ + public CmNotificationSubscriptionNcmpOutEvent toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( + final String subscriptionId, final List rejectedTargetFilters) { + final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = + new CmNotificationSubscriptionNcmpOutEvent(); + final Data cmSubscriptionData = new Data(); + cmSubscriptionData.setSubscriptionId(subscriptionId); + cmSubscriptionData.setRejectedTargets(rejectedTargetFilters); + cmNotificationSubscriptionNcmpOutEvent.setData(cmSubscriptionData); + return cmNotificationSubscriptionNcmpOutEvent; + } + private void populateCmNotificationSubscriptionNcmpOutEventWithCmHandleIds( final Map dmiCmNotificationSubscriptionDetailsMap, final Data cmSubscriptionData) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java new file mode 100644 index 000000000..536693ee4 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java @@ -0,0 +1,35 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.ncmp.api.impl.events.cmsubscription.service; + +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; + +public interface CmNotificationSubscriptionHandlerService { + + /** + * Process cm notification subscription request. + * + * @param cmNotificationSubscriptionNcmpInEvent CM Notification Subscription event + */ + void processSubscriptionCreateRequest( + final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent); + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java new file mode 100644 index 000000000..8204f05c9 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java @@ -0,0 +1,70 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.ncmp.api.impl.events.cmsubscription.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionNcmpOutEventProducer; +import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler; +import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionNcmpOutEventMapper; +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate; +import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CmNotificationSubscriptionHandlerServiceImpl implements CmNotificationSubscriptionHandlerService { + + private final CmNotificationSubscriptionPersistenceService cmNotificationSubscriptionPersistenceService; + private final CmNotificationSubscriptionNcmpOutEventMapper cmNotificationSubscriptionNcmpOutEventMapper; + private final CmNotificationSubscriptionNcmpOutEventProducer cmNotificationSubscriptionNcmpOutEventProducer; + private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; + + @Override + public void processSubscriptionCreateRequest( + final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent) { + final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId(); + final List predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates(); + + if (cmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId(subscriptionId)) { + dmiCmNotificationSubscriptionCacheHandler.add(subscriptionId, predicates); + } else { + final Set subscriptionTargetFilters = predicates.stream().flatMap( + predicate -> predicate.getTargetFilter().stream()).collect(Collectors.toSet()); + rejectAndPublishCmNotificationSubscriptionCreateRequest(subscriptionId, + new ArrayList<>(subscriptionTargetFilters)); + } + } + + private void rejectAndPublishCmNotificationSubscriptionCreateRequest(final String subscriptionId, + final List subscriptionTargetFilters) { + final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = + cmNotificationSubscriptionNcmpOutEventMapper + .toCmNotificationSubscriptionNcmpOutEventForRejectedRequest(subscriptionId, + subscriptionTargetFilters); + cmNotificationSubscriptionNcmpOutEventProducer.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, + "subscriptionCreateResponse", cmNotificationSubscriptionNcmpOutEvent, false); + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy index 614cf6729..8210cf32a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import io.cloudevents.CloudEvent import io.cloudevents.core.builder.CloudEventBuilder import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionHandlerService import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent import org.onap.cps.ncmp.utils.TestUtils @@ -39,8 +40,8 @@ import org.springframework.boot.test.context.SpringBootTest @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpec { - def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCmNotificationSubscriptionCacheHandler) - def objectUnderTest = new CmNotificationSubscriptionNcmpInEventConsumer(mockDmiCmNotificationSubscriptionCacheHandler) + def mockCmNotificationSubscriptionHandlerService = Mock(CmNotificationSubscriptionHandlerService) + def objectUnderTest = new CmNotificationSubscriptionNcmpInEventConsumer(mockCmNotificationSubscriptionHandlerService) def logger = Spy(ListAppender) @Autowired @@ -59,14 +60,14 @@ class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpe } - def 'Consume valid CMSubscription create message'() { - given: 'a cmsubscription event' + def 'Consume valid CmNotificationSubscriptionNcmpInEvent create message'() { + given: 'a cmNotificationSubscription event' 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') + .withType('subscriptionCreateRequest') .withSource(URI.create('some-resource')) .withExtension('correlationid', 'test-cmhandle1').build() def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) @@ -78,13 +79,13 @@ class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpe def loggingEvent = getLoggingEvent() assert loggingEvent.level == Level.INFO and: 'the log indicates the task completed successfully' - assert loggingEvent.formattedMessage == 'Subscription with name cm-subscription-001 to be mapped to hazelcast object...' - and: 'the cache handler method is called once' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('cm-subscription-001',_) + assert loggingEvent.formattedMessage == 'Subscription for source some-resource with subscription id cm-subscription-001 ...' + and: 'the subscription handler service is called once' + 1 * mockCmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest(_) } def getLoggingEvent() { - return logger.list[0] + return logger.list[1] } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy index 93bb480b4..f6bb24c2f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy @@ -52,4 +52,13 @@ class CmNotificationSubscriptionNcmpOutEventMapperSpec extends Specification { result.data.acceptedTargets == ['ch-B'] result.data.rejectedTargets == ['ch-C'] } + + def 'Check for Cm Notification Rejected Subscription Outgoing event mapping'() { + when: 'we try to map the event to send it to client' + def result = objectUnderTest.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest('test-subscription', ['ch-1', 'ch-2']) + then: 'event is mapped correctly for the subscription id' + result.data.subscriptionId == 'test-subscription' + and: 'the cm handle ids are part of correct list' + result.data.withRejectedTargets(['ch-1', 'ch-2']) + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy new file mode 100644 index 000000000..1020f55ea --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy @@ -0,0 +1,63 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 Nordix Foundation. + * ================================================================================ + * 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.ncmp.api.impl.events.cmsubscription.service + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionNcmpOutEventProducer +import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler +import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionNcmpOutEventMapper +import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +class CmNotificationSubscriptionHandlerServiceImplSpec extends Specification{ + + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def mockCmNotificationSubscriptionPersistenceService = Mock(CmNotificationSubscriptionPersistenceService); + def mockCmNotificationSubscriptionNcmpOutEventMapper = Mock(CmNotificationSubscriptionNcmpOutEventMapper); + def mockCmNotificationSubscriptionNcmpOutEventProducer = Mock(CmNotificationSubscriptionNcmpOutEventProducer); + def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCmNotificationSubscriptionCacheHandler); + + def objectUnderTest = new CmNotificationSubscriptionHandlerServiceImpl(mockCmNotificationSubscriptionPersistenceService, mockCmNotificationSubscriptionNcmpOutEventMapper, mockCmNotificationSubscriptionNcmpOutEventProducer, mockDmiCmNotificationSubscriptionCacheHandler) + + def 'Consume valid and unique CmNotificationSubscriptionNcmpInEvent create message'() { + given: 'a cmNotificationSubscriptionNcmp in event' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) + mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId('cm-subscription-001') >> true + when: 'the valid and unique event is consumed' + objectUnderTest.processSubscriptionCreateRequest(testEventConsumed) + then: 'the subscription cache handler is called once' + 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('cm-subscription-001',_) + } + + def 'Consume valid and but non-unique CmNotificationSubscription create message'() { + given: 'a cmNotificationSubscriptionNcmp in event' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) + mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId('cm-subscription-001') >> false + when: 'the valid but non-unique event is consumed' + objectUnderTest.processSubscriptionCreateRequest(testEventConsumed) + then: 'the subscription out event publisher is called once' + 1 * mockCmNotificationSubscriptionNcmpOutEventProducer.publishCmNotificationSubscriptionNcmpOutEvent('cm-subscription-001', 'subscriptionCreateResponse', _, false) + } +} -- cgit 1.2.3-korg