Merge "Get Subscription Delta"
authorPriyank Maheshwari <priyank.maheshwari@est.tech>
Mon, 26 Feb 2024 16:10:23 +0000 (16:10 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 26 Feb 2024 16:10:23 +0000 (16:10 +0000)
91 files changed:
checkstyle/pom.xml
cps-application/pom.xml
cps-application/src/main/resources/application.yml
cps-bom/pom.xml
cps-dependencies/pom.xml
cps-events/pom.xml
cps-ncmp-events/pom.xml
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/lombok.config
cps-ncmp-rest/pom.xml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-service/lombok.config
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java
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/NetworkCmProxyDataServicePropertyHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java [deleted file]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfig.java [deleted file]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducer.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumer.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumer.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducer.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.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/inventory/sync/ModuleSyncTasks.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java [deleted file]
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/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.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/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy [deleted file]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfigSpec.groovy [deleted file]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.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/ModuleSyncServiceSpec.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/operations/DmiModelOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy [deleted file]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/YangDataConverterSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy
cps-ncmp-service/src/test/resources/application.yml
cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionDmiOutEvent.json [new file with mode: 0644]
cps-parent/pom.xml
cps-path-parser/pom.xml
cps-rest/pom.xml
cps-ri/pom.xml
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
cps-service/pom.xml
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/java/org/onap/cps/ncmp/dmi/rest/stub/controller/DmiRestStubController.java
dmi-plugin-demo-and-csit-stub/dmi-plugin-demo-and-csit-stub-service/src/main/resources/application.yml
dmi-plugin-demo-and-csit-stub/pom.xml
docker-compose/docker-compose.yml
docs/cps-ncmp-message-status-codes.rst
docs/deployment.rst
docs/release-notes.rst
integration-test/pom.xml
integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleCreateSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/NcmpCmHandleUpgradeSpec.groovy
integration-test/src/test/resources/application.yml
jacoco-report/pom.xml
pom.xml
releases/3.4.4-container.yaml [new file with mode: 0644]
releases/3.4.4.yaml [new file with mode: 0644]
spotbugs/pom.xml
version.properties

index 95794b1..f794dc2 100644 (file)
@@ -26,7 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>3.4.4-SNAPSHOT</version>
+    <version>3.4.5-SNAPSHOT</version>
 
     <profiles>
         <profile>
index 6466bcf..e7ba2c5 100644 (file)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
             <groupId>org.springdoc</groupId>
             <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-http</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.servlet</groupId>
-            <artifactId>jakarta.servlet-api</artifactId>
-        </dependency>
         <dependency>
             <groupId>io.micrometer</groupId>
             <artifactId>micrometer-registry-prometheus</artifactId>
index e56e0e1..207341c 100644 (file)
@@ -64,7 +64,7 @@ spring:
 
     liquibase:
         change-log: classpath:changelog/changelog-master.yaml
-        labels: ${LIQUIBASE_LABELS}
+        label-filter: ${LIQUIBASE_LABELS}
 
     servlet:
         multipart:
@@ -200,7 +200,6 @@ ncmp:
             parallelism-level: 10
 
     model-loader:
-        subscription: true
         maximum-attempt-count: 20
 
 # Custom Hazelcast Config.
index ecd442c..da315d3 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>3.4.4-SNAPSHOT</version>
+    <version>3.4.5-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
index 2f1a4d2..c3f038d 100644 (file)
@@ -27,7 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>3.4.4-SNAPSHOT</version>
+    <version>3.4.5-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
@@ -85,7 +85,7 @@
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-dependencies</artifactId>
-                <version>3.1.2</version>
+                <version>3.2.2</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
-            <dependency>
-                <groupId>org.springframework</groupId>
-                <artifactId>spring-test</artifactId>
-                <version>6.0.11</version>
-            </dependency>
             <dependency>
                 <groupId>io.springfox</groupId>
                 <artifactId>springfox-boot-starter</artifactId>
             </dependency>
             <dependency>
                 <groupId>io.hypersistence</groupId>
-                <artifactId>hypersistence-utils-hibernate-60</artifactId>
-                <version>3.5.0</version>
+                <artifactId>hypersistence-utils-hibernate-63</artifactId>
+                <version>3.7.3</version>
             </dependency>
             <dependency>
                 <groupId>io.micrometer</groupId>
                 <artifactId>groovy-json</artifactId>
                 <version>${groovy.version}</version>
             </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-server</artifactId>
-                <version>${jetty-version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-http</artifactId>
-                <version>${jetty-version}</version>
-            </dependency>
             <dependency>
                 <groupId>org.jetbrains</groupId>
                 <artifactId>annotations</artifactId>
                 <version>22.0.0</version>
             </dependency>
-            <dependency>
-                <groupId>org.junit.jupiter</groupId>
-                <artifactId>junit-jupiter-api</artifactId>
-                <version>5.10.0</version>
-            </dependency>
             <dependency>
                 <groupId>org.liquibase</groupId>
                 <artifactId>liquibase-core</artifactId>
index 662d150..7db3338 100644 (file)
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 6e63843..c14504d 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 25d4939..ab6d8f8 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
     </parent>
 
     <artifactId>cps-ncmp-rest-stub-app</artifactId>
index 1a8e89b..ebe4d7f 100644 (file)
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
     </parent>
     <artifactId>cps-ncmp-rest-stub-service</artifactId>
 
             <groupId>org.onap.cps</groupId>
             <artifactId>cps-ncmp-rest</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.servlet</groupId>
-            <artifactId>jakarta.servlet-api</artifactId>
-        </dependency>
         <!-- T E S T - D E P E N D E N C I E S -->
         <dependency>
             <groupId>org.spockframework</groupId>
index a35a886..74dd34a 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 0736fc5..e4122df 100644 (file)
@@ -18,3 +18,4 @@
 
 config.stopBubbling = true
 lombok.addLombokGeneratedAnnotation = true
+lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
index f363c01..ae3650e 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 6ec2444..73bd8d7 100755 (executable)
@@ -39,6 +39,7 @@ import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig;
 import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException;
 import org.onap.cps.ncmp.api.impl.inventory.CompositeState;
 import org.onap.cps.ncmp.api.impl.operations.DatastoreType;
@@ -62,6 +63,7 @@ import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
 import org.onap.cps.ncmp.rest.util.DeprecationHelper;
 import org.onap.cps.spi.model.ModuleDefinition;
 import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.util.StringUtils;
@@ -83,6 +85,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler;
     private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler;
     private final DataOperationRequestMapper dataOperationRequestMapper;
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerCmHandle;
 
     /**
index 1fba85b..6776ef0 100644 (file)
@@ -18,3 +18,4 @@
 
 config.stopBubbling = true
 lombok.addLombokGeneratedAnnotation = true
+lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
index 9beb6b7..d6ea71b 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index b9c834c..462679e 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.
@@ -36,7 +36,8 @@ public enum NcmpResponseStatus {
     SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"),
     UNKNOWN_ERROR("108", "Unknown error"),
     CM_HANDLE_ALREADY_EXIST("109", "cm-handle already exists"),
-    CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id");
+    CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"),
+    ALTERNATE_ID_ALREADY_ASSOCIATED("111", "alternate id already associated");
 
     private final String code;
     private final String message;
index 6f92a9e..8890d14 100644 (file)
@@ -241,8 +241,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
     }
 
     private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) {
-        return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter
-                .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()));
+        return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter.convertCmHandleToYangModel(dataNode));
     }
 
     private Collection<String> executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters,
index 05b83b9..ab83486 100755 (executable)
@@ -24,6 +24,7 @@
 
 package org.onap.cps.ncmp.api.impl;
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED;
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND;
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY;
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST;
@@ -35,11 +36,9 @@ import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.vali
 
 import com.google.common.collect.Lists;
 import com.hazelcast.map.IMap;
-import java.text.MessageFormat;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -50,9 +49,9 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.onap.cps.api.CpsDataService;
-import org.onap.cps.ncmp.api.NcmpResponseStatus;
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig;
 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
@@ -66,7 +65,7 @@ import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
 import org.onap.cps.ncmp.api.impl.operations.OperationType;
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel;
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager;
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper;
+import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker;
 import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions;
 import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
@@ -86,6 +85,7 @@ import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.model.ModuleDefinition;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 
@@ -104,39 +104,27 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler;
     private final CpsDataService cpsDataService;
     private final IMap<String, Object> moduleSyncStartedOnCmHandles;
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
     private final TrustLevelManager trustLevelManager;
-    private final CmHandleIdMapper cmHandleIdMapper;
-    private final Map<String, Collection<ModuleReference>> moduleSetTagCache;
+    private final AlternateIdChecker alternateIdChecker;
 
     @Override
     public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
         final DmiPluginRegistration dmiPluginRegistration) {
-        cacheAlternateIds(dmiPluginRegistration.getCreatedCmHandles());
+
         dmiPluginRegistration.validateDmiPluginRegistration();
         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
 
         setTrustLevelPerDmiPlugin(dmiPluginRegistration);
 
-        if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setRemovedCmHandles(
-                parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles()));
-        }
+        processRemovedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse);
 
-        if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setCreatedCmHandles(
-                parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration));
-        }
-        if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setUpdatedCmHandles(
-                networkCmProxyDataServicePropertyHandler
-                    .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
-        }
-        if (dmiPluginRegistration.getUpgradedCmHandles() != null
-            && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) {
-            dmiPluginRegistrationResponse.setUpgradedCmHandles(
-                parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration));
-        }
+        processCreatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse);
+
+        processUpdatedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse);
+
+        processUpgradedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse);
 
         return dmiPluginRegistrationResponse;
     }
@@ -322,48 +310,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState();
     }
 
-    /**
-     * THis method registers a cm handle and initiates modules sync.
-     *
-     * @param dmiPluginRegistration dmi plugin registration information.
-     * @return cm-handle registration response for create cm-handle requests.
-     */
-    public List<CmHandleRegistrationResponse> parseAndProcessCreatedCmHandlesInRegistration(
-        final DmiPluginRegistration dmiPluginRegistration) {
-        final List<NcmpServiceCmHandle> cmHandlesToBeCreated = dmiPluginRegistration.getCreatedCmHandles();
-        final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size());
-        final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(cmHandlesToBeCreated.size());
-        cmHandlesToBeCreated
-                .forEach(cmHandle -> {
-                    final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(
-                            dmiPluginRegistration.getDmiPlugin(),
-                            dmiPluginRegistration.getDmiDataPlugin(),
-                            dmiPluginRegistration.getDmiModelPlugin(),
-                            cmHandle,
-                            cmHandle.getModuleSetTag(),
-                            cmHandle.getAlternateId());
-                    yangModelCmHandles.add(yangModelCmHandle);
-                    initialTrustLevelPerCmHandleId.put(cmHandle.getCmHandleId(), cmHandle.getRegistrationTrustLevel());
-                });
-        return registerNewCmHandles(yangModelCmHandles, initialTrustLevelPerCmHandleId);
-    }
-
-    protected List<CmHandleRegistrationResponse> parseAndProcessDeletedCmHandlesInRegistration(
-        final List<String> tobeRemovedCmHandles) {
+    protected void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
+                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
+        final List<String> tobeRemovedCmHandleIds = dmiPluginRegistration.getRemovedCmHandles();
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
-            new ArrayList<>(tobeRemovedCmHandles.size());
+            new ArrayList<>(tobeRemovedCmHandleIds.size());
         final Collection<YangModelCmHandle> yangModelCmHandles =
-            inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles);
-        final Set<String> moduleSetTags = yangModelCmHandles.stream().map(YangModelCmHandle::getModuleSetTag)
-                .collect(Collectors.toSet());
+            inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandleIds);
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING);
 
         final Set<String> notDeletedCmHandles = new HashSet<>();
-        for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandles, DELETE_BATCH_SIZE)) {
+        for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, 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)));
 
@@ -379,71 +338,155 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
                 }
             }
         }
-
         yangModelCmHandles.removeIf(yangModelCmHandle -> notDeletedCmHandles.contains(yangModelCmHandle.getId()));
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETED);
-        removeEntriesFromAlternateIdCache(yangModelCmHandles);
-
-        return cmHandleRegistrationResponses;
+        dmiPluginRegistrationResponse.setRemovedCmHandles(cmHandleRegistrationResponses);
     }
 
-    private void removeEntriesFromAlternateIdCache(final Collection<YangModelCmHandle> yangModelCmHandles) {
-        for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) {
-            cmHandleIdMapper.removeMapping(yangModelCmHandle.getId());
+    protected void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
+                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
+        final List<NcmpServiceCmHandle> ncmpServiceCmHandles = dmiPluginRegistration.getCreatedCmHandles();
+        final List<CmHandleRegistrationResponse> failedCmHandleRegistrationResponses = new ArrayList<>();
+
+        try {
+            final Collection<String> rejectedCmHandleIds
+                = checkAlternateIds(ncmpServiceCmHandles, failedCmHandleRegistrationResponses);
+
+            final Collection<String> succeededCmHandleIds = persistCmHandlesWithState(dmiPluginRegistration,
+                dmiPluginRegistrationResponse, ncmpServiceCmHandles, rejectedCmHandleIds);
+
+            processTrustLevels(ncmpServiceCmHandles, succeededCmHandleIds);
+
+        } catch (final AlreadyDefinedException alreadyDefinedException) {
+            failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponsesFromXpaths(
+                alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST));
+        } catch (final Exception exception) {
+            final Collection<String> cmHandleIds =
+                ncmpServiceCmHandles.stream().map(NcmpServiceCmHandle::getCmHandleId).collect(Collectors.toList());
+            failedCmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse
+                .createFailureResponses(cmHandleIds, exception));
         }
+        final List<CmHandleRegistrationResponse> mergedCmHandleRegistrationResponses
+            = new ArrayList<>(failedCmHandleRegistrationResponses);
+        mergedCmHandleRegistrationResponses.addAll(dmiPluginRegistrationResponse.getCreatedCmHandles());
+
+        dmiPluginRegistrationResponse.setCreatedCmHandles(mergedCmHandleRegistrationResponses);
     }
 
-    protected List<CmHandleRegistrationResponse> parseAndProcessUpgradedCmHandlesInRegistration(
-        final DmiPluginRegistration dmiPluginRegistration) {
+    protected void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration,
+                                         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
+        dmiPluginRegistrationResponse.setUpdatedCmHandles(networkCmProxyDataServicePropertyHandler
+            .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
+    }
 
-        final List<String> upgradedCmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles();
+    protected void processUpgradedCmHandles(
+        final DmiPluginRegistration dmiPluginRegistration,
+        final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) {
 
+        final List<String> cmHandleIds = dmiPluginRegistration.getUpgradedCmHandles().getCmHandles();
+        final String upgradedModuleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag();
         final Map<YangModelCmHandle, CmHandleState> acceptedCmHandleStatePerCmHandle
-                = new HashMap<>(upgradedCmHandleIds.size());
-
-        final Map<NcmpResponseStatus, List<String>> failedCmHandlesPerResponseStatus
-                = new EnumMap<>(NcmpResponseStatus.class);
-        final List<String> nonExistingCmHandleIds = new ArrayList<>();
-        final List<String> nonReadyCmHandleIds = new ArrayList<>();
-        final List<String> invalidCmHandleIds = new ArrayList<>();
+            = new HashMap<>(cmHandleIds.size());
+        final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses = new ArrayList<>(cmHandleIds.size());
 
-        upgradedCmHandleIds.forEach(cmHandleId -> {
+        for (final String cmHandleId : cmHandleIds) {
             try {
-                if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) {
-                    final YangModelCmHandle yangModelCmHandleWithUpgradeDetails
-                            = createYangModelCmHandle(dmiPluginRegistration, cmHandleId);
-                    acceptedCmHandleStatePerCmHandle.put(yangModelCmHandleWithUpgradeDetails, CmHandleState.LOCKED);
+                final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
+                if (yangModelCmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) {
+                    if (moduleUpgradeCanBeSkipped(yangModelCmHandle, upgradedModuleSetTag)) {
+                        cmHandleUpgradeResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
+                    } else {
+                        updateYangModelCmHandleForUpgrade(yangModelCmHandle, upgradedModuleSetTag);
+                        acceptedCmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED);
+                    }
                 } else {
-                    nonReadyCmHandleIds.add(cmHandleId);
+                    cmHandleUpgradeResponses.add(
+                            CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_READY));
                 }
             } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
                 log.error("Unable to find data node for cm handle id : {} , caused by : {}",
                         cmHandleId, dataNodeNotFoundException.getMessage());
-                nonExistingCmHandleIds.add(cmHandleId);
+                cmHandleUpgradeResponses.add(
+                        CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND));
             } catch (final DataValidationException dataValidationException) {
                 log.error("Unable to upgrade cm handle id: {}, caused by : {}",
                         cmHandleId, dataValidationException.getMessage());
-                invalidCmHandleIds.add(cmHandleId);
+                cmHandleUpgradeResponses.add(
+                        CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID));
             }
-        });
-        failedCmHandlesPerResponseStatus.put(CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
-        failedCmHandlesPerResponseStatus.put(CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
-        failedCmHandlesPerResponseStatus.put(CM_HANDLE_INVALID_ID, invalidCmHandleIds);
-        return mergeAllCmHandleResponses(acceptedCmHandleStatePerCmHandle, failedCmHandlesPerResponseStatus);
-    }
-
-    private static YangModelCmHandle createYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration,
-                                                             final String cmHandleId) {
-        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
-        ncmpServiceCmHandle.setCmHandleId(cmHandleId);
-        final String moduleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag();
-        final String lockReasonWithModuleSetTag = MessageFormat.format(
-                ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT, moduleSetTag);
-        ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY)
+        }
+        cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle));
+        dmiPluginRegistrationResponse.setUpgradedCmHandles(cmHandleUpgradeResponses);
+    }
+
+    private Collection<String> checkAlternateIds(
+        final List<NcmpServiceCmHandle> cmHandlesToBeCreated,
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) {
+        final Collection<String> rejectedCmHandleIds = alternateIdChecker
+            .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated);
+        cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(
+            rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED));
+        return rejectedCmHandleIds;
+    }
+
+    private List<String> persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration,
+                                                   final DmiPluginRegistrationResponse dmiPluginRegistrationResponse,
+                                                   final List<NcmpServiceCmHandle> cmHandlesToBeCreated,
+                                                   final Collection<String> rejectedCmHandleIds) {
+        final List<String> succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size());
+        final List<YangModelCmHandle> yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size());
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
+            new ArrayList<>(cmHandlesToBeCreated.size());
+        for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) {
+            if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) {
+                yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle));
+                cmHandleRegistrationResponses.add(
+                    CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId()));
+                succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId());
+            }
+        }
+        lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister);
+        dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses);
+        return succeededCmHandleIds;
+    }
+
+    private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration,
+                                                   final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        return YangModelCmHandle.toYangModelCmHandle(
+            dmiPluginRegistration.getDmiPlugin(),
+            dmiPluginRegistration.getDmiDataPlugin(),
+            dmiPluginRegistration.getDmiModelPlugin(),
+            ncmpServiceCmHandle,
+            ncmpServiceCmHandle.getModuleSetTag(),
+            ncmpServiceCmHandle.getAlternateId());
+    }
+
+    private void processTrustLevels(final Collection<NcmpServiceCmHandle> cmHandlesToBeCreated,
+                                    final Collection<String> succeededCmHandleIds) {
+        final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size());
+        for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) {
+            if (succeededCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) {
+                initialTrustLevelPerCmHandleId.put(ncmpServiceCmHandle.getCmHandleId(),
+                    ncmpServiceCmHandle.getRegistrationTrustLevel());
+            }
+        }
+        trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId);
+    }
+
+    private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle,
+                                                     final String upgradedModuleSetTag) {
+        if (StringUtils.isBlank(upgradedModuleSetTag)) {
+            return false;
+        }
+        return yangModelCmHandle.getModuleSetTag().equals(upgradedModuleSetTag);
+    }
+
+    private static void updateYangModelCmHandleForUpgrade(final YangModelCmHandle yangModelCmHandle,
+                                                          final String upgradedModuleSetTag) {
+        final String lockReasonWithModuleSetTag = String.format(ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT,
+                upgradedModuleSetTag);
+        yangModelCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY)
                 .withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build());
