Merge "Create schemas for notification service in cps-core"
authorPriyank Maheshwari <priyank.maheshwari@est.tech>
Tue, 13 Feb 2024 09:24:00 +0000 (09:24 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 13 Feb 2024 09:24:00 +0000 (09:24 +0000)
82 files changed:
checkstyle/pom.xml
cps-application/pom.xml
cps-bom/pom.xml
cps-dependencies/pom.xml
cps-events/pom.xml
cps-ncmp-events/pom.xml
cps-ncmp-events/src/main/resources/schemas/lcm/lcm-event-schema-v1.json
cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/pom.xml
cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml
cps-ncmp-rest-stub/pom.xml
cps-ncmp-rest/pom.xml
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/executor/CpsNcmpTaskExecutorSpec.groovy
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfig.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmSubscriptionEventCacheConfig.java with 67% similarity]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmNotificationSubscriptionStatus.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmSubscriptionStatus.java with 78% similarity]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmSubscriptionPredicate.java [deleted file]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmSubscriptionCacheObject.java with 82% similarity]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionPredicate.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/ScopeFilter.java with 89% similarity]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/FilterStrategiesIntegrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/SerializationIntegrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmSubscriptionEventCacheConfigSpec.groovy [deleted file]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpInEventConsumerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasksSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/sync/config/WatchdogSchedulingConfigurerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy
cps-parent/pom.xml
cps-path-parser/pom.xml
cps-rest/pom.xml
cps-ri/pom.xml
cps-service/pom.xml
cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/model/ModuleDefinition.java
csit/tests/cps-model-sync/cps-model-sync.robot
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-app/pom.xml
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/pom.xml
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_ResourcesResponse.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_Response.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_ResourcesResponse.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_Response.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_ResourcesResponse.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_Response.json [new file with mode: 0644]
dmi-plugin-demo-and-csit-stub/pom.xml
docker-compose/docker-compose.yml
docs/api/swagger/ncmp/openapi.yaml
docs/deployment.rst
docs/design.rst
docs/release-notes.rst
integration-test/pom.xml
integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/base/FunctionalSpecBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsAnchorServiceIntegrationSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsDataServiceIntegrationSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/base/CpsPerfTestBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/base/PerfTestBase.groovy
integration-test/src/test/resources/data/bookstore/bookstore-types.yang [new file with mode: 0644]
integration-test/src/test/resources/data/bookstore/bookstore.yang
jacoco-report/pom.xml
pom.xml
releases/3.4.3-container.yaml [new file with mode: 0644]
releases/3.4.3.yaml [new file with mode: 0644]
spotbugs/pom.xml
version.properties

index ff4ecb5..95794b1 100644 (file)
@@ -26,7 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>3.4.3-SNAPSHOT</version>
+    <version>3.4.4-SNAPSHOT</version>
 
     <profiles>
         <profile>
index b3e1fe9..6466bcf 100644 (file)
@@ -3,7 +3,7 @@
   ============LICENSE_START=======================================================
   Copyright (c) 2021 Pantheon.tech.
   Modifications Copyright (C) 2021 Bell Canada.
-  Modifications Copyright (C) 2021-2023 Nordix Foundation
+  Modifications Copyright (C) 2021-2024 Nordix Foundation
   Modifications Copyright (C) 2022 Deutsche Telekom AG
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                        <configuration>
+                            <classifier>springboot</classifier>
+                        </configuration>
+                    </execution>
+                </executions>
             </plugin>
         </plugins>
     </build>
index 54d7602..ecd442c 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>3.4.3-SNAPSHOT</version>
+    <version>3.4.4-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
index e7eb576..2f1a4d2 100644 (file)
@@ -27,7 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>3.4.3-SNAPSHOT</version>
+    <version>3.4.4-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
index b1fd6f9..662d150 100644 (file)
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 36c69c6..6e63843 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 7006b78..e687303 100644 (file)
           "description": "cmHandle id",
           "type": "string"
         },
+        "alternateId": {
+          "description": "alternative id for cmHandle (e.g. 3GPP FDN)",
+          "type": "string"
+        },
         "oldValues": {
           "$ref": "#/definitions/Values"
         },
index cdd9eef..25d4939 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
     </parent>
 
     <artifactId>cps-ncmp-rest-stub-app</artifactId>
index 695065a..1a8e89b 100644 (file)
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
     </parent>
     <artifactId>cps-ncmp-rest-stub-service</artifactId>
 