-        return YangModelCmHandle.toYangModelCmHandle(dmiPluginRegistration.getDmiPlugin(),
-                dmiPluginRegistration.getDmiDataPlugin(), dmiPluginRegistration.getDmiModelPlugin(),
-                ncmpServiceCmHandle, moduleSetTag, ncmpServiceCmHandle.getAlternateId());
     }
 
     private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) {
@@ -473,15 +516,14 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) {
         inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId);
-        inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId
-            + "']");
+        inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']");
         removeDeletedCmHandleFromModuleSyncMap(cmHandleId);
     }
 
-    private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> tobeRemovedCmHandles) {
-        inventoryPersistence.deleteSchemaSetsWithCascade(tobeRemovedCmHandles);
-        inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(tobeRemovedCmHandles));
-        tobeRemovedCmHandles.forEach(this::removeDeletedCmHandleFromModuleSyncMap);
+    private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> cmHandleIds) {
+        inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds);
+        inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds));
+        cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap);
     }
 
     private Collection<String> mapCmHandleIdsToXpaths(final Collection<String> cmHandles) {
@@ -491,39 +533,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     }
 
     // CPS-1239 Robustness cleaning of in progress cache
-    private void removeDeletedCmHandleFromModuleSyncMap(final String deletedCmHandleId) {
-        if (moduleSyncStartedOnCmHandles.remove(deletedCmHandleId) != null) {
-            log.debug("{} removed from in progress map", deletedCmHandleId);
+    private void removeDeletedCmHandleFromModuleSyncMap(final String cmHandleId) {
+        if (moduleSyncStartedOnCmHandles.remove(cmHandleId) != null) {
+            log.debug("{} removed from in progress map", cmHandleId);
         }
     }
 
-    private List<CmHandleRegistrationResponse> registerNewCmHandles(final List<YangModelCmHandle> yangModelCmHandles,
-                                                                    final Map<String, TrustLevel>
-                                                                            initialTrustLevelPerCmHandleId) {
-        final Set<String> cmHandleIds = initialTrustLevelPerCmHandleId.keySet();
-        try {
-            lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandles);
-            trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId);
-            return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds);
-        } catch (final AlreadyDefinedException alreadyDefinedException) {
-            return CmHandleRegistrationResponse.createFailureResponses(
-                    alreadyDefinedException.getAlreadyDefinedObjectNames(), CM_HANDLE_ALREADY_EXIST);
-        } catch (final Exception exception) {
-            return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception);
-        }
-    }
-
-    private List<CmHandleRegistrationResponse> mergeAllCmHandleResponses(
-            final Map<YangModelCmHandle, CmHandleState> acceptedCmHandleStatePerCmHandle,
-            final Map<NcmpResponseStatus, List<String>> failedCmHandlesPerResponseStatus) {
-        final List<CmHandleRegistrationResponse> cmHandleUpgradeResponses
-                = upgradeCmHandles(acceptedCmHandleStatePerCmHandle);
-        failedCmHandlesPerResponseStatus.forEach((ncmpResponseStatus, cmHandleIds) ->
-            cmHandleIds.forEach(cmHandleId -> cmHandleUpgradeResponses.add(CmHandleRegistrationResponse
-                .createFailureResponse(cmHandleId, ncmpResponseStatus))));
-        return cmHandleUpgradeResponses;
-    }
-
     private List<CmHandleRegistrationResponse> upgradeCmHandles(final Map<YangModelCmHandle, CmHandleState>
                                                                     cmHandleStatePerCmHandle) {
         final List<String> cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle);
@@ -549,12 +564,4 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
-    private void cacheAlternateIds(final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
-        for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
-            if (!StringUtils.isEmpty(ncmpServiceCmHandle.getAlternateId())) {
-                cmHandleIdMapper.addMapping(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle.getAlternateId());
-            }
-        }
-    }
-
 }
index 13b3fca..f5d22af 100644 (file)
@@ -45,7 +45,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper;
+import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
@@ -67,7 +67,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
     private final InventoryPersistence inventoryPersistence;
     private final CpsDataService cpsDataService;
     private final JsonObjectMapper jsonObjectMapper;
-    private final CmHandleIdMapper cmHandleIdMapper;
+    private final AlternateIdChecker alternateIdChecker;
 
     /**
      * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes.
@@ -77,12 +77,13 @@ public class NetworkCmProxyDataServicePropertyHandler {
      */
     public List<CmHandleRegistrationResponse> updateCmHandleProperties(
         final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
-        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses
+            = new ArrayList<>(ncmpServiceCmHandles.size());
         for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
             final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
             try {
-                final DataNode existingCmHandleDataNode = inventoryPersistence.getCmHandleDataNode(cmHandleId)
-                        .iterator().next();
+                final DataNode existingCmHandleDataNode = inventoryPersistence
+                    .getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next();
                 updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle);
                 processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle);
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
@@ -105,17 +106,13 @@ public class NetworkCmProxyDataServicePropertyHandler {
 
     private void updateAlternateId(final DataNode existingCmHandleDataNode,
                                    final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        final YangModelCmHandle yangModelCmHandle =
+            YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode);
+        final String currentAlternateId = yangModelCmHandle.getAlternateId();
         final String newAlternateId = ncmpServiceCmHandle.getAlternateId();
-        if (cmHandleIdMapper.addMapping(ncmpServiceCmHandle.getCmHandleId(), newAlternateId)) {
-            try {
-                final YangModelCmHandle yangModelCmHandle =
-                        YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode,
-                                ncmpServiceCmHandle.getCmHandleId());
-                setAndUpdateAlternateId(yangModelCmHandle, newAlternateId);
-            } catch (final Exception e) {
-                cmHandleIdMapper.removeMapping(ncmpServiceCmHandle.getCmHandleId());
-                throw e;
-            }
+        if (alternateIdChecker.canApplyAlternateId(ncmpServiceCmHandle.getCmHandleId(),
+            currentAlternateId, newAlternateId)) {
+            setAndUpdateAlternateId(yangModelCmHandle, newAlternateId);
         }
     }
 
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfig.java
deleted file mode 100644 (file)
index a69d144..0000000
+++ /dev/null
@@ -1,59 +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.config.embeddedcache;
-
-import com.hazelcast.config.MapConfig;
-import java.util.Map;
-import org.onap.cps.cache.HazelcastCacheConfig;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class AlternateIdCacheConfig extends HazelcastCacheConfig {
-
-    private static final MapConfig alternateIdPerCmHandleIdMapConfig =
-            createMapConfig("alternateIdPerCmHandleIdMapConfig");
-
-    private static final MapConfig cmHandleIdPerAlternateIdMapConfig =
-            createMapConfig("cmHandleIdPerAlternateIdMapConfig");
-
-    /**
-     * Distributed instance of alternate id cache containing the alternate id per cm handle id.
-     *
-     * @return the cached map.
-     */
-    @Bean
-    public Map<String, String> alternateIdPerCmHandleId() {
-        return createHazelcastInstance("hazelcastInstanceAlternateIdPerCmHandleIdMap",
-                alternateIdPerCmHandleIdMapConfig).getMap("alternateIdPerCmHandleId");
-    }
-
-    /**
-     * Distributed instance of alternate id cache containing the cm handle id per alternate id.
-     *
-     * @return the cached map.
-     */
-    @Bean
-    public Map<String, String> cmHandleIdPerAlternateId() {
-        return createHazelcastInstance("hazelcastInstanceCmHandleIdPerAlternateIdMap",
-                cmHandleIdPerAlternateIdMapConfig).getMap("cmHandleIdPerAlternateId");
-    }
-}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfig.java
deleted file mode 100644 (file)
index a791a3b..0000000
+++ /dev/null
@@ -1,46 +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.config.MapConfig;
-import java.util.Collection;
-import java.util.Map;
-import org.onap.cps.cache.HazelcastCacheConfig;
-import org.onap.cps.spi.model.ModuleReference;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class ModuleSetTagCacheConfig extends HazelcastCacheConfig {
-
-    private static final MapConfig moduleSetTagCacheMapConfig = createMapConfig("moduleSetTagCacheMapConfig");
-
-    /**
-     * Map instance for cached ModulesSetTags.
-     *
-     * @return configured map of ModuleSetTags
-     */
-    @Bean
-    public Map<String, Collection<ModuleReference>> moduleSetTagCache() {
-        return createHazelcastInstance("moduleSetTags", moduleSetTagCacheMapConfig)
-                .getMap("moduleSetTagCache");
-    }
-}
index 171db52..f12cc9c 100644 (file)
@@ -30,6 +30,10 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class TrustLevelCacheConfig extends HazelcastCacheConfig {
 
+    public static final String TRUST_LEVEL_PER_DMI_PLUGIN_BEAN_NAME = "trustLevelPerDmiPlugin";
+
+    public static final String TRUST_LEVEL_PER_CM_HANDLE_BEAN_NAME = "trustLevelPerCmHandle";
+
     private static final MapConfig trustLevelPerCmHandleCacheConfig =
             createMapConfig("trustLevelPerCmHandleCacheConfig");
 
@@ -41,7 +45,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig {
      *
      * @return configured map of cm handle name as keys to trust-level for values.
      */
-    @Bean
+    @Bean(TRUST_LEVEL_PER_CM_HANDLE_BEAN_NAME)
     public Map<String, TrustLevel> trustLevelPerCmHandle() {
         return createHazelcastInstance("hazelcastInstanceTrustLevelPerCmHandleMap",
                 trustLevelPerCmHandleCacheConfig).getMap("trustLevelPerCmHandle");
@@ -52,7 +56,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig {
      *
      * @return configured map of dmi-plugin name as keys to trust-level for values.
      */
-    @Bean
+    @Bean(TRUST_LEVEL_PER_DMI_PLUGIN_BEAN_NAME)
     public Map<String, TrustLevel> trustLevelPerDmiPlugin() {
         return createHazelcastInstance("hazelcastInstanceTrustLevelPerDmiPluginMap",
                 trustLevelPerDmiPluginCacheConfig).getMap("trustLevelPerDmiPlugin");
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducer.java
new file mode 100644 (file)
index 0000000..5c192a9
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * ============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;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+import java.net.URI;
+import java.util.UUID;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.events.EventsPublisher;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RequiredArgsConstructor
+@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true)
+public class CmNotificationSubscriptionDmiInEventProducer {
+
+    private final EventsPublisher<CloudEvent> eventsPublisher;
+    private final JsonObjectMapper jsonObjectMapper;
+
+    @Value("${app.ncmp.avc.subscription-forward-topic-prefix}")
+    private String cmNotificationSubscriptionDmiInEventTopic;
+
+    /**
+     * Publish the event to the provided dmi plugin with key as subscription id and the event is in Cloud Event format.
+     *
+     * @param subscriptionId                       Cm Subscription Id
+     * @param dmiPluginName                        Dmi Plugin Name
+     * @param eventType                            Type of event
+     * @param cmNotificationSubscriptionDmiInEvent Cm Notification Subscription event for Dmi
+     */
+    public void publishCmNotificationSubscriptionDmiInEvent(final String subscriptionId, final String dmiPluginName,
+            final String eventType, final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) {
+        eventsPublisher.publishCloudEvent(cmNotificationSubscriptionDmiInEventTopic, subscriptionId,
+                buildAndGetCmNotificationDmiInEventAsCloudEvent(subscriptionId, dmiPluginName, eventType,
+                        cmNotificationSubscriptionDmiInEvent));
+
+    }
+
+    private CloudEvent buildAndGetCmNotificationDmiInEventAsCloudEvent(final String subscriptionId,
+            final String dmiPluginName, final String eventType,
+            final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) {
+        return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType)
+                .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.dmi.cm.subscription:1.0.0"))
+                .withExtension("correlationid", subscriptionId.concat("#").concat(dmiPluginName))
+                .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionDmiInEvent)).build();
+    }
+
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumer.java
new file mode 100644 (file)
index 0000000..ea72fd2
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * ============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;
+
+import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
+
+import io.cloudevents.CloudEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class CmNotificationSubscriptionDmiOutEventConsumer {
+
+    /**
+     * Consume the Cm Notification Subscription event from the dmi-plugin.
+     *
+     * @param cmNotificationSubscriptionDmiOutEventConsumerRecord the event to be consumed
+     */
+    @KafkaListener(topics = "${app.ncmp.avc.subscription-response-topic}",
+            containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
+    public void consumeCmNotificationSubscriptionDmiOutEvent(
+            final ConsumerRecord<String, CloudEvent> cmNotificationSubscriptionDmiOutEventConsumerRecord) {
+        final CloudEvent cloudEvent = cmNotificationSubscriptionDmiOutEventConsumerRecord.value();
+        final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent =
+                toTargetEvent(cloudEvent, CmNotificationSubscriptionDmiOutEvent.class);
+        final String correlationId = String.valueOf(cloudEvent.getExtension("correlationid"));
+        if ("subscriptionCreateResponse".equals(cloudEvent.getType()) && cmNotificationSubscriptionDmiOutEvent != null
+                    && correlationId != null) {
+            handleCmSubscriptionCreate(correlationId, cmNotificationSubscriptionDmiOutEvent);
+        }
+    }
+
+    private void handleCmSubscriptionCreate(final String correlationId,
+            final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent) {
+        final String subscriptionId = correlationId.split("#")[0];
+        final String dmiPluginName = correlationId.split("#")[1];
+        log.info("Cm Subscription with id : {} handled by the dmi-plugin : {} has the status : {}", subscriptionId,
+                dmiPluginName, cmNotificationSubscriptionDmiOutEvent.getData().getStatusMessage());
+    }
+}
index bc798af..d3bde01 100644 (file)
@@ -37,9 +37,6 @@ public class CmNotificationSubscriptionNcmpInEventConsumer {
     @Value("${notification.enabled:true}")
     private boolean notificationFeatureEnabled;
 
-    @Value("${ncmp.model-loader.subscription:false}")
-    private boolean subscriptionModelLoaderEnabled;
-
     /**
      * Consume the specified event.
      *
@@ -51,10 +48,8 @@ public class CmNotificationSubscriptionNcmpInEventConsumer {
         final CloudEvent cloudEvent = subscriptionEventConsumerRecord.value();
         final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent =
                 toTargetEvent(cloudEvent, CmNotificationSubscriptionNcmpInEvent.class);
-        if (subscriptionModelLoaderEnabled) {
-            log.info("Subscription with name {} to be mapped to hazelcast object...",
-                    cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId());
-        }
+        log.info("Subscription with name {} to be mapped to hazelcast object...",
+                cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId());
         if ("subscriptionCreated".equals(cloudEvent.getType()) && cmNotificationSubscriptionNcmpInEvent != null) {
             log.info("Subscription for ClientID {} with name {} ...", cloudEvent.getSource(),
                     cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId());
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducer.java
new file mode 100644 (file)
index 0000000..21468c3
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * ============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;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+import java.net.URI;
+import java.util.UUID;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.events.EventsPublisher;
+import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RequiredArgsConstructor
+@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true)
+public class CmNotificationSubscriptionNcmpOutEventProducer {
+
+    private final EventsPublisher<CloudEvent> eventsPublisher;
+    private final JsonObjectMapper jsonObjectMapper;
+
+    @Value("${app.ncmp.avc.subscription-outcome-topic}")
+    private String cmNotificationSubscriptionNcmpOutEventTopic;
+
+    /**
+     * Publish the event to the client who requested the subscription with key as subscription id and event is Cloud
+     * Event compliant.
+     *
+     * @param subscriptionId                         Cm Subscription Id
+     * @param eventType                              Type of event
+     * @param cmNotificationSubscriptionNcmpOutEvent Cm Notification Subscription Event for the client
+     */
+    public void publishCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, final String eventType,
+            final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent) {
+
+        eventsPublisher.publishCloudEvent(cmNotificationSubscriptionNcmpOutEventTopic, subscriptionId,
+                buildAndGetCmNotificationNcmpOutEventAsCloudEvent(subscriptionId, eventType,
+                        cmNotificationSubscriptionNcmpOutEvent));
+
+    }
+
+    private CloudEvent buildAndGetCmNotificationNcmpOutEventAsCloudEvent(final String subscriptionId,
+            final String eventType,
+            final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent) {
+
+        return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType)
+                .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.cm.subscription:1.0.0"))
+                .withExtension("correlationid", subscriptionId)
+                .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionNcmpOutEvent)).build();
+    }
+
+}
index 189fbb5..38f3db9 100644 (file)
@@ -39,6 +39,14 @@ public interface CmNotificationSubscriptionPersistenceService {
     boolean isOngoingCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId,
             final String xpath);
 
+    /**
+     * Check if the subscription ID is unique against ongoing subscriptions.
+     *
+     * @param subscriptionId subscription ID
+     * @return true if subscriptionId is not used in active subscriptions, otherwise false
+     */
+    boolean isUniqueSubscriptionId(final String subscriptionId);
+
     /**
      * Get all ongoing cm notification subscription based on the parameters.
      *
index 63f12ad..6e4997a 100644 (file)
@@ -36,9 +36,13 @@ import org.springframework.stereotype.Service;
 @RequiredArgsConstructor
 public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotificationSubscriptionPersistenceService {
 
+    private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions";
     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']
             """.trim();
+    private static final String SUBSCRIPTION_IDS_CPS_PATH_QUERY = """
+            //filter/subscriptionIds[text()='%s']
+            """.trim();
 
     private final CpsQueryService cpsQueryService;
 
@@ -48,6 +52,13 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
         return !getOngoingCmNotificationSubscriptionIds(datastoreType, cmHandleId, xpath).isEmpty();
     }
 
+    @Override
+    public boolean isUniqueSubscriptionId(final String subscriptionId) {
+        return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+                SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted(subscriptionId),
+                FetchDescendantsOption.OMIT_DESCENDANTS).isEmpty();
+    }
+
     @Override
     public Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType,
             final String cmHandleId, final String xpath) {
index 2d7ad69..a43da29 100644 (file)
@@ -34,12 +34,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig;
 import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType;
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel;
 import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.utils.CpsValidator;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
 @RequiredArgsConstructor
@@ -49,7 +51,11 @@ public class CmHandleQueriesImpl implements CmHandleQueries {
     private static final String DESCENDANT_PATH = "//";
     private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles";
     private final CpsDataPersistenceService cpsDataPersistenceService;
+
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
+
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerCmHandle;
     private final CpsValidator cpsValidator;
 
index dcd0368..e230b3f 100644 (file)
@@ -114,12 +114,20 @@ public interface InventoryPersistence extends NcmpPersistence {
     void saveCmHandleBatch(List<YangModelCmHandle> yangModelCmHandles);
 
     /**
-     * Get data node of given cm handle.
+     * Get data node with the given cm handle id.
      *
      * @param cmHandleId cmHandle ID
      * @return data node
      */
-    Collection<DataNode> getCmHandleDataNode(String cmHandleId);
+    Collection<DataNode> getCmHandleDataNodeByCmHandleId(String cmHandleId);
+
+    /**
+     * Get data node with the given alternate id.
+     *
+     * @param alternateId alternate ID
+     * @return data node
+     */
+    DataNode getCmHandleDataNodeByAlternateId(String alternateId);
 
     /**
      * Get collection of data nodes of given cm handles.
index 3b70786..3ae3dd9 100644 (file)
@@ -22,6 +22,8 @@
 
 package org.onap.cps.ncmp.api.impl.inventory;
 
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
+
 import com.google.common.collect.Lists;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
@@ -37,6 +39,7 @@ import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.ModuleDefinition;
@@ -54,6 +57,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
     private final CpsModuleService cpsModuleService;
     private final CpsAnchorService cpsAnchorService;
     private final CpsValidator cpsValidator;
+    private final CmHandleQueries cmHandleQueries;
 
     /**
      * initialize an inventory persistence object.
@@ -66,18 +70,19 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
      */
     public InventoryPersistenceImpl(final JsonObjectMapper jsonObjectMapper, final CpsDataService cpsDataService,
                                     final CpsModuleService cpsModuleService, final CpsValidator cpsValidator,
-                                    final CpsAnchorService cpsAnchorService) {
+                                    final CpsAnchorService cpsAnchorService, final CmHandleQueries cmHandleQueries) {
         super(jsonObjectMapper, cpsDataService, cpsModuleService, cpsValidator);
         this.cpsModuleService = cpsModuleService;
         this.cpsAnchorService = cpsAnchorService;
         this.cpsValidator = cpsValidator;
+        this.cmHandleQueries = cmHandleQueries;
     }
 
 
     @Override
     public CompositeState getCmHandleState(final String cmHandleId) {
         final DataNode stateAsDataNode = cpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                        createCmHandleXPath(cmHandleId) + "/state", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+                        getXPathForCmHandleById(cmHandleId) + "/state", FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
                 .iterator().next();
         cpsValidator.validateNameCharacters(cmHandleId);
         return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build();
@@ -87,14 +92,14 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
     public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) {
         final String cmHandleJsonData = createStateJsonData(jsonObjectMapper.asJsonString(compositeState));
         cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                createCmHandleXPath(cmHandleId), cmHandleJsonData, OffsetDateTime.now());
+                getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now());
     }
 
     @Override
     public void saveCmHandleStateBatch(final Map<String, CompositeState> cmHandleStatePerCmHandleId) {
         final Map<String, String> cmHandlesJsonDataMap = new HashMap<>();
         cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> cmHandlesJsonDataMap.put(
-                createCmHandleXPath(cmHandleId),
+                getXPathForCmHandleById(cmHandleId),
                 createStateJsonData(jsonObjectMapper.asJsonString(compositeState))));
         cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
                 cmHandlesJsonDataMap, OffsetDateTime.now());
@@ -103,8 +108,8 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
     @Override
     public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
         cpsValidator.validateNameCharacters(cmHandleId);
-        final DataNode dataNode = getCmHandleDataNode(cmHandleId).iterator().next();
-        return YangDataConverter.convertCmHandleToYangModel(dataNode, cmHandleId);
+        final DataNode dataNode = getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next();
+        return YangDataConverter.convertCmHandleToYangModel(dataNode);
     }
 
     @Override
@@ -158,14 +163,26 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
     }
 
     @Override
-    public Collection<DataNode> getCmHandleDataNode(final String cmHandleId) {
-        return this.getDataNode(createCmHandleXPath(cmHandleId));
+    public Collection<DataNode> getCmHandleDataNodeByCmHandleId(final String cmHandleId) {
+        return this.getDataNode(getXPathForCmHandleById(cmHandleId));
+    }
+
+    @Override
+    public DataNode getCmHandleDataNodeByAlternateId(final String alternateId) {
+        final String xPathForCmHandleByAlternateId = getXPathForCmHandleByAlternateId(alternateId);
+        final Collection<DataNode> dataNodes = cmHandleQueries
+            .queryNcmpRegistryByCpsPath(xPathForCmHandleByAlternateId, OMIT_DESCENDANTS);
+        if (dataNodes.isEmpty()) {
+            throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                xPathForCmHandleByAlternateId);
+        }
+        return dataNodes.iterator().next();
     }
 
     @Override
     public Collection<DataNode> getCmHandleDataNodes(final Collection<String> cmHandleIds) {
         final Collection<String> xpaths = new ArrayList<>(cmHandleIds.size());
-        cmHandleIds.forEach(cmHandleId -> xpaths.add(createCmHandleXPath(cmHandleId)));
+        cmHandleIds.forEach(cmHandleId -> xpaths.add(getXPathForCmHandleById(cmHandleId)));
         return this.getDataNodes(xpaths);
     }
 
@@ -174,10 +191,14 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
         return cpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
     }
 
-    private static String createCmHandleXPath(final String cmHandleId) {
+    private static String getXPathForCmHandleById(final String cmHandleId) {
         return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']";
     }
 
+    private static String getXPathForCmHandleByAlternateId(final String alternateId) {
+        return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='" + alternateId + "']";
+    }
+
     private static String createStateJsonData(final String state) {
         return "{\"state\":" + state + "}";
     }
index 22bbc73..794ca5b 100644 (file)
@@ -38,7 +38,6 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
 import org.onap.cps.ncmp.api.impl.inventory.CompositeState;