index a1c5ee0..a35a886 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 27c640a..f363c01 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index f06af6c..4edbf35 100644 (file)
@@ -44,7 +44,7 @@ class NcmpDatastoreRequestHandlerSpec extends Specification {
     def 'Attempt to execute async get request with #scenario.'() {
         given: 'notification feature is turned on/off'
             objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled
-        and: ' a flag to track the network service call'
+        and: 'a flag to track the network service call'
             def networkServiceMethodCalled = false
         and: 'the (mocked) service will use the flag to indicate if it is called'
             mockNetworkCmProxyDataService.getResourceDataForCmHandle('ds', 'ch1', 'resource1', 'options', _, _) >> {
@@ -54,7 +54,7 @@ class NcmpDatastoreRequestHandlerSpec extends Specification {
             objectUnderTest.executeRequest('ds', 'ch1', 'resource1', 'options', topic, false)
         then: 'the task is executed in an async fashion or not'
             expectedCalls * spiedCpsNcmpTaskExecutor.executeTask(*_)
-        and: 'the service request is always invoked within 1 seconds'
+        and: 'the service request is invoked'
             new PollingConditions().within(1) {
                 assert networkServiceMethodCalled == true
             }
@@ -97,7 +97,7 @@ class NcmpDatastoreRequestHandlerSpec extends Specification {
             objectUnderTest.executeRequest('myTopic', dataOperationRequest)
         then: 'the task is executed in an async fashion'
             1 * spiedCpsNcmpTaskExecutor.executeTask(*_)
-        and: 'the network service is invoked within 1 seconds'
+        and: 'the network service is invoked'
             new PollingConditions().within(1) {
                 assert networkServiceMethodCalled == true
             }
index 5559016..010eda9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-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.
@@ -24,10 +24,9 @@ import ch.qos.logback.classic.Level
 import ch.qos.logback.classic.Logger
 import ch.qos.logback.classic.spi.ILoggingEvent
 import ch.qos.logback.core.read.ListAppender
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
 import org.slf4j.LoggerFactory
 import spock.lang.Specification
+import spock.util.concurrent.PollingConditions
 
 class CpsNcmpTaskExecutorSpec extends Specification {
 
@@ -35,25 +34,23 @@ class CpsNcmpTaskExecutorSpec extends Specification {
     def logger = Spy(ListAppender<ILoggingEvent>)
     def enoughTime = 100
 
-    @BeforeEach
     void setup() {
-        ((Logger) LoggerFactory.getLogger(CpsNcmpTaskExecutor.class)).addAppender(logger);
-        logger.start();
+        ((Logger) LoggerFactory.getLogger(CpsNcmpTaskExecutor.class)).addAppender(logger)
+        logger.start()
     }
 
-    @AfterEach
-    void teardown() {
-        ((Logger) LoggerFactory.getLogger(CpsNcmpTaskExecutor.class)).detachAndStopAllAppenders();
+    void cleanup() {
+        ((Logger) LoggerFactory.getLogger(CpsNcmpTaskExecutor.class)).detachAndStopAllAppenders()
     }
 
     def 'Execute successful task.'() {
         when: 'task is executed'
             objectUnderTest.executeTask(taskSupplier(), enoughTime)
-        and: 'wait a little for async execution completion'
-            Thread.sleep(10)
         then: 'an event is logged with level INFO'
-            def loggingEvent = getLoggingEvent()
-            assert loggingEvent.level == Level.INFO
+            new PollingConditions().within(1) {
+                def loggingEvent = getLoggingEvent()
+                assert loggingEvent.level == Level.INFO
+            }
         and: 'the log indicates the task completed successfully'
             assert loggingEvent.formattedMessage == 'Async task completed successfully.'
     }
@@ -61,11 +58,11 @@ class CpsNcmpTaskExecutorSpec extends Specification {
     def 'Execute failing task.'() {
         when: 'task is executed'
             objectUnderTest.executeTask(taskSupplierForFailingTask(), enoughTime)
-        and: 'wait a little for async execution completion'
-            Thread.sleep(10)
         then: 'an event is logged with level ERROR'
-            def loggingEvent = getLoggingEvent()
-            assert loggingEvent.level == Level.ERROR
+            new PollingConditions().within(1) {
+                def loggingEvent = getLoggingEvent()
+                assert loggingEvent.level == Level.ERROR
+            }
         and: 'the original error message is logged'
             assert loggingEvent.formattedMessage.contains('original exception message')
     }
index 93c60fa..9beb6b7 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 4fc8409..05b83b9 100755 (executable)
@@ -107,6 +107,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
     private final TrustLevelManager trustLevelManager;
     private final CmHandleIdMapper cmHandleIdMapper;
+    private final Map<String, Collection<ModuleReference>> moduleSetTagCache;
 
     @Override
     public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
@@ -353,13 +354,16 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             new ArrayList<>(tobeRemovedCmHandles.size());
         final Collection<YangModelCmHandle> yangModelCmHandles =
             inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles);
-
+        final Set<String> moduleSetTags = yangModelCmHandles.stream().map(YangModelCmHandle::getModuleSetTag)
+                .collect(Collectors.toSet());
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING);
 
         final Set<String> notDeletedCmHandles = new HashSet<>();
         for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandles, DELETE_BATCH_SIZE)) {
             try {
                 batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch);
+                moduleSetTags.forEach(moduleSetTagCache::remove);
+                log.debug("Removed module set tags: {}", moduleSetTags);
                 tobeRemovedCmHandleBatch.forEach(cmHandleId ->
                     cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)));
 
@@ -24,26 +24,26 @@ import com.hazelcast.config.MapConfig;
 import com.hazelcast.map.IMap;
 import java.util.Map;
 import org.onap.cps.cache.HazelcastCacheConfig;
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmSubscriptionCacheObject;
+import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
-public class CmSubscriptionEventCacheConfig extends HazelcastCacheConfig {
+public class CmNotificationSubscriptionCacheConfig extends HazelcastCacheConfig {
 
-    private static final MapConfig cmSubscriptionEventCacheMapConfig =
-            createMapConfig("cmSubscriptionEventCacheMapConfig");
+    private static final MapConfig cmNotificationSubscriptionCacheMapConfig =
+            createMapConfig("cmNotificationSubscriptionCacheMapConfig");
 
     /**
-     * Distributed instance of cm subscription information
+     * Distributed instance of cm notification subscription information
      * cache that contains subscription id as key
      * and incoming event data processed per dmi plugin.
      *
      * @return configured map of subscription events.
      */
     @Bean
-    public IMap<String, Map<String, CmSubscriptionCacheObject>> cmSubscriptionEventCache() {
-        return createHazelcastInstance("hazelCastInstanceCmSubscriptionEvents",
-                cmSubscriptionEventCacheMapConfig).getMap("cmSubscriptionEventCache");
+    public IMap<String, Map<String, DmiCmNotificationSubscriptionDetails>> cmNotificationSubscriptionCache() {
+        return createHazelcastInstance("hazelCastInstanceCmNotificationSubscription",
+                cmNotificationSubscriptionCacheMapConfig).getMap("cmNotificationSubscriptionCache");
     }
 }
index 5149675..4f2674a 100644 (file)
@@ -21,6 +21,7 @@
 package org.onap.cps.ncmp.api.impl.config.kafka;
 
 import io.cloudevents.CloudEvent;
+import java.time.Duration;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import org.apache.kafka.clients.producer.ProducerConfig;
@@ -99,6 +100,7 @@ public class KafkaConfig<T> {
         final ConcurrentKafkaListenerContainerFactory<String, T> containerFactory =
                 new ConcurrentKafkaListenerContainerFactory<>();
         containerFactory.setConsumerFactory(legacyEventConsumerFactory());
+        containerFactory.getContainerProperties().setAuthExceptionRetryInterval(Duration.ofSeconds(10));
         return containerFactory;
     }
 
@@ -150,6 +152,7 @@ public class KafkaConfig<T> {
         final ConcurrentKafkaListenerContainerFactory<String, CloudEvent> containerFactory =
                 new ConcurrentKafkaListenerContainerFactory<>();
         containerFactory.setConsumerFactory(cloudEventConsumerFactory());
+        containerFactory.getContainerProperties().setAuthExceptionRetryInterval(Duration.ofSeconds(10));
         return containerFactory;
     }
 
 
 package org.onap.cps.ncmp.api.impl.events.cmsubscription.model;
 
-public enum CmSubscriptionStatus {
+public enum CmNotificationSubscriptionStatus {
 
     ACCEPTED("ACCEPTED"), REJECTED("REJECTED"), PENDING("PENDING");
 
-    private final String cmSubscriptionStatusValue;
+    private final String cmNotificationSubscriptionStatusValue;
 
-    CmSubscriptionStatus(final String cmSubscriptionStatusValue) {
-        this.cmSubscriptionStatusValue = cmSubscriptionStatusValue;
+    CmNotificationSubscriptionStatus(final String cmNotificationSubscriptionStatusValue) {
+        this.cmNotificationSubscriptionStatusValue = cmNotificationSubscriptionStatusValue;
     }
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmSubscriptionPredicate.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmSubscriptionPredicate.java
deleted file mode 100644 (file)
index 262126e..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * ============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.model;
-
-import java.util.List;
-import lombok.Getter;
-import lombok.Setter;
-
-@Getter
-@Setter
-public class CmSubscriptionPredicate {
-
-    private List<String> targetFilter;
-    private ScopeFilter scopeFilter;
-
-}
@@ -26,8 +26,8 @@ import lombok.Setter;
 
 @Getter
 @Setter
-public class CmSubscriptionCacheObject {
+public class DmiCmNotificationSubscriptionDetails {
 
-    private List<CmSubscriptionPredicate> cmSubscriptionPredicates;
-    private CmSubscriptionStatus cmSubscriptionStatus;
+    private List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates;
+    private CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus;
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java
new file mode 100644 (file)
index 0000000..189fbb5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ============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.Collection;
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType;
+
+public interface CmNotificationSubscriptionPersistenceService {
+
+    String NCMP_DATASPACE_NAME = "NCMP-Admin";
+    String CM_SUBSCRIPTIONS_ANCHOR_NAME = "cm-data-subscriptions";
+
+    /**
+     * Check if we have an ongoing cm subscription based on the parameters.
+     *
+     * @param datastoreType valid datastore type
+     * @param cmHandleId    cmhandle id
+     * @param xpath         valid xpath
+     * @return true for ongoing cmsubscription , otherwise false
+     */
+    boolean isOngoingCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId,
+            final String xpath);
+
+    /**
+     * Get all ongoing cm notification subscription based on the parameters.
+     *
+     * @param datastoreType valid datastore type
+     * @param cmHandleId    cmhandle id
+     * @param xpath         valid xpath
+     * @return collection of subscription ids of ongoing cm notification subscription
+     */
+    Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType,
+            final String cmHandleId, final String xpath);
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java
new file mode 100644 (file)
index 0000000..ca9adb3
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * ============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.Collection;
+import java.util.Collections;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.api.CpsQueryService;
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.model.DataNode;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotificationSubscriptionPersistenceService {
+
+    private static final String IS_ONGOING_CM_SUBSCRIPTION_CPS_PATH_QUERY = """
+            /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters/filter[@xpath='%s']""";
+
+    private final CpsQueryService cpsQueryService;
+
+    @Override
+    public boolean isOngoingCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId,
+            final String xpath) {
+        return !getOngoingCmNotificationSubscriptionIds(datastoreType, cmHandleId, xpath).isEmpty();
+    }
+
+    @Override
+    public Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType,
+            final String cmHandleId, final String xpath) {
+
+        final String isOngoingCmSubscriptionCpsPathQuery =
+                IS_ONGOING_CM_SUBSCRIPTION_CPS_PATH_QUERY.formatted(datastoreType.getDatastoreName(), cmHandleId,
+                        escapeQuotesByDoublingThem(xpath));
+        final Collection<DataNode> existingNodes =
+                cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
+                        isOngoingCmSubscriptionCpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS);
+        if (existingNodes.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return (List<String>) existingNodes.iterator().next().getLeaves().get("subscribers");
+    }
+
+    private static String escapeQuotesByDoublingThem(final String inputXpath) {
+        return inputXpath.replace("'", "''");
+    }
+}
index 450bc8c..23d508b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-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.
@@ -95,6 +95,7 @@ public class LcmEventsCreator {
             final NcmpServiceCmHandle existingNcmpServiceCmHandle, final LcmEventType lcmEventType) {
         final Event event = new Event();
         event.setCmHandleId(eventCorrelationId);
+        event.setAlternateId(targetNcmpServiceCmHandle.getAlternateId());
         final CmHandleValuesHolder cmHandleValuesHolder =
                 LcmEventsCreatorHelper.determineEventValues(targetNcmpServiceCmHandle, existingNcmpServiceCmHandle,
                         lcmEventType);
index 750be2d..22bbc73 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -68,6 +68,10 @@ public class ModuleOperationsUtils {
     private static final String LOCK_REASON_DETAILS_MSG_FORMAT = UPGRADE_FORMAT + " Attempt #%d failed: %s";
     private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+");
     private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)");
+    private static final String CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE = """
+            //lock-reason[@reason="MODULE_SYNC_FAILED"
+            or @reason="MODULE_UPGRADE"
+            or @reason="MODULE_UPGRADE_FAILED"]""";
 
     /**
      * Query data nodes for cm handles with an "ADVISED" cm handle state.
@@ -110,8 +114,7 @@ public class ModuleOperationsUtils {
      */
     public List<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() {
         final List<DataNode> lockedCmHandlesAsDataNodeList
-                = cmHandleQueries.queryCmHandleAncestorsByCpsPath(
-                "//lock-reason[@reason=\"MODULE_SYNC_FAILED\" or @reason=\"MODULE_UPGRADE\"]",
+                = cmHandleQueries.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE,
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList);
     }
index b21a2f1..72d3226 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-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.
@@ -91,6 +91,9 @@ public class ModuleSyncService {
                                 existingAnchorName, inUpgrade);
                 updateModuleSetTagCache(moduleSetTag, moduleReferencesFromExistingCmHandle);
             } else {
+                if (inUpgrade) {
+                    deleteSchemaSetIfExists(cmHandleId);
+                }
                 final Collection<ModuleReference> allModuleReferencesFromCmHandle
                         = syncAndCreateSchemaSet(yangModelCmHandle);
                 updateModuleSetTagCache(moduleSetTag, allModuleReferencesFromCmHandle);
index 8175fb5..a88adbd 100644 (file)
@@ -73,7 +73,13 @@ public class CmHandleIdMapper {
 
     public void removeMapping(final String cmHandleId) {
         final String alternateId = alternateIdPerCmHandleId.remove(cmHandleId);
-        cmHandleIdPerAlternateId.remove(alternateId);
+        removeAlternateIdWithValidation(alternateId);
+    }
+
+    private void removeAlternateIdWithValidation(final String alternateId) {
+        if (alternateId != null) {
+            cmHandleIdPerAlternateId.remove(alternateId);
+        }
     }
 
     private void initializeCache() {
index b54c154..3be97e8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-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.
@@ -54,6 +54,7 @@ public class YangDataConverter {
         final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties();
         ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId());
         ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState());
+        ncmpServiceCmHandle.setAlternateId(yangModelCmHandle.getAlternateId());
         setDmiProperties(dmiProperties, ncmpServiceCmHandle);
         setPublicProperties(publicProperties, ncmpServiceCmHandle);
         return ncmpServiceCmHandle;
index ba36b1a..03e53fc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation
+ *  Copyright (C) 2021-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.
@@ -119,7 +119,7 @@ public class YangModelCmHandle {
         yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName);
         yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName);
         yangModelCmHandle.setModuleSetTag(moduleSetTag == null ? StringUtils.EMPTY : moduleSetTag);
-        yangModelCmHandle.setAlternateId(alternateId);
+        yangModelCmHandle.setAlternateId(alternateId == null ? StringUtils.EMPTY : alternateId);
         yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
         yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
                 ncmpServiceCmHandle.getPublicProperties()));
index 59bf942..c7ac8ab 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
-import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
-import org.onap.cps.ncmp.api.models.UpgradedCmHandles
-
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 
+import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
+import org.onap.cps.ncmp.api.models.UpgradedCmHandles
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.hazelcast.map.IMap
 import org.onap.cps.api.CpsDataService
@@ -52,14 +51,11 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.utils.JsonObjectMapper
-import spock.lang.Shared
 import spock.lang.Specification
 
 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
-    @Shared
     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
-
     def mockCpsModuleService = Mock(CpsModuleService)
     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
     def mockDmiDataOperations = Mock(DmiDataOperations)
@@ -74,6 +70,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockTrustLevelManager = Mock(TrustLevelManager)
     def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
     def objectUnderTest = getObjectUnderTest()
+    def mockModuleSetTagCache = [:]
 
     def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() {
         given: 'a registration with operations of all types'
@@ -91,7 +88,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         then: 'cm-handles are removed first'
             1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_)
         and: 'de-registered cm handle entry is removed from in progress map'
-            1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
+            2 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
         then: 'cm-handles are created'
             1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_)
         then: 'cm-handles are updated'
@@ -448,7 +445,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
                 mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
                 stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
-                mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin as Map<String, TrustLevel>, mockTrustLevelManager, mockCmHandleIdMapper))
+                mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockCmHandleIdMapper, mockModuleSetTagCache))
     }
 
     def addPersistedYangModelCmHandles(ids) {
index 49583ce..c835056 100644 (file)
@@ -23,8 +23,6 @@
 
 package org.onap.cps.ncmp.api.impl
 
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
-
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -34,6 +32,7 @@ import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RU
 import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
 import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
 
+import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
 import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
@@ -82,6 +81,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
     def mockTrustLevelManager = Mock(TrustLevelManager)
     def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
+    def mockModuleSetTagCache = [:]
 
     def NO_TOPIC = null
     def NO_REQUEST_ID = null
@@ -102,8 +102,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             stubModuleSyncStartedOnCmHandles,
             stubTrustLevelPerDmiPlugin,
             mockTrustLevelManager,
-            mockCmHandleIdMapper
-    )
+            mockCmHandleIdMapper,
+            mockModuleSetTagCache)
 
     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
index bb082fd..fba1f95 100644 (file)
@@ -23,7 +23,6 @@ package org.onap.cps.ncmp.api.impl.async
 import io.cloudevents.core.builder.CloudEventBuilder
 import org.onap.cps.events.EventsPublisher
 import org.onap.cps.ncmp.api.impl.config.kafka.KafkaConfig
-
 import org.onap.cps.ncmp.api.kafka.ConsumerBaseSpec
 import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
 import org.spockframework.spring.SpringBean
@@ -32,6 +31,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.annotation.DirtiesContext
 import org.testcontainers.spock.Testcontainers
+import spock.util.concurrent.PollingConditions
 import java.util.concurrent.TimeUnit
 
 @SpringBootTest(classes =[DataOperationEventConsumer, AsyncRestRequestResponseEventConsumer, RecordFilterStrategies, KafkaConfig])