@@ -62,10 +61,10 @@ public class ModuleOperationsUtils {
     private final DmiDataOperations dmiDataOperations;
     private final JsonObjectMapper jsonObjectMapper;
     private static final String RETRY_ATTEMPT_KEY = "attempt";
-    public static final String MODULE_SET_TAG_KEY = "moduleSetTag";
-    public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: {0}";
-    private static final String UPGRADE_FORMAT = "Upgrade to ModuleSetTag: %s";
-    private static final String LOCK_REASON_DETAILS_MSG_FORMAT = UPGRADE_FORMAT + " Attempt #%d failed: %s";
+    private static final String MODULE_SET_TAG_KEY = "moduleSetTag";
+    public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: %s";
+    private static final String LOCK_REASON_DETAILS_MSG_FORMAT =
+            MODULE_SET_TAG_MESSAGE_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 = """
@@ -134,10 +133,10 @@ public class ModuleOperationsUtils {
         if (!compositeStateDetails.isEmpty() && compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)) {
             attempt = 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY));
         }
-        final String moduleSetTag = compositeStateDetails.get(MODULE_SET_TAG_KEY);
+        final String moduleSetTag = compositeStateDetails.getOrDefault(MODULE_SET_TAG_KEY, "");
         compositeState.setLockReason(CompositeState.LockReason.builder()
-                .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, StringUtils.isNotBlank(moduleSetTag)
-                        ? moduleSetTag : "not-specified", attempt, errorMessage)).lockReasonCategory(lockReasonCategory)
+                .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, moduleSetTag, attempt, errorMessage))
+                .lockReasonCategory(lockReasonCategory)
                 .build());
     }
 
@@ -221,13 +220,17 @@ public class ModuleOperationsUtils {
      * @param compositeState current lock reason of  cm handle
      * @return true or false based on lock reason category
      */
-    public static boolean isInUpgradeOrUpgradeFailed(final CompositeState compositeState) {
+    public static boolean inUpgradeOrUpgradeFailed(final CompositeState compositeState) {
         return compositeState.getLockReason() != null
                 && (LockReasonCategory.MODULE_UPGRADE.equals(compositeState.getLockReason().getLockReasonCategory())
                 || LockReasonCategory.MODULE_UPGRADE_FAILED.equals(compositeState.getLockReason()
                 .getLockReasonCategory()));
     }
 
+    public static String getUpgradedModuleSetTagFromLockReason(final CompositeState.LockReason lockReason) {
+        return getLockedCompositeStateDetails(lockReason).getOrDefault(MODULE_SET_TAG_KEY, "");
+    }
+
     private String getFirstResource(final Object responseBody) {
         final String jsonObjectAsString = jsonObjectMapper.asJsonString(responseBody);
         final JsonNode overallJsonNode = jsonObjectMapper.convertToJsonNode(jsonObjectAsString);
@@ -238,9 +241,7 @@ public class ModuleOperationsUtils {
 
     private List<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles(
             final List<DataNode> cmHandlesAsDataNodeList) {
-        return cmHandlesAsDataNodeList.stream()
-                .map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle,
-                        cmHandle.getLeaves().get("id").toString())).toList();
+        return cmHandlesAsDataNodeList.stream().map(YangDataConverter::convertCmHandleToYangModel).toList();
     }
 
     private boolean isRetryDue(final CompositeState.LockReason compositeStateLockReason, final OffsetDateTime time) {
index 72d3226..e257112 100644 (file)
@@ -28,10 +28,9 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPE
 import java.time.OffsetDateTime;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
+import lombok.AllArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -40,7 +39,6 @@ import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
-import org.onap.cps.ncmp.api.impl.inventory.CompositeState;
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
@@ -63,54 +61,39 @@ public class ModuleSyncService {
     private final CpsDataService cpsDataService;
     private final CpsAnchorService cpsAnchorService;
     private final JsonObjectMapper jsonObjectMapper;
-    private final Map<String, Collection<ModuleReference>> moduleSetTagCache;
     private static final Map<String, String> NO_NEW_MODULES = Collections.emptyMap();
 
+    @AllArgsConstructor
+    private static final class ModuleDelta {
+        Collection<ModuleReference> allModuleReferences;
+        Map<String, String> newModuleNameToContentMap;
+    }
+
     /**
-     * This method registers a cm handle and initiates modules sync.
+     * This method creates a cm handle and initiates modules sync.
      *
      * @param yangModelCmHandle the yang model of cm handle.
      */
-    public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
-
+    public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
+        final ModuleDelta moduleDelta = getModuleDelta(yangModelCmHandle, yangModelCmHandle.getModuleSetTag());
         final String cmHandleId = yangModelCmHandle.getId();
-        final CompositeState compositeState = yangModelCmHandle.getCompositeState();
-        final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState);
-        final String moduleSetTag = getModuleSetTag(yangModelCmHandle, compositeState, inUpgrade);
-
-        final Collection<ModuleReference> moduleReferencesFromCache = moduleSetTagCache.get(moduleSetTag);
-
-        if (moduleReferencesFromCache == null) {
-            final Optional<DataNode> existingCmHandleWithSameModuleSetTag
-                    = getFirstReadyDataNodeWithModuleSetTag(moduleSetTag);
+        cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId,
+                moduleDelta.newModuleNameToContentMap, moduleDelta.allModuleReferences);
+        cpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId);
+    }
 
-            if (existingCmHandleWithSameModuleSetTag.isPresent()) {
-                final String existingAnchorName = existingCmHandleWithSameModuleSetTag.get().getAnchorName();
-                final Collection<ModuleReference> moduleReferencesFromExistingCmHandle =
-                        upgradeOrCreateSchemaSetUsingModuleSetTag(yangModelCmHandle.getId(), moduleSetTag,
-                                existingAnchorName, inUpgrade);
-                updateModuleSetTagCache(moduleSetTag, moduleReferencesFromExistingCmHandle);
-            } else {
-                if (inUpgrade) {
-                    deleteSchemaSetIfExists(cmHandleId);
-                }
-                final Collection<ModuleReference> allModuleReferencesFromCmHandle
-                        = syncAndCreateSchemaSet(yangModelCmHandle);
-                updateModuleSetTagCache(moduleSetTag, allModuleReferencesFromCmHandle);
-            }
-        } else {
-            if (inUpgrade) {
-                cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId,
-                        NO_NEW_MODULES, moduleReferencesFromCache);
-            } else {
-                cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
-                        cmHandleId, NO_NEW_MODULES, moduleReferencesFromCache);
-            }
-        }
-        if (!inUpgrade) {
-            cpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cmHandleId);
-        }
-        setCmHandleModuleSetTag(yangModelCmHandle, moduleSetTag);
+    /**
+     * This method upgrades a cm handle and initiates modules sync.
+     *
+     * @param yangModelCmHandle the yang model of cm handle.
+     */
+    public void syncAndUpgradeSchemaSet(final YangModelCmHandle yangModelCmHandle) {
+        final String upgradedModuleSetTag = ModuleOperationsUtils.getUpgradedModuleSetTagFromLockReason(
+                yangModelCmHandle.getCompositeState().getLockReason());
+        final ModuleDelta moduleDelta = getModuleDelta(yangModelCmHandle, upgradedModuleSetTag);
+        cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+                yangModelCmHandle.getId(), moduleDelta.newModuleNameToContentMap, moduleDelta.allModuleReferences);
+        setCmHandleModuleSetTag(yangModelCmHandle, upgradedModuleSetTag);
     }
 
     /**
@@ -128,76 +111,41 @@ public class ModuleSyncService {
         }
     }
 
-    private Optional<DataNode> getFirstReadyDataNodeWithModuleSetTag(final String moduleSetTag) {
-        final List<DataNode> dataNodes = StringUtils.isNotBlank(moduleSetTag) ? cmHandleQueries
-                .queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='" + moduleSetTag + "']",
-                        FetchDescendantsOption.OMIT_DESCENDANTS) : Collections.emptyList();
-        return dataNodes.stream().filter(dataNode -> {
-            final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(dataNode.getXpath());
-            return cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY);
-        }).findFirst();
-    }
-
-    private void setCmHandleModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) {
-        final Map<String, Map<String, String>> dmiRegistryProperties = new HashMap<>(1);
-        final Map<String, String> cmHandleProperties = new HashMap<>(2);
-        cmHandleProperties.put("id", upgradedCmHandle.getId());
-        cmHandleProperties.put("module-set-tag", moduleSetTag);
-        dmiRegistryProperties.put("cm-handles", cmHandleProperties);
-        cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
-                jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now());
-    }
-
-    private Collection<ModuleReference> syncAndCreateSchemaSet(final YangModelCmHandle yangModelCmHandle) {
-        final Collection<ModuleReference> allModuleReferencesFromCmHandle =
-                dmiModelOperations.getModuleReferences(yangModelCmHandle);
-        final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService
-                .identifyNewModuleReferences(allModuleReferencesFromCmHandle);
-        final Map<String, String> newModuleNameToContentMap;
-        if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) {
-            newModuleNameToContentMap = NO_NEW_MODULES;
-        } else {
-            newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
-                    identifiedNewModuleReferencesFromCmHandle);
-        }
-        cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
-                yangModelCmHandle.getId(), newModuleNameToContentMap, allModuleReferencesFromCmHandle);
-        return allModuleReferencesFromCmHandle;
-    }
+    private ModuleDelta getModuleDelta(final YangModelCmHandle yangModelCmHandle, final String targetModuleSetTag) {
+        final Collection<ModuleReference> allModuleReferences;
+        final Map<String, String> newYangResources;
 
-    private Collection<ModuleReference> upgradeOrCreateSchemaSetUsingModuleSetTag(final String schemaSetName,
-                                                                                  final String moduleSetTag,
-                                                                                  final String existingAnchorName,
-                                                                                  final boolean inUpgrade) {
-        log.info("Found cm handle having module set tag: {}", moduleSetTag);
-        final Collection<ModuleReference> moduleReferencesFromExistingCmHandle =
-                cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
-                        existingAnchorName);
-        if (inUpgrade) {
-            cpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
-                    NO_NEW_MODULES, moduleReferencesFromExistingCmHandle);
+        final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(targetModuleSetTag);
+        if (cmHandleWithSameModuleSetTag == null) {
+            allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle);
+            newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
+                    cpsModuleService.identifyNewModuleReferences(allModuleReferences));
         } else {
-            cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
-                    schemaSetName, NO_NEW_MODULES, moduleReferencesFromExistingCmHandle);
+            log.info("Found other cm handle having same module set tag: {}", targetModuleSetTag);
+            allModuleReferences = cpsModuleService.getYangResourcesModuleReferences(
+                    NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId());
+            newYangResources = NO_NEW_MODULES;
         }
-        return moduleReferencesFromExistingCmHandle;
+        return new ModuleDelta(allModuleReferences, newYangResources);
     }
 
-    private String getModuleSetTag(final YangModelCmHandle yangModelCmHandle,
-                                   final CompositeState compositeState,
-                                   final boolean inUpgrade) {
-        if (inUpgrade) {
-            return ModuleOperationsUtils.getLockedCompositeStateDetails(compositeState.getLockReason())
-                    .get(ModuleOperationsUtils.MODULE_SET_TAG_KEY);
+    private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) {
+        if (StringUtils.isBlank(moduleSetTag)) {
+            return null;
         }
-        return yangModelCmHandle.getModuleSetTag();
+        final String escapedModuleSetTag = moduleSetTag.replace("'", "''");
+        final List<DataNode> dataNodes = cmHandleQueries.queryNcmpRegistryByCpsPath(
+                NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']",
+                FetchDescendantsOption.DIRECT_CHILDREN_ONLY);
+        return dataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel)
+                .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY)
+                .findFirst().orElse(null);
     }
 
-    private void updateModuleSetTagCache(final String moduleSetTag,
-                                         final Collection<ModuleReference> allModuleReferencesFromCmHandle) {
-        if (StringUtils.isNotBlank(moduleSetTag)) {
-            moduleSetTagCache.putIfAbsent(moduleSetTag, allModuleReferencesFromCmHandle);
-        }
+    private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) {
+        final String jsonForUpdate = jsonObjectMapper.asJsonString(Map.of(
+                "cm-handles", Map.of("id", yangModelCmHandle.getId(), "module-set-tag", newModuleSetTag)));
+        cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
+                jsonForUpdate, OffsetDateTime.now());
     }
-
 }
index e214044..590cb56 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.
@@ -65,14 +65,16 @@ public class ModuleSyncTasks {
             for (final DataNode cmHandleAsDataNode : cmHandlesAsDataNodes) {
                 final String cmHandleId = String.valueOf(cmHandleAsDataNode.getLeaves().get("id"));
                 final YangModelCmHandle yangModelCmHandle =
-                        YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode, cmHandleId);
+                        YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode);
                 final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
-                final boolean inUpgrade = ModuleOperationsUtils.isInUpgradeOrUpgradeFailed(compositeState);
+                final boolean inUpgrade = ModuleOperationsUtils.inUpgradeOrUpgradeFailed(compositeState);
                 try {
-                    if (!inUpgrade) {
+                    if (inUpgrade) {
+                        moduleSyncService.syncAndUpgradeSchemaSet(yangModelCmHandle);
+                    } else {
                         moduleSyncService.deleteSchemaSetIfExists(cmHandleId);
+                        moduleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle);
                     }
-                    moduleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle);
                     yangModelCmHandle.getCompositeState().setLockReason(null);
                     cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY);
                 } catch (final Exception e) {
index 32b5cb7..dbe386d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation
+ *  Copyright (C) 2021-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,7 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -83,6 +84,9 @@ public class DmiModelOperations extends DmiOperations {
      */
     public Map<String, String> getNewYangResourcesFromDmi(final YangModelCmHandle yangModelCmHandle,
                                                           final Collection<ModuleReference> newModuleReferences) {
+        if (newModuleReferences.isEmpty()) {
+            return Collections.emptyMap();
+        }
         final String jsonWithDataAndDmiProperties = getRequestBodyToFetchYangResources(
             newModuleReferences, yangModelCmHandle.getDmiProperties());
         final ResponseEntity<Object> responseEntity = getResourceFromDmiWithJsonData(
index 22f18cd..4c606a9 100644 (file)
@@ -24,10 +24,12 @@ import java.util.Collection;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig;
 import org.onap.cps.ncmp.api.impl.events.avc.ncmptoclient.AvcEventPublisher;
 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
 @Slf4j
@@ -35,7 +37,10 @@ import org.springframework.stereotype.Service;
 @RequiredArgsConstructor
 public class TrustLevelManager {
 
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_CM_HANDLE_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerCmHandle;
+
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
 
     private final InventoryPersistence inventoryPersistence;
index 6ae7ff3..78eaf3e 100644 (file)
@@ -26,8 +26,10 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
 import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
+import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig;
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel;
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -40,6 +42,8 @@ public class DmiPluginWatchDog {
     private final NetworkCmProxyDataService networkCmProxyDataService;
 
     private final TrustLevelManager trustLevelManager;
+
+    @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN_BEAN_NAME)
     private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
 
     /**
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java
new file mode 100644 (file)
index 0000000..4ac6537
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * ============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.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class AlternateIdChecker {
+
+    private final InventoryPersistence inventoryPersistence;
+
+    private static final String NO_CURRENT_ALTERNATE_ID = "";
+
+    /**
+     * Check if the alternate can be applied to the given cm handle (id).
+     * Conditions:
+     * - proposed alternate is blank (it wil be ignored)
+     * - proposed alternate is same as current (no change)
+     * - proposed alternate is not in use for a different cm handle (in the DB)
+     *
+     * @param cmHandleId cm handle id
+     * @param proposedAlternateId proposed alternate id
+     * @return true if the new alternate id not in use or equal to current alternate id, false otherwise
+     */
+    public boolean canApplyAlternateId(final String cmHandleId, final String proposedAlternateId) {
+        String currentAlternateId = "";
+        try {
+            final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
+            currentAlternateId = yangModelCmHandle.getAlternateId();
+        } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
+            // work with blank current alternate id
+        }
+        return this.canApplyAlternateId(cmHandleId, currentAlternateId, proposedAlternateId);
+    }
+
+    /**
+     * Check if the alternate can be applied to the given cm handle.
+     * Conditions:
+     * - proposed alternate is blank (it wil be ignored)
+     * - proposed alternate is same as current (no change)
+     * - proposed alternate is not in use for a different cm handle (in the DB)
+     *
+     * @param cmHandleId   cm handle id
+     * @param currentAlternateId current alternate id
+     * @param proposedAlternateId new alternate id
+     * @return true if the new alternate id not in use or equal to current alternate id, false otherwise
+     */
+    public boolean canApplyAlternateId(final String cmHandleId,
+                                       final String currentAlternateId,
+                                       final String proposedAlternateId) {
+        if (StringUtils.isBlank(currentAlternateId)) {
+            if (alternateIdAlreadyInDb(proposedAlternateId)) {
+                log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already "
+                    + "assigned to a different cm handle", cmHandleId);
+                return false;
+            }
+            return true;
+        }
+        if (currentAlternateId.equals(proposedAlternateId)) {
+            return true;
+        }
+        log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}",
+            cmHandleId, currentAlternateId);
+        return false;
+    }
+
+    /**
+     * Check all alternate ids of a batch of NEW cm handles.
+     * Includes cross-checks in the batch itself for duplicates. Only the first entry encountered wil be accepted.
+     * This method can only be used for NEW cm handle registrations NOT for updating existing ones
+     *
+     * @param newNcmpServiceCmHandles the proposed new cm handles
+     * @return collection of cm handles ids which are acceptable
+     */
+    public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId(
+                                    final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles) {
+        final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size());
+        final Collection<String> rejectedCmHandleIds = new ArrayList<>();
+        for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) {
+            final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
+            final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId();
+            final boolean isAcceptable;
+            if (StringUtils.isEmpty(proposedAlternateId)) {
+                isAcceptable = true;
+            } else {
+                if (acceptedAlternateIds.contains(proposedAlternateId)) {
+                    isAcceptable = false;
+                    log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already "
+                        + "assigned to a different cm handle (in this batch)", cmHandleId);
+                } else {
+                    isAcceptable = canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId);
+                }
+            }
+            if (isAcceptable) {
+                acceptedAlternateIds.add(proposedAlternateId);
+            } else {
+                rejectedCmHandleIds.add(cmHandleId);
+            }
+        }
+        return rejectedCmHandleIds;
+    }
+
+    private boolean alternateIdAlreadyInDb(final String alternateId) {
+        try {
+            inventoryPersistence.getCmHandleDataNodeByAlternateId(alternateId);
+        } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapper.java
deleted file mode 100644 (file)
index a88adbd..0000000
+++ /dev/null
@@ -1,95 +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.utils;
-
-import java.util.Map;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService;
-import org.springframework.stereotype.Service;
-
-@Service
-@Slf4j
-@RequiredArgsConstructor
-public class CmHandleIdMapper {
-
-    private final Map<String, String> alternateIdPerCmHandleId;
-    private final Map<String, String> cmHandleIdPerAlternateId;
-    private final NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService;
-
-    private boolean cacheIsInitialized = false;
-
-    public String cmHandleIdToAlternateId(final String cmHandleId) {
-        initializeCache();
-        return alternateIdPerCmHandleId.get(cmHandleId);
-    }
-
-    public String alternateIdToCmHandleId(final String alternateId) {
-        initializeCache();
-        return cmHandleIdPerAlternateId.get(alternateId);
-    }
-
-    public boolean addMapping(final String cmHandleId, final String alternateId) {
-        initializeCache();
-        return addMappingWithValidation(cmHandleId, alternateId);
-    }
-
-
-    private boolean addMappingWithValidation(final String cmHandleId, final String alternateId) {
-        if (alternateIdPerCmHandleId.containsKey(cmHandleId)) {
-            final String originalAlternateId = alternateIdPerCmHandleId.get(cmHandleId);
-            if (!originalAlternateId.equals(alternateId)) {
-                log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}",
-                        cmHandleId, originalAlternateId);
-            }
-            return false;
-        }
-        if (StringUtils.isBlank(alternateId)) {
-            return false;
-        }
-        alternateIdPerCmHandleId.put(cmHandleId, alternateId);
-        cmHandleIdPerAlternateId.put(alternateId, cmHandleId);
-        return true;
-    }
-
-    public void removeMapping(final String cmHandleId) {
-        final String alternateId = alternateIdPerCmHandleId.remove(cmHandleId);
-        removeAlternateIdWithValidation(alternateId);
-    }
-
-    private void removeAlternateIdWithValidation(final String alternateId) {
-        if (alternateId != null) {
-            cmHandleIdPerAlternateId.remove(alternateId);
-        }
-    }
-
-    private void initializeCache() {
-        if (!cacheIsInitialized) {
-            networkCmProxyCmHandleQueryService.getAllCmHandles().forEach(cmHandle ->
-                addMappingWithValidation(cmHandle.getCmHandleId(), cmHandle.getAlternateId())
-            );
-            log.info("Alternate ID cache initialized from DB with {} cm handle/alternate id pairs ",
-                    alternateIdPerCmHandleId.size());
-            cacheIsInitialized = true;
-        }
-    }
-}
index 3be97e8..3954142 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.utils;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -54,6 +54,7 @@ public class YangDataConverter {
         final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties();
         ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId());
         ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState());
+        ncmpServiceCmHandle.setModuleSetTag(yangModelCmHandle.getModuleSetTag());
         ncmpServiceCmHandle.setAlternateId(yangModelCmHandle.getAlternateId());
         setDmiProperties(dmiProperties, ncmpServiceCmHandle);
         setPublicProperties(publicProperties, ncmpServiceCmHandle);
@@ -75,12 +76,11 @@ public class YangDataConverter {
     /**
      * This method convert cm handle data node to yang model cm handle.
      * @param cmHandleDataNode the datanode of the cm handle
-     * @param cmHandleId the id of the cm handle
      * @return yang model cm handle
      */
-    public static YangModelCmHandle convertCmHandleToYangModel(final DataNode cmHandleDataNode,
-                                                               final String cmHandleId) {
+    public static YangModelCmHandle convertCmHandleToYangModel(final DataNode cmHandleDataNode) {
         final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
+        final String cmHandleId = cmHandleDataNode.getLeaves().get("id").toString();
         ncmpServiceCmHandle.setCmHandleId(cmHandleId);
         populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle);
         return YangModelCmHandle.toYangModelCmHandle(
@@ -100,12 +100,8 @@ public class YangDataConverter {
      */
     public static Collection<YangModelCmHandle> convertDataNodesToYangModelCmHandles(
             final Collection<DataNode> cmHandleDataNodes) {
-        final Collection<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(cmHandleDataNodes.size());
-        cmHandleDataNodes.forEach(dataNode -> {
-            final String cmHandleId = extractCmHandleIdFromXpath(dataNode.getXpath());
-            yangModelCmHandles.add(convertCmHandleToYangModel(dataNode, cmHandleId));
-        });
-        return yangModelCmHandles;
+        return cmHandleDataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel)
+                .collect(Collectors.toList());
     }
 
     /**
index 03e53fc..b2758d9 100644 (file)
@@ -24,7 +24,6 @@ package org.onap.cps.ncmp.api.impl.yangmodels;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Strings;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -94,6 +93,7 @@ public class YangModelCmHandle {
         copy.dmiProperties = original.getDmiProperties() == null ? null : new ArrayList<>(original.getDmiProperties());
         copy.publicProperties =
                 original.getPublicProperties() == null ? null : new ArrayList<>(original.getPublicProperties());
+        copy.moduleSetTag = original.getModuleSetTag();
         copy.alternateId = original.getAlternateId();
         return copy;
     }
@@ -134,7 +134,7 @@ public class YangModelCmHandle {
      * @return dmi service name
      */
     public String resolveDmiServiceName(final RequiredDmiService requiredService) {
-        if (isNullEmptyOrBlank(dmiServiceName)) {
+        if (StringUtils.isBlank(dmiServiceName)) {
             if (RequiredDmiService.DATA.equals(requiredService)) {
                 return dmiDataServiceName;
             }
@@ -151,10 +151,6 @@ public class YangModelCmHandle {
         return yangModelCmHandleProperties;
     }
 
-    private static boolean isNullEmptyOrBlank(final String serviceName) {
-        return Strings.isNullOrEmpty(serviceName) || serviceName.isBlank();
-    }
-
     @AllArgsConstructor
     @Data
     public static class Property {
index 8228322..52b8d69 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 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.
@@ -76,20 +76,22 @@ public class CmHandleRegistrationResponse {
     }
 
     /**
-     * Creates a failure response based on registration error.
+     * Create a failure response of cm handle registration based on xpath and registration error.
+     * Conditions:
+     * - the xpath should be valid according to the cps path, otherwise xpath is not included in the response.
      *
-     * @param failedXpaths       list of failed Xpaths
-     * @param ncmpResponseStatus enum describing the type of registration error
-     * @return CmHandleRegistrationResponse
+     * @param failedXpaths the failed xpaths
+     * @param ncmpResponseStatus type of the registration error
+     * @return collection of cm handle registration response
      */
-    public static List<CmHandleRegistrationResponse> createFailureResponses(final Collection<String> failedXpaths,
-            final NcmpResponseStatus ncmpResponseStatus) {
+    public static List<CmHandleRegistrationResponse> createFailureResponsesFromXpaths(
+        final Collection<String> failedXpaths, final NcmpResponseStatus ncmpResponseStatus) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size());
         for (final String xpath : failedXpaths) {
             try {
                 final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(xpath);
-                cmHandleRegistrationResponses.add(
-                        CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus));
+                cmHandleRegistrationResponses
+                    .add(CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus));
             } catch (IllegalArgumentException | IllegalStateException e) {
                 log.warn("Unexpected xpath {}", xpath);
             }
@@ -97,6 +99,24 @@ public class CmHandleRegistrationResponse {
         return cmHandleRegistrationResponses;
     }
 
+    /**
+     * Create a failure response of cm handle registration based on cm handle id and registration error.
+     *
+     * @param failedCmHandleIds the failed cm handle ids
+     * @param ncmpResponseStatus type of the registration error
+     * @return collection of cm handle registration response
+     */
+    public static List<CmHandleRegistrationResponse> createFailureResponses(
+            final Collection<String> failedCmHandleIds, final NcmpResponseStatus ncmpResponseStatus) {
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
+            new ArrayList<>(failedCmHandleIds.size());
+        for (final String failedCmHandleId : failedCmHandleIds) {
+            cmHandleRegistrationResponses.add(
+                CmHandleRegistrationResponse.createFailureResponse(failedCmHandleId, ncmpResponseStatus));
+        }
+        return cmHandleRegistrationResponses;
+    }
+
     /**
      * Creates a failure response based on other exception.
      *
index 4615af6..7d6a8e1 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.
@@ -50,7 +50,7 @@ public class DmiPluginRegistration {
 
     private List<String> removedCmHandles = Collections.emptyList();
 
-    private UpgradedCmHandles upgradedCmHandles;
+    private UpgradedCmHandles upgradedCmHandles = new UpgradedCmHandles();
 
     /**
      * Validates plugin service names.
index 7cee87a..4c31719 100644 (file)
@@ -32,7 +32,6 @@ import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException;
 import org.onap.cps.ncmp.api.impl.operations.DatastoreType;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 @Slf4j
@@ -45,24 +44,16 @@ public class CmDataSubscriptionModelLoader extends AbstractModelLoader {
     private static final String REGISTRY_DATANODE_NAME = "datastores";
 
     public CmDataSubscriptionModelLoader(final CpsDataspaceService cpsDataspaceService,
-                                         final CpsModuleService cpsModuleService,
-                                         final CpsAnchorService cpsAnchorService,
-                                         final CpsDataService cpsDataService) {
+            final CpsModuleService cpsModuleService, final CpsAnchorService cpsAnchorService,
+            final CpsDataService cpsDataService) {
         super(cpsDataspaceService, cpsModuleService, cpsAnchorService, cpsDataService);
     }
 
-    @Value("${ncmp.model-loader.subscription:true}")
-    private boolean subscriptionModelLoaderEnabled;
-
     @Override
     public void onboardOrUpgradeModel() {
-        if (subscriptionModelLoaderEnabled) {
-            waitUntilDataspaceIsAvailable(NCMP_DATASPACE_NAME);
-            onboardSubscriptionModels();
-            log.info("Subscription Models onboarded successfully");
-        } else {
-            log.info("Subscription Model Loader is disabled");
-        }
+        waitUntilDataspaceIsAvailable(NCMP_DATASPACE_NAME);
+        onboardSubscriptionModels();
+        log.info("Subscription Models onboarded successfully");
     }
 
     private void onboardSubscriptionModels() {
index c7ac8ab..cb7e1ef 100644 (file)
@@ -27,8 +27,9 @@ 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 org.onap.cps.ncmp.api.impl.inventory.CompositeState
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
+import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
 import org.onap.cps.ncmp.api.models.UpgradedCmHandles
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.hazelcast.map.IMap
@@ -68,9 +69,20 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockModuleSyncStartedOnCmHandles = Mock(IMap<String, Object>)
     def trustLevelPerDmiPlugin = [:]
     def mockTrustLevelManager = Mock(TrustLevelManager)
-    def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
-    def objectUnderTest = getObjectUnderTest()
-    def mockModuleSetTagCache = [:]
+    def mockAlternateIdChecker = Mock(AlternateIdChecker)
+
+    def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
+        mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
+        stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
+        mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockAlternateIdChecker))
+
+    def setup() {
+        // always accept all cm handles
+        mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
+
+        // always can find all cm handles in DB
+        mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } }
+    }
 
     def 'DMI Registration: Create, Update, Delete & Upgrade operations are processed in the right order'() {
         given: 'a registration with operations of all types'
@@ -81,20 +93,22 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag'))
         and: 'cm handles are persisted'
             mockInventoryPersistence.getYangModelCmHandles(['cmhandle-2']) >> [new YangModelCmHandle()]
+            mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: CmHandleState.READY))
         and: 'cm handle is in READY state'
             mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> true
         when: 'registration is processed'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
         then: 'cm-handles are removed first'
-            1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_)
+            1 * objectUnderTest.processRemovedCmHandles(*_)
         and: 'de-registered cm handle entry is removed from in progress map'
-            2 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
+            1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
         then: 'cm-handles are created'
-            1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_)
+            1 * objectUnderTest.processCreatedCmHandles(*_)
         then: 'cm-handles are updated'
-            1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_)
+            1 * objectUnderTest.processUpdatedCmHandles(*_)
+            1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> []
         then: 'cm-handles are upgraded'
-            1 * objectUnderTest.parseAndProcessUpgradedCmHandlesInRegistration(*_)
+            1 * objectUnderTest.processUpgradedCmHandles(*_)
     }
 
     def 'DMI Registration upgrade operation with upgrade node state #scenario'() {
@@ -102,15 +116,15 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
             dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag'))
         and: 'exception while checking cm handle state'
-            mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> isReady
+            mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> new YangModelCmHandle(id: 'cmhandle-3', moduleSetTag: '', compositeState: new CompositeState(cmHandleState: cmHandleState))
         when: 'registration is processed'
             def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
         then: 'upgrade operation contains expected error code'
-            assert result.upgradedCmHandles.status[0] == expectedResponseStatus
+            assert result.upgradedCmHandles[0].status == expectedResponseStatus
         where: 'the following parameters are used'
-            scenario    | isReady || expectedResponseStatus
-            'READY'     | true    || Status.SUCCESS
-            'Not READY' | false   || Status.FAILURE
+            scenario    | cmHandleState        || expectedResponseStatus
+            'READY'     | CmHandleState.READY  || Status.SUCCESS
+            'Not READY' | CmHandleState.LOCKED || Status.FAILURE
     }
 
     def 'DMI Registration upgrade with exception #scenario'() {
@@ -118,7 +132,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
             dmiRegistration.setUpgradedCmHandles(new UpgradedCmHandles(cmHandles: ['cmhandle-3'], moduleSetTag: 'some-module-set-tag'))
         and: 'exception while checking cm handle state'
-            mockCmHandleQueries.cmHandleHasState('cmhandle-3', CmHandleState.READY) >> { throw exception }
+            mockInventoryPersistence.getYangModelCmHandle('cmhandle-3') >> { throw exception }
         when: 'registration is processed'
             def result = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
         then: 'upgrade operation contains expected error code'
@@ -129,29 +143,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'cm handle is invalid' | new DataValidationException('some error message', 'some error details')  || '110'
     }
 
-    def 'DMI Registration: Response from all operations types are in response'() {
-        given: 'a registration with operations of all three types'
-            def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
-            dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
-            dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
-            dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
-        and: 'update cm-handles can be processed successfully'
-            def updateResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-2')]
-            mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses
-        and: 'create cm-handles can be processed successfully'
-            def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')]
-            objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) >> createdResponses
-        and: 'delete cm-handles can be processed successfully'
-            def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')]
-            objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) >> removeResponses
-        when: 'registration is processed'
-            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
-        then: 'response has values from all operations'
-            response.removedCmHandles == removeResponses
-            response.createdCmHandles == createdResponses
-            response.updatedCmHandles == updateResponses
-    }
-
     def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() {
         given: 'a registration '
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin,
@@ -160,7 +151,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         when: 'update registration and sync module is called with correct DMI plugin information'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'create cm handles registration and sync modules is called with the correct plugin information'
-            1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)
+            1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _)
         and: 'dmi is added to the dmi trustLevel map'
             assert trustLevelPerDmiPlugin.size() == 1
             assert trustLevelPerDmiPlugin.containsKey(expectedDmiPluginRegisteredName)
@@ -182,7 +173,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def exceptionThrown = thrown(DmiRequestException.class)
             assert exceptionThrown.getMessage().contains(expectedMessageDetails)
         and: 'registration is not called'
-            0 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)
+            0 * objectUnderTest.processCreatedCmHandles(*_)
         where:
             scenario                         | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
             'empty DMI plugins'              | ''         | ''             | ''            || 'No DMI plugin service names'
@@ -223,22 +214,18 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'without dmi & public properties' | [:]                      | [:]                            || [:]                                        | [:]
     }
 
-    def 'Add CM-Handle to trustLevelPerCmHandle Successfully with: #scenario.'() {
-        given: 'a registration with trustLevel and populated cache'
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
-            dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: TrustLevel.NONE),
-                                                      new NcmpServiceCmHandle(cmHandleId: cmHandleId, registrationTrustLevel: registrationTrustLevel)]
+    def 'Add CM-Handle #scenario.'() {
+        given: ' registration details for one cm handles'
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
+                createdCmHandles:[new NcmpServiceCmHandle(cmHandleId: 'ch-1', registrationTrustLevel: registrationTrustLevel)])
         when: 'registration is updated'
-            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'a successful response is received'
-            assert response.createdCmHandles.size() == expectedNumberOfCreatedCmHandles
-        and: 'trustLevel is set for the created cm-handle'
-            1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(_)
+            objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+        then: 'trustLevel is set for the created cm-handle'
+            1 * mockTrustLevelManager.handleInitialRegistrationOfTrustLevels(expectedMapping)
         where:
-            scenario                                 | cmHandleId | registrationTrustLevel || expectedNumberOfCreatedCmHandles
-            'new trusted cm handle'                  | 'ch-new'   | TrustLevel.COMPLETE    || 2
-            'existing cm handle without trust level' | 'ch-1'     | null                   || 1
-            'new cm handle without trust level'      | 'ch-new'   | null                   || 2
+            scenario                 | registrationTrustLevel || expectedMapping
+            'with trusted cm handle' | TrustLevel.COMPLETE    || [ 'ch-1' : TrustLevel.COMPLETE ]
+            'without trust level'    | null                   || [ 'ch-1' : null ]
     }
 
     def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() {
@@ -303,17 +290,15 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     def 'Remove CmHandle Successfully: #scenario'() {
         given: 'a registration'
-            addPersistedYangModelCmHandles(['cmhandle'])
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
-                removedCmHandles: ['cmhandle'])
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', removedCmHandles: ['cmhandle'])
         and: '#scenario'
-            mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >>
-                { if (!schemaSetExist) { throw new SchemaSetNotFoundException("", "") } }
+            mockCpsModuleService.deleteSchemaSetsWithCascade(_, ['cmhandle']) >>  { if (!schemaSetExist) { throw new SchemaSetNotFoundException('', '') } }
         when: 'registration is updated to delete cmhandle'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'the cmHandle state is updated to "DELETING"'
-            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_)
-        and: 'method to delete relevant schema set is called once'
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >>
+                { args -> args[0].values()[0] == CmHandleState.DELETING }
+        then: 'method to delete relevant schema set is called once'
             1 * mockInventoryPersistence.deleteSchemaSetsWithCascade(_)
         and: 'method to delete relevant list/list element is called once'
             1 * mockInventoryPersistence.deleteDataNodes(_)
@@ -324,7 +309,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.cmHandle == 'cmhandle'
             }
         and: 'the cmHandle state is updated to "DELETED"'
-            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_)
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >>
+                { args -> args[0].values()[0] == CmHandleState.DELETED }
+        and: 'No cm handles state updates for "upgraded cm handles"'
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:])
         where:
             scenario                                            | schemaSetExist
             'schema-set exists and can be deleted successfully' | true
@@ -332,9 +320,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     }
 
     def 'Remove CmHandle: Partial Success'() {
-        given: 'some unique yang model cm handles'
-            addPersistedYangModelCmHandles(['cmhandle1', 'cmhandle2', 'cmhandle3'])
-        and: 'a registration with three cm-handles to be deleted'
+        given: 'a registration with three cm-handles to be deleted'
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle1', 'cmhandle2', 'cmhandle3'])
         and: 'cm-handle deletion fails on batch'
@@ -351,7 +337,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle1')
         and: 'successfully de-registered cm handle 3 is removed from in progress map even though it was already being removed'
             1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle3') >> 'already in progress'
-        and: 'failed de-registered cm handle entries should not be removed from in progress map'
+        and: 'failed de-registered cm handle entries should NOT be removed from in progress map'
             0 * mockModuleSyncStartedOnCmHandles.remove('cmhandle2')
         and: '1st and 3rd cm-handle deletes successfully'
             with(response.removedCmHandles[0]) {
@@ -374,11 +360,13 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.size() == 2
                 assert it.every { entry -> entry.value == CmHandleState.DELETED }
             })
+        and: 'No cm handles state updates for "upgraded cm handles"'
+            1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch([:])
+
     }
 
     def 'Remove CmHandle Error Handling: Schema Set Deletion failed'() {
         given: 'a registration'
-            addPersistedYangModelCmHandles(['cmhandle'])
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle'])
         and: 'schema set batch deletion failed with unknown error'
@@ -405,7 +393,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     def 'Remove CmHandle Error Handling: #scenario'() {
         given: 'a registration'
-            addPersistedYangModelCmHandles(['cmhandle'])
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
                 removedCmHandles: ['cmhandle'])
         and: 'cm-handle deletion fails on batch'
@@ -433,23 +420,12 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     def 'Adding data to alternate id caches.'() {
         given: 'a registration with three CM Handles to be created'
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
-                    createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1', alternateId: 'my-alternate-id-1')])
+            def ncmpServiceCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1', alternateId: 'my-alternate-id-1')]
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', createdCmHandles: ncmpServiceCmHandles)
         when: 'the DMI plugin registration happens'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
         then: 'the new alternate id is added to the cache'
-            1 * mockCmHandleIdMapper.addMapping('cmhandle1', 'my-alternate-id-1')
+            1 * mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(ncmpServiceCmHandles) >> ['cmhandle1']
     }
 
-    def getObjectUnderTest() {
-        return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
-                mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmHandleQueries,
-                stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService,
-                mockModuleSyncStartedOnCmHandles, trustLevelPerDmiPlugin, mockTrustLevelManager, mockCmHandleIdMapper, mockModuleSetTagCache))
-    }
-
-    def addPersistedYangModelCmHandles(ids) {
-        def yangModelCmHandles = ids.collect { new YangModelCmHandle(id:it) }
-        mockInventoryPersistence.getYangModelCmHandles(ids) >> yangModelCmHandles
-    }
 }
index c835056..9b4fe14 100644 (file)
@@ -23,6 +23,8 @@
 
 package org.onap.cps.ncmp.api.impl
 
+import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
+
 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
@@ -32,7 +34,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 org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
 import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler
@@ -54,7 +56,7 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.api.models.DataOperationRequest
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.spi.model.ConditionProperties
-import spock.lang.Shared
+
 import java.util.stream.Collectors
 import org.onap.cps.utils.JsonObjectMapper
 import com.fasterxml.jackson.databind.ObjectMapper
@@ -80,14 +82,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
     def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
     def mockTrustLevelManager = Mock(TrustLevelManager)
-    def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
-    def mockModuleSetTagCache = [:]
+    def mockAlternateIdChecker = Mock(AlternateIdChecker)
 
     def NO_TOPIC = null
     def NO_REQUEST_ID = null
-    @Shared
     def OPTIONS_PARAM = '(a=1,b=2)'
-    @Shared
     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'test-cm-handle-id')
 
     def objectUnderTest = new NetworkCmProxyDataServiceImpl(
@@ -102,8 +101,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             stubModuleSyncStartedOnCmHandles,
             stubTrustLevelPerDmiPlugin,
             mockTrustLevelManager,
-            mockCmHandleIdMapper,
-            mockModuleSetTagCache)
+            mockAlternateIdChecker)
 
     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
@@ -177,14 +175,17 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         given: 'the system returns a yang modelled cm handle'
             def dmiServiceName = 'some service name'
             def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
-                lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
-                lastUpdateTime: 'some-timestamp',
-                dataSyncEnabled: false,
-                dataStores: dataStores())
+                    lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.MODULE_SYNC_FAILED).details("lock details").build(),
+                    lastUpdateTime: 'some-timestamp',
+                    dataSyncEnabled: false,
+                    dataStores: dataStores())
             def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
             def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
+            def moduleSetTag = 'some-module-set-tag'
+            def alternateId = 'some-alternate-id'
             def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
-                dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
+                    dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState,
+                    moduleSetTag: moduleSetTag, alternateId: alternateId)
             1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
         when: 'getting cm handle details for a given cm handle id from ncmp service'
             def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
@@ -192,13 +193,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             result.class == NcmpServiceCmHandle.class
         and: 'the cm handle contains the cm handle id'
             result.cmHandleId == 'some-cm-handle'
+        and: 'the cm handle contains the alternate id'
+            result.alternateId == 'some-alternate-id'
+        and: 'the cm handle contains the module-set-tag'
+            result.moduleSetTag == 'some-module-set-tag'
         and: 'the cm handle contains the DMI Properties'
             result.dmiProperties ==[ Book:'Romance Novel' ]
         and: 'the cm handle contains the public Properties'
             result.publicProperties == [ "Public Book":'Public Romance Novel' ]
         and: 'the cm handle contains the cm handle composite state'
             result.compositeState == compositeState
-
     }
 
     def 'Get cm handle public properties'() {
@@ -268,8 +272,11 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                     dmiDataPlugin: 'service2')
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
+        and: 'no rejected cm handles because of alternate ids'
+            mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
         when: 'parse and create cm handle in dmi registration then sync module'
-            objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration)
+            mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
+            objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
         then: 'system persists the cm handle state'
             1 * mockLcmEventsCmHandleStateHandler.initiateStateAdvised(_) >> {
                 args -> {
@@ -280,7 +287,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                     }
             }
     }
-    
+
     def 'Execute cm handle id search'() {
         given: 'valid CmHandleQueryApiParameters input'
             def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
index f94c34c..e00a426 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
-
 import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.ncmp.api.impl.utils.CmHandleIdMapper
-
-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
-import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
-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.api.CpsDataService
-import org.onap.cps.utils.JsonObjectMapper
 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
-import org.onap.cps.spi.exceptions.DataValidationException
+import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
+import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
+import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Specification
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
+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.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
+import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
+
 class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def mockInventoryPersistence = Mock(InventoryPersistence)
     def mockCpsDataService = Mock(CpsDataService)
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
-    def mockCmHandleIdMapper = Mock(CmHandleIdMapper)
+    def mockAlternateIdChecker = Mock(AlternateIdChecker)
 
-    def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockCmHandleIdMapper)
+    def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker)
     def static cmHandleId = 'myHandle1'
     def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']"
 
@@ -58,11 +56,11 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                                     new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/additional-properties[@name='additionalProp2']").withLeaves(['name': 'additionalProp2', 'value': 'additionalValue2']).build(),
                                     new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp3']").withLeaves(['name': 'publicProp3', 'value': 'publicValue3']).build(),
                                     new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/public-properties[@name='publicProp4']").withLeaves(['name': 'publicProp4', 'value': 'publicValue4']).build()]
-    def static cmHandleDataNodeAsCollection = [new DataNode(xpath: cmHandleXpath, childDataNodes: propertyDataNodes)]
+    def static cmHandleDataNodeAsCollection = [new DataNode(xpath: cmHandleXpath, childDataNodes: propertyDataNodes, leaves: ['id': cmHandleId])]
 
     def 'Update CM Handle Public Properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNodeAsCollection
+            mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection
         and: 'an update cm handle request with public properties updates'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)]
         when: 'update data node leaves is called with the update request'
@@ -84,7 +82,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update DMI Properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> cmHandleDataNodeAsCollection
+            mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> cmHandleDataNodeAsCollection
         and: 'an update cm handle request with DMI properties updates'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)]
         when: 'update data node leaves is called with the update request'
@@ -107,8 +105,8 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update CM Handle Properties, remove all properties: #scenario'() {
         given: 'the CPS service return a CM handle'
-            def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: originalPropertyDataNodes)
-            mockInventoryPersistence.getCmHandleDataNode(cmHandleId) >> [cmHandleDataNode]
+            def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId], childDataNodes: originalPropertyDataNodes)
+            mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [cmHandleDataNode]
         and: 'an update cm handle request that removes all public properties(existing and non-existing)'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])]
         when: 'update data node leaves is called with the update request'
@@ -131,7 +129,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
         given: 'cm handles request'
             def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])]
         and: 'data node cannot be found'
-            mockInventoryPersistence.getCmHandleDataNode(*_) >> { throw exception }
+            mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> { throw exception }
         when: 'update data node leaves is called using correct parameters'
             def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
         then: 'one failed registration response'
@@ -156,7 +154,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                                          new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
                                          new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])]
         and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle'
-            mockInventoryPersistence.getCmHandleDataNode(*_) >> cmHandleDataNodeAsCollection >> {
+            mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(*_) >> cmHandleDataNodeAsCollection >> {
                 throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) } >> cmHandleDataNodeAsCollection
         when: 'update data node leaves is called using correct parameters'
             def cmHandleResponseList = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
@@ -184,9 +182,9 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
 
     def 'Update CM Handle Alternate ID with #scenario'() {
         given: 'an existing cm handle'
-            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath)
+            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId])
         and: 'an update request with an alternate id'
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1' )
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')
         when: 'update alternate id method is called with the update request'
             objectUnderTest.updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle)
         then: 'the update node leaves method is invoked as many times as expected'
@@ -194,7 +192,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
                     { args ->
                         assert args[3].contains('alt-1')
                     }
-            mockCmHandleIdMapper.addMapping(cmHandleId, 'alt-1') >> isNewMapping
+            mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> isNewMapping
         where: 'following updates are attempted'
             scenario                | isNewMapping || callsToDataService
             'new alternate id   '   | true         || 1
@@ -204,9 +202,9 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
     def 'Alternate ID removed from cache when persisting fails.'() {
         given: 'an existing data node and an update request with an alternate id'
             def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')
-            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['alternate-id': null])
-        and: 'a new mapping is added'
-            mockCmHandleIdMapper.addMapping(cmHandleId, 'alt-1') >> true
+            DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': null])
+        and: 'an applicable alternate id for the cm handle'
+            mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> true
         and: 'but an exception occurs while saving'
             def originalException = new NullPointerException('some exception')
             mockCpsDataService.updateNodeLeaves(*_) >> { throw originalException }
@@ -215,8 +213,6 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
         then: 'the original exception is thrown up'
             def thrownException = thrown(NullPointerException)
             assert thrownException == originalException
-        and: 'the mapping is removed from the cache'
-            1 * mockCmHandleIdMapper.removeMapping(cmHandleId)
     }
 
     def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/AlternateIdCacheConfigSpec.groovy
deleted file mode 100644 (file)
index 71c5293..0000000
+++ /dev/null
@@ -1,59 +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.config.embeddedcache
-
-import com.hazelcast.core.Hazelcast
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import spock.lang.Specification
-
-@SpringBootTest(classes = [AlternateIdCacheConfig])
-class AlternateIdCacheConfigSpec extends Specification {
-
-    @Autowired
-    private Map<String, String> cmHandleIdPerAlternateId
-    @Autowired
-    private Map<String, String> alternateIdPerCmHandleId
-
-    def 'Embedded (hazelcast) cache for alternate id - cm handle id caches.'() {
-        expect: 'system is able to create an instance of the Alternate ID Cache'
-            assert null != cmHandleIdPerAlternateId
-            assert null != alternateIdPerCmHandleId
-        and: 'there are at least 2 instances'
-            assert Hazelcast.allHazelcastInstances.size() > 1
-        and: 'Alternate ID Caches are present'
-            assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceAlternateIdPerCmHandleIdMap')
-                    && Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceCmHandleIdPerAlternateIdMap')
-    }
-
-    def 'Verify configs of the alternate id distributed objects.'(){
-        when: 'retrieving the map config of module set tag'
-            def alternateIdConfig =  Hazelcast.getHazelcastInstanceByName('hazelcastInstanceAlternateIdPerCmHandleIdMap').config
-            def alternateIdMapConfig = alternateIdConfig.mapConfigs.get('alternateIdPerCmHandleIdMapConfig')
-            def cmHandleIdConfig =  Hazelcast.getHazelcastInstanceByName('hazelcastInstanceCmHandleIdPerAlternateIdMap').config
-            def cmHandleIdIdMapConfig = cmHandleIdConfig.mapConfigs.get('cmHandleIdPerAlternateIdMapConfig')
-        then: 'the map configs have the correct number of backups'
-            assert alternateIdMapConfig.backupCount == 3
-            assert alternateIdMapConfig.asyncBackupCount == 3
-            assert cmHandleIdIdMapConfig.backupCount == 3
-            assert cmHandleIdIdMapConfig.asyncBackupCount == 3
-    }
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/ModuleSetTagCacheConfigSpec.groovy
deleted file mode 100644 (file)
index 8a6a32b..0000000
+++ /dev/null
@@ -1,76 +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.config.Config
-import com.hazelcast.core.Hazelcast
-import org.onap.cps.spi.model.ModuleReference
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import spock.lang.Specification
-
-@SpringBootTest(classes = [ModuleSetTagCacheConfig])
-class ModuleSetTagCacheConfigSpec extends Specification {
-
-    @Autowired
-    private Map<String, Collection<ModuleReference>> moduleSetTagCache
-
-    def 'Embedded (hazelcast) caches for module set tag.'() {
-        expect: 'system is able to create an instance of a map to hold module set tags'
-            assert null != moduleSetTagCache
-        and: 'there is at least 1 instance'
-            assert Hazelcast.allHazelcastInstances.size() > 0
-        and: 'hazelcast instance name of module set tag is correct'
-            assert Hazelcast.allHazelcastInstances.name.contains('moduleSetTags')
-    }
-
-    def 'Verify configs of module set tag distributed object.'(){
-        when: 'retrieving the map config of module set tag'
-            def cacheConfig =  Hazelcast.getHazelcastInstanceByName('moduleSetTags').config
-            def moduleSetTagMapConfig = cacheConfig.mapConfigs.get('moduleSetTagCacheMapConfig')
-        then: 'the module set tag map config has correct backup counts'
-            assert moduleSetTagMapConfig.backupCount == 3
-            assert moduleSetTagMapConfig.asyncBackupCount == 3
-    }
-
-    def 'Verify deployment network configs of distributed cache of module set tag object.'() {
-        given: 'network config of module set tag cache'
-            def moduleSetTagNetworkConfig = Hazelcast.getHazelcastInstanceByName('moduleSetTags').config.networkConfig
-        expect: 'module set tag cache has the correct settings'
-            assert moduleSetTagNetworkConfig.join.autoDetectionConfig.enabled
-            assert !moduleSetTagNetworkConfig.join.kubernetesConfig.enabled
-    }
-
-    def 'Verify network of module set tag cache'() {
-        given: 'Synchronization config object and test configuration'
-        def objectUnderTest = new ModuleSetTagCacheConfig()
-        def testConfig = new Config()
-        when: 'kubernetes properties are enabled'
-            objectUnderTest.cacheKubernetesEnabled = true
-            objectUnderTest.cacheKubernetesServiceName = 'test-service-name'
-        and: 'method called to update the discovery mode'
-            objectUnderTest.updateDiscoveryMode(testConfig)
-        then: 'applied properties are reflected'
-            assert testConfig.networkConfig.join.kubernetesConfig.enabled
-            assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name'
-
-    }
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy
new file mode 100644 (file)
index 0000000..cd9b8dd
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * ============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
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import io.cloudevents.CloudEvent
+import org.onap.cps.events.EventsPublisher
+import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Cmhandle
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Data
+import org.onap.cps.utils.JsonObjectMapper
+import spock.lang.Specification
+
+class CmNotificationSubscriptionDmiInEventProducerSpec extends Specification {
+
+    def mockEventsPublisher = Mock(EventsPublisher)
+    def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
+    def objectUnderTest = new CmNotificationSubscriptionDmiInEventProducer(mockEventsPublisher, jsonObjectMapper)
+
+    def 'Create and Publish Cm Notification Subscription DMI In Event'() {
+        given: 'a cm subscription for a dmi plugin'
+            def subscriptionId = 'test-subscription-id'
+            def dmiPluginName = 'test-dmiplugin'
+            def eventType = 'subscriptionCreateRequest'
+            def cmNotificationSubscriptionDmiInEvent = new CmNotificationSubscriptionDmiInEvent(data: new Data(cmhandles: [new Cmhandle(cmhandleId: 'test-1', privateProperties: [:])]))
+        and: 'also we have target topic for dmiPlugin'
+            objectUnderTest.cmNotificationSubscriptionDmiInEventTopic = 'dmiplugin-test-topic'
+        when: 'the event is published'
+            objectUnderTest.publishCmNotificationSubscriptionDmiInEvent(subscriptionId, dmiPluginName, eventType, cmNotificationSubscriptionDmiInEvent)
+        then: 'the event contains the required attributes'
+            1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> {
+                args ->
+                    {
+                        assert args[0] == 'dmiplugin-test-topic'
+                        assert args[1] == subscriptionId
+                        def cmNotificationSubscriptionDmiInEventAsCloudEvent = (args[2] as CloudEvent)
+                        assert cmNotificationSubscriptionDmiInEventAsCloudEvent.getExtension('correlationid') == subscriptionId + '#' + dmiPluginName
+                        assert cmNotificationSubscriptionDmiInEventAsCloudEvent.type == 'subscriptionCreateRequest'
+                        assert cmNotificationSubscriptionDmiInEventAsCloudEvent.source.toString() == 'NCMP'
+                        assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionDmiInEventAsCloudEvent, CmNotificationSubscriptionDmiInEvent) == cmNotificationSubscriptionDmiInEvent
+                    }
+            }
+    }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy
new file mode 100644 (file)
index 0000000..4f0132e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * ============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
+
+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 com.fasterxml.jackson.databind.ObjectMapper
+import io.cloudevents.CloudEvent
+import io.cloudevents.core.builder.CloudEventBuilder
+import org.apache.kafka.clients.consumer.ConsumerRecord
+import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent
+import org.onap.cps.ncmp.utils.TestUtils
+import org.onap.cps.utils.JsonObjectMapper
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+
+@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper])
+class CmNotificationSubscriptionDmiOutEventConsumerSpec extends MessagingBaseSpec {
+
+    def objectUnderTest = new CmNotificationSubscriptionDmiOutEventConsumer()
+    def logger = Spy(ListAppender<ILoggingEvent>)
+
+    @Autowired
+    JsonObjectMapper jsonObjectMapper
+
+    @Autowired
+    ObjectMapper objectMapper
+
+    void setup() {
+        ((Logger) LoggerFactory.getLogger(CmNotificationSubscriptionDmiOutEventConsumer.class)).addAppender(logger)
+        logger.start()
+    }
+
+    void cleanup() {
+        ((Logger) LoggerFactory.getLogger(CmNotificationSubscriptionDmiOutEventConsumer.class)).detachAndStopAllAppenders()
+    }
+
+
+    def 'Consume valid CM Subscription response from DMI Plugin'() {
+        given: 'a cmsubscription event'
+            def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionDmiOutEvent.json')
+            def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionDmiOutEvent.class)
+            def testCloudEventSent = CloudEventBuilder.v1()
+                .withData(objectMapper.writeValueAsBytes(testEventSent))
+                .withId('random-uuid')
+                .withType('subscriptionCreateResponse')
+                .withSource(URI.create('test-dmi-plugin-name'))
+                .withExtension('correlationid', 'sub-1#test-dmi-plugin-name').build()
+            def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
+        when: 'the valid event is consumed'
+            objectUnderTest.consumeCmNotificationSubscriptionDmiOutEvent(consumerRecord)
+        then: 'an event is logged with level INFO'
+            def loggingEvent = getLoggingEvent()
+            assert loggingEvent.level == Level.INFO
+        and: 'the log indicates the task completed successfully'
+            assert loggingEvent.formattedMessage == 'Cm Subscription with id : sub-1 handled by the dmi-plugin : test-dmi-plugin-name has the status : accepted'
+    }
+
+    def getLoggingEvent() {
+        return logger.list[0]
+    }
+
+}
index 6a3d4be..1074229 100644 (file)
@@ -71,8 +71,6 @@ class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpe
             def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
         and: 'notifications are enabled'
             objectUnderTest.notificationFeatureEnabled = true
-        and: 'subscription model loader is enabled'
-            objectUnderTest.subscriptionModelLoaderEnabled = true
         when: 'the valid event is consumed'
             objectUnderTest.consumeSubscriptionEvent(consumerRecord)
         then: 'an event is logged with level INFO'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy
new file mode 100644 (file)
index 0000000..7c1a148
--- /dev/null
@@ -0,0 +1,44 @@
+package org.onap.cps.ncmp.api.impl.events.cmsubscription
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import io.cloudevents.CloudEvent
+import org.onap.cps.events.EventsPublisher
+import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper
+import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent
+import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.Data
+import org.onap.cps.utils.JsonObjectMapper
+import spock.lang.Specification
+
+class CmNotificationSubscriptionNcmpOutEventProducerSpec extends Specification {
+
+    def mockEventsPublisher = Mock(EventsPublisher)
+    def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
+    def objectUnderTest = new CmNotificationSubscriptionNcmpOutEventProducer(mockEventsPublisher, jsonObjectMapper)
+
+    def 'Create and Publish Cm Notification Subscription DMI In Event'() {
+        given: 'a cm subscription response for the client'
+            def subscriptionId = 'test-subscription-id'
+            def eventType = 'subscriptionCreateResponse'
+            def cmNotificationSubscriptionNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent(data: new Data(subscriptionId: 'sub-1', acceptedTargets: ['ch-1', 'ch-2']))
+        and: 'also we have target topic for publishing to client'
+            objectUnderTest.cmNotificationSubscriptionNcmpOutEventTopic = 'client-test-topic'
+        when: 'the event is published'
+            objectUnderTest.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, eventType, cmNotificationSubscriptionNcmpOutEvent)
+        then: 'the event contains the required attributes'
+            1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> {
+                args ->
+                    {
+                        assert args[0] == 'client-test-topic'
+                        assert args[1] == subscriptionId
+                        def cmNotificationSubscriptionNcmpOutEventAsCloudEvent = (args[2] as CloudEvent)
+                        assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.getExtension('correlationid') == subscriptionId
+                        assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.type == 'subscriptionCreateResponse'
+                        assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.source.toString() == 'NCMP'
+                        assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionNcmpOutEventAsCloudEvent, CmNotificationSubscriptionNcmpOutEvent) == cmNotificationSubscriptionNcmpOutEvent
+                    }
+            }
+    }
+
+
+}
index e951c31..eb0e110 100644 (file)
@@ -38,7 +38,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
             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
+                    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'
@@ -48,4 +48,20 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
             'valid datanodes present' | [new DataNode(xpath: '/cps/path', leaves: ['subscriptionIds': ['sub-1', 'sub-2']])] || true
             'no datanodes present'    | []                                                                                  || false
     }
-}
+
+    def 'Checking uniqueness of incoming subscription ID'() {
+        given: 'a cps path with a subscription ID for querying'
+            def cpsPathQuery = objectUnderTest.SUBSCRIPTION_IDS_CPS_PATH_QUERY.formatted('some-sub')
+        and: 'relevant datanodes are returned'
+            1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >>
+                    dataNodes
+        when: 'a subscription ID is tested for uniqueness'
+            def result = objectUnderTest.isUniqueSubscriptionId('some-sub')
+        then: 'result is as expected'
+            assert result == isValidSubscriptionId
+        where: 'following scenarios are used'
+            scenario               | dataNodes        || isValidSubscriptionId
+            'datanodes present'    | [new DataNode()] || false
+            'no datanodes present' | []               || true
+    }
+}
\ No newline at end of file
index a3b923f..cbc985f 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.inventory
 
-import org.onap.cps.api.CpsAnchorService
-
-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
-import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
-import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NO_TIMESTAMP
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.api.CpsAnchorService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import org.onap.cps.spi.CascadeDeleteAllowed
 import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.ModuleDefinition
 import org.onap.cps.spi.model.ModuleReference
-import org.onap.cps.utils.JsonObjectMapper
 import org.onap.cps.spi.utils.CpsValidator
+import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Shared
 import spock.lang.Specification
+
 import java.time.OffsetDateTime
 import java.time.ZoneOffset
 import java.time.format.DateTimeFormatter
 
+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
+import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT
+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.NO_TIMESTAMP
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+
 class InventoryPersistenceImplSpec extends Specification {
 
     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
@@ -60,14 +62,16 @@ class InventoryPersistenceImplSpec extends Specification {
 
     def mockCpsValidator = Mock(CpsValidator)
 
+    def mockCmHandleQueries = Mock(CmHandleQueries)
+
     def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
-            mockCpsValidator, mockCpsAnchorService)
+            mockCpsValidator, mockCpsAnchorService, mockCmHandleQueries)
 
     def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
             .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
 
     def cmHandleId = 'some-cm-handle'
-    def leaves = ["dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
+    def leaves = ["id":cmHandleId,"dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
     def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
 
     def cmHandleId2 = 'another-cm-handle'
@@ -115,7 +119,7 @@ class InventoryPersistenceImplSpec extends Specification {
 
     def "Handling missing service names as null."() {
         given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
-            def dataNode = new DataNode(childDataNodes:[], leaves: [:])
+            def dataNode = new DataNode(childDataNodes:[], leaves: ['id':cmHandleId])
             mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
         when: 'retrieving the yang modelled cm handle'
             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
@@ -129,7 +133,7 @@ class InventoryPersistenceImplSpec extends Specification {
 
     def "Retrieve multiple YangModelCmHandles"() {
         given: 'the cps data service returns 2 data nodes from the DMI registry'
-            def dataNodes = [new DataNode(xpath: xpath), new DataNode(xpath: xpath2)]
+            def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])]
             mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , INCLUDE_ALL_DESCENDANTS) >> dataNodes
         when: 'retrieving the yang modelled cm handle'
             def results = objectUnderTest.getYangModelCmHandles([cmHandleId, cmHandleId2])
@@ -283,13 +287,32 @@ class InventoryPersistenceImplSpec extends Specification {
 
     def 'Get cmHandle data node'() {
         given: 'expected xPath to get cmHandle data node'
-            def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']';
+            def expectedXPath = '/dmi-registry/cm-handles[@id=\'sample cmHandleId\']'
         when: 'the method to get data nodes is called'
-            objectUnderTest.getCmHandleDataNode('sample cmHandleId')
+            objectUnderTest.getCmHandleDataNodeByCmHandleId('sample cmHandleId')
         then: 'the data persistence service method to get cmHandle data node is invoked once with expected xPath'
             1 * mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, expectedXPath, INCLUDE_ALL_DESCENDANTS)
     }
 
+    def 'Get cm handle data node'() {
+        given: 'expected xPath to get cmHandle data node'
+            def expectedXPath = '/dmi-registry/cm-handles[@alternate-id=\'alternate id\']'
+        and: 'query service is invoked with expected xpath'
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS) >> [new DataNode()]
+        expect: 'getting the cm handle data node'
+            assert objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id') == new DataNode()
+    }
+
+    def 'Attempt to get non existing cm handle data node by alternate id'() {
+        given: 'query service is invoked and returns empty collection of data nodes'
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> []
+        when: 'getting the cm handle data node'
+            objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id')
+        then: 'no data found exception thrown'
+            def thrownException = thrown(DataNodeNotFoundException)
+            assert thrownException.getMessage().contains('DataNode not found')
+    }
+
     def 'Get CM handles that has given module names'() {
         when: 'the method to get cm handles is called'
             objectUnderTest.getCmHandleIdsWithGivenModules(['sample-module-name'])
index 827a548..44bc182 100644 (file)
@@ -125,7 +125,7 @@ class ModuleOperationsUtilsSpec extends Specification{
         where:
             scenario               | moduleSetTag       || expectedDetails
             'a module set tag'     | 'someModuleSetTag' || 'someModuleSetTag'
-            'empty module set tag' | ''                 || 'not-specified'
+            'empty module set tag' | ''                 || ''
     }
 
     def 'Get all locked cm-Handles where lock reasons are model sync failed or upgrade'() {
index de783ed..0c60e88 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.
@@ -26,7 +26,6 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPE
 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE
 
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
-import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
@@ -50,16 +49,18 @@ class ModuleSyncServiceSpec extends Specification {
     def mockCmHandleQueries = Mock(CmHandleQueries)
     def mockCpsDataService = Mock(CpsDataService)
     def mockJsonObjectMapper = Mock(JsonObjectMapper)
-    def mockModuleSetTagCache = [:]
 
     def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService,
-            mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper, mockModuleSetTagCache)
+            mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
 
     def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
-    def static cmHandleWithModuleSetTag = new DataNodeBuilder().withXpath("//cm-handles[@module-set-tag='tag-1'][@id='otherId']").withAnchor('otherId').build()
+    def static cmHandleWithModuleSetTag = new DataNodeBuilder()
+            .withXpath("/dmi-registry/cm-handles[@id='otherId']")
+            .withLeaves(['id': 'otherId', 'module-set-tag': 'tag-1'])
+            .withAnchor('otherId').build()
 
-    def 'Sync model for a NEW cm handle without cache with #scenario'() {
-        given: 'a cm handle having some state'
+    def 'Sync model for a NEW cm handle using module set tags: #scenario.'() {
+        given: 'a cm handle state to be synced'
             def ncmpServiceCmHandle = new NcmpServiceCmHandle()
             ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED).build())
             ncmpServiceCmHandle.cmHandleId = 'ch-1'
@@ -68,22 +69,22 @@ class ModuleSyncServiceSpec extends Specification {
             def moduleReferences =  [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ]
             mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
-            mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap
+            mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, identifiedNewModuleReferences) >> newModuleNameContentToMap
         and: 'the module service identifies #identifiedNewModuleReferences.size() new modules'
-            mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReferences(identifiedNewModuleReferences)
-        and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='new-tag-1']",
-                    FetchDescendantsOption.OMIT_DESCENDANTS) >> []
+            mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> identifiedNewModuleReferences
+        and: 'system contains other cm handle with "same tag" (that is READY)'
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
         when: 'module sync is triggered'
-            objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
+            objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
         then: 'create schema set from module is invoked with correct parameters'
             1 * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', newModuleNameContentToMap, moduleReferences)
         and: 'anchor is created with the correct parameters'
             1 * mockCpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1')
         where: 'the following parameters are used'
-            scenario         | existingModuleResourcesInCps         | identifiedNewModuleReferences | newModuleNameContentToMap     | moduleSetTag
-            'one new module' | [['module2': '2'], ['module3': '3']] | [['module1': '1']]            | [module1: 'some yang source'] | ''
-            'no new module'  | [['module1': '1'], ['module2': '2']] | []                            | [:]                           | 'new-tag-1'
+            scenario                  | existingModuleResourcesInCps         | identifiedNewModuleReferences         | newModuleNameContentToMap     | moduleSetTag | existingCmHandlesWithSameTag
+            'one new module, new tag' | [['module2': '2'], ['module3': '3']] | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | ''           | []
+            'no new module, new tag'  | [['module1': '1'], ['module2': '2']] | []                                    | [:]                           | 'new-tag-1'  | []
+            'same tag'                | [['module1': '1'], ['module2': '2']] | []                                    | [:]                           | 'same-tag'   | [cmHandleWithModuleSetTag]
     }
 
     def 'Upgrade model for an existing cm handle with Module Set Tag where the modules are #scenario'() {
@@ -95,33 +96,29 @@ class ModuleSyncServiceSpec extends Specification {
             def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'tag-1', '')
         and: 'some module references'
             def moduleReferences =  [ new ModuleReference('module1','1') ]
-        and: 'cache or DMI operations returns some module references for upgraded cm handle'
-            if (populateCache) {
-                mockModuleSetTagCache.put('tag-1', moduleReferences)
-            } else {
-                mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
-            }
+        and: 'DMI operations returns some module references for upgraded cm handle'
+            mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
+            mockDmiModelOperations.getNewYangResourcesFromDmi(_, []) >> [:]
         and: 'none of these module references are new (unknown to the system)'
-            mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> []
+            mockCpsModuleService.identifyNewModuleReferences(_) >> []
         and: 'CPS-Core returns list of existing module resources for TBD'
             mockCpsModuleService.getYangResourcesModuleReferences(*_) >> [ new ModuleReference('module1','1') ]
         and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='tag-1']", FetchDescendantsOption.OMIT_DESCENDANTS) >> existingCmHandlesWithSameTag
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
         and: 'the other cm handle is a state ready'
             mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true
         when: 'module sync is triggered'
-            objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
+            objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle)
         then: 'update schema set from module is invoked for the upgraded cm handle'
-            expectedCallsToUpgradeSchemaSet * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences)
-        and: 'create schema set from module is invoked for the upgraded cm handle'
-            expectedCallsToCeateSchemaSet * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences)
+            1 * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'upgraded-ch', [:], moduleReferences)
+        and: 'create schema set from module is not invoked for the upgraded cm handle'
+            0 * mockCpsModuleService.createSchemaSetFromModules(*_)
         and: 'No anchor is created for the upgraded cm handle'
             0 * mockCpsAnchorService.createAnchor(*_)
         where: 'the following parameters are used'
-            scenario      | populateCache | existingCmHandlesWithSameTag || expectedCallsToUpgradeSchemaSet | expectedCallsToCeateSchemaSet
-            'new'         | false         | []                           || 0                               | 1
-            'in cache'    | true          | []                           || 1                               | 0
-            'in database' | false         | [cmHandleWithModuleSetTag]   || 1                               | 0
+            scenario      | existingCmHandlesWithSameTag
+            'new'         | []
+            'in database' | [cmHandleWithModuleSetTag]
     }
 
     def 'upgrade model for a existing cm handle'() {
@@ -136,10 +133,10 @@ class ModuleSyncServiceSpec extends Specification {
             def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
             mockCpsModuleService.getYangResourcesModuleReferences(*_)>> moduleReferences
         and: 'a cm handle with the same moduleSetTag can be found in the registry'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='targetModuleSetTag']",
-                FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1', 'cm-handle-state': 'READY'])]
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1'],
+                    childDataNodes: [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']/state', leaves: ['cm-handle-state': 'READY'])])]
         when: 'module upgrade is triggered'
-            objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
+            objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle)
         then: 'the upgrade is delegated to the module service (with the correct parameters)'
             1 * mockCpsModuleService.upgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', Collections.emptyMap(), moduleReferences)
     }
@@ -171,13 +168,4 @@ class ModuleSyncServiceSpec extends Specification {
             result == unsupportedOperationException
     }
 
-    def toModuleReferences(moduleReferenceAsMap) {
-        def moduleReferences = [].withDefault { [:] }
-        moduleReferenceAsMap.forEach(property ->
-            property.forEach((moduleName, revision) -> {
-                moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision))
-            }))
-        return moduleReferences
-    }
-
 }
index ece2d9f..6c0d6df 100644 (file)
@@ -85,8 +85,8 @@ class ModuleSyncTasksSpec extends Specification {
             1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-1')
             1 * mockModuleSyncService.deleteSchemaSetIfExists('cm-handle-2')
         and: 'module sync service is invoked for each cm handle'
-            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
-            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') }
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assert args[0].id == 'cm-handle-1' }
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assert args[0].id == 'cm-handle-2' }
         and: 'the state handler is called for the both cm handles'
             1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args ->
                 assertBatch(args, ['cm-handle-1', 'cm-handle-2'], CmHandleState.READY)
@@ -102,7 +102,7 @@ class ModuleSyncTasksSpec extends Specification {
             def cmHandleState = new CompositeState(cmHandleState: CmHandleState.ADVISED)
             1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> cmHandleState
         and: 'module sync service attempts to sync the cm handle and throws an exception'
-            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
         when: 'module sync is executed'
             objectUnderTest.performModuleSync([cmHandle], batchCount)
         then: 'update lock reason, details and attempts is invoked'
@@ -123,7 +123,8 @@ class ModuleSyncTasksSpec extends Specification {
                 .LockReason.builder().lockReasonCategory(lockReasonCategory).details(lockReasonDetails).build())
             1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> expectedCmHandleState
         and: 'module sync service attempts to sync/upgrade the cm handle and throws an exception'
-            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
+            mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
+            mockModuleSyncService.syncAndUpgradeSchemaSet(*_) >> { throw new Exception('some exception') }
         when: 'module sync is executed'
             objectUnderTest.performModuleSync([cmHandle], batchCount)
         then: 'update lock reason, details and attempts is invoked'
@@ -170,7 +171,7 @@ class ModuleSyncTasksSpec extends Specification {
         when: 'module sync poll is executed'
             objectUnderTest.performModuleSync([cmHandle1], batchCount)
         then: 'module sync service is invoked for cm handle'
-            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
         and: 'the entry for other cm handle is still in the progress map'
             assert moduleSyncStartedOnCmHandles.get('other-cm-handle') != null
     }
@@ -202,14 +203,6 @@ class ModuleSyncTasksSpec extends Specification {
         return new DataNode(anchorName: cmHandleId, leaves: ['id': cmHandleId, 'cm-handle-state': cmHandleState])
     }
 
-    def assertYamgModelCmHandleArgument(args, expectedCmHandleId) {
-        {
-            def yangModelCmHandle = args[0]
-            assert yangModelCmHandle.id == expectedCmHandleId
-        }
-        return true
-    }
-
     def assertBatch(args, expectedCmHandleStatePerCmHandleIds, expectedCmHandleState) {
         {
             Map<YangModelCmHandle, CmHandleState> actualCmHandleStatePerCmHandle = args[0]
index d1025f9..a105f84 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation
+ *  Copyright (C) 2021-2024 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,12 +51,6 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
     @SpringBean
     JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
 
-    @SpringBean
-    TaskExecutor stubbedTaskExecutor = Stub()
-
-    @SpringBean
-    DmiServiceNameOrganizer stubbedDmiServiceNameOrganizer = Stub()
-
     def 'Retrieving module references.'() {
         given: 'a cm handle'
             mockYangModelCmHandleRetrieval([])
@@ -148,23 +142,33 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         and: 'a positive response from DMI service when it is called with the expected parameters'
             def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
             mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
-                    '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
+                    '{"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
         when: 'get new yang resources from DMI service'
-            def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, unknownModuleReferences)
+            def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences)
         then: 'the result is the response from DMI service'
             assert result == [mod1:'some yang source']
         where: 'the following DMI properties are used'
-            scenario                                | dmiProperties               | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest
-            'with module references and properties' | [yangModelCmHandleProperty] | newModuleReferences     || '{"prop1":"val1"}'                    | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
-            'without module references'             | [yangModelCmHandleProperty] | []                      || '{"prop1":"val1"}'                    | ''
-            'without properties'                    | []                          | newModuleReferences     || '{}'                                  | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
+            scenario                                | dmiProperties               || expectedAdditionalPropertiesInRequest
+            'with module references and properties' | [yangModelCmHandleProperty] || '{"prop1":"val1"}'
+            'without properties'                    | []                          || '{}'
+    }
+
+    def 'Retrieving yang resources from DMI with no module references.'() {
+        given: 'a cm handle'
+            mockYangModelCmHandleRetrieval([])
+        when: 'a get new yang resources from DMI is called with no module references'
+            def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, [])
+        then: 'no resources are returned'
+            assert result == [:]
+        and: 'no request is sent to DMI'
+            0 * mockDmiRestClient.postOperationWithJsonData(*_)
     }
 
     def 'Retrieving yang resources from DMI with null DMI properties.'() {
         given: 'a cm handle'
             mockYangModelCmHandleRetrieval(null)
         when: 'a get new yang resources from DMI is called'
-            objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, [])
+            objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('mod1', 'A')])
         then: 'a null pointer is thrown (we might need to address this later)'
             thrown(NullPointerException)
     }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/AlternateIdCheckerSpec.groovy
new file mode 100644 (file)
index 0000000..aaa0344
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * ============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.utils
+
+
+import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException
+import org.onap.cps.spi.model.DataNode
+import org.onap.cps.spi.model.DataNodeBuilder
+import spock.lang.Specification
+
+class AlternateIdCheckerSpec extends Specification {
+
+    def mockInventoryPersistenceService = Mock(InventoryPersistence)
+    def someDataNode = new DataNodeBuilder().build()
+    def dataNodeFoundException = new DataNodeNotFoundException('', '')
+
+    def objectUnderTest = new AlternateIdChecker(mockInventoryPersistenceService)
+
+    def 'Check new cm handle with new alternate id.'() {
+        given: 'inventory persistence can not find cm handle id'
+            mockInventoryPersistenceService.getYangModelCmHandle('ch 1') >> {throw dataNodeFoundException}
+        and: 'inventory persistence can not find alternate id'
+            mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId('alternate id') >> {throw dataNodeFoundException}
+        expect: 'mapping can be added'
+             assert objectUnderTest.canApplyAlternateId('ch 1', 'alternate id')
+    }
+
+    def 'Check new cm handle with used alternate id.'() {
+        given: 'inventory persistence can not find cm handle id'
+            mockInventoryPersistenceService.getYangModelCmHandle('ch 1') >> {throw dataNodeFoundException}
+        and: 'inventory persistence can find alternate id'
+            mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId('alternate id') >> { someDataNode }
+        expect: 'mapping can not be added'
+            assert objectUnderTest.canApplyAlternateId('ch 1', 'alternate id') == false
+    }
+
+    def 'Check for existing cm handle with #currentAlternateId.'() {
+        given: 'a cm handle with the #currentAlternateId'
+            def yangModelCmHandle = new YangModelCmHandle(alternateId: currentAlternateId)
+        and: 'inventory service finds the cm handle'
+            mockInventoryPersistenceService.getYangModelCmHandle('my cm handle') >> yangModelCmHandle
+        expect: 'add mapping returns expected result'
+            assert canAdd == objectUnderTest.canApplyAlternateId('my cm handle', 'same alternate id')
+        where: 'following alternate ids is used'
+            currentAlternateId   || canAdd
+            'same alternate id'  || true
+            'other alternate id' || false
+    }
+
+    def 'Check a batch of NEW cm handles with #scenario.'() {
+        given: 'a batch of 2 new cm handles alternate id ids #alt1 and #alt2'
+            def batch = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: alt1),
+                         new NcmpServiceCmHandle(cmHandleId: 'ch-2', alternateId: alt2)]
+        and: 'the database already contains cm handle(s) with these alternate ids: #alreadyinDb'
+            mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >>
+                {  args -> altAlreadyInDb.contains(args[0]) ? new DataNode() : throwDataNodeNotFoundException() }
+        when: 'the batch of new cm handles is checked'
+            def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch)
+        then: 'the result only contains the ids of the acceptable cm handles'
+            assert result.contains('ch-1') == rejectCh1
+            assert result.contains('ch-2') == rejectCh2
+        where: 'the following alternate ids are used'
+            scenario                          | alt1   | alt2   | altAlreadyInDb  || rejectCh1 | rejectCh2
+            'no alternate ids'                | ''     | ''     | ['dont matter'] || false      | false
+            'new alternate ids'               | 'fdn1' | 'fdn2' | ['other fdn']   || false      | false
+            'one already used alternate id'   | 'fdn1' | 'fdn2' | ['fdn1']        || true       | false
+            'two already used alternate ids'  | 'fdn1' | 'fdn2' | ['fdn1','fdn2'] || true       | true
+            'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || false      | true
+    }
+
+    def throwDataNodeNotFoundException() {
+        // cannot 'return' an exception in conditional stub behavior, so hence a method call that will always throw this exception
+        throw dataNodeFoundException
+    }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleIdMapperSpec.groovy
deleted file mode 100644 (file)
index 55ccdf3..0000000
+++ /dev/null
@@ -1,126 +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.utils
-
-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.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.slf4j.LoggerFactory
-import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService
-import spock.lang.Specification
-
-class CmHandleIdMapperSpec extends Specification {
-
-    def alternateIdPerCmHandle = new HashMap<String, String>()
-    def cmHandlePerAlternateId = new HashMap<String, String>()
-    def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
-
-    def objectUnderTest = new CmHandleIdMapper(alternateIdPerCmHandle, cmHandlePerAlternateId, mockCpsCmHandlerQueryService)
-
-    def logger = Spy(ListAppender<ILoggingEvent>)
-
-    def setup() {
-        ((Logger) LoggerFactory.getLogger(CmHandleIdMapper.class)).addAppender(logger)
-        logger.start()
-        mockCpsCmHandlerQueryService.getAllCmHandles() >> []
-        assert objectUnderTest.addMapping('my cmhandle id', 'my alternate id')
-    }
-
-    void cleanup() {
-        ((Logger) LoggerFactory.getLogger(CmHandleIdMapper.class)).detachAndStopAllAppenders()
-    }
-
-    def 'Checking entries in the cache.'() {
-        expect: 'the alternate id can be converted to cmhandle id'
-            assert objectUnderTest.alternateIdToCmHandleId('my alternate id') == 'my cmhandle id'
-        and: 'the cmhandle id can be converted to alternate id'
-            assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id'
-    }
-
-    def 'Attempt adding #scenario alternate id.'() {
-        expect: 'cmhandle id - alternate id mapping fails'
-            assert objectUnderTest.addMapping('ch-1', alternateId) == false
-        and: 'alternate id looked up by cmhandle id unsuccessfully'
-            assert objectUnderTest.cmHandleIdToAlternateId('ch-1') == null
-        where: 'alternate id has an invalid value'
-            scenario | alternateId
-            'empty'  | ''
-            'blank'  | '  '
-            'null'   | null
-    }
-
-    def 'Remove an entry from the cache.'() {
-        when: 'removing an entry'
-            objectUnderTest.removeMapping('my cmhandle id')
-        then: 'converting alternate id returns null'
-            assert objectUnderTest.alternateIdToCmHandleId('my alternate id') == null
-        and: 'converting cmhandle id returns null'
-            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')
-        expect: 'still returns the original alternate id'
-            assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id'
-        and: 'converting other alternate id returns null'
-            assert objectUnderTest.alternateIdToCmHandleId('other id') == null
-        and: 'a warning is logged with the original alternate id'
-            def lastLoggingEvent = logger.list[1]
-            assert lastLoggingEvent.level == Level.WARN
-            assert lastLoggingEvent.formattedMessage.contains('my alternate id')
-    }
-
-    def 'Update existing alternate id with the same value.'() {
-        expect: 'update an existing alternate id with the same value returns false (no update)'
-            assert objectUnderTest.addMapping('my cmhandle id', 'my alternate id') == false
-        and: 'conversion still returns the original alternate id'
-            assert objectUnderTest.cmHandleIdToAlternateId('my cmhandle id') == 'my alternate id'
-    }
-
-    def 'Initializing cache #scenario.'() {
-        when: 'the cache is (re-)initialized'
-            objectUnderTest.cacheIsInitialized = false
-            objectUnderTest.initializeCache()
-        then: 'the alternate id can be converted to cmhandle id'
-            assert objectUnderTest.alternateIdToCmHandleId('alt-1') == convertedCmHandleId
-        and: 'the cm handle id can be converted to alternate id'
-            assert objectUnderTest.cmHandleIdToAlternateId('ch-1') == convertedAlternatId
-        and: 'the query service is called to get the initial data'
-            1 * mockCpsCmHandlerQueryService.getAllCmHandles() >> persistedCmHandles
-        where: 'the initial data has a cm handle #scenario'
-            scenario                  | persistedCmHandles                                                  || convertedAlternatId | convertedCmHandleId
-            'with alternate id'       | [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: 'alt-1')] || 'alt-1'             | 'ch-1'
-            'without alternate id'    | [new NcmpServiceCmHandle(cmHandleId: 'ch-1')]                       || null                | null
-            'with blank alternate id' | [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: ' ')]     || null                | null
-    }
-}
\ No newline at end of file
index 6d733dc..0ae348c 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.
@@ -31,9 +31,10 @@ class YangDataConverterSpec extends Specification{
                     leaves: ['name': 'dmiProp1', 'value': 'dmiValue1'])
             def dataNodePublicProperties = new DataNode(xpath:'/public-properties[@name="pubProp1"]',
                     leaves: ['name': 'pubProp1', 'value': 'pubValue1'])
-            def dataNodeCmHandle = new DataNode(childDataNodes:[dataNodeAdditionalProperties, dataNodePublicProperties])
+            def dataNodeCmHandle = new DataNode(leaves:['id':'sample-id'], childDataNodes:[dataNodeAdditionalProperties, dataNodePublicProperties])
         when: 'the dataNode is converted'
-            def yangModelCmHandle = YangDataConverter.convertCmHandleToYangModel(dataNodeCmHandle,'sample-id')
+            def yangModelCmHandle =
+                    YangDataConverter.convertCmHandleToYangModel(dataNodeCmHandle)
         then: 'the converted object has the correct id'
             assert yangModelCmHandle.id == 'sample-id'
         and: 'the additional (dmi, private) properties are included'
@@ -46,8 +47,8 @@ class YangDataConverterSpec extends Specification{
 
     def 'Convert multiple cm handle data nodes'(){
         given: 'two data nodes in a collection'
-            def dataNodes = [new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'some-cm-handle\']'),
-                             new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'another-cm-handle\']')]
+            def dataNodes = [new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'some-cm-handle\']', leaves: ['id':'some-cm-handle']),
+                             new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'another-cm-handle\']', leaves: ['id':'another-cm-handle'])]
         when: 'the data nodes are converted'
             def yangModelCmHandles = YangDataConverter.convertDataNodesToYangModelCmHandles(dataNodes)
         then: 'verify both have returned and CmHandleIds are correct'
index d76f912..7803ae3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
- *  Modifications Copyright (C) 2023 Nordix Foundation
+ *  Modifications 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,6 +21,7 @@
 
 package org.onap.cps.ncmp.api.models
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
 import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
 
@@ -71,7 +72,7 @@ class CmHandleRegistrationResponseSpec extends Specification {
     def 'Failed cm-handle Registration with multiple responses.'() {
         when: 'cm-handle failure response is created for 2 xpaths'
             def cmHandleRegistrationResponses =
-                CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST)
+                CmHandleRegistrationResponse.createFailureResponsesFromXpaths(["somePathWithId[@id='123']", "somePathWithId[@id='456']"], CM_HANDLE_ALREADY_EXIST)
         then: 'the response has the correct cm handle ids'
             assert cmHandleRegistrationResponses.size() == 2
             assert cmHandleRegistrationResponses.stream().map(it -> it.cmHandle).collect(Collectors.toList())
@@ -81,9 +82,20 @@ class CmHandleRegistrationResponseSpec extends Specification {
     def 'Failed cm-handle Registration with multiple responses with an unexpected xpath.'() {
         when: 'cm-handle failure response is created for one valid and one unexpected xpath'
             def cmHandleRegistrationResponses =
-                CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST)
+                CmHandleRegistrationResponse.createFailureResponsesFromXpaths(["somePathWithId[@id='123']", "valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST)
         then: 'the response has only one entry'
             assert cmHandleRegistrationResponses.size() == 1
     }
 
+    def 'Failed cm-handle registration based on cm handle id and registration error'() {
+        when: 'the failure response is created with "alternate id already associated" error code for 1 cm handle'
+            def cmHandleRegistrationResponses =
+                    CmHandleRegistrationResponse.createFailureResponses(['ch 1'], ALTERNATE_ID_ALREADY_ASSOCIATED)
+        then: 'the response with expected values'
+            assert cmHandleRegistrationResponses[0].cmHandle == 'ch 1'
+            assert cmHandleRegistrationResponses[0].status == Status.FAILURE
+            assert cmHandleRegistrationResponses[0].ncmpResponseStatus == ALTERNATE_ID_ALREADY_ASSOCIATED
+            assert cmHandleRegistrationResponses[0].errorText == 'alternate id already associated'
+    }
+
 }
index bde9961..f3b405b 100644 (file)
 
 package org.onap.cps.ncmp.init
 
-import org.onap.cps.api.CpsAnchorService
-import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException
-import org.onap.cps.spi.exceptions.AlreadyDefinedException
-
-import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
-
 import ch.qos.logback.classic.Level
 import ch.qos.logback.classic.Logger
 import ch.qos.logback.core.read.ListAppender
-import org.onap.cps.api.CpsDataspaceService
+import org.onap.cps.api.CpsAnchorService
 import org.onap.cps.api.CpsDataService
+import org.onap.cps.api.CpsDataspaceService
 import org.onap.cps.api.CpsModuleService
+import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException
+import org.onap.cps.spi.exceptions.AlreadyDefinedException
 import org.onap.cps.spi.model.Dataspace
 import org.slf4j.LoggerFactory
 import org.springframework.boot.context.event.ApplicationReadyEvent
 import org.springframework.context.annotation.AnnotationConfigApplicationContext
 import spock.lang.Specification
 
+import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
+
 class CmDataSubscriptionModelLoaderSpec extends Specification {
 
     def mockCpsDataspaceService = Mock(CpsDataspaceService)
@@ -67,9 +66,7 @@ class CmDataSubscriptionModelLoaderSpec extends Specification {
     }
 
     def 'Onboard subscription model via application ready event.'() {
-        given:'model loader is enabled'
-            objectUnderTest.subscriptionModelLoaderEnabled = true
-        and: 'dataspace is ready for use'
+        given: 'dataspace is ready for use'
             mockCpsDataspaceService.getDataspace(NCMP_DATASPACE_NAME) >> new Dataspace('')
         when: 'the application is ready'
             objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
@@ -81,14 +78,14 @@ class CmDataSubscriptionModelLoaderSpec extends Specification {
             1 * mockCpsDataService.saveData(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', '{"datastores":{}}', _)
         and: 'the data service is called once to create datastore for Passthrough-operational'
             1 * mockCpsDataService.saveData(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', '/datastores',
-                    '{"datastore":[{"name":"ncmp-datastore:passthrough-operational","cm-handles":{}}]}', _, _)
+                '{"datastore":[{"name":"ncmp-datastore:passthrough-operational","cm-handles":{}}]}', _, _)
         and: 'the data service is called once to create datastore for Passthrough-running'
             1 * mockCpsDataService.saveData(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', '/datastores',
-                    '{"datastore":[{"name":"ncmp-datastore:passthrough-running","cm-handles":{}}]}', _, _)
+                '{"datastore":[{"name":"ncmp-datastore:passthrough-running","cm-handles":{}}]}', _, _)
     }
 
     def 'Create node for datastore with already defined exception.'() {
-        given:'the data service throws an Already Defined exception'
+        given: 'the data service throws an Already Defined exception'
             mockCpsDataService.saveData(*_) >> { throw AlreadyDefinedException.forDataNodes([], 'some context') }
         when: 'attempt to create datastore'
             objectUnderTest.createDatastore('some datastore')
@@ -110,16 +107,4 @@ class CmDataSubscriptionModelLoaderSpec extends Specification {
             assert thrown.details.contains('test message')
     }
 
-    def 'Subscription model loader disabled.' () {
-        given: 'model loader is disabled'
-            objectUnderTest.subscriptionModelLoaderEnabled = false
-        when: 'application is ready'
-            objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
-        then: 'no interaction with admin service'
-            0 * mockCpsDataspaceService.getDataspace(_)
-        then: 'a message is logged that the function is disabled'
-            def logs = loggingListAppender.list.toString()
-            assert logs.contains('Subscription Model Loader is disabled')
-    }
-
 }
index a4bb4e8..a3283ff 100644 (file)
@@ -50,8 +50,6 @@ ncmp:
         async-executor:
             parallelism-level: 3
 
-    model-loader:
-        subscription: true
 
 # Custom Hazelcast Config.
 hazelcast:
diff --git a/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionDmiOutEvent.json b/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionDmiOutEvent.json
new file mode 100644 (file)
index 0000000..f0b78fb
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "data": {
+    "statusCode": "1",
+    "statusMessage": "accepted"
+  }
+}
\ No newline at end of file
index 6e7554b..b5a9ad1 100644 (file)
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.4.4-SNAPSHOT</version>
+    <version>3.4.5-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
                 <plugin>
                     <groupId>org.springframework.boot</groupId>
                     <artifactId>spring-boot-maven-plugin</artifactId>
-                    <version>3.1.2</version>
+                    <version>3.2.2</version>
                     <executions>
                         <execution>
                             <goals>
index e6e00dc..da0265c 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 022c3b8..f8ec757 100644 (file)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 2fd369b..2801d64 100644 (file)
@@ -26,7 +26,7 @@
     <parent>\r
         <groupId>org.onap.cps</groupId>\r
         <artifactId>cps-parent</artifactId>\r
-        <version>3.4.4-SNAPSHOT</version>\r
+        <version>3.4.5-SNAPSHOT</version>\r
         <relativePath>../cps-parent/pom.xml</relativePath>\r
     </parent>\r
 \r
@@ -71,7 +71,7 @@
         <!-- Add Hibernate support for Postgres datatype JSONB and Postgres arrays -->\r
         <dependency>\r
             <groupId>io.hypersistence</groupId>\r
-            <artifactId>hypersistence-utils-hibernate-60</artifactId>\r
+            <artifactId>hypersistence-utils-hibernate-63</artifactId>\r
         </dependency>\r
         <dependency>\r
             <groupId>org.projectlombok</groupId>\r
index 1e42702..b449a78 100755 (executable)
@@ -69,6 +69,7 @@ import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
 import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.retry.RetryContext;
 import org.springframework.retry.annotation.Backoff;
 import org.springframework.retry.annotation.Retryable;
 import org.springframework.retry.support.RetrySynchronizationManager;
@@ -277,8 +278,8 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
                                 dataIntegrityViolationException, newYangResourceEntities);
                 convertedException.ifPresent(
                         e -> {
-                            int retryCount = RetrySynchronizationManager.getContext() == null ? 0
-                                    : RetrySynchronizationManager.getContext().getRetryCount();
+                            final RetryContext context = RetrySynchronizationManager.getContext();
+                            int retryCount = context == null ? 0 : context.getRetryCount();
                             log.warn("Cannot persist duplicated yang resource. System will attempt this method "
                                     + "up to 5 times. Current retry count : {}", ++retryCount, e);
                         });
index de716df..93a4d3d 100644 (file)
@@ -29,7 +29,7 @@
   <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.4.4-SNAPSHOT</version>
+    <version>3.4.5-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
   </parent>
 
       <artifactId>cloudevents-spring</artifactId>
     </dependency>
     <!-- T E S T   D E P E N D E N C I E S -->
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-test</artifactId>
-      <scope>test</scope>
-    </dependency>
     <dependency>
       <groupId>org.codehaus.groovy</groupId>
       <artifactId>groovy</artifactId>
index eca8871..053ac1b 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
     </parent>
 
     <artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
             <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-server</artifactId>
-            <version>11.0.16</version>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.servlet</groupId>
-            <artifactId>jakarta.servlet-api</artifactId>
-            <version>6.0.0</version>
-        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
index d3142f8..47ad877 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,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
     </parent>
     <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
 
             </exclusions>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.googlecode.json-simple</groupId>
             <artifactId>json-simple</artifactId>
@@ -48,4 +52,4 @@
             <artifactId>cps-ncmp-rest</artifactId>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
index 772eb05..d77cbcc 100644 (file)
@@ -89,7 +89,7 @@ public class DmiRestStubController {
      * @return ResponseEntity response entity having module response as json string.
      */
     @PostMapping("/v1/ch/{cmHandleId}/modules")
-    public ResponseEntity<String> getModuleReferences(@PathVariable final String cmHandleId,
+    public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
                                                       @RequestBody final Object moduleReferencesRequest) {
         delay(moduleReferencesDelayMs);
         final String moduleResponseContent = getModuleResourceResponse(cmHandleId,
@@ -108,7 +108,7 @@ public class DmiRestStubController {
      */
     @PostMapping("/v1/ch/{cmHandleId}/moduleResources")
     public ResponseEntity<String> retrieveModuleResources(
-            @PathVariable final String cmHandleId,
+            @PathVariable("cmHandleId") final String cmHandleId,
             @RequestBody final Object moduleResourcesReadRequest) {
         delay(moduleResourcesDelayMs);
         final String moduleResourcesResponseContent = getModuleResourceResponse(cmHandleId,
index de097a6..0847d40 100644 (file)
@@ -1,5 +1,5 @@
 #  ============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.
@@ -17,6 +17,9 @@
 #  ============LICENSE_END=========================================================
 server:
     port: 8092
+    jetty:
+        threads:
+            max: 25
 
 rest:
     api:
@@ -36,6 +39,12 @@ spring:
             value-serializer: io.cloudevents.kafka.CloudEventSerializer
             client-id: cps-core
 
+management:
+    endpoints:
+        web:
+            exposure:
+                include: health
+
 app:
     ncmp:
         async-m2m:
index 9fb40e3..a16a954 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 9ccb64b..de427af 100644 (file)
@@ -56,9 +56,6 @@ services:
       DMI_PASSWORD: ${DMI_PASSWORD:-cpsr0cks!}
       KAFKA_BOOTSTRAP_SERVER: kafka:29092
       notification.enabled: 'true'
-      notification.async.executor.time-out-value-in-ms: 2000
-      NCMP_TIMERS_ADVISED-MODULES-SYNC_SLEEP-TIME-MS: ${ADVISED_MODULES_SYNC_SLEEP_TIME_MS:-30000}
-      NCMP_TIMERS_CM-HANDLE-DATA-SYNC_SLEEP-TIME-MS: ${CMHANDLE_DATA_SYNC_SLEEP_TIME_MS:-30000}
       JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
     restart: unless-stopped
     depends_on:
index 20a5ae3..90590a2 100644 (file)
@@ -1,6 +1,6 @@
 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
 .. http://creativecommons.org/licenses/by/4.0
-.. Copyright (C) 2023 Nordix Foundation
+.. Copyright (C) 2023-2024 Nordix Foundation
 
 .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
 .. _dataOperationMessageStatusCodes:
@@ -38,6 +38,8 @@ CPS-NCMP Message Status Codes
     +-----------------+------------------------------------------------------+-----------------------------------+
     | 110             | cm-handle has an invalid character(s) in id          | Inventory                         |
     +-----------------+------------------------------------------------------+-----------------------------------+
+    | 111             | alternate id already associated                      | Inventory                         |
+    +-----------------+------------------------------------------------------+-----------------------------------+
 
 .. note::
 
index 3b5aad1..c1ee1eb 100644 (file)
@@ -335,9 +335,5 @@ Below are the list of distributed datastructures that we have.
 +--------------+------------------------------------+-----------------------------------------------------------+
 | cps-ncmp     | cmNotificationSubscriptionCache    | Stores and tracks cm notification subscription requests.  |
 +--------------+------------------------------------+-----------------------------------------------------------+
-| cps-ncmp     | alternateIdPerCmHandleId           | Stores the alternate id for each cm handle id.            |
-+--------------+------------------------------------+-----------------------------------------------------------+
-| cps-ncmp     | cmHandleIdPerAlternateId           | Stores the cm handle id for each alternate id.            |
-+--------------+------------------------------------+-----------------------------------------------------------+
 
-Total number of caches : 10
+Total number of caches : 8
index 779a347..49dc463 100644 (file)
@@ -16,6 +16,35 @@ CPS Release Notes
 ..      * * *   NEW DELHI   * * *
 ..      =========================
 
+Version: 3.4.5
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project**                      |                                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images**                    | onap/cps-and-ncmp:3.4.5                                |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation**              | 3.4.5 New Delhi                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release date**                     | Not yet released                                       |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.4.5
+
+
+Features
+--------
+
+
 Version: 3.4.4
 ==============
 
@@ -32,16 +61,19 @@ Release Data
 | **Release designation**              | 3.4.4 New Delhi                                        |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
-| **Release date**                     | Not yet released                                       |
+| **Release date**                     | 2024 February 23                                       |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
 
 Bug Fixes
 ---------
 3.4.4
+    - `CPS-2027 <https://jira.onap.org/browse/CPS-2027>`_ Upgrade Yang modules using module set tag functionalities fix
 
 Features
 --------
+    - `CPS-2057 <https://jira.onap.org/browse/CPS-2057>`_ Leaf lists are sorted by default if Yang model does not specify order.
+    - `CPS-2087 <https://jira.onap.org/browse/CPS-2087>`_ Performance improvement of CPS Path Queries.
 
 
 Version: 3.4.3
index 3d0125a..5a40ed2 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
index 6707753..4369b79 100644 (file)
@@ -122,8 +122,11 @@ class NcmpCmHandleCreateSpec extends CpsIntegrationSpecBase {
                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState('ch-3').cmHandleState
             })
 
+        and: 'the CM-handle has expected moduleSetTag'
+            assert objectUnderTest.getNcmpServiceCmHandle('ch-3').moduleSetTag == 'B'
+
         and: 'the CM-handle has expected modules from module set "B": M1 and M3'
-            ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
+            assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences('ch-3').moduleName.sort()
 
         cleanup: 'deregister CM handles'
             deregisterCmHandles(DMI_URL, ['ch-1', 'ch-2', 'ch-3'])
index 6b550a7..ffa4382 100644 (file)
@@ -28,7 +28,6 @@ import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.models.UpgradedCmHandles
 import org.springframework.http.HttpStatus
-import spock.lang.Ignore
 import spock.util.concurrent.PollingConditions
 
 import static org.springframework.test.web.client.match.MockRestRequestMatchers.anything
@@ -50,7 +49,6 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
         objectUnderTest = networkCmProxyDataService
     }
 
-    @Ignore
     def 'Upgrade CM-handle with new moduleSetTag or no moduleSetTag.'() {
         given: 'an existing CM-handle with expected initial modules: M1 and M2'
             registerCmHandle(DMI_URL, CM_HANDLE_ID, initialModuleSetTag, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE)
@@ -82,6 +80,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
             })
 
+        and: 'the CM-handle has expected moduleSetTag'
+            assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag
+
         and: 'CM-handle has expected updated modules: M1 and M3'
             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
 
@@ -124,6 +125,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
                 assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
             })
 
+        and: 'the CM-handle has expected moduleSetTag'
+            assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == updatedModuleSetTag
+
         and: 'CM-handle has expected updated modules: M1 and M3'
             assert ['M1', 'M3'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
 
@@ -136,7 +140,6 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
             'moduleSet1'        | 'moduleSet2'
     }
 
-    @Ignore
     def 'Skip upgrade of CM-handle with same moduleSetTag as before.'() {
         given: 'an existing CM-handle with expected initial modules: M1 and M2'
             registerCmHandle(DMI_URL, CM_HANDLE_ID, 'same', INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE)
@@ -150,7 +153,10 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
         then: 'CM-handle remains in READY state'
             assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(CM_HANDLE_ID).cmHandleState
 
-        and: 'CM-handle has same modules as before: M1 and M2'
+        and: 'the CM-handle has same moduleSetTag as before'
+            assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'same'
+
+        then: 'CM-handle has same modules as before: M1 and M2'
             assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(CM_HANDLE_ID).moduleName.sort()
 
         cleanup: 'deregister CM-handle'
@@ -159,13 +165,13 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
 
     def 'Upgrade of CM-handle fails due to DMI error.'() {
         given: 'an existing CM-handle'
-            registerCmHandle(DMI_URL, CM_HANDLE_ID, NO_MODULE_SET_TAG, INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE)
+            registerCmHandle(DMI_URL, CM_HANDLE_ID, 'oldTag', INITIAL_MODULE_REFERENCES_RESPONSE, INITIAL_MODULE_RESOURCES_RESPONSE)
 
         and: 'DMI returns error code'
             mockDmiServer.expect(anything()).andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE))
 
         when: "CM-handle is upgraded"
-            def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: NO_MODULE_SET_TAG)
+            def cmHandlesToUpgrade = new UpgradedCmHandles(cmHandles: [CM_HANDLE_ID], moduleSetTag: 'newTag')
             networkCmProxyDataService.updateDmiRegistrationAndSyncModule(
                     new DmiPluginRegistration(dmiPlugin: DMI_URL, upgradedCmHandles: cmHandlesToUpgrade))
 
@@ -180,6 +186,9 @@ class NcmpCmHandleUpgradeSpec extends CpsIntegrationSpecBase {
                 assert cmHandleCompositeState.lockReason.lockReasonCategory == LockReasonCategory.MODULE_UPGRADE_FAILED
             })
 
+        and: 'the CM-handle has same moduleSetTag as before'
+            assert objectUnderTest.getNcmpServiceCmHandle(CM_HANDLE_ID).moduleSetTag == 'oldTag'
+
         cleanup: 'deregister CM-handle'
             deregisterCmHandle(DMI_URL, CM_HANDLE_ID)
     }
index d1307cd..f77cb02 100644 (file)
@@ -195,7 +195,6 @@ ncmp:
       parallelism-level: 1
 
   model-loader:
-    subscription: true
     maximum-attempt-count: 20
 
   servlet:
index 60aa6b4..c95b73a 100644 (file)
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.4.4-SNAPSHOT</version>
+        <version>3.4.5-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/pom.xml b/pom.xml
index 3f67efb..5a71e18 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.4-SNAPSHOT</version>\r
+    <version>3.4.5-SNAPSHOT</version>\r
     <packaging>pom</packaging>\r
 \r
     <name>cps</name>\r
diff --git a/releases/3.4.4-container.yaml b/releases/3.4.4-container.yaml
new file mode 100644 (file)
index 0000000..01170f7
--- /dev/null
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.4.4
+project: cps
+log_dir: cps-maven-docker-stage-master/935/
+ref: 8c37c03acae232829ee4e3aab636800741159de9
+containers:
+  - name: 'cps-and-ncmp'
+    version: '3.4.4-20240223T140746Z'
diff --git a/releases/3.4.4.yaml b/releases/3.4.4.yaml
new file mode 100644 (file)
index 0000000..642770a
--- /dev/null
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/943/
+project: cps
+version: 3.4.4
index 4a15e4e..7b38301 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>3.4.4-SNAPSHOT</version>
+    <version>3.4.5-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
index 369d363..daf4035 100644 (file)
@@ -22,7 +22,7 @@
 
 major=3
 minor=4
-patch=4
+patch=5
 
 base_version=${major}.${minor}.${patch}