@@ -50,28 +50,34 @@ class FilterStrategiesIntegrationSpec extends ConsumerBaseSpec {
     def topic
 
     def 'Legacy event consumer with cloud event.'() {
-        given: 'a cloud event of type: #eventType'
+        given: 'a data operation cloud event type'
             def cloudEvent = CloudEventBuilder.v1().withId('some id')
                 .withType('DataOperationEvent')
                 .withSource(URI.create('some-source'))
                 .build()
         when: 'send the cloud event'
             cloudEventKafkaTemplate.send(topic, cloudEvent)
-        and: 'wait a little for async processing of message'
+        then: 'wait a little for async processing of message (must wait to try to avoid false positives)'
             TimeUnit.MILLISECONDS.sleep(300)
-        then: 'event is not consumed'
+        and: 'event is not consumed'
             0 * mockEventsPublisher.publishEvent(*_)
     }
 
     def 'Legacy event consumer with valid legacy event.'() {
-        given: 'a cloud event of type: #eventType'
+        given: 'a legacy event'
             DmiAsyncRequestResponseEvent legacyEvent = new DmiAsyncRequestResponseEvent(eventId:'legacyEventId', eventTarget:'legacyEventTarget')
+        and: 'a flag to track the publish event call'
+            def publishEventMethodCalled = false
+        and: 'the (mocked) events publisher will use the flag to indicate if it is called'
+            mockEventsPublisher.publishEvent(*_) >> {
+                publishEventMethodCalled = true
+            }
         when: 'send the cloud event'
             legacyEventKafkaTemplate.send(topic, legacyEvent)
-        and: 'wait a little for async processing of message'
-            TimeUnit.MILLISECONDS.sleep(300)
         then: 'the event is consumed by the (legacy) AsynRestRequest consumer'
-            1 * mockEventsPublisher.publishEvent(*_)
+            new PollingConditions().within(1) {
+                assert publishEventMethodCalled == true
+            }
     }
 
     def 'Filtering Cloud Events on Type.'() {
@@ -80,17 +86,23 @@ class FilterStrategiesIntegrationSpec extends ConsumerBaseSpec {
                 .withType(eventType)
                 .withSource(URI.create('some-source'))
                 .build()
+        and: 'a flag to track the publish event call'
+            def publishEventMethodCalled = false
+        and: 'the (mocked) events publisher will use the flag to indicate if it is called'
+            mockEventsPublisher.publishCloudEvent(*_) >> {
+                publishEventMethodCalled = true
+            }
         when: 'send the cloud event'
             cloudEventKafkaTemplate.send(topic, cloudEvent)
-        and: 'wait a little for async processing of message'
-            TimeUnit.MILLISECONDS.sleep(300)
         then: 'the event has only been forwarded for the correct type'
-            expectedNUmberOfCallsToPublishForwardedEvent * mockEventsPublisher.publishCloudEvent(*_)
+            new PollingConditions(initialDelay: 0.3).within(1) {
+                assert publishEventMethodCalled == expectCallToPublishEventMethod
+            }
         where: 'the following event types are used'
-            eventType                                        || expectedNUmberOfCallsToPublishForwardedEvent
-            'DataOperationEvent'                             || 1
-            'other type'                                     || 0
-            'any type contain the word "DataOperationEvent"' || 1
+            eventType                                        || expectCallToPublishEventMethod
+            'DataOperationEvent'                             || true
+            'other type'                                     || false
+            'any type contain the word "DataOperationEvent"' || true
     }
 
     //TODO Toine, add positive test with data to prove event is converted correctly (using correct factory)
@@ -98,9 +110,9 @@ class FilterStrategiesIntegrationSpec extends ConsumerBaseSpec {
     def 'Non cloud events on same Topic.'() {
         when: 'sending a non-cloud event on the same topic'
             legacyEventKafkaTemplate.send(topic, 'simple string event')
-        and: 'wait a little for async processing of message'
+        then: 'wait a little for async processing of message (must wait to try to avoid false positives)'
             TimeUnit.MILLISECONDS.sleep(300)
-        then: 'the event is not processed by this consumer'
+        and: 'the event is not processed by this consumer'
             0 * mockEventsPublisher.publishCloudEvent(*_)
     }
 
index 212c673..ee89333 100644 (file)
@@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import io.cloudevents.core.builder.CloudEventBuilder
 import org.onap.cps.events.EventsPublisher
 import org.onap.cps.ncmp.api.impl.config.kafka.KafkaConfig
-
 import org.onap.cps.ncmp.api.kafka.ConsumerBaseSpec
 import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
 import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent
@@ -38,8 +37,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.annotation.DirtiesContext
 import org.testcontainers.spock.Testcontainers
-
-import java.util.concurrent.TimeUnit
+import spock.util.concurrent.PollingConditions
 
 @SpringBootTest(classes =[DataOperationEventConsumer, AsyncRestRequestResponseEventConsumer, RecordFilterStrategies, KafkaConfig])
 @DirtiesContext
@@ -59,33 +57,38 @@ class SerializationIntegrationSpec extends ConsumerBaseSpec {
     @Value('${app.ncmp.async-m2m.topic}')
     def topic
 
-    def capturedForwardedEvent
-
     def 'Forwarding DataOperation Event Data.'() {
         given: 'a data operation cloud event'
             def cloudEvent = createCloudEvent()
+        and: 'a flag to track the publish cloud event call'
+            def publishCloudEventMethodCalled = false
+        and: 'the (mocked) events publisher will use the flag to indicate if it is called and will capture the cloud event'
+            mockEventsPublisher.publishCloudEvent('some client topic', 'some-correlation-id', cloudEvent) >> {
+                publishCloudEventMethodCalled = true
+            }
         when: 'send the event'
             cloudEventKafkaTemplate.send(topic, cloudEvent)
-        and: 'wait a little for async processing of message'
-            TimeUnit.MILLISECONDS.sleep(300)
         then: 'the event has been forwarded'
-            1 * mockEventsPublisher.publishCloudEvent('some client topic', 'some-correlation-id', _) >> { args -> { capturedForwardedEvent = args[2] } }
-        and: 'the forwarded event is identical to the event that was sent'
-            assert capturedForwardedEvent == cloudEvent
+            new PollingConditions().within(1) {
+                assert publishCloudEventMethodCalled == true
+            }
     }
 
     def 'Forwarding AsyncRestRequestResponse Event Data.'() {
         given: 'async request response legacy event'
             def dmiAsyncRequestResponseEvent = new DmiAsyncRequestResponseEvent(eventId: 'my-event-id',eventTarget: 'some client topic')
+        and: 'a flag to track the publish event call'
+            def publishEventMethodCalled = false
+        and: 'the (mocked) events publisher will use the flag to indicate if it is called and will capture the event'
+            mockEventsPublisher.publishEvent(*_) >> {
+                publishEventMethodCalled = true
+            }
         when: 'send the event'
             legacyEventKafkaTemplate.send(topic, dmiAsyncRequestResponseEvent)
-        and: 'wait a little for async processing of message'
-            TimeUnit.MILLISECONDS.sleep(300)
         then: 'the event has been forwarded'
-            1 * mockEventsPublisher.publishEvent('some client topic', 'my-event-id', _) >> { args -> { capturedForwardedEvent = args[2] } }
-        and: 'the captured id and target of the forwarded event is same as the one that was sent'
-            assert capturedForwardedEvent.eventId == dmiAsyncRequestResponseEvent.eventId
-            assert capturedForwardedEvent.eventTarget == dmiAsyncRequestResponseEvent.eventTarget
+            new PollingConditions().within(1) {
+                assert publishEventMethodCalled == true
+            }
     }
 
     def createCloudEvent() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy
new file mode 100644 (file)
index 0000000..a3f41c8
--- /dev/null
@@ -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.config.embeddedcache
+
+import com.hazelcast.core.Hazelcast
+import com.hazelcast.map.IMap
+import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus
+import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails
+import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import spock.lang.Specification
+
+@SpringBootTest(classes = [CmNotificationSubscriptionCacheConfig])
+class CmNotificationSubscriptionCacheConfigSpec extends Specification {
+
+    @Autowired
+    IMap<String, Map<String, DmiCmNotificationSubscriptionDetails>> cmNotificationSubscriptionCache;
+
+    def 'Embedded (hazelcast) cache for Cm Notification Subscription Cache.'() {
+        expect: 'system is able to create an instance of the Cm Notification Subscription Cache'
+            assert null != cmNotificationSubscriptionCache
+        and: 'there is at least 1 instance'
+            assert Hazelcast.allHazelcastInstances.size() > 0
+        and: 'Cm Notification Subscription Cache is present'
+            assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCmNotificationSubscription')
+    }
+
+    def 'Provided CM Subscription data'() {
+        given: 'a cm subscription properties'
+            def subscriptionId = 'sub123'
+            def dmiPluginName = 'dummydmi'
+            def cmSubscriptionPredicate = new DmiCmNotificationSubscriptionPredicate(targetCmHandleIds: ['cmhandle1', 'cmhandle2'], datastoreType: DatastoreType.PASSTHROUGH_RUNNING, xpaths: ['/a/b/c'])
+            def cmSubscriptionCacheObject = new DmiCmNotificationSubscriptionDetails(dmiCmNotificationSubscriptionPredicates: [cmSubscriptionPredicate], cmNotificationSubscriptionStatus: CmNotificationSubscriptionStatus.PENDING)
+        when: 'the cache is populated'
+            cmNotificationSubscriptionCache.put(subscriptionId, [(dmiPluginName): cmSubscriptionCacheObject])
+        then: 'the values are present in memory'
+            assert cmNotificationSubscriptionCache.get(subscriptionId) != null
+        and: 'properties match'
+            assert dmiPluginName == cmNotificationSubscriptionCache.get(subscriptionId).keySet()[0]
+            assert cmSubscriptionCacheObject.cmNotificationSubscriptionStatus == cmNotificationSubscriptionCache.get(subscriptionId).values().cmNotificationSubscriptionStatus[0]
+            assert cmSubscriptionCacheObject.dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds == cmNotificationSubscriptionCache.get(subscriptionId).values().dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds[0]
+    }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmSubscriptionEventCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmSubscriptionEventCacheConfigSpec.groovy
deleted file mode 100644 (file)
index f1eae14..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 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.config.embeddedcache
-
-import com.hazelcast.core.Hazelcast
-import com.hazelcast.map.IMap
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmSubscriptionCacheObject
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmSubscriptionPredicate
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmSubscriptionStatus
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.ScopeFilter
-import org.onap.cps.ncmp.api.impl.operations.DatastoreType
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import spock.lang.Specification
-
-@SpringBootTest(classes = [CmSubscriptionEventCacheConfig])
-class CmSubscriptionEventCacheConfigSpec extends Specification {
-
-    @Autowired
-    IMap<String, Map<String, CmSubscriptionCacheObject>> cmSubscriptionEventCache;
-
-    def 'Embedded (hazelcast) cache for Cm Subscription Event Cache.'() {
-        expect: 'system is able to create an instance of the Forwarded Subscription Event Cache'
-            assert null != cmSubscriptionEventCache
-        and: 'there is at least 1 instance'
-            assert Hazelcast.allHazelcastInstances.size() > 0
-        and: 'Forwarded Subscription Event Cache is present'
-            assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCmSubscriptionEvents')
-    }
-
-    def 'Provided CM Subscription data'() {
-        given: 'a cm subscription properties'
-            def subscriptionId = 'sub123'
-            def dmiPluginName = 'dummydmi'
-            def cmSubscriptionPredicate = new CmSubscriptionPredicate(targetFilter: ['cmhandle1', 'cmhandle2'], scopeFilter: new ScopeFilter(datastoreType: DatastoreType.PASSTHROUGH_RUNNING, xpathFilters: ['/a/b/c']))
-            def cmSubscriptionCacheObject = new CmSubscriptionCacheObject(cmSubscriptionPredicates: [cmSubscriptionPredicate] , cmSubscriptionStatus: CmSubscriptionStatus.PENDING)
-        when: 'the cache is populated'
-            cmSubscriptionEventCache.put(subscriptionId, [(dmiPluginName): cmSubscriptionCacheObject])
-        then: 'the values are present in memory'
-            assert cmSubscriptionEventCache.get(subscriptionId) != null
-        and: 'properties match'
-            assert dmiPluginName == cmSubscriptionEventCache.get(subscriptionId).keySet()[0]
-            assert cmSubscriptionCacheObject.cmSubscriptionStatus == cmSubscriptionEventCache.get(subscriptionId).values().cmSubscriptionStatus[0]
-            assert cmSubscriptionCacheObject.cmSubscriptionPredicates[0].targetFilter == cmSubscriptionEventCache.get(subscriptionId).values().cmSubscriptionPredicates[0].targetFilter[0]
-    }
-}
index 501714a..3b1709d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START========================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-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.
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.context.ContextConfiguration
 import spock.lang.Specification
+import spock.util.concurrent.PollingConditions
 import java.util.concurrent.BlockingQueue
 import java.util.concurrent.TimeUnit
 
@@ -121,8 +122,10 @@ class SynchronizationCacheConfigSpec extends Specification {
             moduleSyncStartedOnCmHandles.put('testKeyModuleSync', 'toBeExpired' as Object, 1, TimeUnit.SECONDS)
         then: 'the entry is present in the map'
             assert moduleSyncStartedOnCmHandles.get('testKeyModuleSync') != null
-        and: 'the entry expires in less then 2 seconds'
-            waitMax2SecondsForKeyExpiration(moduleSyncStartedOnCmHandles, 'testKeyModuleSync')
+        and: 'the entry expires'
+            new PollingConditions().within(10) {
+                assert moduleSyncStartedOnCmHandles.get('testKeyModuleSync') == null
+            }
     }
 
     def 'Time to Live Verify for Data Sync Semaphore'() {
@@ -130,16 +133,10 @@ class SynchronizationCacheConfigSpec extends Specification {
             dataSyncSemaphores.put('testKeyDataSync', Boolean.TRUE, 1, TimeUnit.SECONDS)
         then: 'the entry is present in the map'
             assert dataSyncSemaphores.get('testKeyDataSync') != null
-        and: 'the entry expires in less then 2 seconds'
-            waitMax2SecondsForKeyExpiration(dataSyncSemaphores, 'testKeyDataSync')
-    }
-
-    def waitMax2SecondsForKeyExpiration(map, key) {
-        def count = 0
-        while ( map.get(key)!=null && ++count <= 20 ) {
-            sleep(100)
-        }
-        return count < 20 // Should have expired in less the 20 x 100ms = 2 seconds!
+        and: 'the entry expires'
+            new PollingConditions().within(10) {
+                assert dataSyncSemaphores.get('testKeyDataSync') == null
+            }
     }
 
 }
index 57e77eb..44d6eb6 100644 (file)
@@ -28,8 +28,6 @@ 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.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
 import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
 import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.client_to_ncmp.CmSubscriptionNcmpInEvent
 import org.onap.cps.ncmp.utils.TestUtils
@@ -50,15 +48,13 @@ class CmSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpec {
     @Autowired
     ObjectMapper objectMapper
 
-    @BeforeEach
     void setup() {
-        ((Logger) LoggerFactory.getLogger(CmSubscriptionNcmpInEventConsumer.class)).addAppender(logger);
-        logger.start();
+        ((Logger) LoggerFactory.getLogger(CmSubscriptionNcmpInEventConsumer.class)).addAppender(logger)
+        logger.start()
     }
 
-    @AfterEach
-    void teardown() {
-        ((Logger) LoggerFactory.getLogger(CmSubscriptionNcmpInEventConsumer.class)).detachAndStopAllAppenders();
+    void cleanup() {
+        ((Logger) LoggerFactory.getLogger(CmSubscriptionNcmpInEventConsumer.class)).detachAndStopAllAppenders()
     }
 
 
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy
new file mode 100644 (file)
index 0000000..f6103ef
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * ============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.api.CpsQueryService
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType
+import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.spi.model.DataNode
+import spock.lang.Specification
+
+class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification {
+
+    def mockCpsQueryService = Mock(CpsQueryService)
+
+    def objectUnderTest = new CmNotificationSubscriptionPersistenceServiceImpl(mockCpsQueryService)
+
+    def 'Check ongoing cm subscription #scenario'() {
+        given: 'a valid cm subscription query'
+            def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']";
+        and: 'datanodes optionally returned'
+            1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
+                cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode
+        when: 'we check for an ongoing cm subscription'
+            def response = objectUnderTest.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/cps/path')
+        then: 'we get expected response'
+            assert response == isOngoingCmSubscription
+        where: 'following scenarios are used'
+            scenario                  | dataNode                                                                        || isOngoingCmSubscription
+            'valid datanodes present' | [new DataNode(xpath: '/cps/path', leaves: ['subscribers': ['sub-1', 'sub-2']])] || true
+            'no datanodes present'    | []                                                                              || false
+    }
+}
index 0917953..b7c3b87 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-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.
@@ -173,4 +173,19 @@ class LcmEventsCreatorSpec extends Specification {
             assert result.eventCorrelationId == cmHandleId
             assert result.eventId != null
     }
+
+    def 'Map the LcmEvent for alternate IDs when #scenario'() {
+        given: 'NCMP cm handle details with current and old alternate IDs'
+            def existingNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: existingAlternateId, compositeState: new CompositeState(dataSyncEnabled: false))
+            def targetNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: targetAlternateId, compositeState: new CompositeState(dataSyncEnabled: false))
+        when: 'the event is populated'
+            def result = objectUnderTest.populateLcmEvent(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle)
+        then: 'the alternate ID is present or is an empty string in the payload'
+            assert result.event.alternateId == expectedEventAlternateId
+        where: 'the following alternate IDs are provided'
+            scenario                                      | existingAlternateId | targetAlternateId || expectedEventAlternateId
+            'same new and old alternate ID'               | 'someAlternateId'   | 'someAlternateId' || 'someAlternateId'
+            'blank new and old alternate ID'              | ''                  | ''                || ''
+            'new alternate id and blank old alternate ID' | ''                  | 'someAlternateId' || 'someAlternateId'
+    }
 }
\ No newline at end of file
index 8f3d8d9..827a548 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -128,10 +128,9 @@ class ModuleOperationsUtilsSpec extends Specification{
             'empty module set tag' | ''                 || 'not-specified'
     }
 
-    def 'Get all locked Cm-Handle where Lock Reason is MODULE_SYNC_FAILED cm handle #scenario'() {
+    def 'Get all locked cm-Handles where lock reasons are model sync failed or upgrade'() {
         given: 'the cps (persistence service) returns a collection of data nodes'
-            mockCmHandleQueries.queryCmHandleAncestorsByCpsPath(
-                    '//lock-reason[@reason="MODULE_SYNC_FAILED" or @reason="MODULE_UPGRADE"]',
+            mockCmHandleQueries.queryCmHandleAncestorsByCpsPath(ModuleOperationsUtils.CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE,
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
         when: 'get locked Misbehaving cm handle is called'
             def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade()
index 3bdac18..ece2d9f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022-2023 Nordix Foundation
+ *  Copyright (C) 2022-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,6 @@
 
 package org.onap.cps.ncmp.api.impl.inventory.sync
 
-
 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_SYNC_FAILED
 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE_FAILED
 
@@ -32,8 +31,6 @@ import ch.qos.logback.core.read.ListAppender
 import com.hazelcast.config.Config
 import com.hazelcast.instance.impl.HazelcastInstanceFactory
 import com.hazelcast.map.IMap
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
@@ -50,15 +47,13 @@ class ModuleSyncTasksSpec extends Specification {
 
     def logger = Spy(ListAppender<ILoggingEvent>)
 
-    @BeforeEach
     void setup() {
-        ((Logger) LoggerFactory.getLogger(ModuleSyncTasks.class)).addAppender(logger);
-        logger.start();
+        ((Logger) LoggerFactory.getLogger(ModuleSyncTasks.class)).addAppender(logger)
+        logger.start()
     }
 
-    @AfterEach
-    void teardown() {
-        ((Logger) LoggerFactory.getLogger(ModuleSyncTasks.class)).detachAndStopAllAppenders();
+    void cleanup() {
+        ((Logger) LoggerFactory.getLogger(ModuleSyncTasks.class)).detachAndStopAllAppenders()
     }
 
     def mockInventoryPersistence = Mock(InventoryPersistence)
index 7760b39..f37d5a4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-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.
 
 package org.onap.cps.ncmp.api.impl.inventory.sync.config
 
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
-import org.onap.cps.ncmp.api.impl.inventory.sync.config.WatchdogSchedulingConfigurer
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.context.ConfigurableApplicationContext
 import org.springframework.test.context.ContextConfiguration
 import spock.lang.Specification
 
 @SpringBootTest
-@ContextConfiguration(classes = [ConfigurableApplicationContext, WatchdogSchedulingConfigurer])
+@ContextConfiguration(classes = [WatchdogSchedulingConfigurer])
 class WatchdogSchedulingConfigurerSpec extends Specification {
 
     @Autowired
-    private ConfigurableApplicationContext applicationContext;
-
-    def watchdogSchedulingConfigurer;
-
-    @BeforeEach
-    void setup() {
-        watchdogSchedulingConfigurer = (WatchdogSchedulingConfigurer) applicationContext.getBean("watchdogSchedulingConfigurer")
-    }
-
-    @AfterEach
-    void tearDown() {
-        if (applicationContext != null) {
-            applicationContext.close()
-        }
-    }
+    WatchdogSchedulingConfigurer watchdogSchedulingConfigurer
 
     def 'Validate watchdog scheduling configuration'() {
         given: 'task scheduler configuration properties are loaded as map'
index 3f2148f..2229b32 100644 (file)
@@ -21,8 +21,6 @@
 
 package org.onap.cps.ncmp.api.impl.operations
 
-import org.onap.cps.events.EventsPublisher
-
 import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
@@ -33,8 +31,8 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_D
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING
 
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.events.EventsPublisher
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
-
 import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
 import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder
 import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext
@@ -49,6 +47,7 @@ import org.springframework.http.ResponseEntity
 import org.springframework.test.context.ContextConfiguration
 import org.springframework.http.HttpStatus
 import spock.lang.Shared
+import spock.util.concurrent.PollingConditions
 import java.util.concurrent.TimeoutException
 
 @SpringBootTest
@@ -101,20 +100,24 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def dataOperationBatchRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
             def dataOperationRequest = spiedJsonObjectMapper.convertJsonString(dataOperationBatchRequestJsonData, DataOperationRequest.class)
             dataOperationRequest.dataOperationDefinitions[0].cmHandleIds = [cmHandleId]
-            def requestBodyAsJsonStringArg = null
         and: 'a positive response from DMI service when it is called with valid request parameters'
             def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED)
             def expectedDmiBatchResourceDataUrl = "ncmp/v1/data/topic=my-topic-name"
             def expectedBatchRequestAsJson = '{"operations":[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","cmHandleProperties":{"prop1":"val1"}}]}]}'
             mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ.operationName) >> responseFromDmi
             dmiServiceUrlBuilder.getDataOperationRequestUrl(_, _) >> expectedDmiBatchResourceDataUrl
+        and: ' a flag to track the post operation call'
+            def postOperationWithJsonDataMethodCalled = false
+        and: 'the (mocked) dmi rest client will use the flag to indicate it is called and capture the request body'
+            mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, expectedBatchRequestAsJson, READ) >> {
+                postOperationWithJsonDataMethodCalled = true
+            }
         when: 'get resource data for group of cm handles are invoked'
             objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'requestId')
-        then: 'wait a little to allow execution of service method by task executor (on separate thread)'
-            Thread.sleep(100)
-        then: 'validate ncmp generated dmi request body json args'
-            1 * mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ) >> { args -> requestBodyAsJsonStringArg = args[1] }
-            assert requestBodyAsJsonStringArg == expectedBatchRequestAsJson
+        then: 'validate the post operation was called and ncmp generated dmi request body json args'
+            new PollingConditions().within(1) {
+                assert postOperationWithJsonDataMethodCalled == true
+            }
     }
 
     def 'Execute (async) data operation from DMI service for #scenario.'() {
index 0a2962e..55ccdf3 100644 (file)
@@ -78,6 +78,15 @@ class CmHandleIdMapperSpec extends Specification {
             assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == null
     }
 
+    def 'Attempt to remove a non-existing entry from the cache.'() {
+        when: 'removing an entry that is not cached'
+            objectUnderTest.removeMapping('non-cached cmhandle id')
+        then: 'deleting from the cmhandle cache returns null'
+            assert alternateIdPerCmHandle.remove('non-cached cmhandle id') == null
+        and: 'removal from the alternate id cache is skipped'
+            0 * cmHandlePerAlternateId.remove(_)
+    }
+
     def 'Cannot update existing alternate id.'() {
         given: 'attempt to update an existing alternate id'
             objectUnderTest.addMapping('my cmhandle id', 'other id')
index 2856b98..6e7554b 100644 (file)
@@ -3,7 +3,7 @@
   ============LICENSE_START=======================================================
   Copyright (c) 2021 Pantheon.tech.
   Modifications Copyright (C) 2021 Bell Canada.
-  Modifications Copyright (C) 2021-2023 Nordix Foundation.
+  Modifications Copyright (C) 2021-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.
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.4.3-SNAPSHOT</version>
+    <version>3.4.4-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
                         <include>**/*Test.java</include> <!-- Just in case of having also "normal" JUnit tests -->
                     </includes>
                     <excludes>
-                        <exclude>**/IT*.java</exclude>
+                        <!-- maven-failsafe-plugin will run performance tests in the integration-test module,
+                             so performance tests will not affect Jacoco coverage (jacoco-report module is configured
+                             to aggregate results from tests run with maven-surefire-plugin only) -->
+                        <exclude>**/*PerfTest.java</exclude>
                     </excludes>
                     <environmentVariables>
                         <!--
index 4b743a7..e6e00dc 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 0b31577..022c3b8 100644 (file)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 6006768..2fd369b 100644 (file)
@@ -26,7 +26,7 @@
     <parent>\r
         <groupId>org.onap.cps</groupId>\r
         <artifactId>cps-parent</artifactId>\r
-        <version>3.4.3-SNAPSHOT</version>\r
+        <version>3.4.4-SNAPSHOT</version>\r
         <relativePath>../cps-parent/pom.xml</relativePath>\r
     </parent>\r
 \r
index e661f34..de716df 100644 (file)
@@ -29,7 +29,7 @@
   <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.4.3-SNAPSHOT</version>
+    <version>3.4.4-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
   </parent>
 
index 0f9eb3c..14b949e 100644 (file)
@@ -117,8 +117,8 @@ public class CpsModuleServiceImpl implements CpsModuleService {
         final Collection<String> anchorNames = cpsAnchorService.getAnchors(dataspaceName, schemaSetNames)
             .stream().map(Anchor::getName).collect(Collectors.toSet());
         cpsAnchorService.deleteAnchors(dataspaceName, anchorNames);
-        cpsModulePersistenceService.deleteUnusedYangResourceModules();
         cpsModulePersistenceService.deleteSchemaSets(dataspaceName, schemaSetNames);
+        cpsModulePersistenceService.deleteUnusedYangResourceModules();
         for (final String schemaSetName : schemaSetNames) {
             yangTextSchemaSourceSetCache.removeFromCache(dataspaceName, schemaSetName);
         }
index d7a5b38..909b7bc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-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.
 
 package org.onap.cps.spi.model;
 
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 
 @Getter
 @Setter
+@EqualsAndHashCode(callSuper = true)
+@ToString
 public class ModuleDefinition extends ModuleReference {
 
     private static final long serialVersionUID = -6591435720836327732L;
index 3e8551f..bb881f6 100644 (file)
@@ -34,14 +34,15 @@ ${auth}                   Basic Y3BzdXNlcjpjcHNyMGNrcyE=
 ${ncmpInventoryBasePath}  /ncmpInventory
 ${ncmpBasePath}           /ncmp
 ${dmiUrl}                 http://${DMI_HOST}:${DMI_PORT}
-${jsonDataCreate}         {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","createdCmHandles":[{"cmHandle":"ietfYang-PNFDemo","cmHandleProperties":{"Book1":"Sci-Fi Book"},"publicCmHandleProperties":{"Contact":"storeemail@bookstore.com", "Contact2":"storeemail2@bookstore.com"}}]}
-${jsonDataUpdate}         {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","updatedCmHandles":[{"cmHandle":"ietfYang-PNFDemo","cmHandleProperties":{"Book1":"Romance Book"},"publicCmHandleProperties":{"Contact":"newemailforstore@bookstore.com"}}]}
+${createPayload}         {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","createdCmHandles":[{"cmHandle":"ietfYang-PNFDemo","cmHandleProperties":{"Book1":"Sci-Fi Book"},"publicCmHandleProperties":{"Contact":"storeemail@bookstore.com", "Contact2":"storeemail2@bookstore.com"}},{"cmHandle":"CmHandleForDelete"}]}
+${updatePayload}         {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","updatedCmHandles":[{"cmHandle":"ietfYang-PNFDemo","cmHandleProperties":{"Book1":"Romance Book"},"publicCmHandleProperties":{"Contact":"newemailforstore@bookstore.com"}}]}
+${deletePayload}         {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","removedCmHandles":["CmHandleForDelete"]}
 
 *** Test Cases ***
 Register data node and sync modules.
     ${uri}=              Set Variable       ${ncmpInventoryBasePath}/v1/ch
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
-    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonDataCreate}
+    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${createPayload}
     Should Be Equal As Strings              ${response.status_code}   200
 
 Get CM Handle details and confirm it has been registered.
@@ -60,7 +61,7 @@ Get CM Handle details and confirm it has been registered.
 Update data node and sync modules.
     ${uri}=              Set Variable       ${ncmpInventoryBasePath}/v1/ch
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
-    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonDataUpdate}
+    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${updatePayload}
     Should Be Equal As Strings              ${response.status_code}   200
 
 Get CM Handle details and confirm it has been updated.
@@ -76,6 +77,17 @@ Get CM Handle details and confirm it has been updated.
            END
     END
 
+Delete cm handle
+    ${uri}=              Set Variable       ${ncmpInventoryBasePath}/v1/ch
+    ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
+    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${deletePayload}
+    Should Be Equal As Strings              ${response.status_code}   200
+
+Get cm handle details and confirm it has been deleted
+    ${uri}=              Set Variable       ${ncmpBasePath}/v1/ch/CmHandleForDelete
+    ${headers}=          Create Dictionary  Authorization=${auth}
+    ${response}=         GET On Session     CPS_URL   ${uri}   headers=${headers}   expected_status=404
+
 Get modules for registered data node
     ${uri}=              Set Variable       ${ncmpBasePath}/v1/ch/ietfYang-PNFDemo/modules
     ${headers}=          Create Dictionary  Authorization=${auth}
index cbb5155..eca8871 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
     </parent>
 
     <artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
index 9b6e402..d3142f8 100644 (file)
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
     </parent>
     <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
 
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_ResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_ResourcesResponse.json
new file mode 100644 (file)
index 0000000..5d71391
--- /dev/null
@@ -0,0 +1,12 @@
+[
+       {
+               "moduleName": "M1",
+               "revision": "2024-01-01",
+               "yangSource": "module M1 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M1\";\n  prefix \"yang\";\n\n  organization\n   \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n  contact\n   \"WG Web:   <http://tools.ietf.org/wg/netmod/>\n    WG List:  <mailto:netmod@ietf.org>\n\n    WG Chair: David Kessens\n              <mailto:david.kessens@nsn.com>\n\n    WG Chair: Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\n\n    Editor:   Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n  description\n   \"This module contains a collection of generally useful derived\n    YANG data types.\n\n    Copyright (c) 2013 IETF Trust and the persons identified as\n    authors of the code.  All rights reserved.\n\n    Redistribution and use in source and binary forms, with or\n    without modification, is permitted pursuant to, and subject\n    to the license terms contained in, the Simplified BSD License\n    set forth in Section 4.c of the IETF Trust's Legal Provisions\n    Relating to IETF Documents\n    (http://trustee.ietf.org/license-info).\n\n    This version of this YANG module is part of RFC 6991; see\n    the RFC itself for full legal notices.\";\n\n  revision 2024-01-01 {\n    description\n     \"This revision adds the following new data types:\n      - yang-identifier\n      - hex-string\n      - uuid\n      - dotted-quad\";\n    reference\n     \"RFC 6991: Common YANG Data Types\";\n  }\n\n  revision 2010-09-24 {\n    description\n     \"Initial revision.\";\n    reference\n     \"RFC 6021: Common YANG Data Types\";\n  }\n\n  /*** collection of counter and gauge types ***/\n\n  typedef counter32 {\n    type uint32;\n    description\n     \"The counter32 type represents a non-negative integer\n      that monotonically increases until it reaches a\n      maximum value of 2^32-1 (4294967295 decimal), when it\n      wraps around and starts increasing again from zero.\n\n      Counters have no defined 'initial' value, and thus, a\n      single value of a counter has (in general) no information\n      content.  Discontinuities in the monotonically increasing\n      value normally occur at re-initialization of the\n      management system, and at other times as specified in the\n      description of a schema node using this type.  If such\n      other times can occur, for example, the creation of\n      a schema node of type counter32 at times other than\n      re-initialization, then a corresponding schema node\n      should be defined, with an appropriate type, to indicate\n      the last discontinuity.\n\n      The counter32 type should not be used for configuration\n      schema nodes.  A default statement SHOULD NOT be used in\n      combination with the type counter32.\n\n      In the value set and its semantics, this type is equivalent\n      to the Counter32 type of the SMIv2.\";\n    reference\n     \"RFC 2578: Structure of Management Information Version 2\n                (SMIv2)\";\n  }\n}\n"
+       },
+       {
+               "moduleName": "M2",
+               "revision": "2024-01-02",
+               "yangSource": "module M2 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M2\";\n  prefix \"yang\";\n\n  organization\n   \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n  contact\n   \"WG Web:   <http://tools.ietf.org/wg/netmod/>\n    WG List:  <mailto:netmod@ietf.org>\n\n    WG Chair: David Kessens\n              <mailto:david.kessens@nsn.com>\n\n    WG Chair: Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\n\n    Editor:   Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n  description\n   \"This module contains a collection of generally useful derived\n    YANG data types.\n\n    Copyright (c) 2013 IETF Trust and the persons identified as\n    authors of the code.  All rights reserved.\n\n    Redistribution and use in source and binary forms, with or\n    without modification, is permitted pursuant to, and subject\n    to the license terms contained in, the Simplified BSD License\n    set forth in Section 4.c of the IETF Trust's Legal Provisions\n    Relating to IETF Documents\n    (http://trustee.ietf.org/license-info).\n\n    This version of this YANG module is part of RFC 6991; see\n    the RFC itself for full legal notices.\";\n\n  revision 2024-01-02 {\n    description\n     \"This revision adds the following new data types:\n      - yang-identifier\n      - hex-string\n      - uuid\n      - dotted-quad\";\n    reference\n     \"RFC 6991: Common YANG Data Types\";\n  }\n\n  revision 2010-09-24 {\n    description\n     \"Initial revision.\";\n    reference\n     \"RFC 6021: Common YANG Data Types\";\n  }\n\n  /*** collection of counter and gauge types ***/\n\n  typedef counter32 {\n    type uint32;\n    description\n     \"The counter32 type represents a non-negative integer\n      that monotonically increases until it reaches a\n      maximum value of 2^32-1 (4294967295 decimal), when it\n      wraps around and starts increasing again from zero.\n\n      Counters have no defined 'initial' value, and thus, a\n      single value of a counter has (in general) no information\n      content.  Discontinuities in the monotonically increasing\n      value normally occur at re-initialization of the\n      management system, and at other times as specified in the\n      description of a schema node using this type.  If such\n      other times can occur, for example, the creation of\n      a schema node of type counter32 at times other than\n      re-initialization, then a corresponding schema node\n      should be defined, with an appropriate type, to indicate\n      the last discontinuity.\n\n      The counter32 type should not be used for configuration\n      schema nodes.  A default statement SHOULD NOT be used in\n      combination with the type counter32.\n\n      In the value set and its semantics, this type is equivalent\n      to the Counter32 type of the SMIv2.\";\n    reference\n     \"RFC 2578: Structure of Management Information Version 2\n                (SMIv2)\";\n  }\n}\n"
+       }
+]
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_Response.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreAWithModules_M1_M2_Response.json
new file mode 100644 (file)
index 0000000..9f20564
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "schemas": [
+               {
+                       "moduleName": "M1",
+                       "revision": "2024-01-01"
+               },
+               {
+                       "moduleName": "M2",
+                       "revision": "2024-01-02"
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_ResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_ResourcesResponse.json
new file mode 100644 (file)
index 0000000..ef9b85f
--- /dev/null
@@ -0,0 +1,12 @@
+[
+       {
+               "moduleName": "M1",
+               "revision": "2024-01-01",
+               "yangSource": "module M1 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M1\";\n  prefix \"yang\";\n\n  organization\n   \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n  contact\n   \"WG Web:   <http://tools.ietf.org/wg/netmod/>\n    WG List:  <mailto:netmod@ietf.org>\n\n    WG Chair: David Kessens\n              <mailto:david.kessens@nsn.com>\n\n    WG Chair: Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\n\n    Editor:   Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n  description\n   \"This module contains a collection of generally useful derived\n    YANG data types.\n\n    Copyright (c) 2013 IETF Trust and the persons identified as\n    authors of the code.  All rights reserved.\n\n    Redistribution and use in source and binary forms, with or\n    without modification, is permitted pursuant to, and subject\n    to the license terms contained in, the Simplified BSD License\n    set forth in Section 4.c of the IETF Trust's Legal Provisions\n    Relating to IETF Documents\n    (http://trustee.ietf.org/license-info).\n\n    This version of this YANG module is part of RFC 6991; see\n    the RFC itself for full legal notices.\";\n\n  revision 2024-01-01 {\n    description\n     \"This revision adds the following new data types:\n      - yang-identifier\n      - hex-string\n      - uuid\n      - dotted-quad\";\n    reference\n     \"RFC 6991: Common YANG Data Types\";\n  }\n\n  revision 2010-09-24 {\n    description\n     \"Initial revision.\";\n    reference\n     \"RFC 6021: Common YANG Data Types\";\n  }\n\n  /*** collection of counter and gauge types ***/\n\n  typedef counter32 {\n    type uint32;\n    description\n     \"The counter32 type represents a non-negative integer\n      that monotonically increases until it reaches a\n      maximum value of 2^32-1 (4294967295 decimal), when it\n      wraps around and starts increasing again from zero.\n\n      Counters have no defined 'initial' value, and thus, a\n      single value of a counter has (in general) no information\n      content.  Discontinuities in the monotonically increasing\n      value normally occur at re-initialization of the\n      management system, and at other times as specified in the\n      description of a schema node using this type.  If such\n      other times can occur, for example, the creation of\n      a schema node of type counter32 at times other than\n      re-initialization, then a corresponding schema node\n      should be defined, with an appropriate type, to indicate\n      the last discontinuity.\n\n      The counter32 type should not be used for configuration\n      schema nodes.  A default statement SHOULD NOT be used in\n      combination with the type counter32.\n\n      In the value set and its semantics, this type is equivalent\n      to the Counter32 type of the SMIv2.\";\n    reference\n     \"RFC 2578: Structure of Management Information Version 2\n                (SMIv2)\";\n  }\n}\n"
+       },
+       {
+               "moduleName": "M3",
+               "revision": "2024-01-03",
+               "yangSource": "module M3 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M3\";\n  prefix \"yang\";\n\n  organization\n   \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n  contact\n   \"WG Web:   <http://tools.ietf.org/wg/netmod/>\n    WG List:  <mailto:netmod@ietf.org>\n\n    WG Chair: David Kessens\n              <mailto:david.kessens@nsn.com>\n\n    WG Chair: Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\n\n    Editor:   Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n  description\n   \"This module contains a collection of generally useful derived\n    YANG data types.\n\n    Copyright (c) 2013 IETF Trust and the persons identified as\n    authors of the code.  All rights reserved.\n\n    Redistribution and use in source and binary forms, with or\n    without modification, is permitted pursuant to, and subject\n    to the license terms contained in, the Simplified BSD License\n    set forth in Section 4.c of the IETF Trust's Legal Provisions\n    Relating to IETF Documents\n    (http://trustee.ietf.org/license-info).\n\n    This version of this YANG module is part of RFC 6991; see\n    the RFC itself for full legal notices.\";\n\n  revision 2024-01-03 {\n    description\n     \"This revision adds the following new data types:\n      - yang-identifier\n      - hex-string\n      - uuid\n      - dotted-quad\";\n    reference\n     \"RFC 6991: Common YANG Data Types\";\n  }\n\n  revision 2010-09-24 {\n    description\n     \"Initial revision.\";\n    reference\n     \"RFC 6021: Common YANG Data Types\";\n  }\n\n  /*** collection of counter and gauge types ***/\n\n  typedef counter32 {\n    type uint32;\n    description\n     \"The counter32 type represents a non-negative integer\n      that monotonically increases until it reaches a\n      maximum value of 2^32-1 (4294967295 decimal), when it\n      wraps around and starts increasing again from zero.\n\n      Counters have no defined 'initial' value, and thus, a\n      single value of a counter has (in general) no information\n      content.  Discontinuities in the monotonically increasing\n      value normally occur at re-initialization of the\n      management system, and at other times as specified in the\n      description of a schema node using this type.  If such\n      other times can occur, for example, the creation of\n      a schema node of type counter32 at times other than\n      re-initialization, then a corresponding schema node\n      should be defined, with an appropriate type, to indicate\n      the last discontinuity.\n\n      The counter32 type should not be used for configuration\n      schema nodes.  A default statement SHOULD NOT be used in\n      combination with the type counter32.\n\n      In the value set and its semantics, this type is equivalent\n      to the Counter32 type of the SMIv2.\";\n    reference\n     \"RFC 2578: Structure of Management Information Version 2\n                (SMIv2)\";\n  }\n}\n"
+       }
+]
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_Response.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreBWithModules_M1_M3_Response.json
new file mode 100644 (file)
index 0000000..513c749
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "schemas": [
+               {
+                       "moduleName": "M1",
+                       "revision": "2024-01-01"
+               },
+               {
+                       "moduleName": "M3",
+                       "revision": "2024-01-03"
+               }
+       ]
+}
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_ResourcesResponse.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_ResourcesResponse.json
new file mode 100644 (file)
index 0000000..5d71391
--- /dev/null
@@ -0,0 +1,12 @@
+[
+       {
+               "moduleName": "M1",
+               "revision": "2024-01-01",
+               "yangSource": "module M1 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M1\";\n  prefix \"yang\";\n\n  organization\n   \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n  contact\n   \"WG Web:   <http://tools.ietf.org/wg/netmod/>\n    WG List:  <mailto:netmod@ietf.org>\n\n    WG Chair: David Kessens\n              <mailto:david.kessens@nsn.com>\n\n    WG Chair: Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\n\n    Editor:   Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n  description\n   \"This module contains a collection of generally useful derived\n    YANG data types.\n\n    Copyright (c) 2013 IETF Trust and the persons identified as\n    authors of the code.  All rights reserved.\n\n    Redistribution and use in source and binary forms, with or\n    without modification, is permitted pursuant to, and subject\n    to the license terms contained in, the Simplified BSD License\n    set forth in Section 4.c of the IETF Trust's Legal Provisions\n    Relating to IETF Documents\n    (http://trustee.ietf.org/license-info).\n\n    This version of this YANG module is part of RFC 6991; see\n    the RFC itself for full legal notices.\";\n\n  revision 2024-01-01 {\n    description\n     \"This revision adds the following new data types:\n      - yang-identifier\n      - hex-string\n      - uuid\n      - dotted-quad\";\n    reference\n     \"RFC 6991: Common YANG Data Types\";\n  }\n\n  revision 2010-09-24 {\n    description\n     \"Initial revision.\";\n    reference\n     \"RFC 6021: Common YANG Data Types\";\n  }\n\n  /*** collection of counter and gauge types ***/\n\n  typedef counter32 {\n    type uint32;\n    description\n     \"The counter32 type represents a non-negative integer\n      that monotonically increases until it reaches a\n      maximum value of 2^32-1 (4294967295 decimal), when it\n      wraps around and starts increasing again from zero.\n\n      Counters have no defined 'initial' value, and thus, a\n      single value of a counter has (in general) no information\n      content.  Discontinuities in the monotonically increasing\n      value normally occur at re-initialization of the\n      management system, and at other times as specified in the\n      description of a schema node using this type.  If such\n      other times can occur, for example, the creation of\n      a schema node of type counter32 at times other than\n      re-initialization, then a corresponding schema node\n      should be defined, with an appropriate type, to indicate\n      the last discontinuity.\n\n      The counter32 type should not be used for configuration\n      schema nodes.  A default statement SHOULD NOT be used in\n      combination with the type counter32.\n\n      In the value set and its semantics, this type is equivalent\n      to the Counter32 type of the SMIv2.\";\n    reference\n     \"RFC 2578: Structure of Management Information Version 2\n                (SMIv2)\";\n  }\n}\n"
+       },
+       {
+               "moduleName": "M2",
+               "revision": "2024-01-02",
+               "yangSource": "module M2 {\n\n  namespace \"urn:ietf:params:xml:ns:yang:M2\";\n  prefix \"yang\";\n\n  organization\n   \"IETF NETMOD (NETCONF Data Modeling Language) Working Group\";\n\n  contact\n   \"WG Web:   <http://tools.ietf.org/wg/netmod/>\n    WG List:  <mailto:netmod@ietf.org>\n\n    WG Chair: David Kessens\n              <mailto:david.kessens@nsn.com>\n\n    WG Chair: Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\n\n    Editor:   Juergen Schoenwaelder\n              <mailto:j.schoenwaelder@jacobs-university.de>\";\n\n  description\n   \"This module contains a collection of generally useful derived\n    YANG data types.\n\n    Copyright (c) 2013 IETF Trust and the persons identified as\n    authors of the code.  All rights reserved.\n\n    Redistribution and use in source and binary forms, with or\n    without modification, is permitted pursuant to, and subject\n    to the license terms contained in, the Simplified BSD License\n    set forth in Section 4.c of the IETF Trust's Legal Provisions\n    Relating to IETF Documents\n    (http://trustee.ietf.org/license-info).\n\n    This version of this YANG module is part of RFC 6991; see\n    the RFC itself for full legal notices.\";\n\n  revision 2024-01-02 {\n    description\n     \"This revision adds the following new data types:\n      - yang-identifier\n      - hex-string\n      - uuid\n      - dotted-quad\";\n    reference\n     \"RFC 6991: Common YANG Data Types\";\n  }\n\n  revision 2010-09-24 {\n    description\n     \"Initial revision.\";\n    reference\n     \"RFC 6021: Common YANG Data Types\";\n  }\n\n  /*** collection of counter and gauge types ***/\n\n  typedef counter32 {\n    type uint32;\n    description\n     \"The counter32 type represents a non-negative integer\n      that monotonically increases until it reaches a\n      maximum value of 2^32-1 (4294967295 decimal), when it\n      wraps around and starts increasing again from zero.\n\n      Counters have no defined 'initial' value, and thus, a\n      single value of a counter has (in general) no information\n      content.  Discontinuities in the monotonically increasing\n      value normally occur at re-initialization of the\n      management system, and at other times as specified in the\n      description of a schema node using this type.  If such\n      other times can occur, for example, the creation of\n      a schema node of type counter32 at times other than\n      re-initialization, then a corresponding schema node\n      should be defined, with an appropriate type, to indicate\n      the last discontinuity.\n\n      The counter32 type should not be used for configuration\n      schema nodes.  A default statement SHOULD NOT be used in\n      combination with the type counter32.\n\n      In the value set and its semantics, this type is equivalent\n      to the Counter32 type of the SMIv2.\";\n    reference\n     \"RFC 2578: Structure of Management Information Version 2\n                (SMIv2)\";\n  }\n}\n"
+       }
+]
\ No newline at end of file
diff --git a/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_Response.json b/dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/module/bookStoreCWithModules_M1_M2_Response.json
new file mode 100644 (file)
index 0000000..9f20564
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       "schemas": [
+               {
+                       "moduleName": "M1",
+                       "revision": "2024-01-01"
+               },
+               {
+                       "moduleName": "M2",
+                       "revision": "2024-01-02"
+               }
+       ]
+}
\ No newline at end of file
index d708f7c..9fb40e3 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 11d39b5..906945d 100644 (file)
@@ -1,7 +1,7 @@
 # ============LICENSE_START=======================================================
 # Copyright (c) 2020 Pantheon.tech.
 # Modifications Copyright (C) 2021 Bell Canada.
-# Modifications Copyright (C) 2022-2023 Nordix Foundation.
+# Modifications Copyright (C) 2022-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.
@@ -31,6 +31,14 @@ services:
       POSTGRES_DB: cpsdb
       POSTGRES_USER: ${DB_USERNAME:-cps}
       POSTGRES_PASSWORD: ${DB_PASSWORD:-cps}
+    deploy:
+      resources:
+        reservations:
+          cpus: '1'
+          memory: 1G
+        limits:
+          cpus: '6'
+          memory: 3G
 
   cps-and-ncmp:
     container_name: cps-and-ncmp
@@ -55,6 +63,14 @@ services:
     restart: unless-stopped
     depends_on:
       - dbpostgresql
+    deploy:
+      resources:
+        reservations:
+          cpus: '2'
+          memory: 2G
+        limits:
+          cpus: '3'
+          memory: 3G
 
   ### if kafka is not required comment out zookeeper and kafka ###
   zookeeper:
index 8098fae..a1cc5d9 100644 (file)
@@ -918,9 +918,9 @@ paths:
       - network-cm-proxy
   /v1/ch/{cm-handle}/modules/definitions:
     get:
-      description: "Fetch all module definitions (name, revision, yang resource) for\
-        \ a given cm handle"
-      operationId: getModuleDefinitionsByCmHandleId
+      description: "Get module definitions (module name, revision, yang resource)\
+        \ with options to filter on module name and revision"
+      operationId: getModuleDefinitions
       parameters:
       - description: "The identifier for a network function, network element, subnetwork\
           \ or any other cm object by managed Network CM Proxy"
@@ -930,6 +930,21 @@ paths:
         schema:
           example: my-cm-handle
           type: string
+      - description: Filter for a module name.This is an optional parameter
+        in: query
+        name: module-name
+        required: false
+        schema:
+          example: my-module
+          type: string
+      - description: Filter for a module revision.This is an optional parameter and
+          ignored when no module name is supplied
+        in: query
+        name: revision
+        required: false
+        schema:
+          example: 2024-01-22
+          type: string
       responses:
         "200":
           content:
@@ -969,8 +984,7 @@ paths:
               schema:
                 $ref: '#/components/schemas/ErrorMessage'
           description: Internal Server Error
-      summary: "Fetch all module definitions (name, revision, yang resource) for a\
-        \ given cm handle"
+      summary: Get module definitions
       tags:
       - network-cm-proxy
   /v1/ch/searches:
@@ -1666,6 +1680,23 @@ components:
       schema:
         default: /
         type: string
+    moduleNameInQuery:
+      description: Filter for a module name.This is an optional parameter
+      in: query
+      name: module-name
+      required: false
+      schema:
+        example: my-module
+        type: string
+    revisionInQuery:
+      description: Filter for a module revision.This is an optional parameter and
+        ignored when no module name is supplied
+      in: query
+      name: revision
+      required: false
+      schema:
+        example: 2024-01-22
+        type: string
     dataSyncEnabled:
       description: Is used to enable or disable the data synchronization flag
       in: query
index ca7824d..3b5aad1 100644 (file)
@@ -333,7 +333,7 @@ Below are the list of distributed datastructures that we have.
 +--------------+------------------------------------+-----------------------------------------------------------+
 | cps-ncmp     | moduleSetTagCacheMapConfig         | Stores the module set tags for cm handles.                |
 +--------------+------------------------------------+-----------------------------------------------------------+
-| cps-ncmp     | cmSubscriptionEventCache           | Stores and tracks cm notification subscription requests.  |
+| cps-ncmp     | cmNotificationSubscriptionCache    | Stores and tracks cm notification subscription requests.  |
 +--------------+------------------------------------+-----------------------------------------------------------+
 | cps-ncmp     | alternateIdPerCmHandleId           | Stores the alternate id for each cm handle id.            |
 +--------------+------------------------------------+-----------------------------------------------------------+
index 80eb5f1..af1f5ab 100644 (file)
@@ -72,6 +72,12 @@ CPS Path
 Several CPS APIs use the cps-path (or cpsPath in Java API) parameter.
 The CPS Path is described in detail in :doc:`cps-path`.
 
+CPS Delta
+=========
+
+CPS Delta feature provides the ability to find the delta/difference between two JSON configurations.
+The CPS Delta feature is described in detail in :doc:`cps-delta-feature`.
+
 NCMP CM Handle Querying
 =======================
 
index f5cef76..191a04d 100644 (file)
@@ -16,6 +16,34 @@ CPS Release Notes
 ..      * * *   NEW DELHI   * * *
 ..      =========================
 
+Version: 3.4.4
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project**                      |                                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images**                    | onap/cps-and-ncmp:3.4.4                                |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation**              | 3.4.4 New Delhi                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release date**                     | Not yet released                                       |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.4.4
+
+Features
+--------
+
+
 Version: 3.4.3
 ==============
 
@@ -32,18 +60,27 @@ Release Data
 | **Release designation**              | 3.4.3 New Delhi                                        |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
-| **Release date**                     | Not yet released                                       |
+| **Release date**                     | 2024 February 07                                       |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
 
 Bug Fixes
 ---------
 3.4.3
-
+    - `CPS-2000 <https://jira.onap.org/browse/CPS-2000>`_ Fix for Schema object cache not being distributed.
+    - `CPS-2027 <https://jira.onap.org/browse/CPS-2027>`_ Fixes for upgrade yang modules using module set tag.
+    - `CPS-2070 <https://jira.onap.org/browse/CPS-2070>`_ Add retry interval for Kafka consumer.
 
 Features
 --------
     - `CPS-1824 <https://jira.onap.org/browse/CPS-1824>`_ CPS Delta between 2 anchors.
+    - `CPS-2072 <https://jira.onap.org/browse/CPS-2072>`_ Add maven classifier to Spring Boot JAR.
+
+Notes
+-----
+The maven build of cps-application has been changed so that the JAR produced by spring-boot-maven-plugin has a
+*-springboot* classifier (`CPS-2072 <https://jira.onap.org/browse/CPS-2072>`_). This means that the filename
+of the Spring Boot JAR is *cps-application-3.4.3-springboot.jar*.
 
 Version: 3.4.2
 ==============
index f03b1c6..6947a94 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
   ============LICENSE_START=======================================================
-  Copyright (c) 2023 Nordix Foundation
+  Copyright (c) 2023-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.
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
-
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>integration-test</artifactId>
     </dependencies>
 
     <profiles>
+        <!-- Performance tests are run with maven-failsafe-plugin using a separate profile, so they will
+             not affect Jacoco coverage. Heap size is set here to ensure consistent test environment. -->
         <profile>
-            <id>default</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
+            <id>include-performance</id>
+            <properties>
+                <failsafeArgLine>-Xms512m -Xmx512m</failsafeArgLine>
+            </properties>
             <build>
                 <plugins>
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-surefire-plugin</artifactId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
                         <configuration>
-                            <excludes>
-                                <exclude>%regex[.*PerfTest.*]</exclude>
-                            </excludes>
+                            <includes>
+                                <include>**/*PerfTest.java</include>
+                            </includes>
                         </configuration>
                     </plugin>
                 </plugins>
             </build>
         </profile>
-        <profile>
-            <id>include-performance</id>
-        </profile>
     </profiles>
 
-
 </project>
index 6dec3db..b59e0ff 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-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.
@@ -89,8 +89,7 @@ class CpsIntegrationSpecBase extends Specification {
     def setup() {
         if (!initialized) {
             cpsDataspaceService.createDataspace(GENERAL_TEST_DATASPACE)
-            def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
-            cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, [bookstore : bookstoreModelFileContent])
+            createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE)
             initialized = true;
         }
     }
@@ -111,6 +110,20 @@ class CpsIntegrationSpecBase extends Specification {
         return new File('src/test/resources/data/' + filename).text
     }
 
+    def getBookstoreYangResourcesNameToContentMap() {
+        def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
+        def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
+        return [bookstore: bookstoreModelFileContent, bookstoreTypes: bookstoreTypesFileContent]
+    }
+
+    def createStandardBookStoreSchemaSet(targetDataspace) {
+        cpsModuleService.createSchemaSet(targetDataspace, BOOKSTORE_SCHEMA_SET, getBookstoreYangResourcesNameToContentMap())
+    }
+
+    def createStandardBookStoreSchemaSet(targetDataspace, targetSchemaSet) {
+        cpsModuleService.createSchemaSet(targetDataspace, targetSchemaSet, getBookstoreYangResourcesNameToContentMap())
+    }
+
     def dataspaceExists(dataspaceName) {
         try {
             cpsDataspaceService.getDataspace(dataspaceName)
index 20687c3..b3b5842 100644 (file)
@@ -53,11 +53,9 @@ class FunctionalSpecBase extends CpsIntegrationSpecBase {
         cpsDataspaceService.createDataspace(FUNCTIONAL_TEST_DATASPACE_1)
         cpsDataspaceService.createDataspace(FUNCTIONAL_TEST_DATASPACE_2)
         cpsDataspaceService.createDataspace(FUNCTIONAL_TEST_DATASPACE_3)
-        def bookstoreYangModelAsString = readResourceDataFile('bookstore/bookstore.yang')
-        cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
-        cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_2, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
-        cpsModuleService.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_3, BOOKSTORE_SCHEMA_SET, [bookstore: bookstoreYangModelAsString])
-
+        createStandardBookStoreSchemaSet(FUNCTIONAL_TEST_DATASPACE_1)
+        createStandardBookStoreSchemaSet(FUNCTIONAL_TEST_DATASPACE_2)
+        createStandardBookStoreSchemaSet(FUNCTIONAL_TEST_DATASPACE_3)
     }
 
     def addBookstoreData() {
index 99dab84..04c5dfc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-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.
 
 package org.onap.cps.integration.functional
 
+import java.time.OffsetDateTime
+
 import org.onap.cps.api.CpsAnchorService
 import org.onap.cps.integration.base.CpsIntegrationSpecBase
 import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.exceptions.AlreadyDefinedException
 import org.onap.cps.spi.exceptions.AnchorNotFoundException
 
-import java.time.OffsetDateTime
-
 class CpsAnchorServiceIntegrationSpec extends CpsIntegrationSpecBase {
 
     CpsAnchorService objectUnderTest
@@ -56,8 +56,7 @@ class CpsAnchorServiceIntegrationSpec extends CpsIntegrationSpecBase {
             objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'anchor1')
             objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, 'anchor2')
         and: '1 anchor with "other" schema set is created'
-            def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
-            cpsModuleService.createSchemaSet(GENERAL_TEST_DATASPACE, 'otherSchemaSet', [someFileName: bookstoreModelFileContent])
+            createStandardBookStoreSchemaSet(GENERAL_TEST_DATASPACE, 'otherSchemaSet')
             objectUnderTest.createAnchor(GENERAL_TEST_DATASPACE, 'otherSchemaSet', 'anchor3')
         then: 'there are 3 anchors in the general test database'
             assert objectUnderTest.getAnchors(GENERAL_TEST_DATASPACE).size() == 3
@@ -69,7 +68,7 @@ class CpsAnchorServiceIntegrationSpec extends CpsIntegrationSpecBase {
 
     def 'Querying anchor(name)s (depends on previous test!).'() {
         expect: 'there are now 3 anchors using the "stores" module (both schema sets use the same modules) '
-            assert objectUnderTest.queryAnchorNames(GENERAL_TEST_DATASPACE, ['stores']).size() == 3
+            assert objectUnderTest.queryAnchorNames(GENERAL_TEST_DATASPACE, ['stores', 'bookstore-types']).size() == 3
         and: 'there are no anchors using both "stores" and a "unused-model"'
             assert objectUnderTest.queryAnchorNames(GENERAL_TEST_DATASPACE, ['stores', 'unused-model']).size() == 0
     }
index 3843a9f..6499653 100644 (file)
@@ -555,8 +555,7 @@ class CpsDataServiceIntegrationSpec extends FunctionalSpecBase {
             assert deltaReportEntities.get('xpaths').containsAll(["/bookstore/categories[@code='1']/books[@title='The Gruffalo']", "/bookstore/categories[@code='1']/books[@title='Matilda']"])
         and: 'the delta report also has expected source and target data of child nodes'
             assert deltaReportEntities.get('sourcePayload').containsAll(expectedSourceDataInChildNode)
-            assert deltaReportEntities.get('targetPayload').containsAll(expectedTargetDataInChildNode)
-
+            //assert deltaReportEntities.get('targetPayload').containsAll(expectedTargetDataInChildNode) CPS-2057
     }
 
     def getDeltaReportEntities(List<DeltaReport> deltaReport) {
index 8029ae0..3807a14 100644 (file)
@@ -35,8 +35,11 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
 
     CpsModuleService objectUnderTest
 
-    private static def originalNumberOfModuleReferences = 1
-    private static def existingModuleReference = new ModuleReference('stores','2020-09-15')
+    private static def originalNumberOfModuleReferences = 2 // bookstore has two modules
+    private static def bookStoreModuleReference = new ModuleReference('stores','2024-01-30')
+    private static def bookStoreModuleReferenceWithNamespace = new ModuleReference('stores','2024-01-30', 'org:onap:cps:sample')
+    private static def bookStoreTypesModuleReference = new ModuleReference('bookstore-types','2024-01-30')
+    private static def bookStoreTypesModuleReferenceWithNamespace = new ModuleReference('bookstore-types','2024-01-30', 'org:onap:cps:types:sample')
     static def NEW_RESOURCE_REVISION = '2023-05-10'
     static def NEW_RESOURCE_CONTENT = 'module test_module {\n' +
         '    yang-version 1.1;\n' +
@@ -53,6 +56,8 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
     def newYangResourcesNameToContentMap = [:]
     def moduleReferences = []
     def noNewModules = [:]
+    def bookstoreModelFileContent = readResourceDataFile('bookstore/bookstore.yang')
+    def bookstoreTypesFileContent = readResourceDataFile('bookstore/bookstore-types.yang')
 
     def setup() {
         objectUnderTest = cpsModuleService
@@ -67,7 +72,7 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
             populateNewYangResourcesNameToContentMapAndAllModuleReferences(numberOfNewModules)
         when: 'the new schema set is created'
             objectUnderTest.createSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, 'newSchemaSet', newYangResourcesNameToContentMap)
-        then: 'the number of module references has increased by #expectedIncrease'
+        then: 'the number of module references has increased by #numberOfNewModules'
             def yangResourceModuleReferences = objectUnderTest.getYangResourceModuleReferences(FUNCTIONAL_TEST_DATASPACE_1)
             originalNumberOfModuleReferences + numberOfNewModules == yangResourceModuleReferences.size()
         cleanup:
@@ -106,9 +111,9 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
         where: 'the following module references are provided'
             scenario                        | numberOfNewModules | existingModuleReferences                          || expectedNumberOfModulesForAnchor
             'empty schema set'              | 0                  | [ ]                                               || 0
-            'one existing module'           | 0                  | [ existingModuleReference ]                       || 1
+            'one existing module'           | 0                  | [bookStoreModuleReference ]                       || 1
             'two new modules'               | 2                  | [ ]                                               || 2
-            'two new modules, one existing' | 2                  | [ existingModuleReference ]                       || 3
+            'two new modules, one existing' | 2                  | [bookStoreModuleReference ]                       || 3
             'over max batch size #modules'  | 101                | [ ]                                               || 101
             'two valid, one invalid module' | 2                  | [ new ModuleReference('NOT EXIST','IRRELEVANT') ] || 2
     }
@@ -149,25 +154,34 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
         when: 'the module definitions for an anchor are retrieved'
             def result = objectUnderTest.getModuleDefinitionsByAnchorName(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1)
         then: 'the correct module definitions are returned'
-            result == [new ModuleDefinition('stores','2020-09-15','')]
+            assert result.size() == 2
+            assert result.contains(new ModuleDefinition('stores','2024-01-30',bookstoreModelFileContent))
+            assert result.contains(new ModuleDefinition('bookstore-types','2024-01-30', bookstoreTypesFileContent))
     }
 
     def 'Retrieving module definitions: #scenarios'() {
         when: 'module definitions for module name are retrieved'
             def result = objectUnderTest.getModuleDefinitionsByAnchorAndModule(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, moduleName, moduleRevision)
         then: 'the correct module definitions are returned'
-            result == [new ModuleDefinition('stores','2020-09-15','')]
+            if (expectedNumberOfDefinitions > 0) {
+                assert result.size() == expectedNumberOfDefinitions
+                def expectedModuleDefinition = new ModuleDefinition('stores', '2024-01-30', bookstoreModelFileContent)
+                assert result[0] == expectedModuleDefinition
+            }
         where: 'following parameters are used'
-            scenarios               | moduleName | moduleRevision
-            'both specified'        | 'stores'   | '2020-09-15'
-            'module name specified' | 'stores'   | null
+            scenarios                          | moduleName | moduleRevision || expectedNumberOfDefinitions
+            'correct module name and revision' | 'stores'   | '2024-01-30'   || 1
+            'correct module name'              | 'stores'   | null           || 1
+            'incorrect module name'            | 'other'    | null           || 0
+            'incorrect revision'               | 'stores'   | '2025-11-22'   || 0
     }
 
     def 'Retrieving yang resource module references by anchor.'() {
         when: 'the yang resource module references for an anchor are retrieved'
             def result = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1)
         then: 'the correct module references are returned'
-            result == [ existingModuleReference ]
+            assert result.size() == 2
+            assert result.containsAll(bookStoreModuleReference, bookStoreTypesModuleReference)
     }
 
     def 'Identifying new module references with #scenario'() {
@@ -179,18 +193,19 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
         where: 'the following data is used'
             scenario                                | moduleReferences                                                       || expectedResult
             'just new module references'            | [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')] || [new ModuleReference('new1', 'r1'), new ModuleReference('new2', 'r1')]
-            'one new module,one existing reference' | [new ModuleReference('new1', 'r1'), existingModuleReference]           || [new ModuleReference('new1', 'r1')]
-            'no new module references'              | [existingModuleReference]                                              || []
+            'one new module,one existing reference' | [new ModuleReference('new1', 'r1'), bookStoreModuleReference]          || [new ModuleReference('new1', 'r1')]
+            'no new module references'              | [bookStoreModuleReference]                                             || []
             'no module references'                  | []                                                                     || []
             'module references collection is null'  | null                                                                   || []
     }
 
     def 'Retrieve schema set.'() {
-        when: 'a specific schema set is retreived'
+        when: 'a specific schema set is retrieved'
             def result = objectUnderTest.getSchemaSet(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_SCHEMA_SET)
         then: 'the result has the correct name and module(s)'
             assert result.name == 'bookstoreSchemaSet'
-            assert result.moduleReferences == [ new ModuleReference('stores', '2020-09-15', 'org:onap:ccsdk:sample') ]
+            assert result.moduleReferences.size() == 2
+            assert result.moduleReferences.containsAll(bookStoreModuleReferenceWithNamespace, bookStoreTypesModuleReferenceWithNamespace)
     }
 
     def 'Retrieve all schema sets.'() {
@@ -290,14 +305,14 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
             def newModuleReferences = [new ModuleReference('new_0','2000-01-01'),new ModuleReference('new_1','2001-01-01')]
         and: 'a list of all module references (normally retrieved from node)'
             def allModuleReferences = []
-            allModuleReferences.add(existingModuleReference)
+            allModuleReferences.add(bookStoreModuleReference)
             allModuleReferences.addAll(newModuleReferences)
         when: 'the schema set is upgraded'
             objectUnderTest.upgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, 'targetSchema', newYangResourcesNameToContentMap, allModuleReferences)
         then: 'the new anchor has the correct new and existing modules'
             def yangResourceModuleReferencesAfterUpgrade = objectUnderTest.getYangResourcesModuleReferences(FUNCTIONAL_TEST_DATASPACE_1, 'targetAnchor')
             assert yangResourceModuleReferencesAfterUpgrade.size() == 3
-            assert yangResourceModuleReferencesAfterUpgrade.contains(existingModuleReference)
+            assert yangResourceModuleReferencesAfterUpgrade.contains(bookStoreModuleReference)
             assert yangResourceModuleReferencesAfterUpgrade.containsAll(newModuleReferences);
         cleanup:
             objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['targetSchema'])
index 920f407..68dfb4a 100644 (file)
@@ -44,8 +44,7 @@ class CpsPerfTestBase extends PerfTestBase {
 
     def setupPerformanceInfraStructure() {
         cpsDataspaceService.createDataspace(CPS_PERFORMANCE_TEST_DATASPACE)
-        def modelAsString = readResourceDataFile('bookstore/bookstore.yang')
-        cpsModuleService.createSchemaSet(CPS_PERFORMANCE_TEST_DATASPACE, BOOKSTORE_SCHEMA_SET, [bookstore: modelAsString])
+        createStandardBookStoreSchemaSet(CPS_PERFORMANCE_TEST_DATASPACE)
     }
 
     def createInitialData() {
index a96d7f6..ce0aab4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-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.
@@ -21,7 +21,6 @@
 package org.onap.cps.integration.performance.base
 
 import org.onap.cps.integration.base.CpsIntegrationSpecBase
-import org.springframework.util.StopWatch
 
 abstract class PerfTestBase extends CpsIntegrationSpecBase {
 
diff --git a/integration-test/src/test/resources/data/bookstore/bookstore-types.yang b/integration-test/src/test/resources/data/bookstore/bookstore-types.yang
new file mode 100644 (file)
index 0000000..5ad7b6e
--- /dev/null
@@ -0,0 +1,18 @@
+module bookstore-types {
+  yang-version 1.1;
+  namespace "org:onap:cps:types:sample";
+
+  prefix types;
+
+  revision "2024-01-30" {
+    description
+      "Sample Types";
+  }
+
+  typedef year {
+    type uint16 {
+      range "1000..9999";
+    }
+  }
+
+}
\ No newline at end of file
index 9c6c42e..2abde65 100644 (file)
@@ -1,18 +1,22 @@
 module stores {
     yang-version 1.1;
-    namespace "org:onap:ccsdk:sample";
+    namespace "org:onap:cps:sample";
 
     prefix book-store;
 
-    revision "2020-09-15" {
+    import bookstore-types {
+        prefix "types";
+        revision-date 2024-01-30;
+    }
+
+    revision "2024-01-30" {
         description
-        "Sample Model";
+            "Extracted bookstore types";
     }
 
-    typedef year {
-        type uint16 {
-            range "1000..9999";
-        }
+    revision "2020-09-15" {
+        description
+        "Sample Model";
     }
 
     list bookstore-address {
@@ -106,7 +110,7 @@ module stores {
                     type string;
                 }
                 leaf-list editions {
-                    type year;
+                    type types:year;
                 }
                 leaf price {
                     type uint64;
index 47486e8..60aa6b4 100644 (file)
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.3-SNAPSHOT</version>
+        <version>3.4.4-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/pom.xml b/pom.xml
index 42d2bfa..3f67efb 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
 \r
     <groupId>org.onap.cps</groupId>\r
     <artifactId>cps-aggregator</artifactId>\r
-    <version>3.4.3-SNAPSHOT</version>\r
+    <version>3.4.4-SNAPSHOT</version>\r
     <packaging>pom</packaging>\r
 \r
     <name>cps</name>\r
diff --git a/releases/3.4.3-container.yaml b/releases/3.4.3-container.yaml
new file mode 100644 (file)
index 0000000..685e230
--- /dev/null
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.4.3
+project: cps
+log_dir: cps-maven-docker-stage-master/934/
+ref: 7ce495b1e5871d42392b0b45fd3babd9fd5bd634
+containers:
+  - name: 'cps-and-ncmp'
+    version: '3.4.3-20240207T150310Z'
\ No newline at end of file
diff --git a/releases/3.4.3.yaml b/releases/3.4.3.yaml
new file mode 100644 (file)
index 0000000..43423e1
--- /dev/null
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/942/
+project: cps
+version: 3.4.3
\ No newline at end of file
index 8fcf040..4a15e4e 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>3.4.3-SNAPSHOT</version>
+    <version>3.4.4-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
index 6c691bf..369d363 100644 (file)
@@ -22,7 +22,7 @@
 
 major=3
 minor=4
-patch=3
+patch=4
 
 base_version=${major}.${minor}.${patch}