Merge "Add memory usage to integration tests [UPDATED]"
authorToine Siebelink <toine.siebelink@est.tech>
Tue, 17 Oct 2023 13:17:08 +0000 (13:17 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 17 Oct 2023 13:17:08 +0000 (13:17 +0000)
114 files changed:
.gitignore
checkstyle/pom.xml
cps-application/pom.xml
cps-bom/pom.xml
cps-dependencies/pom.xml
cps-events/pom.xml
cps-ncmp-events/pom.xml
cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json [new file with mode: 0644]
cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json [new file with mode: 0644]
cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json [new file with mode: 0644]
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/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy [new file with mode: 0644]
cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy [new file with mode: 0644]
cps-ncmp-rest-stub/pom.xml
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/pom.xml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapper.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapper.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpEventResponseCode.java with 79% similarity]
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/client/DmiRestClient.java
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/EventsPublisher.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisher.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.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/CompositeStateUtils.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.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/inventory/sync/ModuleSyncWatchdog.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/SyncUtils.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java [new file with mode: 0644]
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/CmHandleQueryConditions.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.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/api/models/DmiPluginRegistrationResponse.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java [moved from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java with 70% similarity]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy
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/TrustLevelCacheConfigSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmSubscriptionNcmpOutEventPublisherSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
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-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy
cps-service/pom.xml
cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
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/pom.xml
docker-compose/docker-compose.yml
docs/cps-ncmp-message-status-codes.rst
docs/cps-path.rst
docs/cps-stubs.rst [new file with mode: 0644]
docs/deployment.rst
docs/design.rst
docs/index.rst
docs/release-notes.rst
docs/spelling_wordlist.txt [new file with mode: 0644]
docs/tox.ini
integration-test/pom.xml
integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsModuleServiceIntegrationSpec.groovy
jacoco-report/pom.xml
pom.xml
releases/3.3.8-container.yaml [new file with mode: 0644]
releases/3.3.8.yaml [new file with mode: 0644]
spotbugs/pom.xml
version.properties

index b4abc48..c59fc47 100755 (executable)
@@ -27,10 +27,10 @@ tmp/
 .checkstyle
 
 /.tox
-/_build/*
 /__pycache__/*
 
 /docs/docs/
 /docs/.vscode/
+/docs/_build/*
 
 /test-tools/metrics-reports/
index 4cf950e..7d190fe 100644 (file)
@@ -26,7 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>3.3.8-SNAPSHOT</version>
+    <version>3.3.9-SNAPSHOT</version>
 
     <profiles>
         <profile>
index 3b5069a..f4e3e91 100755 (executable)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-actuator</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>
             <groupId>io.micrometer</groupId>
             <artifactId>micrometer-tracing-bridge-brave</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+        </dependency>
+
         <!-- T E S T   D E P E N D E N C I E S -->
         <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>cglib-nodep</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.tngtech.archunit</groupId>
+            <artifactId>archunit-junit5</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>com.tngtech.archunit</groupId>
-            <artifactId>archunit-junit5</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.dataformat</groupId>
-            <artifactId>jackson-dataformat-xml</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>
+
     </dependencies>
 
     <build>
index 0f5cb4b..b9da8b2 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>3.3.8-SNAPSHOT</version>
+    <version>3.3.9-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
index f6931c3..cff596f 100755 (executable)
@@ -27,7 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>3.3.8-SNAPSHOT</version>
+    <version>3.3.9-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
@@ -41,6 +41,7 @@
         <sonar.skip>true</sonar.skip>
         <testcontainers.version>1.18.3</testcontainers.version>
         <mapstruct.version>1.4.2.Final</mapstruct.version>
+        <jetty-version>11.0.16</jetty-version>
     </properties>
 
     <build>
@@ -70,6 +71,7 @@
 
     <dependencyManagement>
         <dependencies>
+            <!-- S P R I N G   D E P E N D E N C I E S -->
             <dependency>
                 <groupId>org.springdoc</groupId>
                 <artifactId>springdoc-openapi-ui</artifactId>
                 <scope>import</scope>
             </dependency>
             <dependency>
-                <groupId>org.opendaylight.yangtools</groupId>
-                <artifactId>yangtools-artifacts</artifactId>
-                <version>8.0.6</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-            <dependency>
-                <groupId>io.swagger.core.v3</groupId>
-                <artifactId>swagger-annotations</artifactId>
-                <version>2.2.10</version>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-test</artifactId>
+                <version>6.0.11</version>
             </dependency>
             <dependency>
                 <groupId>io.springfox</groupId>
                 <version>3.1.2</version>
             </dependency>
             <dependency>
-                <groupId>com.google.code.gson</groupId>
-                <artifactId>gson</artifactId>
-                <version>2.8.9</version>
-            </dependency>
-            <dependency>
-                <groupId>io.hypersistence</groupId>
-                <artifactId>hypersistence-utils-hibernate-60</artifactId>
-                <version>3.5.0</version>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-cache</artifactId>
+                <version>3.1.2</version>
             </dependency>
+            <!-- A L P H A B E T I C A L   D E P E N D E N C I E S -->
             <dependency>
-                <groupId>org.antlr</groupId>
-                <artifactId>antlr4-runtime</artifactId>
-                <version>4.9.2</version>
+                <groupId>cglib</groupId>
+                <artifactId>cglib-nodep</artifactId>
+                <version>3.1</version>
             </dependency>
             <dependency>
-                <groupId>org.codehaus.groovy</groupId>
-                <artifactId>groovy</artifactId>
-                <version>${groovy.version}</version>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-databind</artifactId>
+                <version>2.15.2</version>
             </dependency>
             <dependency>
-                <groupId>org.codehaus.groovy</groupId>
-                <artifactId>groovy-json</artifactId>
-                <version>${groovy.version}</version>
+                <groupId>com.fasterxml.jackson.dataformat</groupId>
+                <artifactId>jackson-dataformat-xml</artifactId>
+                <version>2.13.1</version>
             </dependency>
             <dependency>
-                <groupId>org.spockframework</groupId>
-                <artifactId>spock-core</artifactId>
-                <version>2.4-M1-groovy-3.0</version>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs</artifactId>
+                <version>4.2.0</version>
             </dependency>
             <dependency>
-                <groupId>org.spockframework</groupId>
-                <artifactId>spock-spring</artifactId>
-                <version>2.4-M1-groovy-3.0</version>
+                <groupId>com.google.code.gson</groupId>
+                <artifactId>gson</artifactId>
+                <version>2.8.9</version>
             </dependency>
             <dependency>
-                <groupId>cglib</groupId>
-                <artifactId>cglib-nodep</artifactId>
-                <version>3.1</version>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>31.1-jre</version>
             </dependency>
             <dependency>
-                <groupId>io.cloudevents</groupId>
-                <artifactId>cloudevents-bom</artifactId>
-                <version>2.5.0</version>
-                <type>pom</type>
-                <scope>import</scope>
+                <groupId>com.hazelcast</groupId>
+                <artifactId>hazelcast-spring</artifactId>
+                <version>5.3.1</version>
             </dependency>
             <dependency>
-                <groupId>org.apache.commons</groupId>
-                <artifactId>commons-lang3</artifactId>
-                <version>3.11</version>
+                <groupId>com.tngtech.archunit</groupId>
+                <artifactId>archunit-junit5</artifactId>
+                <version>0.18.0</version>
+                <scope>test</scope>
             </dependency>
             <dependency>
-                <groupId>org.jetbrains</groupId>
-                <artifactId>annotations</artifactId>
-                <version>22.0.0</version>
+                <groupId>jakarta.validation</groupId>
+                <artifactId>jakarta.validation-api</artifactId>
+                <version>3.0.2</version>
             </dependency>
             <dependency>
-                <groupId>org.testcontainers</groupId>
-                <artifactId>testcontainers-bom</artifactId>
-                <version>1.18.3</version>
+                <groupId>io.cloudevents</groupId>
+                <artifactId>cloudevents-bom</artifactId>
+                <version>2.5.0</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
             <dependency>
-                <groupId>com.github.spotbugs</groupId>
-                <artifactId>spotbugs</artifactId>
-                <version>4.2.0</version>
+                <groupId>io.gsonfire</groupId>
+                <artifactId>gson-fire</artifactId>
+                <version>1.8.5</version>
             </dependency>
             <dependency>
-                <groupId>org.liquibase</groupId>
-                <artifactId>liquibase-core</artifactId>
-                <version>4.16.0</version>
+                <groupId>io.hypersistence</groupId>
+                <artifactId>hypersistence-utils-hibernate-60</artifactId>
+                <version>3.5.0</version>
             </dependency>
             <dependency>
-                <groupId>com.tngtech.archunit</groupId>
-                <artifactId>archunit-junit5</artifactId>
-                <version>0.18.0</version>
-                <scope>test</scope>
+                <groupId>io.micrometer</groupId>
+                <artifactId>micrometer-tracing-bridge-brave</artifactId>
+                <version>1.0.0</version>
             </dependency>
             <dependency>
-                <groupId>org.mapstruct</groupId>
-                <artifactId>mapstruct</artifactId>
-                <version>${mapstruct.version}</version>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-annotations</artifactId>
+                <version>2.2.10</version>
             </dependency>
             <dependency>
-                <groupId>org.mapstruct</groupId>
-                <artifactId>mapstruct-processor</artifactId>
-                <version>${mapstruct.version}</version>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-models</artifactId>
+                <version>2.2.15</version>
             </dependency>
             <dependency>
                 <groupId>net.logstash.logback</groupId>
                 <version>7.0.1</version>
             </dependency>
             <dependency>
-                <groupId>org.codehaus.janino</groupId>
-                <artifactId>janino</artifactId>
-                <version>3.1.7</version>
-            </dependency>
-            <dependency>
-                <groupId>com.hazelcast</groupId>
-                <artifactId>hazelcast-spring</artifactId>
-                <version>5.3.1</version>
-            </dependency>
-            <dependency>
-                <groupId>com.google.guava</groupId>
-                <artifactId>guava</artifactId>
-                <version>31.1-jre</version>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-runtime</artifactId>
+                <version>4.9.2</version>
             </dependency>
             <dependency>
-                <groupId>javax.validation</groupId>
-                <artifactId>validation-api</artifactId>
-                <version>2.0.1.Final</version>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>3.11</version>
             </dependency>
             <dependency>
                 <groupId>org.apache.maven.plugins</groupId>
                 <version>3.3.1</version>
             </dependency>
             <dependency>
-                <groupId>jakarta.validation</groupId>
-                <artifactId>jakarta.validation-api</artifactId>
-                <version>3.0.2</version>
+                <groupId>org.codehaus.groovy</groupId>
+                <artifactId>groovy</artifactId>
+                <version>${groovy.version}</version>
             </dependency>
             <dependency>
-                <groupId>io.micrometer</groupId>
-                <artifactId>micrometer-tracing-bridge-brave</artifactId>
-                <version>1.0.0</version>
+                <groupId>org.codehaus.janino</groupId>
+                <artifactId>janino</artifactId>
+                <version>3.1.7</version>
             </dependency>
             <dependency>
-                <groupId>com.fasterxml.jackson.dataformat</groupId>
-                <artifactId>jackson-dataformat-xml</artifactId>
-                <version>2.13.1</version>
+                <groupId>org.codehaus.groovy</groupId>
+                <artifactId>groovy-json</artifactId>
+                <version>${groovy.version}</version>
             </dependency>
             <dependency>
-                <groupId>io.swagger.core.v3</groupId>
-                <artifactId>swagger-models</artifactId>
-                <version>2.2.15</version>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-server</artifactId>
+                <version>${jetty-version}</version>
             </dependency>
             <dependency>
-                <groupId>org.projectlombok</groupId>
-                <artifactId>lombok</artifactId>
-                <version>1.18.24</version>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-http</artifactId>
+                <version>${jetty-version}</version>
             </dependency>
             <dependency>
-                <groupId>io.gsonfire</groupId>
-                <artifactId>gson-fire</artifactId>
-                <version>1.8.5</version>
+                <groupId>org.jetbrains</groupId>
+                <artifactId>annotations</artifactId>
+                <version>22.0.0</version>
             </dependency>
             <dependency>
-                <groupId>com.fasterxml.jackson.core</groupId>
-                <artifactId>jackson-databind</artifactId>
-                <version>2.15.2</version>
+                <groupId>org.junit.jupiter</groupId>
+                <artifactId>junit-jupiter-api</artifactId>
+                <version>5.10.0</version>
             </dependency>
             <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-server</artifactId>
-                <version>11.0.14</version>
+                <groupId>org.liquibase</groupId>
+                <artifactId>liquibase-core</artifactId>
+                <version>4.16.0</version>
             </dependency>
             <dependency>
-                <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-http</artifactId>
-                <version>11.0.14</version>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct</artifactId>
+                <version>${mapstruct.version}</version>
             </dependency>
             <dependency>
-                <groupId>jakarta.servlet</groupId>
-                <artifactId>jakarta.servlet-api</artifactId>
-                <version>6.0.0</version>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct-processor</artifactId>
+                <version>${mapstruct.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.openapitools</groupId>
                 <version>0.2.4</version>
             </dependency>
             <dependency>
-                <groupId>org.junit.jupiter</groupId>
-                <artifactId>junit-jupiter-api</artifactId>
-                <version>5.10.0</version>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>8.0.6</version>
+                <type>pom</type>
+                <scope>import</scope>
             </dependency>
             <dependency>
-                <groupId>org.springframework</groupId>
-                <artifactId>spring-test</artifactId>
-                <version>6.0.11</version>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>1.18.24</version>
+            </dependency>
+            <dependency>
+                <groupId>org.testcontainers</groupId>
+                <artifactId>testcontainers-bom</artifactId>
+                <version>1.18.3</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.spockframework</groupId>
+                <artifactId>spock-core</artifactId>
+                <version>2.4-M1-groovy-3.0</version>
+            </dependency>
+            <dependency>
+                <groupId>org.spockframework</groupId>
+                <artifactId>spock-spring</artifactId>
+                <version>2.4-M1-groovy-3.0</version>
             </dependency>
         </dependencies>
     </dependencyManagement>
index d776ccb..6d16203 100644 (file)
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 6a5ba4b..a59e925 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
diff --git a/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-dmi-in-event-schema-1.0.0.json
new file mode 100644 (file)
index 0000000..caffb63
--- /dev/null
@@ -0,0 +1,94 @@
+{
+  "$schema": "https://json-schema.org/draft/2019-09/schema",
+  "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-merge-dmi-in-event-schema:1.0.0",
+  "$ref": "#/definitions/CmSubscriptionMergeDmiInEvent",
+  "definitions": {
+    "CmSubscriptionMergeDmiInEvent": {
+      "description": "The payload for cm subscription merge event incoming message from NCMP.",
+      "type": "object",
+      "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_dmi.CmSubscriptionMergeDmiInEvent",
+      "additionalProperties": false,
+      "properties": {
+        "data": {
+          "$ref": "#/definitions/data"
+        }
+      },
+      "required": [
+        "data"
+      ]
+    },
+    "data": {
+      "type": "object",
+      "description": "The actual data containing information about the targets and subscription",
+      "additionalProperties": false,
+      "properties": {
+        "cmhandles": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "description": "Details for the target cmhandles",
+            "additionalProperties": false,
+            "properties": {
+              "cmhandleId": {
+                "type": "string"
+              },
+              "private-properties": {
+                "type": "object",
+                "existingJavaType": "java.util.Map<String,String>",
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        },
+        "predicates": {
+          "type": "array",
+          "description": "Additional values to be added into the subscription",
+          "items": {
+            "type": "object",
+            "properties": {
+              "targetFilter": {
+                "description": "CM Handles to be targeted by the subscription",
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              },
+              "scopeFilter": {
+                "type": "object",
+                "properties": {
+                  "datastore": {
+                    "description": "datastore which is to be used by the subscription",
+                    "type": "string",
+                    "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
+                  },
+                  "xpath-filter": {
+                    "description": "filter to be applied to the CM Handles through this event",
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  }
+                },
+                "additionalProperties": false,
+                "required": [
+                  "xpath-filter"
+                ]
+              }
+            },
+            "additionalProperties": false,
+            "required": [
+              "targetFilter"
+            ]
+          },
+          "additionalProperties": false
+        }
+      },
+      "required": [
+        "cmhandles",
+        "predicates"
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/cmsubscriptionmerge/cm-subscription-ncmp-in-event-schema-1.0.0.json
new file mode 100644 (file)
index 0000000..2742062
--- /dev/null
@@ -0,0 +1,74 @@
+{
+  "$id": "urn:cps:org.onap.cps.ncmp.events:cm-subscription-merge-ncmp-in-event:1.0.0",
+  "$ref": "#/definitions/CmSubscriptionMergeNcmpInEvent",
+  "$schema": "https://json-schema.org/draft/2019-09/schema",
+  "definitions": {
+    "CmSubscriptionMergeNcmpInEvent": {
+      "description": "The payload for subscription merge event.",
+      "javaType": "org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.client_to_ncmp.CmSubscriptionMergeNcmpInEvent",
+      "properties": {
+        "data": {
+          "properties": {
+            "subscriptionId": {
+              "description": "The subscription details.",
+              "type": "string"
+            },
+            "predicates": {
+              "type": "array",
+              "description": "Additional values to be added into the subscription",
+              "items": {
+                "type": "object",
+                "properties": {
+                  "targetFilter": {
+                    "description": "CM Handles to be targeted by the subscription",
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  },
+                  "scopeFilter": {
+                    "type": "object",
+                    "properties": {
+                      "datastore": {
+                        "description": "datastore which is to be used by the subscription",
+                        "type": "string",
+                        "enum": ["ncmp-datastore:passthrough-operational", "ncmp-datastore:passthrough-running"]
+                      },
+                      "xpath-filter": {
+                        "description": "filter to be applied to the CM Handles through this event",
+                        "type": "array",
+                        "items": {
+                          "type": "string"
+                        }
+                      }
+                    },
+                    "additionalProperties": false,
+                    "required": [
+                      "xpath-filter"
+                    ]
+                  }
+                },
+                "additionalProperties": false,
+                "required": [
+                  "targetFilter"
+                ]
+              },
+              "additionalProperties": false
+            }
+          },
+          "required": [
+            "subscriptionId",
+            "predicates"
+          ],
+          "type": "object",
+          "additionalProperties": false
+        }
+      },
+      "type": "object",
+      "additionalProperties": false,
+      "required": [
+        "data"
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json
new file mode 100644 (file)
index 0000000..e1796fb
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "$schema": "https://json-schema.org/draft/2019-09/schema",
+  "$id": "urn:cps:org.onap.cps.ncmp.events:device-trust-level-event-schema:1.0.0",
+  "$ref": "#/definitions/DeviceTrustLevel",
+  "definitions": {
+    "DeviceTrustLevel" : {
+      "description": "The payload for device trust level event.",
+      "type": "object",
+      "javaType": "org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel",
+      "properties": {
+        "data": {
+          "type": "object",
+          "properties": {
+            "trustLevel": {
+              "type": "string"
+            }
+          },
+          "required": [
+            "trustLevel"
+          ],
+          "additionalProperties": false
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "data"
+      ]
+    }
+  }
+}
\ No newline at end of file
index 275313c..205a33c 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
     </parent>
 
     <artifactId>cps-ncmp-rest-stub-app</artifactId>
index 435cc70..d2cce87 100644 (file)
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-ncmp-rest-stub</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-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>
             <artifactId>spock-core</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-spring</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
+    
 </project>
\ No newline at end of file
index 198b14f..e33af45 100644 (file)
@@ -141,13 +141,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
     }
 
     @Override
-    public ResponseEntity<Void> createResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier,
-                                                                     final String datastoreName, final String cmHandle,
-                                                                     @Valid final Object body,
+    public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle,
+                                                                     @NotNull @Valid final String resourceIdentifier, 
+                                                                     @Valid final Object body, 
                                                                      final String contentType) {
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
-
+    
     @Override
     public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle,
                                                                      @NotNull @Valid final String resourceIdentifier,
@@ -183,13 +183,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
     }
 
     @Override
-    public ResponseEntity<Object> patchResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier,
-                                                                      final String datastoreName, final String cmHandle,
-                                                                      @Valid final Object body,
+    public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle,
+                                                                      @NotNull @Valid final String resourceIdentifier, 
+                                                                      @Valid final Object body, 
                                                                       final String contentType) {
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
-
+    
     @Override
     public ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName, final String cmHandle,
                                                                @Valid final String cpsPath, @Valid final String options,
@@ -218,9 +218,10 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
     }
 
     @Override
-    public ResponseEntity<Object> updateResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier,
-                                                                       final String datastoreName,
-                                                                       final String cmHandle, @Valid final Object body,
+    public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName, 
+                                                                       final String cmHandle, 
+                                                                       @NotNull @Valid final String resourceIdentifier, 
+                                                                       @Valid final Object body,
                                                                        final String contentType) {
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
 
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy
new file mode 100644 (file)
index 0000000..b36e25a
--- /dev/null
@@ -0,0 +1,82 @@
+/*-
+ * ============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.rest.stub
+
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.test.context.SpringBootContextLoader
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.boot.test.web.client.TestRestTemplate
+import org.springframework.boot.test.web.server.LocalServerPort
+import org.springframework.http.HttpStatus
+import org.springframework.test.context.ActiveProfiles
+import org.springframework.test.context.ContextConfiguration
+
+import com.fasterxml.jackson.core.JsonProcessingException
+import com.fasterxml.jackson.core.type.TypeReference
+import com.fasterxml.jackson.databind.JsonMappingException
+import com.fasterxml.jackson.databind.ObjectMapper
+
+import spock.lang.Shared
+import spock.lang.Specification
+
+@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+class SampleCpsNcmpClientSpec extends Specification {
+
+    static final String CM_HANDLE = "anything"
+
+    static final String DATA_STORE_NAME = DatastoreType.PASSTHROUGH_OPERATIONAL.getDatastoreName()
+
+    @LocalServerPort
+    def port
+
+    final def testRestTemplate = new TestRestTemplate()
+
+    @Value('${rest.api.ncmp-stub-base-path}')
+    def stubBasePath
+
+    @Autowired
+    ObjectMapper objectMapper
+
+    def 'Test the invocation of the stub API' () throws JsonMappingException, JsonProcessingException {
+
+        when: 'Get resource data for cm handle URL is invoked'
+            def url = "${getBaseUrl()}/v1/ch/${CM_HANDLE}/data/ds/${DATA_STORE_NAME}?resourceIdentifier=parent&options=(a=1,b=2)"
+            def response = testRestTemplate.getForEntity(url, String.class)
+        then: 'Response is OK'
+            response.getStatusCode() == HttpStatus.OK
+
+        and: 'Response body contains customized stub response that contains correct bookstore category code'
+            def typeRef = new TypeReference<HashMap<String, Object>>() {}
+            def map = objectMapper.readValue(response.getBody(), typeRef)
+            def obj1 = (Map<String, Object>) map.get("stores:bookstore")
+            def obj2 = (List<Object>) obj1.get("categories")
+            def obj3 = (Map<String, Object>) obj2.iterator().next()
+
+            assert obj3.get("code") == "02"
+    }
+
+    def String getBaseUrl() {
+        return "http://localhost:" + port + stubBasePath
+    }
+}
diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy
new file mode 100644 (file)
index 0000000..7938423
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============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.rest.stub
+
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.context.annotation.ComponentScan
+
+@SpringBootApplication
+@ComponentScan(basePackages = ["org.onap.cps.ncmp.rest.stub", "org.onap.cps.ncmp.rest.stub.controller", "org.onap.cps.ncmp.rest.api"])
+class TestApplication {
+}
index 4db8617..9e62ff5 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 9bae794..022e2ba 100644 (file)
@@ -87,6 +87,8 @@ components:
           items:
             type: string
           example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ]
+        upgradedCmHandles:
+          $ref: '#/components/schemas/UpgradedCmHandles'
     DmiPluginRegistrationErrorResponse:
       type: object
       properties:
@@ -102,6 +104,10 @@ components:
           type: array
           items:
             $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+        failedUpgradeCmHandles:
+          type: array
+          items:
+            $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
     CmHandlerRegistrationErrorResponse:
       type: object
       properties:
@@ -135,6 +141,20 @@ components:
       additionalProperties:
         type: string
         example: my-property
+    #Module upgrade schema
+    UpgradedCmHandles:
+      required:
+        - cmHandles
+      type: object
+      properties:
+        cmHandles:
+          type: array
+          items:
+            type: string
+          example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ]
+        moduleSetTag:
+          type: string
+          example: 'my-module-set-tag'
 
     #Response Schemas
     RestModuleReference:
index ef34b1d..6a52d9f 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-jetty</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.swagger.core.v3</groupId>
             <artifactId>swagger-annotations</artifactId>
             <groupId>org.mapstruct</groupId>
             <artifactId>mapstruct-processor</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.openapitools</groupId>
+            <artifactId>jackson-databind-nullable</artifactId>
+        </dependency>
         <!-- T E S T   D E P E N D E N C I E S -->
         <dependency>
             <groupId>org.codehaus.groovy</groupId>
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.openapitools</groupId>
-            <artifactId>jackson-databind-nullable</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
index b3f36f9..af785d5 100644 (file)
@@ -48,6 +48,9 @@ public interface NcmpRestInputMapper {
     @Mapping(source = "removedCmHandles", target = "removedCmHandles",
         nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
         nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
+    @Mapping(source = "upgradedCmHandles", target = "upgradedCmHandles",
+            nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+            nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
     DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration);
 
     @Mapping(source = "cmHandle", target = "cmHandleId")
index 87f9d83..453abca 100755 (executable)
@@ -110,23 +110,23 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
             getFailedResponses(dmiPluginRegistrationResponse.getUpdatedCmHandles()));
         dmiPluginRegistrationErrorResponse.setFailedRemovedCmHandles(
             getFailedResponses(dmiPluginRegistrationResponse.getRemovedCmHandles()));
-
+        dmiPluginRegistrationErrorResponse.setFailedUpgradeCmHandles(
+                getFailedResponses(dmiPluginRegistrationResponse.getUpgradedCmHandles()));
         return dmiPluginRegistrationErrorResponse;
     }
 
     private List<CmHandlerRegistrationErrorResponse> getFailedResponses(
-        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponseList) {
+            final List<CmHandleRegistrationResponse> cmHandleRegistrationResponseList) {
         return cmHandleRegistrationResponseList.stream()
-            .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE)
-            .map(this::toCmHandleRegistrationErrorResponse)
-            .collect(Collectors.toList());
+                .filter(cmHandleRegistrationResponse -> cmHandleRegistrationResponse.getStatus() == Status.FAILURE)
+                .map(this::toCmHandleRegistrationErrorResponse).collect(Collectors.toList());
     }
 
     private CmHandlerRegistrationErrorResponse toCmHandleRegistrationErrorResponse(
         final CmHandleRegistrationResponse registrationResponse) {
         return new CmHandlerRegistrationErrorResponse()
             .cmHandle(registrationResponse.getCmHandle())
-            .errorCode(registrationResponse.getRegistrationError().errorCode)
+            .errorCode(registrationResponse.getNcmpResponseStatus().getCode())
             .errorText(registrationResponse.getErrorText());
     }
 
index 82dc483..b436540 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.onap.cps.ncmp.rest.mapper;
 
+import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING;
+
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.Named;
@@ -75,8 +77,10 @@ public interface CmHandleStateMapper {
     @Named("toExternalLockReason")
     static LockReason toExternalLockReason(CompositeState.LockReason internalLockReason) {
         final LockReason externalLockReason = new LockReason();
-        if (internalLockReason.getLockReasonCategory() != null) {
-            externalLockReason.setReason("LOCKED_MISBEHAVING");
+        if (internalLockReason.getLockReasonCategory() == null) {
+            externalLockReason.setReason(LOCKED_MISBEHAVING.name());
+        } else {
+            externalLockReason.setReason(internalLockReason.getLockReasonCategory().name());
         }
         externalLockReason.setDetails(internalLockReason.getDetails());
         return externalLockReason;
index 6bfa593..de044d0 100644 (file)
@@ -615,8 +615,7 @@ class NetworkCmProxyControllerSpec extends Specification {
         def expectedContent = [
             '"state":',
             '"cmHandleState":"ADVISED"',
-            '"reason":"LOCKED_MISBEHAVING"',
-            '"details":"lock details"',
+            '"lockReason":{"reason":"MODULE_SYNC_FAILED","details":"lock details"}',
             '"lastUpdateTime":"2022-12-31T20:30:40.000+0000"',
             '"dataSyncEnabled":false',
             '"dataSyncState":',
index e755094..1d03be1 100644 (file)
@@ -219,14 +219,14 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
             responseBody.getFailedUpdatedCmHandles() == expectedFailedUpdateCmHandle
             responseBody.getFailedRemovedCmHandles() == expectedFailedRemovedCmHandle
         where:
-            scenario               | createCmHandleResponse         | updateCmHandleResponse         | removeCmHandleResponse         || expectedFailedCreatedCmHandle       | expectedFailedUpdateCmHandle        | expectedFailedRemovedCmHandle
-            'only create failed'   | failedResponse('cm-handle-1')  | successResponse('cm-handle-2') | successResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | []                                  | []
-            'only update failed'   | successResponse('cm-handle-1') | failedResponse('cm-handle-2')  | successResponse('cm-handle-3') || []                                  | [failedRestResponse('cm-handle-2')] | []
-            'only delete failed'   | successResponse('cm-handle-1') | successResponse('cm-handle-2') | failedResponse('cm-handle-3')  || []                                  | []                                  | [failedRestResponse('cm-handle-3')]
-            'all three failed'     | failedResponse('cm-handle-1')  | failedResponse('cm-handle-2')  | failedResponse('cm-handle-3')  || [failedRestResponse('cm-handle-1')] | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')]
-            'create update failed' | failedResponse('cm-handle-1')  | failedResponse('cm-handle-2')  | successResponse('cm-handle-3') || [failedRestResponse('cm-handle-1')] | [failedRestResponse('cm-handle-2')] | []
-            'create delete failed' | failedResponse('cm-handle-1')  | successResponse('cm-handle-2') | failedResponse('cm-handle-3')  || [failedRestResponse('cm-handle-1')] | []                                  | [failedRestResponse('cm-handle-3')]
-            'update delete failed' | successResponse('cm-handle-1') | failedResponse('cm-handle-2')  | failedResponse('cm-handle-3')  || []                                  | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')]
+        scenario               | createCmHandleResponse                 | updateCmHandleResponse                 | removeCmHandleResponse                 || expectedFailedCreatedCmHandle                 | expectedFailedUpdateCmHandle                  | expectedFailedRemovedCmHandle
+        'only create failed'   | expectedFailedResponse('cm-handle-1')  | expectedSuccessResponse('cm-handle-2') | expectedSuccessResponse('cm-handle-3') || [expectedUnknownErrorResponse('cm-handle-1')] | []                                            | []
+        'only update failed'   | expectedSuccessResponse('cm-handle-1') | expectedFailedResponse('cm-handle-2')  | expectedSuccessResponse('cm-handle-3') || []                                            | [expectedUnknownErrorResponse('cm-handle-2')] | []
+        'only delete failed'   | expectedSuccessResponse('cm-handle-1') | expectedSuccessResponse('cm-handle-2') | expectedFailedResponse('cm-handle-3')  || []                                            | []                                            | [expectedUnknownErrorResponse('cm-handle-3')]
+        'all three failed'     | expectedFailedResponse('cm-handle-1')  | expectedFailedResponse('cm-handle-2')  | expectedFailedResponse('cm-handle-3')  || [expectedUnknownErrorResponse('cm-handle-1')] | [expectedUnknownErrorResponse('cm-handle-2')] | [expectedUnknownErrorResponse('cm-handle-3')]
+        'create update failed' | expectedFailedResponse('cm-handle-1')  | expectedFailedResponse('cm-handle-2')  | expectedSuccessResponse('cm-handle-3') || [expectedUnknownErrorResponse('cm-handle-1')] | [expectedUnknownErrorResponse('cm-handle-2')] | []
+        'create delete failed' | expectedFailedResponse('cm-handle-1')  | expectedSuccessResponse('cm-handle-2') | expectedFailedResponse('cm-handle-3')  || [expectedUnknownErrorResponse('cm-handle-1')] | []                                            | [expectedUnknownErrorResponse('cm-handle-3')]
+        'update delete failed' | expectedSuccessResponse('cm-handle-1') | expectedFailedResponse('cm-handle-2')  | expectedFailedResponse('cm-handle-3')  || []                                            | [expectedUnknownErrorResponse('cm-handle-2')] | [expectedUnknownErrorResponse('cm-handle-3')]
     }
 
     def 'Get all cm handle IDs by DMI plugin identifier.'() {
@@ -246,15 +246,15 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
             assert response.contentAsString.contains('cm-handle-id-2')
     }
 
-    def failedRestResponse(cmHandle) {
-        return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '00', 'errorText': 'Failed')
+    def expectedUnknownErrorResponse(cmHandle) {
+        return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '108', 'errorText': 'Failed')
     }
 
-    def failedResponse(cmHandle) {
+    def expectedFailedResponse(cmHandle) {
         return CmHandleRegistrationResponse.createFailureResponse(cmHandle, new RuntimeException('Failed'))
     }
 
-    def successResponse(cmHandle) {
+    def expectedSuccessResponse(cmHandle) {
         return CmHandleRegistrationResponse.createSuccessResponse(cmHandle)
     }
 
index 1fa83a5..f394f91 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-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.
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.rest.mapper
 
+import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.LOCKED_MISBEHAVING
 import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_SYNC_FAILED
 
 import org.mapstruct.factory.Mappers
@@ -55,7 +56,7 @@ class CmHandleStateMapperSpec extends Specification {
         and: 'mapped result should have correct values'
             assert !result.dataSyncEnabled
             assert result.lastUpdateTime == formattedDateAndTime
-            assert result.lockReason.reason == 'LOCKED_MISBEHAVING'
+            assert result.lockReason.reason == MODULE_SYNC_FAILED.name()
             assert result.lockReason.details == 'locked details'
             assert result.cmHandleState == 'ADVISED'
             assert result.dataSyncState.operational.getSyncState() != null
@@ -69,17 +70,17 @@ class CmHandleStateMapperSpec extends Specification {
 
     def 'Internal to External Lock Reason Mapping of #scenario'() {
         given: 'a LOCKED composite state with locked reason of #scenario'
-            def compositeState = new CompositeStateBuilder()
+        def compositeState = new CompositeStateBuilder()
                 .withCmHandleState(CmHandleState.LOCKED)
                 .withLockReason(lockReason, '').build()
         when: 'the composite state is mapped to a CMHandle composite state'
-            def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState)
+        def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState)
         then: 'the composite state contains the expected lock Reason and details'
-            result.getLockReason().getReason() == expectedExternalLockReason
+        result.getLockReason().getReason() == (expectedExternalLockReason as String)
         where:
         scenario             | lockReason         || expectedExternalLockReason
-        'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || 'LOCKED_MISBEHAVING'
-        'null value'         | null               || null
+        'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || MODULE_SYNC_FAILED
+        'null value'         | null               || LOCKED_MISBEHAVING
     }
 
 }
index 527d70b..d7c1774 100644 (file)
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
     <artifactId>cps-ncmp-service</artifactId>
 
     <properties>
-        <minimum-coverage>0.98</minimum-coverage>
+        <minimum-coverage>0.96</minimum-coverage>
     </properties>
     <dependencies>
         <dependency>
@@ -23,7 +23,7 @@ package org.onap.cps.ncmp.api;
 import lombok.Getter;
 
 @Getter
-public enum NcmpEventResponseCode {
+public enum NcmpResponseStatus {
 
     SUCCESS("0", "Successfully applied changes"),
     SUCCESSFULLY_APPLIED_SUBSCRIPTION("1", "successfully applied subscription"),
@@ -33,13 +33,16 @@ public enum NcmpEventResponseCode {
     UNABLE_TO_READ_RESOURCE_DATA("103", "dmi plugin service is not able to read resource data"),
     PARTIALLY_APPLIED_SUBSCRIPTION("104", "partially applied subscription"),
     SUBSCRIPTION_NOT_APPLICABLE("105", "subscription not applicable for all cm handles"),
-    SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles");
+    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");
 
-    private final String statusCode;
-    private final String statusMessage;
+    private final String code;
+    private final String message;
 
-    NcmpEventResponseCode(final String statusCode, final String statusMessage) {
-        this.statusCode = statusCode;
-        this.statusMessage = statusMessage;
+    NcmpResponseStatus(final String code, final String message) {
+        this.code = code;
+        this.message = message;
     }
 }
index 58732b2..1f6c948 100644 (file)
@@ -24,6 +24,7 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DM
 import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_MODULES;
 import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_PROPERTIES;
 import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_CPS_PATH;
+import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_TRUST_LEVEL;
 import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties;
 import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties;
 import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
@@ -70,7 +71,8 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
         return executeQueries(cmHandleQueryServiceParameters,
             this::executeCpsPathQuery,
             this::queryCmHandlesByPublicProperties,
-            this::executeModuleNameQuery);
+            this::executeModuleNameQuery,
+                this::queryCmHandlesByTrustLevel);
     }
 
     @Override
@@ -117,9 +119,10 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
                 getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(),
                         InventoryQueryConditions.HAS_ALL_ADDITIONAL_PROPERTIES.getName());
 
-        return privatePropertyQueryPairs.isEmpty()
-                ? NO_QUERY_TO_EXECUTE
-                : cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs);
+        if (privatePropertyQueryPairs.isEmpty()) {
+            return NO_QUERY_TO_EXECUTE;
+        }
+        return cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs);
     }
 
     private Collection<String> queryCmHandlesByPublicProperties(
@@ -129,9 +132,23 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
                 getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(),
                         HAS_ALL_PROPERTIES.getConditionName());
 
-        return publicPropertyQueryPairs.isEmpty()
-                ? NO_QUERY_TO_EXECUTE
-                : cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs);
+        if (publicPropertyQueryPairs.isEmpty()) {
+            return NO_QUERY_TO_EXECUTE;
+        }
+        return cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs);
+    }
+
+    private Collection<String> queryCmHandlesByTrustLevel(final CmHandleQueryServiceParameters
+                                                                  cmHandleQueryServiceParameters) {
+
+        final Map<String, String> trustLevelPropertyQueryPairs =
+                getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(),
+                        WITH_TRUST_LEVEL.getConditionName());
+
+        if (trustLevelPropertyQueryPairs.isEmpty()) {
+            return NO_QUERY_TO_EXECUTE;
+        }
+        return cmHandleQueries.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs);
     }
 
     private Collection<String> executeModuleNameQuery(
@@ -157,7 +174,8 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH
         }
         try {
             cpsPathQueryResult = collectCmHandleIdsFromDataNodes(
-                cmHandleQueries.queryCmHandleDataNodesByCpsPath(cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS));
+                cmHandleQueries.queryCmHandleAncestorsByCpsPath(
+                        cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS));
         } catch (final PathParsingException pathParsingException) {
             throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(),
                     pathParsingException);
index f8adde8..a37b271 100755 (executable)
 
 package org.onap.cps.ncmp.api.impl;
 
+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;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID;
+import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE;
 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.utils.RestQueryParametersValidator.validateCmHandleQueryParameters;
 
 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;
@@ -48,6 +54,7 @@ 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;
 import org.onap.cps.ncmp.api.impl.inventory.CompositeState;
+import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder;
 import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils;
 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState;
 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
@@ -61,11 +68,11 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
-import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 import org.onap.cps.ncmp.api.models.DataOperationRequest;
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
 import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.ncmp.api.models.UpgradedCmHandles;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.CpsException;
@@ -92,7 +99,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler;
     private final CpsDataService cpsDataService;
     private final IMap<String, Object> moduleSyncStartedOnCmHandles;
-    private final IMap<String, TrustLevel> trustLevelPerDmiPlugin;
+    private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
 
     @Override
     public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
@@ -102,18 +109,23 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
         if (!dmiPluginRegistration.getRemovedCmHandles().isEmpty()) {
             dmiPluginRegistrationResponse.setRemovedCmHandles(
-                    parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles()));
+                    parseAndProcessDeletedCmHandlesInRegistration(dmiPluginRegistration.getRemovedCmHandles()));
         }
 
         if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
             dmiPluginRegistrationResponse.setCreatedCmHandles(
-                    parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration));
+                    parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration));
         }
         if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
             dmiPluginRegistrationResponse.setUpdatedCmHandles(
                     networkCmProxyDataServicePropertyHandler
                             .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
         }
+        if (dmiPluginRegistration.getUpgradedCmHandles() != null
+                && !dmiPluginRegistration.getUpgradedCmHandles().getCmHandles().isEmpty()) {
+            dmiPluginRegistrationResponse.setUpgradedCmHandles(
+                    parseAndProcessUpgradedCmHandlesInRegistration(dmiPluginRegistration));
+        }
 
         setTrustLevelPerDmiPlugin(dmiPluginRegistration);
 
@@ -210,8 +222,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
      */
     @Override
     public void setDataSyncEnabled(final String cmHandleId, final boolean dataSyncEnabled) {
-        final CompositeState compositeState = inventoryPersistence
-                .getCmHandleState(cmHandleId);
+        final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
         if (compositeState.getDataSyncEnabled().equals(dataSyncEnabled)) {
             log.info("Data-Sync Enabled flag is already: {} ", dataSyncEnabled);
         } else if (compositeState.getCmHandleState() != CmHandleState.READY) {
@@ -274,8 +285,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
      */
     @Override
     public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) {
-        final YangModelCmHandle yangModelCmHandle =
-                inventoryPersistence.getYangModelCmHandle(cmHandleId);
+        final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId);
         final List<YangModelCmHandle.Property> yangModelPublicProperties = yangModelCmHandle.getPublicProperties();
         final Map<String, String> cmHandlePublicProperties = new HashMap<>();
         YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties);
@@ -299,7 +309,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
      * @param dmiPluginRegistration dmi plugin registration information.
      * @return cm-handle registration response for create cm-handle requests.
      */
-    public List<CmHandleRegistrationResponse> parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
+    public List<CmHandleRegistrationResponse> parseAndProcessCreatedCmHandlesInRegistration(
             final DmiPluginRegistration dmiPluginRegistration) {
         final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>();
         dmiPluginRegistration.getCreatedCmHandles()
@@ -308,18 +318,19 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
                             dmiPluginRegistration.getDmiPlugin(),
                             dmiPluginRegistration.getDmiDataPlugin(),
                             dmiPluginRegistration.getDmiModelPlugin(),
-                            cmHandle);
+                            cmHandle,
+                            cmHandle.getModuleSetTag());
                     cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.ADVISED);
                 });
         return registerNewCmHandles(cmHandleStatePerCmHandle);
     }
 
-    protected List<CmHandleRegistrationResponse> parseAndRemoveCmHandlesInDmiRegistration(
+    protected List<CmHandleRegistrationResponse> parseAndProcessDeletedCmHandlesInRegistration(
             final List<String> tobeRemovedCmHandles) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
                 new ArrayList<>(tobeRemovedCmHandles.size());
         final Collection<YangModelCmHandle> yangModelCmHandles =
-            inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles);
+                inventoryPersistence.getYangModelCmHandles(tobeRemovedCmHandles);
 
         updateCmHandleStateBatch(yangModelCmHandles, CmHandleState.DELETING);
 
@@ -349,6 +360,42 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         return cmHandleRegistrationResponses;
     }
 
+    protected List<CmHandleRegistrationResponse> parseAndProcessUpgradedCmHandlesInRegistration(
+            final DmiPluginRegistration dmiPluginRegistration) {
+
+        final UpgradedCmHandles upgradedCmHandles = dmiPluginRegistration.getUpgradedCmHandles();
+        final String moduleSetTag = dmiPluginRegistration.getUpgradedCmHandles().getModuleSetTag();
+        final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle =
+                new HashMap<>(upgradedCmHandles.getCmHandles().size());
+        final Collection<String> notReadyCmHandles = new ArrayList<>(upgradedCmHandles.getCmHandles().size());
+        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
+        final String formattedModuleSetTag = MessageFormat.format("new moduleSetTag: {0}", moduleSetTag);
+
+        upgradedCmHandles.getCmHandles().forEach(cmHandleId -> {
+            if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) {
+                ncmpServiceCmHandle.setCmHandleId(cmHandleId);
+                ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder()
+                        .withCmHandleState(CmHandleState.READY)
+                        .withLockReason(MODULE_UPGRADE, formattedModuleSetTag).build());
+                final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(
+                        dmiPluginRegistration.getDmiPlugin(),
+                        dmiPluginRegistration.getDmiDataPlugin(),
+                        dmiPluginRegistration.getDmiModelPlugin(),
+                        ncmpServiceCmHandle,
+                        moduleSetTag);
+                cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED);
+            } else {
+                notReadyCmHandles.add(cmHandleId);
+            }
+        });
+
+        final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses
+                = upgradeCmHandles(cmHandleStatePerCmHandle);
+        cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(notReadyCmHandles,
+                CM_HANDLES_NOT_READY));
+        return cmHandleRegistrationResponses;
+    }
+
     private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) {
         try {
             deleteCmHandleFromDbAndModuleSyncMap(cmHandleId);
@@ -356,13 +403,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
             log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
                 cmHandleId, dataNodeNotFoundException.getMessage());
-            return CmHandleRegistrationResponse.createFailureResponse(cmHandleId,
-                RegistrationError.CM_HANDLE_DOES_NOT_EXIST);
+            return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND);
         } catch (final DataValidationException dataValidationException) {
             log.error("Unable to de-register cm-handle id: {}, caused by: {}",
                 cmHandleId, dataValidationException.getMessage());
-            return CmHandleRegistrationResponse.createFailureResponse(cmHandleId,
-                RegistrationError.CM_HANDLE_INVALID_ID);
+            return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID);
         } catch (final Exception exception) {
             log.error("Unable to de-register cm-handle id : {} , caused by : {}", cmHandleId, exception.getMessage());
             return CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception);
@@ -404,20 +449,35 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private List<CmHandleRegistrationResponse> registerNewCmHandles(final Map<YangModelCmHandle, CmHandleState>
                                                                             cmHandleStatePerCmHandle) {
-        final List<String> cmHandleIds = cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId)
-                .collect(Collectors.toList());
+        final List<String> cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle);
         try {
             lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle);
             return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds);
         } catch (final AlreadyDefinedException alreadyDefinedException) {
             return CmHandleRegistrationResponse.createFailureResponses(
                     alreadyDefinedException.getAlreadyDefinedObjectNames(),
-                    RegistrationError.CM_HANDLE_ALREADY_EXIST);
+                    CM_HANDLE_ALREADY_EXIST);
+        } catch (final Exception exception) {
+            return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception);
+        }
+    }
+
+    private List<CmHandleRegistrationResponse> upgradeCmHandles(final Map<YangModelCmHandle, CmHandleState>
+                                                                        cmHandleStatePerCmHandle) {
+        final List<String> cmHandleIds = getCmHandleIds(cmHandleStatePerCmHandle);
+        log.info("Moving cm handles : {} into locked (for upgrade) state.", cmHandleIds);
+        try {
+            lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle);
+            return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds);
         } catch (final Exception exception) {
             return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception);
         }
     }
 
+    private static List<String> getCmHandleIds(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) {
+        return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList();
+    }
+
     private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) {
         if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) {
             trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE);
index 2f61b22..401b190 100644 (file)
@@ -22,6 +22,8 @@
 
 package org.onap.cps.ncmp.api.impl;
 
+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.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY;
 import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY;
 
@@ -38,7 +40,6 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
-import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
@@ -75,13 +76,12 @@ public class NetworkCmProxyDataServicePropertyHandler {
                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
                     cmHandleId, e.getMessage());
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
-                    .createFailureResponse(cmHandleId, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+                    .createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND));
             } catch (final DataValidationException e) {
                 log.error("Unable to update cm handle : {}, caused by : {}",
                     cmHandleId, e.getMessage());
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(cmHandleId,
-                        RegistrationError.CM_HANDLE_INVALID_ID));
+                    CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID));
             } catch (final Exception exception) {
                 log.error("Unable to update cmHandle : {} , caused by : {}",
                     cmHandleId, exception.getMessage());
index 6a8310c..e8ce050 100644 (file)
@@ -75,10 +75,8 @@ public class DmiRestClient {
             final HttpEntity<Object> httpHeaders = new HttpEntity<>(configureHttpHeaders(new HttpHeaders()));
             final JsonNode dmiPluginHealthStatus = restTemplate.getForObject(dmiPluginBaseUrl + "/manage/health",
                     JsonNode.class, httpHeaders);
-            if (dmiPluginHealthStatus != null) {
-                if (dmiPluginHealthStatus.get("status").asText().equals("UP")) {
-                    return DmiPluginStatus.UP;
-                }
+            if (dmiPluginHealthStatus != null && dmiPluginHealthStatus.get("status").asText().equals("UP")) {
+                return DmiPluginStatus.UP;
             }
         } catch (final Exception exception) {
             log.warn("Could not send request for health check since {}", exception.getMessage());
index ebe9905..171db52 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.config.embeddedcache;
 
-import com.hazelcast.collection.ISet;
 import com.hazelcast.config.MapConfig;
-import com.hazelcast.config.SetConfig;
-import com.hazelcast.map.IMap;
+import java.util.Map;
 import org.onap.cps.cache.HazelcastCacheConfig;
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel;
 import org.springframework.context.annotation.Bean;
@@ -32,21 +30,21 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class TrustLevelCacheConfig extends HazelcastCacheConfig {
 
-    private static final SetConfig untrustworthyCmHandlesSetConfig =
-            createSetConfig("untrustworthyCmHandlesSetConfig");
+    private static final MapConfig trustLevelPerCmHandleCacheConfig =
+            createMapConfig("trustLevelPerCmHandleCacheConfig");
 
     private static final MapConfig trustLevelPerDmiPluginCacheConfig =
             createMapConfig("trustLevelPerDmiPluginCacheConfig");
 
     /**
-     * Distributed collection of untrustworthy cm handles.
+     * Distributed instance of trust level cache containing the trust level per cm handle.
      *
-     * @return instance of distributed set of untrustworthy cm handles.
+     * @return configured map of cm handle name as keys to trust-level for values.
      */
     @Bean
-    public ISet<String> untrustworthyCmHandlesSet() {
-        return createHazelcastInstance("untrustworthyCmHandlesSet",
-                untrustworthyCmHandlesSetConfig).getSet("untrustworthyCmHandlesSet");
+    public Map<String, TrustLevel> trustLevelPerCmHandle() {
+        return createHazelcastInstance("hazelcastInstanceTrustLevelPerCmHandleMap",
+                trustLevelPerCmHandleCacheConfig).getMap("trustLevelPerCmHandle");
     }
 
     /**
@@ -55,7 +53,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig {
      * @return configured map of dmi-plugin name as keys to trust-level for values.
      */
     @Bean
-    public IMap<String, TrustLevel> trustLevelPerDmiPlugin() {
+    public Map<String, TrustLevel> trustLevelPerDmiPlugin() {
         return createHazelcastInstance("hazelcastInstanceTrustLevelPerDmiPluginMap",
                 trustLevelPerDmiPluginCacheConfig).getMap("trustLevelPerDmiPlugin");
     }
index 49e455e..355e5cd 100644 (file)
@@ -64,8 +64,8 @@ public class EventsPublisher<T> {
                 cloudEventKafkaTemplate.send(topicName, eventKey, event);
         eventFuture.whenComplete((result, e) -> {
             if (e == null) {
-                log.debug("Successfully published event to topic : {} , Event : {}",
-                        result.getRecordMetadata().topic(), result.getProducerRecord().value());
+                log.debug("Successfully published event to topic : {} , Event : {}", result.getRecordMetadata().topic(),
+                        result.getProducerRecord().value());
 
             } else {
                 log.error("Unable to publish event to topic : {} due to {}", topicName, e.getMessage());
@@ -85,14 +85,7 @@ public class EventsPublisher<T> {
     public void publishEvent(final String topicName, final String eventKey, final T event) {
         final CompletableFuture<SendResult<String, T>> eventFuture =
                 legacyKafkaEventTemplate.send(topicName, eventKey, event);
-        eventFuture.whenComplete((result, e) -> {
-            if (e == null) {
-                log.debug("Successfully published event to topic : {} , Event : {}",
-                        result.getRecordMetadata().topic(), result.getProducerRecord().value());
-            } else {
-                log.error("Unable to publish event to topic : {} due to {}", topicName, e.getMessage());
-            }
-        });
+        handleLegacyEventCallback(topicName, eventFuture);
     }
 
     /**
@@ -107,16 +100,8 @@ public class EventsPublisher<T> {
 
         final ProducerRecord<String, T> producerRecord =
                 new ProducerRecord<>(topicName, null, eventKey, event, eventHeaders);
-        final CompletableFuture<SendResult<String, T>> eventFuture =
-                legacyKafkaEventTemplate.send(producerRecord);
-        eventFuture.whenComplete((result, ex) -> {
-            if (ex != null) {
-                log.error("Unable to publish event to topic : {} due to {}", topicName, ex.getMessage());
-            } else {
-                log.debug("Successfully published event to topic : {} , Event : {}",
-                        result.getRecordMetadata().topic(), result.getProducerRecord().value());
-            }
-        });
+        final CompletableFuture<SendResult<String, T>> eventFuture = legacyKafkaEventTemplate.send(producerRecord);
+        handleLegacyEventCallback(topicName, eventFuture);
     }
 
     /**
@@ -133,6 +118,18 @@ public class EventsPublisher<T> {
         publishEvent(topicName, eventKey, convertToKafkaHeaders(eventHeaders), event);
     }
 
+    private void handleLegacyEventCallback(final String topicName,
+            final CompletableFuture<SendResult<String, T>> eventFuture) {
+        eventFuture.whenComplete((result, e) -> {
+            if (e != null) {
+                log.error("Unable to publish event to topic : {} due to {}", topicName, e.getMessage());
+            } else {
+                log.debug("Successfully published event to topic : {} , Event : {}", result.getRecordMetadata().topic(),
+                        result.getProducerRecord().value());
+            }
+        });
+    }
+
     private Headers convertToKafkaHeaders(final Map<String, Object> eventMessageHeaders) {
         final Headers eventHeaders = new RecordHeaders();
         eventMessageHeaders.forEach((key, value) -> eventHeaders.add(key, SerializationUtils.serialize(value)));
index 473538c..a0fd81c 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.events.cmsubscription;
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.PARTIALLY_APPLIED_SUBSCRIPTION;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_NOT_APPLICABLE;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_PENDING;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESSFULLY_APPLIED_SUBSCRIPTION;
+import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.ACCEPTED;
+import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.PENDING;
+import static org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus.REJECTED;
+
 import io.cloudevents.CloudEvent;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NcmpEventResponseCode;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
 import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence;
 import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
@@ -100,48 +107,39 @@ public class CmSubscriptionNcmpOutEventPublisher {
                     cmSubscriptionStatus.setStatus(SubscriptionStatus.fromString(status));
                     cmSubscriptionStatus.setDetails(details);
                     return cmSubscriptionStatus;
-                }).collect(Collectors.toList());
+                }).toList();
     }
 
-    private NcmpEventResponseCode decideOnNcmpEventResponseCodeForSubscription(
+    private NcmpResponseStatus decideOnNcmpEventResponseCodeForSubscription(
             final Map<String, Map<String, String>> cmHandleIdToStatusAndDetailsAsMap) {
 
-        final boolean isAllTargetsPending = isAllTargetCmHandleStatusMatch(cmHandleIdToStatusAndDetailsAsMap,
-                SubscriptionStatus.PENDING);
-
-        final boolean isAllTargetsRejected = isAllTargetCmHandleStatusMatch(cmHandleIdToStatusAndDetailsAsMap,
-                SubscriptionStatus.REJECTED);
-
-        final boolean isAllTargetsAccepted = isAllTargetCmHandleStatusMatch(cmHandleIdToStatusAndDetailsAsMap,
-                SubscriptionStatus.ACCEPTED);
-
-        if (isAllTargetsAccepted) {
-            return NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION;
-        } else if (isAllTargetsRejected) {
-            return NcmpEventResponseCode.SUBSCRIPTION_NOT_APPLICABLE;
-        } else if (isAllTargetsPending) {
-            return NcmpEventResponseCode.SUBSCRIPTION_PENDING;
-        } else {
-            return NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION;
+        if (allTargetsHaveStatus(cmHandleIdToStatusAndDetailsAsMap, ACCEPTED)) {
+            return SUCCESSFULLY_APPLIED_SUBSCRIPTION;
+        }
+        if (allTargetsHaveStatus(cmHandleIdToStatusAndDetailsAsMap, REJECTED)) {
+            return SUBSCRIPTION_NOT_APPLICABLE;
+        }
+        if (allTargetsHaveStatus(cmHandleIdToStatusAndDetailsAsMap, PENDING)) {
+            return SUBSCRIPTION_PENDING;
         }
+        return PARTIALLY_APPLIED_SUBSCRIPTION;
     }
 
-    private boolean isAllTargetCmHandleStatusMatch(
-            final Map<String, Map<String, String>> cmHandleIdToStatusAndDetailsAsMap,
-            final SubscriptionStatus subscriptionStatus) {
+    private boolean allTargetsHaveStatus(final Map<String, Map<String, String>> cmHandleIdToStatusAndDetailsAsMap,
+                                         final SubscriptionStatus subscriptionStatus) {
         return cmHandleIdToStatusAndDetailsAsMap.values().stream()
                 .allMatch(entryset -> entryset.containsValue(subscriptionStatus.toString()));
     }
 
     private CmSubscriptionNcmpOutEvent fromCmSubscriptionEvent(
             final CmSubscriptionEvent cmSubscriptionEvent,
-            final NcmpEventResponseCode ncmpEventResponseCode) {
+            final NcmpResponseStatus ncmpResponseStatus) {
 
         final CmSubscriptionNcmpOutEvent cmSubscriptionNcmpOutEvent =
                 cmSubscriptionEventToCmSubscriptionNcmpOutEventMapper.toCmSubscriptionNcmpOutEvent(
                         cmSubscriptionEvent);
-        cmSubscriptionNcmpOutEvent.getData().setStatusCode(Integer.parseInt(ncmpEventResponseCode.getStatusCode()));
-        cmSubscriptionNcmpOutEvent.getData().setStatusMessage(ncmpEventResponseCode.getStatusMessage());
+        cmSubscriptionNcmpOutEvent.getData().setStatusCode(Integer.parseInt(ncmpResponseStatus.getCode()));
+        cmSubscriptionNcmpOutEvent.getData().setStatusMessage(ncmpResponseStatus.getMessage());
 
         return cmSubscriptionNcmpOutEvent;
     }
index 98ba953..4120970 100644 (file)
@@ -25,7 +25,6 @@ import io.cloudevents.CloudEvent;
 import io.cloudevents.core.CloudEventUtils;
 import io.cloudevents.core.data.PojoCloudEventData;
 import io.cloudevents.jackson.PojoCloudEventDataMapper;
-import io.cloudevents.rw.CloudEventRWException;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -51,9 +50,9 @@ public class CloudEventMapper {
             mappedCloudEvent =
                     CloudEventUtils.mapData(cloudEvent, PojoCloudEventDataMapper.from(objectMapper, targetEventClass));
 
-        } catch (final CloudEventRWException cloudEventRwException) {
+        } catch (final RuntimeException runtimeException) {
             log.error("Unable to map cloud event to target event class type : {} with cause : {}", targetEventClass,
-                    cloudEventRwException.getMessage());
+                    runtimeException.getMessage());
         }
 
         return mappedCloudEvent == null ? null : mappedCloudEvent.getValue();
index 4776788..81467db 100644 (file)
@@ -44,6 +44,14 @@ public interface CmHandleQueries {
      */
     Collection<String> queryCmHandlePublicProperties(Map<String, String> publicPropertyQueryPairs);
 
+    /**
+     * Query CmHandles based on Trust Level.
+     *
+     * @param trustLevelPropertyQueryPairs trust level properties for query
+     * @return CmHandles which have desired trust level
+     */
+    Collection<String> queryCmHandlesByTrustLevel(Map<String, String> trustLevelPropertyQueryPairs);
+
     /**
      * Method which returns cm handles by the cm handles state.
      *
@@ -52,13 +60,22 @@ public interface CmHandleQueries {
      */
     List<DataNode> queryCmHandlesByState(CmHandleState cmHandleState);
 
+    /**
+     * Method to return data nodes with ancestor representing the cm handles.
+     *
+     * @param cpsPath cps path for which the cmHandle is requested
+     * @return a list of data nodes representing the cm handles.
+     */
+    List<DataNode> queryCmHandleAncestorsByCpsPath(String cpsPath,
+                                                   FetchDescendantsOption fetchDescendantsOption);
+
     /**
      * Method to return data nodes representing the cm handles.
      *
      * @param cpsPath cps path for which the cmHandle is requested
      * @return a list of data nodes representing the cm handles.
      */
-    List<DataNode> queryCmHandleDataNodesByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption);
+    List<DataNode> queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption);
 
     /**
      * Method to check the state of a cm handle with given id.
index b3ade4f..e5cf8ed 100644 (file)
@@ -35,6 +35,8 @@ import java.util.Map;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType;
+import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel;
+import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelFilter;
 import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
@@ -45,9 +47,9 @@ import org.springframework.stereotype.Component;
 public class CmHandleQueriesImpl implements CmHandleQueries {
 
     private static final String DESCENDANT_PATH = "//";
-
-    private final CpsDataPersistenceService cpsDataPersistenceService;
     private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles";
+    private final CpsDataPersistenceService cpsDataPersistenceService;
+    private final Map<String, TrustLevel> trustLevelPerCmHandle;
 
     @Override
     public Collection<String> queryCmHandleAdditionalProperties(final Map<String, String> privatePropertyQueryPairs) {
@@ -59,17 +61,32 @@ public class CmHandleQueriesImpl implements CmHandleQueries {
         return queryCmHandleAnyProperties(publicPropertyQueryPairs, PropertyType.PUBLIC);
     }
 
+    @Override
+    public Collection<String> queryCmHandlesByTrustLevel(final Map<String, String> trustLevelPropertyQueryPairs) {
+        final String trustLevelProperty = trustLevelPropertyQueryPairs.values().iterator().next();
+        final TrustLevel targetTrustLevel = TrustLevel.valueOf(trustLevelProperty);
+
+        final TrustLevelFilter trustLevelFilter = new TrustLevelFilter(targetTrustLevel, trustLevelPerCmHandle);
+        return trustLevelFilter.getAllCmHandleIdsByTargetTrustLevel();
+    }
+
     @Override
     public List<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) {
-        return queryCmHandleDataNodesByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]",
+        return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]",
             INCLUDE_ALL_DESCENDANTS);
     }
 
     @Override
-    public List<DataNode> queryCmHandleDataNodesByCpsPath(final String cpsPath,
-            final FetchDescendantsOption fetchDescendantsOption) {
+    public List<DataNode> queryNcmpRegistryByCpsPath(final String cpsPath,
+                                                     final FetchDescendantsOption fetchDescendantsOption) {
         return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption);
+                cpsPath, fetchDescendantsOption);
+    }
+
+    @Override
+    public List<DataNode> queryCmHandleAncestorsByCpsPath(final String cpsPath,
+                                                          final FetchDescendantsOption fetchDescendantsOption) {
+        return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption);
     }
 
     @Override
@@ -81,7 +98,7 @@ public class CmHandleQueriesImpl implements CmHandleQueries {
 
     @Override
     public List<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
-        return queryCmHandleDataNodesByCpsPath("//state/datastores" + "/operational[@sync-state=\""
+        return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\""
                 + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS);
     }
 
@@ -114,7 +131,8 @@ public class CmHandleQueriesImpl implements CmHandleQueries {
                 + publicPropertyQueryPair.getKey()
                 + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]";
 
-            final Collection<DataNode> dataNodes = queryCmHandleDataNodesByCpsPath(cpsPath, OMIT_DESCENDANTS);
+            final Collection<DataNode> dataNodes = queryCmHandleAncestorsByCpsPath(cpsPath,
+                    OMIT_DESCENDANTS);
             if (cmHandleIds == null) {
                 cmHandleIds = collectCmHandleIdsFromDataNodes(dataNodes);
             } else {
index ef4b299..99cca8c 100644 (file)
@@ -100,7 +100,9 @@ public class CompositeStateUtils {
             compositeState.setLastUpdateTimeNow();
             final String oldLockReasonDetails = compositeState.getLockReason().getDetails();
             final CompositeState.LockReason lockReason =
-                    CompositeState.LockReason.builder().details(oldLockReasonDetails).build();
+                    CompositeState.LockReason.builder()
+                            .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory())
+                            .details(oldLockReasonDetails).build();
             compositeState.setLockReason(lockReason);
         };
     }
index 8306619..e2b2c6b 100644 (file)
@@ -21,5 +21,8 @@
 package org.onap.cps.ncmp.api.impl.inventory;
 
 public enum LockReasonCategory {
-    MODULE_SYNC_FAILED, MODULE_UPGRADE, MODULE_UPGRADE_FAILED
+    MODULE_SYNC_FAILED,
+    MODULE_UPGRADE,
+    MODULE_UPGRADE_FAILED,
+    LOCKED_MISBEHAVING
 }
index b2949c2..8e17ab9 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.inventory.sync;
 
+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 java.time.OffsetDateTime;
+import java.util.Arrays;
 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.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.onap.cps.api.CpsAdminService;
+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.inventory.LockReasonCategory;
 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;
 import org.onap.cps.spi.CascadeDeleteAllowed;
+import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
+import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.ModuleReference;
+import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.stereotype.Service;
 
 @Slf4j
@@ -43,16 +61,31 @@ public class ModuleSyncService {
 
     private final DmiModelOperations dmiModelOperations;
     private final CpsModuleService cpsModuleService;
-
     private final CpsAdminService cpsAdminService;
+    private final CmHandleQueries cmHandleQueries;
+    private final CpsDataService cpsDataService;
+    private final JsonObjectMapper jsonObjectMapper;
 
     /**
      * This method registers a cm handle and initiates modules sync.
      *
-     * @param yangModelCmHandle the yang model of cm handle.
+     * @param upgradedCmHandle the yang model of cm handle.
      */
-    public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
+    public void syncAndCreateOrUpgradeSchemaSetAndAnchor(final YangModelCmHandle upgradedCmHandle) {
+
+        final String moduleSetTag = extractModuleSetTag(upgradedCmHandle.getCompositeState());
+        final Optional<DataNode> existingCmHandleWithSameModuleSetTag
+                = getFirstReadyDataNodeWithModuleSetTag(moduleSetTag);
 
+        if (existingCmHandleWithSameModuleSetTag.isPresent()) {
+            upgradeUsingModuleSetTag(upgradedCmHandle, moduleSetTag);
+        } else {
+            syncAndCreateSchemaSetAndAnchor(upgradedCmHandle);
+        }
+        setCmHandleModuleSetTag(upgradedCmHandle, moduleSetTag);
+    }
+
+    private void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
         final Collection<ModuleReference> allModuleReferencesFromCmHandle =
                 dmiModelOperations.getModuleReferences(yangModelCmHandle);
 
@@ -73,8 +106,8 @@ public class ModuleSyncService {
                                           final Map<String, String> newModuleNameToContentMap,
                                           final Collection<ModuleReference> allModuleReferencesFromCmHandle) {
         final String schemaSetAndAnchorName = yangModelCmHandle.getId();
-        cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName,
-                        newModuleNameToContentMap, allModuleReferencesFromCmHandle);
+        cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+                schemaSetAndAnchorName, newModuleNameToContentMap, allModuleReferencesFromCmHandle);
         cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName,
             schemaSetAndAnchorName);
     }
@@ -94,4 +127,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 void upgradeUsingModuleSetTag(final YangModelCmHandle upgradedCmHandle, final String moduleSetTag) {
+        log.info("Found cm handle having module set tag: {}", moduleSetTag);
+        final Collection<ModuleReference> moduleReferencesFromExistingCmHandle =
+                cpsModuleService.getYangResourcesModuleReferences(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR);
+        final String upgradedSchemaSetAndAnchorName = upgradedCmHandle.getId();
+        final Map<String, String> noNewModules = Collections.emptyMap();
+        cpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME,
+                upgradedSchemaSetAndAnchorName, noNewModules, moduleReferencesFromExistingCmHandle);
+    }
+
+    private static String extractModuleSetTag(final CompositeState compositeState) {
+        return compositeState.getLockReason() != null && compositeState.getLockReason().getLockReasonCategory()
+                == LockReasonCategory.MODULE_UPGRADE
+                ? Arrays.stream(compositeState.getLockReason().getDetails().split(":")).toList().get(1).trim()
+                : StringUtils.EMPTY;
+    }
+
 }
index 7306f71..c19dbeb 100644 (file)
@@ -60,7 +60,8 @@ public class ModuleSyncTasks {
     public CompletableFuture<Void> performModuleSync(final Collection<DataNode> cmHandlesAsDataNodes,
                                                      final AtomicInteger batchCounter) {
         try {
-            final Map<YangModelCmHandle, CmHandleState> cmHandelStatePerCmHandle = new HashMap<>();
+            final Map<YangModelCmHandle, CmHandleState> cmHandelStatePerCmHandle
+                    = new HashMap<>(cmHandlesAsDataNodes.size());
             for (final DataNode cmHandleAsDataNode : cmHandlesAsDataNodes) {
                 final String cmHandleId = String.valueOf(cmHandleAsDataNode.getLeaves().get("id"));
                 final YangModelCmHandle yangModelCmHandle =
@@ -68,16 +69,18 @@ public class ModuleSyncTasks {
                 final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
                 try {
                     moduleSyncService.deleteSchemaSetIfExists(cmHandleId);
-                    moduleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle);
+                    moduleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle);
+                    yangModelCmHandle.getCompositeState().setLockReason(null);
                     cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY);
                 } catch (final Exception e) {
-                    log.warn("Processing of {} module sync failed due to reason {}.", cmHandleId, e.getMessage());
+                    log.warn("Processing of {} module sync failed due to reason {}.",
+                            cmHandleId, e.getMessage());
                     syncUtils.updateLockReasonDetailsAndAttempts(compositeState,
                             LockReasonCategory.MODULE_SYNC_FAILED, e.getMessage());
                     setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason());
                     cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED);
                 }
-                log.info("{} is now in {} state", cmHandleId, compositeState.getCmHandleState().name());
+                log.info("{} is now in {} state", cmHandleId, cmHandelStatePerCmHandle.get(yangModelCmHandle).name());
             }
             lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandelStatePerCmHandle);
         } finally {
@@ -96,7 +99,7 @@ public class ModuleSyncTasks {
         final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(failedCmHandles.size());
         for (final YangModelCmHandle failedCmHandle : failedCmHandles) {
             final CompositeState compositeState = failedCmHandle.getCompositeState();
-            final boolean isReadyForRetry = syncUtils.needsModuleSyncRetry(compositeState);
+            final boolean isReadyForRetry = syncUtils.needsModuleSyncRetryOrUpgrade(compositeState);
             log.info("Retry for cmHandleId : {} is {}", failedCmHandle.getId(), isReadyForRetry);
             if (isReadyForRetry) {
                 final String resetCmHandleId = failedCmHandle.getId();
index 6ba52ee..75781eb 100644 (file)
@@ -90,7 +90,7 @@ public class ModuleSyncWatchdog {
     @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:300000}")
     public void resetPreviouslyFailedCmHandles() {
         log.info("Processing module sync retry-watchdog waking up.");
-        final List<YangModelCmHandle> failedCmHandles = syncUtils.getModuleSyncFailedCmHandles();
+        final List<YangModelCmHandle> failedCmHandles = syncUtils.getCmHandlesThatFailedModelSyncOrUpgrade();
         log.info("Retrying {} cmHandles", failedCmHandles.size());
         moduleSyncTasks.resetFailedCmHandles(failedCmHandles);
     }
index c50bd42..ab85c21 100644 (file)
@@ -35,7 +35,6 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries;
@@ -100,13 +99,14 @@ public class SyncUtils {
     }
 
     /**
-     * Query data nodes for cm handles with an "LOCKED" cm handle state with reason MODULE_SYNC_FAILED".
+     * Query data nodes for cm handles with an "LOCKED" cm handle state with reason.
      *
      * @return a random LOCKED yang model cm handle, return null if not found
      */
-    public List<YangModelCmHandle> getModuleSyncFailedCmHandles() {
-        final List<DataNode> lockedCmHandlesAsDataNodeList = cmHandleQueries.queryCmHandleDataNodesByCpsPath(
-                "//lock-reason[@reason=\"MODULE_SYNC_FAILED\"]",
+    public List<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() {
+        final List<DataNode> lockedCmHandlesAsDataNodeList
+                = cmHandleQueries.queryCmHandleAncestorsByCpsPath(
+                "//lock-reason[@reason=\"MODULE_SYNC_FAILED\" or @reason=\"MODULE_UPGRADE\"]",
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList);
     }
@@ -139,32 +139,39 @@ public class SyncUtils {
      * @param compositeState the composite state currently in the locked state
      * @return if the retry mechanism should be attempted
      */
-    public boolean needsModuleSyncRetry(final CompositeState compositeState) {
-        final OffsetDateTime time =
-                OffsetDateTime.parse(compositeState.getLastUpdateTime(),
-                        DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
-        final Matcher matcher = retryAttemptPattern.matcher(compositeState.getLockReason().getDetails());
+    public boolean needsModuleSyncRetryOrUpgrade(final CompositeState compositeState) {
+        final OffsetDateTime time = OffsetDateTime.parse(compositeState.getLastUpdateTime(),
+                DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
+        final CompositeState.LockReason lockReason = compositeState.getLockReason();
+
         final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED
-                == compositeState.getLockReason().getLockReasonCategory();
-        if (!failedDuringModuleSync) {
-            log.info("Locked for other reason");
-            return false;
-        }
-        final int timeInMinutesUntilNextAttempt;
-        if (matcher.find()) {
-            timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(matcher.group(1)));
-        } else {
-            timeInMinutesUntilNextAttempt = 1;
-            log.info("First Attempt: no current attempts found.");
-        }
-        final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes();
-        if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) {
-            log.info("Time until next attempt is {} minutes: ",
-                timeInMinutesUntilNextAttempt - timeSinceLastAttempt);
-            return false;
+                == lockReason.getLockReasonCategory();
+        final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE
+                == lockReason.getLockReasonCategory();
+
+        if (failedDuringModuleSync) {
+            final int timeInMinutesUntilNextAttempt;
+            final Matcher matcher = retryAttemptPattern.matcher(lockReason.getDetails());
+            if (matcher.find()) {
+                timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(matcher.group(1)));
+            } else {
+                timeInMinutesUntilNextAttempt = 1;
+                log.info("First Attempt: no current attempts found.");
+            }
+            final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes();
+            if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) {
+                log.info("Time until next attempt is {} minutes: ",
+                        timeInMinutesUntilNextAttempt - timeSinceLastAttempt);
+                return false;
+            }
+            log.info("Retry due now");
+            return true;
+        } else if (moduleUpgrade) {
+            log.info("Locked for module upgrade.");
+            return true;
         }
-        log.info("Retry due now");
-        return true;
+        log.info("Locked for other reason");
+        return false;
     }
 
     /**
@@ -196,6 +203,6 @@ public class SyncUtils {
             final List<DataNode> cmHandlesAsDataNodeList) {
         return cmHandlesAsDataNodeList.stream()
                 .map(cmHandle -> YangDataConverter.convertCmHandleToYangModel(cmHandle,
-                        cmHandle.getLeaves().get("id").toString())).collect(Collectors.toList());
+                        cmHandle.getLeaves().get("id").toString())).toList();
     }
 }
index 8f76a45..b8edecc 100644 (file)
@@ -21,6 +21,8 @@
 
 package org.onap.cps.ncmp.api.impl.operations;
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA;
 import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING;
 import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ;
 
@@ -30,7 +32,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NcmpEventResponseCode;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
 import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
 import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
@@ -230,9 +232,7 @@ public class DmiDataOperations extends DmiOperations {
                                                                  final Map<String, List<DmiDataOperation>>
                                                                 groupsOutPerDmiServiceName) {
 
-        groupsOutPerDmiServiceName.entrySet().forEach(groupsOutPerDmiServiceNameEntry -> {
-            final String dmiServiceName = groupsOutPerDmiServiceNameEntry.getKey();
-            final List<DmiDataOperation> dmiDataOperationRequestBodies = groupsOutPerDmiServiceNameEntry.getValue();
+        groupsOutPerDmiServiceName.forEach((dmiServiceName, dmiDataOperationRequestBodies) -> {
             final String dmiDataOperationResourceUrl =
                     getDmiServiceDataOperationRequestUrl(dmiServiceName, topicParamInQuery, requestId);
             sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies);
@@ -261,18 +261,18 @@ public class DmiDataOperations extends DmiOperations {
             final String topicName = dataOperationResourceUrlParameters.get("topic").get(0);
             final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0);
 
-            final MultiValueMap<DmiDataOperation, Map<NcmpEventResponseCode, List<String>>>
+            final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>>
                     cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
 
             dmiDataOperationRequestBodies.forEach(dmiDataOperationRequestBody -> {
                 final List<String> cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream()
-                        .map(CmHandle::getId).collect(Collectors.toList());
+                        .map(CmHandle::getId).toList();
                 if (throwable.getCause() instanceof HttpClientRequestException) {
                     cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
-                            Map.of(NcmpEventResponseCode.UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds));
+                            Map.of(UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds));
                 } else {
                     cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
-                            Map.of(NcmpEventResponseCode.DMI_SERVICE_NOT_RESPONDING, cmHandleIds));
+                            Map.of(DMI_SERVICE_NOT_RESPONDING, cmHandleIds));
                 }
             });
             ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId,
index 458c1b8..b6d74d9 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.trustlevel;
 
-import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
-
-import com.hazelcast.collection.ISet;
 import io.cloudevents.CloudEvent;
 import io.cloudevents.kafka.impl.KafkaHeaders;
+import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper;
+import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel;
 import org.springframework.kafka.annotation.KafkaListener;
 import org.springframework.stereotype.Component;
 
@@ -36,7 +36,8 @@ import org.springframework.stereotype.Component;
 @RequiredArgsConstructor
 public class DeviceHeartbeatConsumer {
 
-    private final ISet<String> untrustworthyCmHandlesSet;
+    private static final String CLOUD_EVENT_ID_HEADER_NAME = "ce_id";
+    private final Map<String, TrustLevel> trustLevelPerCmHandle;
 
     /**
      * Listening the device heartbeats.
@@ -47,23 +48,16 @@ public class DeviceHeartbeatConsumer {
             containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
     public void heartbeatListener(final ConsumerRecord<String, CloudEvent> deviceHeartbeatConsumerRecord) {
 
-        final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), "ce_id");
+        final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(),
+                CLOUD_EVENT_ID_HEADER_NAME);
 
         final DeviceTrustLevel deviceTrustLevel =
-                toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class);
-
-        if (deviceTrustLevel == null || deviceTrustLevel.getTrustLevel() == null) {
-            log.warn("No or Invalid trust level defined");
-            return;
-        }
+                CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class);
 
-        if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.NONE)) {
-            untrustworthyCmHandlesSet.add(cmHandleId);
-            log.debug("Added cmHandleId to untrustworthy set : {}", cmHandleId);
-        } else if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.COMPLETE) && untrustworthyCmHandlesSet.contains(
-                cmHandleId)) {
-            untrustworthyCmHandlesSet.remove(cmHandleId);
-            log.debug("Removed cmHandleId from untrustworthy set : {}", cmHandleId);
+        if (cmHandleId != null && deviceTrustLevel != null) {
+            final String trustLevel = deviceTrustLevel.getData().getTrustLevel();
+            trustLevelPerCmHandle.put(cmHandleId, TrustLevel.valueOf(trustLevel));
+            log.debug("Added cmHandleId to trustLevelPerCmHandle map as {}:{}", cmHandleId, trustLevel);
         }
     }
 
index f4254bb..8d1f8e9 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.trustlevel;
 
+import lombok.Getter;
+
+@Getter
 public enum TrustLevel {
-    NONE, COMPLETE;
+    NONE(0), COMPLETE(99);
+
+    private final int value;
+
+    TrustLevel(final int value) {
+        this.value = value;
+    }
+
 }
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java
new file mode 100644 (file)
index 0000000..3b704ae
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  ============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.trustlevel;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+public class TrustLevelFilter implements Comparable<TrustLevel> {
+
+    @EqualsAndHashCode.Include
+    private final TrustLevel targetTrustLevel;
+    private final Map<String, TrustLevel> trustLevelPerCmHandle;
+
+    @Override
+    public int compareTo(@NonNull final TrustLevel other) {
+        return Integer.compare(this.targetTrustLevel.getValue(), other.getValue());
+    }
+
+    /**
+     * This method return cm handles that matches with given trust level.
+     *
+     * @return cm handle ids.
+     */
+    public Collection<String> getAllCmHandleIdsByTargetTrustLevel() {
+        final Collection<String> resultCmHandleIds = new HashSet<>();
+        trustLevelPerCmHandle.entrySet().forEach(cmHandleTrustLevelEntrySet -> {
+            if (compareTo(cmHandleTrustLevelEntrySet.getValue()) == 0) {
+                resultCmHandleIds.add(cmHandleTrustLevelEntrySet.getKey());
+            }
+        });
+        return resultCmHandleIds;
+    }
+
+}
index dac32aa..39f8802 100644 (file)
@@ -20,7 +20,7 @@
 
 package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability;
 
-import com.hazelcast.map.IMap;
+import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
@@ -33,7 +33,7 @@ import org.springframework.stereotype.Service;
 @Service
 public class DMiPluginWatchDog {
 
-    private final IMap<String, TrustLevel> trustLevelPerDmiPlugin;
+    private final Map<String, TrustLevel> trustLevelPerDmiPlugin;
 
     private final DmiRestClient dmiRestClient;
 
@@ -46,7 +46,7 @@ public class DMiPluginWatchDog {
      */
     @Scheduled(fixedDelayString = "${ncmp.timers.trust-evel.dmi-availability-watchdog-ms:30000}")
     public void watchDmiPluginAliveness() {
-        trustLevelPerDmiPlugin.keySet().forEach((dmiPluginName) -> {
+        trustLevelPerDmiPlugin.keySet().forEach(dmiPluginName -> {
             final DmiPluginStatus dmiPluginStatus = dmiRestClient.getDmiPluginStatus(dmiPluginName);
             log.debug("Trust level for dmi-plugin: {} is {}", dmiPluginName, dmiPluginStatus.toString());
             if (DmiPluginStatus.UP.equals(dmiPluginStatus)) {
index b1bb7f7..a597760 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-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.
@@ -29,7 +29,8 @@ import lombok.Getter;
 public enum CmHandleQueryConditions {
     HAS_ALL_PROPERTIES("hasAllProperties"),
     HAS_ALL_MODULES("hasAllModules"),
-    WITH_CPS_PATH("cmHandleWithCpsPath");
+    WITH_CPS_PATH("cmHandleWithCpsPath"),
+    WITH_TRUST_LEVEL("cmHandleWithTrustLevel");
 
     public static final Collection<String> ALL_CONDITION_NAMES = Arrays.stream(CmHandleQueryConditions.values())
         .map(CmHandleQueryConditions::getConditionName).collect(Collectors.toList());
index 1b19075..b6a04d3 100644 (file)
@@ -86,7 +86,8 @@ public class YangDataConverter {
                 (String) cmHandleDataNode.getLeaves().get("dmi-service-name"),
                 (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
                 (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"),
-                ncmpServiceCmHandle
+                ncmpServiceCmHandle,
+                (String) cmHandleDataNode.getLeaves().get("module-set-tag")
         );
     }
 
@@ -105,7 +106,12 @@ public class YangDataConverter {
         return yangModelCmHandles;
     }
 
-    private static String extractCmHandleIdFromXpath(final String xpath) {
+    /**
+     * This method extract cm handle id from xpath of data node.
+     * @param xpath for data node of the cm handle
+     * @return cm handle Id
+     */
+    public static String extractCmHandleIdFromXpath(final String xpath) {
         final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath);
         matcher.find();
         return matcher.group(1);
index 65cda94..61da706 100644 (file)
@@ -28,7 +28,7 @@ import java.util.Map;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NcmpEventResponseCode;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
 import org.onap.cps.ncmp.api.impl.events.NcmpCloudEventBuilder;
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation;
 import org.onap.cps.ncmp.events.async1_0_0.Data;
@@ -51,7 +51,7 @@ public class DataOperationEventCreator {
     public static CloudEvent createDataOperationEvent(final String clientTopic,
                                                       final String requestId,
                                                       final MultiValueMap<DmiDataOperation,
-                                                              Map<NcmpEventResponseCode, List<String>>>
+                                                              Map<NcmpResponseStatus, List<String>>>
                                                               cmHandleIdsPerResponseCodesPerOperation) {
         final DataOperationEvent dataOperationEvent = new DataOperationEvent();
         final Data data = createPayloadFromDataOperationResponses(cmHandleIdsPerResponseCodesPerOperation);
@@ -62,7 +62,7 @@ public class DataOperationEventCreator {
     }
 
     private static Data createPayloadFromDataOperationResponses(final MultiValueMap<DmiDataOperation,
-            Map<NcmpEventResponseCode, List<String>>> cmHandleIdsPerResponseCodesPerOperation) {
+            Map<NcmpResponseStatus, List<String>>> cmHandleIdsPerResponseCodesPerOperation) {
         final Data data = new Data();
         final List<org.onap.cps.ncmp.events.async1_0_0.Response> responses = new ArrayList<>();
         cmHandleIdsPerResponseCodesPerOperation.forEach((dmiDataOperation, cmHandleIdsPerResponseCodes) ->
@@ -75,13 +75,13 @@ public class DataOperationEventCreator {
 
     private static List<Response> createResponseFromDataOperationResponses(
             final DmiDataOperation dmiDataOperation,
-            final Map<NcmpEventResponseCode, List<String>> cmHandleIdsPerResponseCodeEntries) {
+            final Map<NcmpResponseStatus, List<String>> cmHandleIdsPerResponseCodeEntries) {
         final List<org.onap.cps.ncmp.events.async1_0_0.Response> responses = new ArrayList<>();
         cmHandleIdsPerResponseCodeEntries.forEach((ncmpEventResponseCode, cmHandleIds) -> {
             final Response response = new Response();
             response.setOperationId(dmiDataOperation.getOperationId());
-            response.setStatusCode(ncmpEventResponseCode.getStatusCode());
-            response.setStatusMessage(ncmpEventResponseCode.getStatusMessage());
+            response.setStatusCode(ncmpEventResponseCode.getCode());
+            response.setStatusMessage(ncmpEventResponseCode.getMessage());
             response.setIds(cmHandleIds);
             response.setResourceIdentifier(dmiDataOperation.getResourceIdentifier());
             response.setOptions(dmiDataOperation.getOptions());
index e95d4f4..e78f090 100644 (file)
@@ -20,6 +20,9 @@
 
 package org.onap.cps.ncmp.api.impl.utils.data.operation;
 
+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 io.cloudevents.CloudEvent;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -32,7 +35,7 @@ import java.util.stream.Collectors;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NcmpEventResponseCode;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
 import org.onap.cps.ncmp.api.impl.operations.CmHandle;
@@ -68,7 +71,7 @@ public class ResourceDataOperationRequestUtils {
             final Collection<YangModelCmHandle> yangModelCmHandles) {
 
         final Map<String, List<DmiDataOperation>> dmiDataOperationsOutPerDmiServiceName = new HashMap<>();
-        final MultiValueMap<DmiDataOperation, Map<NcmpEventResponseCode,
+        final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus,
                 List<String>>> cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
         final Set<String> nonReadyCmHandleIdsLookup = filterAndGetNonReadyCmHandleIds(yangModelCmHandles);
 
@@ -102,10 +105,10 @@ public class ResourceDataOperationRequestUtils {
             }
             populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
                     DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
-                    NcmpEventResponseCode.CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
+                    CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
             populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
                     DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
-                    NcmpEventResponseCode.CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
+                    CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
         }
         if (!cmHandleIdsPerResponseCodesPerOperation.isEmpty()) {
             publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
@@ -124,7 +127,7 @@ public class ResourceDataOperationRequestUtils {
     public static void publishErrorMessageToClientTopic(final String clientTopic,
                                                          final String requestId,
                                                          final MultiValueMap<DmiDataOperation,
-                                                                 Map<NcmpEventResponseCode, List<String>>>
+                                                                 Map<NcmpResponseStatus, List<String>>>
                                                                     cmHandleIdsPerResponseCodesPerOperation) {
         final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic,
                 requestId, cmHandleIdsPerResponseCodesPerOperation);
@@ -175,13 +178,13 @@ public class ResourceDataOperationRequestUtils {
     }
 
     private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap<DmiDataOperation,
-            Map<NcmpEventResponseCode, List<String>>> cmHandleIdsPerResponseCodesPerOperation,
+            Map<NcmpResponseStatus, List<String>>> cmHandleIdsPerResponseCodesPerOperation,
                                                                         final DmiDataOperation dmiDataOperation,
-                                                                        final NcmpEventResponseCode
-                                                                                ncmpEventResponseCode,
+                                                                        final NcmpResponseStatus
+                                                                                 ncmpResponseStatus,
                                                                         final List<String> cmHandleIds) {
         if (!cmHandleIds.isEmpty()) {
-            cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpEventResponseCode, cmHandleIds));
+            cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(ncmpResponseStatus, cmHandleIds));
         }
     }
 }
index 52fc81f..d148f37 100644 (file)
@@ -64,6 +64,9 @@ public class YangModelCmHandle {
     @JsonProperty("dmi-model-service-name")
     private String dmiModelServiceName;
 
+    @JsonProperty("module-set-tag")
+    private String moduleSetTag;
+
     @JsonProperty("additional-properties")
     private List<Property> dmiProperties;
 
@@ -102,12 +105,14 @@ public class YangModelCmHandle {
     public static YangModelCmHandle toYangModelCmHandle(final String dmiServiceName,
                                                         final String dmiDataServiceName,
                                                         final String dmiModelServiceName,
-                                                        final NcmpServiceCmHandle ncmpServiceCmHandle) {
+                                                        final NcmpServiceCmHandle ncmpServiceCmHandle,
+                                                        final String moduleSetTag) {
         final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle();
         yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleId());
         yangModelCmHandle.setDmiServiceName(dmiServiceName);
         yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName);
         yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName);
+        yangModelCmHandle.setModuleSetTag(moduleSetTag);
         yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
         yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
                 ncmpServiceCmHandle.getPublicProperties()));
index d5b27b6..e007491 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
- *  Modifications Copyright (C) 2022 Nordix Foundation
+ *  Modifications Copyright (C) 2022-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.
 
 package org.onap.cps.ncmp.api.models;
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 import lombok.Builder;
 import lombok.Data;
-import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 
 @Data
 @Builder
@@ -39,11 +39,9 @@ public class CmHandleRegistrationResponse {
 
     private final String cmHandle;
     private final Status status;
-    private RegistrationError registrationError;
+    private NcmpResponseStatus ncmpResponseStatus;
     private String errorText;
 
-    private static final Pattern cmHandleIdInXpathPattern = Pattern.compile("\\[@id='(.*?)']");
-
     /**
      * Creates a failure response based on exception.
      *
@@ -56,7 +54,7 @@ public class CmHandleRegistrationResponse {
         return CmHandleRegistrationResponse.builder()
             .cmHandle(cmHandleId)
             .status(Status.FAILURE)
-            .registrationError(RegistrationError.UNKNOWN_ERROR)
+            .ncmpResponseStatus(UNKNOWN_ERROR)
             .errorText(exception.getMessage()).build();
     }
 
@@ -64,15 +62,15 @@ public class CmHandleRegistrationResponse {
      * Creates a failure response based on registration error.
      *
      * @param cmHandleId          cmHandleId
-     * @param registrationError registrationError
+     * @param ncmpResponseStatus registration error code and status
      * @return CmHandleRegistrationResponse
      */
     public static CmHandleRegistrationResponse createFailureResponse(final String cmHandleId,
-        final RegistrationError registrationError) {
+        final NcmpResponseStatus ncmpResponseStatus) {
         return CmHandleRegistrationResponse.builder().cmHandle(cmHandleId)
             .status(Status.FAILURE)
-            .registrationError(registrationError)
-            .errorText(registrationError.errorText)
+            .ncmpResponseStatus(ncmpResponseStatus)
+            .errorText(ncmpResponseStatus.getMessage())
             .build();
     }
 
@@ -80,18 +78,18 @@ public class CmHandleRegistrationResponse {
      * Creates a failure response based on registration error.
      *
      * @param failedXpaths       list of failed Xpaths
-     * @param registrationError enum describing the type of registration error
+     * @param ncmpResponseStatus enum describing the type of registration error
      * @return CmHandleRegistrationResponse
      */
     public static List<CmHandleRegistrationResponse> createFailureResponses(final Collection<String> failedXpaths,
-            final RegistrationError registrationError) {
+            final NcmpResponseStatus ncmpResponseStatus) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size());
         for (final String xpath : failedXpaths) {
-            final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath);
-            if (matcher.find()) {
+            try {
+                final String cmHandleId = YangDataConverter.extractCmHandleIdFromXpath(xpath);
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(matcher.group(1), registrationError));
-            } else {
+                        CmHandleRegistrationResponse.createFailureResponse(cmHandleId, ncmpResponseStatus));
+            } catch (IllegalArgumentException | IllegalStateException e) {
                 log.warn("Unexpected xpath {}", xpath);
             }
         }
@@ -109,7 +107,7 @@ public class CmHandleRegistrationResponse {
             final Exception exception) {
         return cmHandleIds.stream()
                 .map(cmHandleId -> CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception))
-                .collect(Collectors.toList());
+                .toList();
     }
 
     public static CmHandleRegistrationResponse createSuccessResponse(final String cmHandle) {
@@ -118,23 +116,10 @@ public class CmHandleRegistrationResponse {
     }
 
     public static List<CmHandleRegistrationResponse> createSuccessResponses(final List<String> cmHandleIds) {
-        return cmHandleIds.stream().map(CmHandleRegistrationResponse::createSuccessResponse)
-                .collect(Collectors.toList());
+        return cmHandleIds.stream().map(CmHandleRegistrationResponse::createSuccessResponse).toList();
     }
 
     public enum Status {
         SUCCESS, FAILURE;
     }
-
-    @RequiredArgsConstructor
-    public enum RegistrationError {
-        UNKNOWN_ERROR("00", "Unknown error"),
-        CM_HANDLE_ALREADY_EXIST("01", "cm-handle already exists"),
-        CM_HANDLE_DOES_NOT_EXIST("02", "cm-handle does not exist"),
-        CM_HANDLE_INVALID_ID("03", "cm-handle has an invalid character(s) in id");
-
-        public final String errorCode;
-        public final String errorText;
-
-    }
 }
index 953b3c4..4615af6 100644 (file)
@@ -50,6 +50,8 @@ public class DmiPluginRegistration {
 
     private List<String> removedCmHandles = Collections.emptyList();
 
+    private UpgradedCmHandles upgradedCmHandles;
+
     /**
      * Validates plugin service names.
      * @throws NcmpException if validation fails.
index 8a3d264..ee03417 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
+ *  Modifications 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.
@@ -31,4 +32,5 @@ public class DmiPluginRegistrationResponse {
     private List<CmHandleRegistrationResponse> createdCmHandles = Collections.emptyList();
     private List<CmHandleRegistrationResponse> updatedCmHandles = Collections.emptyList();
     private List<CmHandleRegistrationResponse> removedCmHandles = Collections.emptyList();
+    private List<CmHandleRegistrationResponse> upgradedCmHandles = Collections.emptyList();
 }
\ No newline at end of file
index c46a8c2..0b50346 100644 (file)
@@ -52,6 +52,9 @@ public class NcmpServiceCmHandle {
     @JsonSetter(nulls = Nulls.AS_EMPTY)
     private CompositeState compositeState;
 
+    @JsonSetter(nulls = Nulls.AS_EMPTY)
+    private String moduleSetTag;
+
     /**
      * NcmpServiceCmHandle copy constructor.
      *
@@ -63,5 +66,6 @@ public class NcmpServiceCmHandle {
         this.publicProperties = new LinkedHashMap<>(ncmpServiceCmHandle.getPublicProperties());
         this.compositeState = ncmpServiceCmHandle.getCompositeState() != null ? new CompositeState(
                 ncmpServiceCmHandle.getCompositeState()) : null;
+        this.moduleSetTag = ncmpServiceCmHandle.getModuleSetTag();
     }
 }
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.ncmp.api.impl.trustlevel;
+package org.onap.cps.ncmp.api.models;
 
-import java.io.Serializable;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@AllArgsConstructor
-@Data
-@NoArgsConstructor
-class DeviceTrustLevel implements Serializable {
-
-    private static final long serialVersionUID = -1705715024067165212L;
-
-    private TrustLevel trustLevel;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Collections;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 
+@Getter
+@Setter
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class UpgradedCmHandles {
+    private List<String> cmHandles = Collections.emptyList();
+    private String moduleSetTag;
 }
+
index c2e2b91..7c410cc 100644 (file)
@@ -39,7 +39,7 @@ import spock.lang.Specification
 class NetworkCmProxyCmHandleQueryServiceSpec extends Specification {
 
     def cmHandleQueries = Mock(CmHandleQueries)
-    def partiallyMockedCmHandleQueries = Spy(CmHandleQueriesImpl)
+    def partiallyMockedCmHandleQueries = Spy(CmHandleQueries)
     def mockInventoryPersistence = Mock(InventoryPersistence)
 
     def dmiRegistry = new DataNode(xpath: NCMP_DMI_REGISTRY_PARENT, childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4']))
@@ -53,7 +53,7 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification {
             def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
             cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
         and: 'the query get the cm handle datanodes excluding all descendants returns a datanode'
-            cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])]
+            cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['id':'some-cmhandle-id'])]
         when: 'the query is executed for cm handle ids'
             def result = objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
         then: 'the correct expected cm handles ids are returned'
@@ -66,7 +66,7 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification {
             def conditionProperties = createConditionProperties('cmHandleWithCpsPath', [['cpsPath' : '/some/cps/path']])
             cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
         and: 'cmHandleQueries throws a path parsing exception'
-            cmHandleQueries.queryCmHandleDataNodesByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException }
+            cmHandleQueries.queryCmHandleAncestorsByCpsPath('/some/cps/path', FetchDescendantsOption.OMIT_DESCENDANTS) >> { throw thrownException }
         when: 'the query is executed for cm handle ids'
             objectUnderTest.queryCmHandleIds(cmHandleQueryParameters)
         then: 'a data validation exception is thrown'
@@ -106,6 +106,17 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification {
             'No anchors are returned' | []
     }
 
+    def 'Query cm handles with some trust level query parameters'() {
+        given: 'a trust level condition property'
+            def trustLevelQueryParameters = new CmHandleQueryServiceParameters()
+            def trustLevelConditionProperties = createConditionProperties('cmHandleWithTrustLevel', [['trustLevel': 'COMPLETE'] as Map])
+            trustLevelQueryParameters.setCmHandleQueryParameters([trustLevelConditionProperties])
+        when: 'the query is being executed'
+            objectUnderTest.queryCmHandleIds(trustLevelQueryParameters)
+        then: 'the query is being delegated to the cm handle query service with correct parameter'
+            1 * cmHandleQueries.queryCmHandlesByTrustLevel(['trustLevel': 'COMPLETE'] as Map)
+    }
+
     def 'Query cm handle details with module names when #scenario from query.'() {
         given: 'a modules condition property'
             def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
index 46666b9..9e4737f 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
+import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
+
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.hazelcast.map.IMap
 import org.onap.cps.api.CpsDataService
@@ -45,12 +51,6 @@ import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Shared
 import spock.lang.Specification
 
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_ALREADY_EXIST
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_INVALID_ID
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
-
 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     @Shared
@@ -80,11 +80,11 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         when: 'registration is processed'
             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
         then: 'cm-handles are removed first'
-            1 * objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_)
+            1 * objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_)
         and: 'de-registered cm handle entry is removed from in progress map'
             1 * mockModuleSyncStartedOnCmHandles.remove('cmhandle-2')
         then: 'cm-handles are created'
-            1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_)
+            1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_)
         then: 'cm-handles are updated'
             1 * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_)
     }
@@ -100,10 +100,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(*_) >> updateResponses
         and: 'create cm-handles can be processed successfully'
             def createdResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-1')]
-            objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(*_) >> createdResponses
+            objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(*_) >> createdResponses
         and: 'delete cm-handles can be processed successfully'
             def removeResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-3')]
-            objectUnderTest.parseAndRemoveCmHandlesInDmiRegistration(*_) >> removeResponses
+            objectUnderTest.parseAndProcessDeletedCmHandlesInRegistration(*_) >> removeResponses
         when: 'registration is processed'
             def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
         then: 'response has values from all operations'
@@ -120,7 +120,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.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
+            1 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)
         and: 'dmi is added to the trustLevel map'
             1 * mockTrustLevelPerDmiPlugin.put(dmiPluginRegisteredName, TrustLevel.COMPLETE)
         where:
@@ -141,7 +141,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             def exceptionThrown = thrown(DmiRequestException.class)
             assert exceptionThrown.getMessage().contains(expectedMessageDetails)
         and: 'registration is not called'
-            0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
+            0 * objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(dmiPluginRegistration)
         where:
             scenario                         | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
             'empty DMI plugins'              | ''         | ''             | ''            || 'No DMI plugin service names'
@@ -203,7 +203,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             response.createdCmHandles.each {
                 assert it.cmHandle == 'cmhandle2'
                 assert it.status == Status.FAILURE
-                assert it.registrationError == CM_HANDLE_ALREADY_EXIST
+                assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST
                 assert it.errorText == 'cm-handle already exists'
             }
     }
@@ -221,7 +221,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             with(response.createdCmHandles[0]) {
                 assert it.status == Status.FAILURE
                 assert it.cmHandle ==  'cmhandle'
-                assert it.registrationError == expectedError
+                assert it.ncmpResponseStatus == expectedError
                 assert it.errorText == expectedErrorText
             }
         where:
@@ -236,7 +236,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         and: 'cm-handle updates can be processed successfully'
             def updateOperationResponse = [CmHandleRegistrationResponse.createSuccessResponse('cm-handle-1'),
                                            CmHandleRegistrationResponse.createFailureResponse('cm-handle-2', new Exception("Failed")),
-                                           CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLE_DOES_NOT_EXIST),
+                                           CmHandleRegistrationResponse.createFailureResponse('cm-handle-3', CM_HANDLES_NOT_FOUND),
                                            CmHandleRegistrationResponse.createFailureResponse('cm handle 4', CM_HANDLE_INVALID_ID)]
             mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(_) >> updateOperationResponse
         when: 'registration is updated'
@@ -310,7 +310,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
         and: '2nd cm-handle deletion fails'
             with(response.removedCmHandles[1]) {
                 assert it.status == Status.FAILURE
-                assert it.registrationError == UNKNOWN_ERROR
+                assert it.ncmpResponseStatus == UNKNOWN_ERROR
                 assert it.errorText == 'Failed'
                 assert it.cmHandle == 'cmhandle2'
             }
@@ -344,7 +344,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.status == Status.FAILURE
                 assert it.cmHandle == 'cmhandle'
                 assert it.errorText == 'Failed'
-                assert it.registrationError == UNKNOWN_ERROR
+                assert it.ncmpResponseStatus == UNKNOWN_ERROR
             }
     }
 
@@ -364,16 +364,16 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             with(response.removedCmHandles[0]) {
                 assert it.status == Status.FAILURE
                 assert it.cmHandle == 'cmhandle'
-                assert it.registrationError == expectedError
+                assert it.ncmpResponseStatus == expectedError
                 assert it.errorText == expectedErrorText
             }
         and: 'the cm handle state is not updated to "DELETED"'
             0 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_, CmHandleState.DELETED)
         where:
-            scenario                     | cmHandleId             | deleteListElementException                ||  expectedError           | expectedErrorText
-            'cm-handle does not exist'   | 'cmhandle'             | new DataNodeNotFoundException('', '', '') || CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist'
-            'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '')       || CM_HANDLE_INVALID_ID     | 'cm-handle has an invalid character(s) in id'
-            'an unexpected exception'    | 'cmhandle'             | new RuntimeException('Failed')            || UNKNOWN_ERROR            | 'Failed'
+        scenario                     | cmHandleId             | deleteListElementException                || expectedError        | expectedErrorText
+        'cm-handle does not exist'   | 'cmhandle'             | new DataNodeNotFoundException('', '', '') || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found'
+        'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException('', '')       || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
+        'an unexpected exception'    | 'cmhandle'             | new RuntimeException('Failed')            || UNKNOWN_ERROR        | 'Failed'
     }
 
     def getObjectUnderTest() {
index 01a0600..af65cfc 100644 (file)
@@ -76,7 +76,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService)
     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
     def stubModuleSyncStartedOnCmHandles = Stub(IMap<String, Object>)
-    def stubTrustLevelPerDmiPlugin = Stub(IMap<String, TrustLevel>)
+    def stubTrustLevelPerDmiPlugin = Stub(Map<String, TrustLevel>)
 
     def NO_TOPIC = null
     def NO_REQUEST_ID = null
@@ -261,7 +261,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
             mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
         when: 'parse and create cm handle in dmi registration then sync module'
-            objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(mockDmiPluginRegistration)
+            objectUnderTest.parseAndProcessCreatedCmHandlesInRegistration(mockDmiPluginRegistration)
         then: 'system persists the cm handle state'
             1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> {
                 args -> {
index 610400f..6439f0b 100644 (file)
@@ -24,13 +24,13 @@ package org.onap.cps.ncmp.api.impl
 
 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.ncmp.api.impl.inventory.InventoryPersistence
 import org.onap.cps.spi.exceptions.DataValidationException
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_DOES_NOT_EXIST
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.CM_HANDLE_INVALID_ID
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError.UNKNOWN_ERROR
-import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.model.DataNode
@@ -131,14 +131,14 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
             with(response.get(0)) {
                 assert it.status == Status.FAILURE
                 assert it.cmHandle == cmHandleId
-                assert it.registrationError == expectedError
+                assert it.ncmpResponseStatus == expectedError
                 assert it.errorText == expectedErrorText
             }
         where:
-            scenario                   | cmHandleId               | exception                                                                                           || expectedError            | expectedErrorText
-            'Cm Handle does not exist' | 'cmHandleId'             | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR)                                    || CM_HANDLE_DOES_NOT_EXIST | 'cm-handle does not exist'
-            'Unknown'                  | 'cmHandleId'             | new RuntimeException('Failed')                                                                      || UNKNOWN_ERROR            | 'Failed'
-            'Invalid cm handle id'     | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID     | 'cm-handle has an invalid character(s) in id'
+        scenario                   | cmHandleId               | exception                                                                                           || expectedError        | expectedErrorText
+        'Cm Handle does not exist' | 'cmHandleId'             | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR)                        || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found'
+        'Unknown'                  | 'cmHandleId'             | new RuntimeException('Failed')                                                                      || UNKNOWN_ERROR        | 'Failed'
+        'Invalid cm handle id'     | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
     }
 
     def 'Multiple update operations in a single request'() {
@@ -166,8 +166,8 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
             with(cmHandleResponseList.get(1)) {
                 assert it.status == Status.FAILURE
                 assert it.cmHandle == cmHandleId
-                assert it.registrationError == CM_HANDLE_DOES_NOT_EXIST
-                assert it.errorText == "cm-handle does not exist"
+                assert it.ncmpResponseStatus == CM_HANDLES_NOT_FOUND
+                assert it.errorText == 'cm handle id(s) not found'
             }
         then: 'the replace list method is called twice'
             2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_)
index 6184a97..3eff96d 100644 (file)
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.config.embeddedcache
 
 import com.hazelcast.config.Config
 import com.hazelcast.core.Hazelcast
-import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
@@ -32,7 +31,10 @@ import spock.lang.Specification
 class TrustLevelCacheConfigSpec extends Specification {
 
     @Autowired
-    private IMap<String, TrustLevel> trustLevelPerDmiPlugin
+    private Map<String, TrustLevel> trustLevelPerDmiPlugin
+
+    @Autowired
+    private Map<String, TrustLevel> trustLevelPerCmHandle
 
     def 'Hazelcast cache for trust level per dmi plugin'() {
         expect: 'system is able to create an instance of the trust level per dmi plugin cache'
@@ -43,23 +45,29 @@ class TrustLevelCacheConfigSpec extends Specification {
             assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerDmiPluginMap')
     }
 
-    def 'Verify Trust Level Per Dmi Plugin Cache for basic hazelcast map operations'() {
-        when: 'the key inserted into Trust Level Per Dmi Plugin Cache'
-            trustLevelPerDmiPlugin.put('dmi1', TrustLevel.COMPLETE)
-            trustLevelPerDmiPlugin.put('dmi2', TrustLevel.NONE)
-        then: 'the value for each dmi can be retrieved'
-            assert trustLevelPerDmiPlugin.get('dmi1') == TrustLevel.COMPLETE
-            assert trustLevelPerDmiPlugin.get('dmi2') == TrustLevel.NONE
+    def 'Hazelcast cache for trust level per cm handle'() {
+        expect: 'system is able to create an instance of the trust level per cm handle cache'
+            assert null != trustLevelPerCmHandle
+        and: 'there is at least 1 instance'
+            assert Hazelcast.allHazelcastInstances.size() > 0
+        and: 'Hazelcast cache instance for trust level is present'
+            assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerCmHandleMap')
     }
 
-    def 'Verify configs for Distributed Caches'(){
-        given: 'the Trust Level Per Dmi Plugin Cache config'
-            def trustLevelDmiPerPluginCacheConfig =  Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerDmiPluginMap').config
-            def trustLevelDmiPerPluginCacheMapConfig =  trustLevelDmiPerPluginCacheConfig.mapConfigs.get('trustLevelPerDmiPluginCacheConfig')
-        expect: 'system created instance with correct config'
-            assert trustLevelDmiPerPluginCacheConfig.clusterName == 'cps-and-ncmp-test-caches'
-            assert trustLevelDmiPerPluginCacheMapConfig.backupCount == 3
-            assert trustLevelDmiPerPluginCacheMapConfig.asyncBackupCount == 3
+    def 'Trust level cache configurations: #scenario'() {
+        when: 'retrieving the cache config for trustLevel'
+            def cacheConfig = Hazelcast.getHazelcastInstanceByName(hazelcastInstanceName).config
+        then: 'the cache config has the right cluster'
+            assert cacheConfig.clusterName == 'cps-and-ncmp-test-caches'
+        when: 'retrieving the map config for trustLevel'
+            def mapConfig = cacheConfig.mapConfigs.get(hazelcastMapConfigName)
+        then: 'the map config has the correct backup counts'
+            assert mapConfig.backupCount == 3
+            assert mapConfig.asyncBackupCount == 3
+        where: 'the following caches are used'
+            scenario         | hazelcastInstanceName                        | hazelcastMapConfigName
+            'cmhandle map'   | 'hazelcastInstanceTrustLevelPerCmHandleMap'  | 'trustLevelPerCmHandleCacheConfig'
+            'dmi plugin map' | 'hazelcastInstanceTrustLevelPerDmiPluginMap' | 'trustLevelPerDmiPluginCacheConfig'
     }
 
     def 'Verify deployment network configs for Distributed Caches'() {
@@ -70,6 +78,14 @@ class TrustLevelCacheConfigSpec extends Specification {
             assert !trustLevelDmiPerPluginCacheConfig.join.kubernetesConfig.enabled
     }
 
+    def 'Verify deployment network configs for Cm Handle Distributed Caches'() {
+        given: 'the Trust Level Per Cm Handle Cache config'
+            def trustLevelPerCmHandlePluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerCmHandleMap').config.networkConfig
+        expect: 'system created instance with correct config'
+            assert trustLevelPerCmHandlePluginCacheConfig.join.autoDetectionConfig.enabled
+            assert !trustLevelPerCmHandlePluginCacheConfig.join.kubernetesConfig.enabled
+    }
+
     def 'Verify network config'() {
         given: 'Synchronization config object and test configuration'
             def objectUnderTest = new TrustLevelCacheConfig()
index cc14195..a0567cb 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.events.cmsubscription
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESSFULLY_APPLIED_SUBSCRIPTION
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_PENDING
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUBSCRIPTION_NOT_APPLICABLE
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.PARTIALLY_APPLIED_SUBSCRIPTION
+
 import com.fasterxml.jackson.databind.ObjectMapper
 import io.cloudevents.CloudEvent
 import io.cloudevents.core.builder.CloudEventBuilder
 import org.mapstruct.factory.Mappers
-import org.onap.cps.ncmp.api.NcmpEventResponseCode
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher
 import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionPersistence
 import org.onap.cps.ncmp.api.impl.utils.DataNodeBaseSpec
@@ -98,9 +102,9 @@ class CmSubscriptionNcmpOutEventPublisherSpec extends DataNodeBaseSpec {
         then: 'the result will be equal to ncmp out event'
             expectedResult == ncmpOutEvent
         where: 'the following values are used'
-            scenario             | ncmpEventResponseCode                                        || statusMessage                          ||  statusCode
-            'is full outcome'    | NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION      || 'successfully applied subscription'    ||  1
-            'is partial outcome' | NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION         || 'partially applied subscription'       ||  104
+        scenario             | ncmpEventResponseCode             || statusMessage                       || statusCode
+        'is full outcome'    | SUCCESSFULLY_APPLIED_SUBSCRIPTION || 'successfully applied subscription' || 1
+        'is partial outcome' | PARTIALLY_APPLIED_SUBSCRIPTION    || 'partially applied subscription'    || 104
     }
 
     def 'Check cm handle id to status map to see if it is a full outcome response'() {
@@ -109,16 +113,16 @@ class CmSubscriptionNcmpOutEventPublisherSpec extends DataNodeBaseSpec {
         then: 'the result will be as expected'
             response == expectedOutcomeResponseDecision
         where: 'the following values are used'
-            scenario                                          | cmHandleIdToStatusAndDetailsAsMap                                                                                                                                                   || expectedOutcomeResponseDecision
-            'The map contains PENDING status'                 | [CMHandle1: [details:'Subscription forwarded to dmi plugin',status:'PENDING'] as Map] as Map                                                                                        || NcmpEventResponseCode.SUBSCRIPTION_PENDING
-            'The map contains ACCEPTED status'                | [CMHandle1: [details:'',status:'ACCEPTED'] as Map] as Map                                                                                                                           || NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION
-            'The map contains REJECTED status'                | [CMHandle1: [details:'Cm handle does not exist',status:'REJECTED'] as Map] as Map                                                                                                   || NcmpEventResponseCode.SUBSCRIPTION_NOT_APPLICABLE
-            'The map contains PENDING and PENDING statuses'   | [CMHandle1: [details:'Some details',status:'PENDING'] as Map, CMHandle2: [details:'Some details',status:'PENDING'] as Map as Map] as Map                                            || NcmpEventResponseCode.SUBSCRIPTION_PENDING
-            'The map contains ACCEPTED and ACCEPTED statuses' | [CMHandle1: [details:'',status:'ACCEPTED'] as Map, CMHandle2: [details:'',status:'ACCEPTED'] as Map as Map] as Map                                                                  || NcmpEventResponseCode.SUCCESSFULLY_APPLIED_SUBSCRIPTION
-            'The map contains REJECTED and REJECTED statuses' | [CMHandle1: [details:'Reject details',status:'REJECTED'] as Map, CMHandle2: [details:'Reject details',status:'REJECTED'] as Map as Map] as Map                                      || NcmpEventResponseCode.SUBSCRIPTION_NOT_APPLICABLE
-            'The map contains PENDING and ACCEPTED statuses'  | [CMHandle1: [details:'Some details',status:'PENDING'] as Map, CMHandle2: [details:'',status:'ACCEPTED'] as Map as Map] as Map                                                       || NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION
-            'The map contains REJECTED and ACCEPTED statuses' | [CMHandle1: [details:'Cm handle does not exist',status:'REJECTED'] as Map, CMHandle2: [details:'',status:'ACCEPTED'] as Map as Map] as Map                                          || NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION
-            'The map contains PENDING and REJECTED statuses'  | [CMHandle1: [details:'Subscription forwarded to dmi plugin',status:'PENDING'] as Map, CMHandle2: [details:'Cm handle does not exist',status:'REJECTED'] as Map as Map] as Map       || NcmpEventResponseCode.PARTIALLY_APPLIED_SUBSCRIPTION
+        scenario                                          | cmHandleIdToStatusAndDetailsAsMap                                                                                                                                                   || expectedOutcomeResponseDecision
+        'The map contains PENDING status'                 | [CMHandle1: [details: 'Subscription forwarded to dmi plugin', status: 'PENDING'] as Map] as Map                                                                                     || SUBSCRIPTION_PENDING
+        'The map contains ACCEPTED status'                | [CMHandle1: [details: '', status: 'ACCEPTED'] as Map] as Map                                                                                                                        || SUCCESSFULLY_APPLIED_SUBSCRIPTION
+        'The map contains REJECTED status'                | [CMHandle1: [details: 'Cm handle does not exist', status: 'REJECTED'] as Map] as Map                                                                                                || SUBSCRIPTION_NOT_APPLICABLE
+        'The map contains PENDING and PENDING statuses'   | [CMHandle1: [details: 'Some details', status: 'PENDING'] as Map, CMHandle2: [details: 'Some details', status: 'PENDING'] as Map as Map] as Map                                      || SUBSCRIPTION_PENDING
+        'The map contains ACCEPTED and ACCEPTED statuses' | [CMHandle1: [details: '', status: 'ACCEPTED'] as Map, CMHandle2: [details: '', status: 'ACCEPTED'] as Map as Map] as Map                                                            || SUCCESSFULLY_APPLIED_SUBSCRIPTION
+        'The map contains REJECTED and REJECTED statuses' | [CMHandle1: [details: 'Reject details', status: 'REJECTED'] as Map, CMHandle2: [details: 'Reject details', status: 'REJECTED'] as Map as Map] as Map                                || SUBSCRIPTION_NOT_APPLICABLE
+        'The map contains PENDING and ACCEPTED statuses'  | [CMHandle1: [details: 'Some details', status: 'PENDING'] as Map, CMHandle2: [details: '', status: 'ACCEPTED'] as Map as Map] as Map                                                 || PARTIALLY_APPLIED_SUBSCRIPTION
+        'The map contains REJECTED and ACCEPTED statuses' | [CMHandle1: [details: 'Cm handle does not exist', status: 'REJECTED'] as Map, CMHandle2: [details: '', status: 'ACCEPTED'] as Map as Map] as Map                                    || PARTIALLY_APPLIED_SUBSCRIPTION
+        'The map contains PENDING and REJECTED statuses'  | [CMHandle1: [details: 'Subscription forwarded to dmi plugin', status: 'PENDING'] as Map, CMHandle2: [details: 'Cm handle does not exist', status: 'REJECTED'] as Map as Map] as Map || PARTIALLY_APPLIED_SUBSCRIPTION
     }
 
 }
index 380aea4..e6d3831 100644 (file)
@@ -34,17 +34,30 @@ class CloudEventMapperSpec extends Specification {
     @Autowired
     JsonObjectMapper jsonObjectMapper
 
-    def 'Cloud event to Target event type when it is #scenario'() {
-        expect: 'Events mapped correctly'
-            assert mappedCloudEvent == (CloudEventMapper.toTargetEvent(testCloudEvent(), targetClass) != null)
+    def 'Cloud event to target event type'() {
+        given: 'a cloud event with valid payload'
+            def cloudEvent = testCloudEvent(new CmSubscriptionNcmpInEvent())
+        when: 'the cloud event mapped to target event'
+            def result = CloudEventMapper.toTargetEvent((cloudEvent), CmSubscriptionNcmpInEvent.class)
+        then: 'the cloud event is mapped'
+            assert result instanceof CmSubscriptionNcmpInEvent
+    }
+
+    def 'Cloud event to target event type when it is #scenario'() {
+        given: 'a cloud event with invalid payload'
+            def cloudEvent = testCloudEvent(payload)
+        when: 'the cloud event mapped to target event'
+            def result = CloudEventMapper.toTargetEvent(cloudEvent, CmSubscriptionNcmpInEvent.class)
+        then: 'result is null'
+            assert result == null
         where: 'below are the scenarios'
-            scenario                | targetClass                     || mappedCloudEvent
-            'valid concrete type'   | CmSubscriptionNcmpInEvent.class || true
-            'invalid concrete type' | ArrayList.class                 || false
+            scenario               | payload
+            'invalid payload type' | ArrayList.class
+            'without payload'      | null
     }
 
-    def testCloudEvent() {
-        return CloudEventBuilder.v1().withData(jsonObjectMapper.asJsonBytes(new CmSubscriptionNcmpInEvent()))
+    def testCloudEvent(payload) {
+        return CloudEventBuilder.v1().withData(jsonObjectMapper.asJsonBytes(payload))
             .withId("cmhandle1")
             .withSource(URI.create('test-source'))
             .withDataSchema(URI.create('test'))
index 76ef23f..39c4fe6 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.operations
 
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
+import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING
+
 import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.ncmp.api.NcmpEventResponseCode
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher
 import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
@@ -41,13 +49,6 @@ import org.springframework.http.HttpStatus
 import spock.lang.Shared
 import java.util.concurrent.TimeoutException
 
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
-import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
-
 @SpringBootTest
 @ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, NcmpConfiguration.DmiProperties, DmiDataOperations])
 class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
@@ -128,12 +129,12 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def eventDataValue = extractDataValue(actualDataOperationCloudEvent)
             assert eventDataValue.operationId == dmiDataOperation.operationId
             assert eventDataValue.ids == dmiDataOperation.cmHandles.id
-            assert eventDataValue.statusCode == responseCode.statusCode
-            assert eventDataValue.statusMessage == responseCode.statusMessage
+            assert eventDataValue.statusCode == responseCode.code
+            assert eventDataValue.statusMessage == responseCode.message
         where: 'the following exceptions are occurred'
             scenario                        | exception                                                                                                || responseCode
-            'http client request exception' | new HttpClientRequestException('error-message', 'error-details', HttpStatus.SERVICE_UNAVAILABLE.value()) || NcmpEventResponseCode.UNABLE_TO_READ_RESOURCE_DATA
-            'timeout exception'             | new TimeoutException()                                                                                   || NcmpEventResponseCode.DMI_SERVICE_NOT_RESPONDING
+            'http client request exception' | new HttpClientRequestException('error-message', 'error-details', HttpStatus.SERVICE_UNAVAILABLE.value()) || UNABLE_TO_READ_RESOURCE_DATA
+            'timeout exception'             | new TimeoutException()                                                                                   || DMI_SERVICE_NOT_RESPONDING
     }
 
     def 'call get all resource data.'() {
index 48de23d..80778b9 100644 (file)
 package org.onap.cps.ncmp.api.impl.trustlevel
 
 import com.fasterxml.jackson.databind.ObjectMapper
-import com.hazelcast.collection.ISet
+import com.hazelcast.map.IMap
 import io.cloudevents.CloudEvent
 import io.cloudevents.core.builder.CloudEventBuilder
 import org.apache.kafka.clients.consumer.ConsumerRecord
+import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel
 import org.onap.cps.utils.JsonObjectMapper
+import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import spock.lang.Specification
 
 @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper])
 class DeviceHeartbeatConsumerSpec extends Specification {
 
-    def mockUntrustworthyCmHandlesSet = Mock(ISet<String>)
+    def mockTrustLevelPerCmHandle = Mock(Map<String, TrustLevel>)
+
+    def objectUnderTest = new DeviceHeartbeatConsumer(mockTrustLevelPerCmHandle)
     def objectMapper = new ObjectMapper()
 
-    def objectUnderTest = new DeviceHeartbeatConsumer(mockUntrustworthyCmHandlesSet)
+    @Autowired
+    JsonObjectMapper jsonObjectMapper
+
+    def static trustLevelString = '{"data":{"trustLevel": "COMPLETE"}}'
 
-    def 'Operations to be done in an empty untrustworthy set for #scenario'() {
-        given: 'an event with trustlevel as #trustLevel'
-            def incomingEvent = testCloudEvent(trustLevel)
-        and: 'transformed as a kafka record'
-            def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent)
+    def 'Consume a trustlevel event'() {
+        given: 'an event from dmi with trust level complete'
+            def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class)
+            def eventFromDmi = createTrustLevelEvent(payload)
+        and: 'transformed to a consumer record with a cloud event id (ce_id)'
+            def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi)
             consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
         when: 'the event is consumed'
             objectUnderTest.heartbeatListener(consumerRecord)
-        then: 'untrustworthy cmhandles are stored'
-            untrustworthyCmHandlesSetInvocationForAdd * mockUntrustworthyCmHandlesSet.add(_)
-        and: 'trustworthy cmHandles will be removed from untrustworthy set'
-            untrustworthyCmHandlesSetInvocationForContains * mockUntrustworthyCmHandlesSet.contains(_)
-
-        where: 'below scenarios are applicable'
-            scenario         | trustLevel          || untrustworthyCmHandlesSetInvocationForAdd | untrustworthyCmHandlesSetInvocationForContains
-            'None trust'     | TrustLevel.NONE     || 1                                         | 0
-            'Complete trust' | TrustLevel.COMPLETE || 0                                         | 1
+        then: 'cm handles are stored with correct trust level'
+            1 * mockTrustLevelPerCmHandle.put('"cmhandle1"', TrustLevel.COMPLETE)
     }
 
-    def 'Invalid trust'() {
-        when: 'we provide an invalid trust in the event'
-            def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', testCloudEvent(null))
-            consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
+    def 'Consume trustlevel event without cloud event id'() {
+        given: 'an event from dmi'
+            def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class)
+            def eventFromDmi = createTrustLevelEvent(payload)
+        and: 'transformed to a consumer record WITHOUT Cloud event ID (ce_id)'
+            def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi)
+        when: 'the event is consumed'
             objectUnderTest.heartbeatListener(consumerRecord)
-        then: 'no interaction with the untrustworthy cmhandles set'
-            0 * mockUntrustworthyCmHandlesSet.add(_)
-            0 * mockUntrustworthyCmHandlesSet.contains(_)
-            0 * mockUntrustworthyCmHandlesSet.remove(_)
-        and: 'control flow returns without any exception'
-            noExceptionThrown()
-
+        then: 'no cm handle has been stored in the map'
+            0 * mockTrustLevelPerCmHandle.put(*_)
     }
 
-    def 'Remove trustworthy cmhandles from untrustworthy cmhandles set'() {
-        given: 'an event with COMPLETE trustlevel'
-            def incomingEvent = testCloudEvent(TrustLevel.COMPLETE)
-        and: 'transformed as a kafka record'
-            def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent)
-            consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1'))
-        and: 'untrustworthy cmhandles set contains cmhandle1'
-            1 * mockUntrustworthyCmHandlesSet.contains(_) >> true
+    def 'Consume a trust level event without payload'() {
+        given: 'a consumer record with ce_id header but without payload'
+            def consumerRecord = new ConsumerRecord<String, CloudEvent>('test-device-heartbeat', 0, 0, 'cmhandle1', createTrustLevelEvent(null))
+            consumerRecord.headers().add('some_other_header_value', objectMapper.writeValueAsBytes('cmhandle1'))
         when: 'the event is consumed'
             objectUnderTest.heartbeatListener(consumerRecord)
-        then: 'cmhandle removed from untrustworthy cmhandles set'
-            1 * mockUntrustworthyCmHandlesSet.remove(_) >> {
-                args ->
-                    {
-                        args[0].equals('cmhandle1')
-                    }
-            }
-
+        then: 'no cm handle has been stored in the map'
+            0 * mockTrustLevelPerCmHandle.put(*_)
     }
 
-    def testCloudEvent(trustLevel) {
-        return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(new DeviceTrustLevel(trustLevel)))
+    def createTrustLevelEvent(eventPayload) {
+        return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(eventPayload))
             .withId("cmhandle1")
             .withSource(URI.create('DMI'))
             .withDataSchema(URI.create('test'))
-            .withType('org.onap.cm.events.trustlevel-notification')
+            .withType('org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel')
             .build()
     }
 
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy
new file mode 100644 (file)
index 0000000..8f6621d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *  ============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.trustlevel
+
+
+import spock.lang.Specification
+
+class TrustLevelFilterSpec extends Specification {
+
+    def targetTrustLevel = TrustLevel.COMPLETE
+
+    def trustLevelPerCmHandle = [ 'my completed cm handle': TrustLevel.COMPLETE, 'my untrusted cm handle': TrustLevel.NONE ]
+
+    def objectUnderTest = new TrustLevelFilter(targetTrustLevel, trustLevelPerCmHandle)
+
+    def 'Obtain cm handle ids by a given trust level value'() {
+        when: 'cm handles are retrieved'
+            def result = objectUnderTest.getAllCmHandleIdsByTargetTrustLevel()
+        then: 'the result only contains the completed cm handle'
+            assert result.size() == 1
+            assert result[0] == 'my completed cm handle'
+    }
+}
index af546b7..b6259bd 100644 (file)
@@ -20,7 +20,6 @@
 
 package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability
 
-import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.impl.client.DmiRestClient
 import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
 import spock.lang.Specification
@@ -28,7 +27,7 @@ import spock.lang.Specification
 class DMiPluginWatchDogSpec extends Specification {
 
 
-    def mockTrustLevelPerDmiPlugin = Mock(IMap<String, TrustLevel>)
+    def mockTrustLevelPerDmiPlugin = Mock(Map<String, TrustLevel>)
     def mockDmiRestClient = Mock(DmiRestClient)
     def objectUnderTest = new DMiPluginWatchDog(mockTrustLevelPerDmiPlugin, mockDmiRestClient)
 
index f0e2d9f..100705f 100644 (file)
@@ -26,10 +26,11 @@ class CmHandleQueryConditionsSpec extends Specification {
 
     def 'CmHandle query condition names.'() {
         expect: '3 conditions with the correct names'
-            assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 3
+            assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 4
             assert CmHandleQueryConditions.ALL_CONDITION_NAMES.containsAll('hasAllProperties',
                                                                            'hasAllModules',
-                                                                           'cmHandleWithCpsPath')
+                                                                           'cmHandleWithCpsPath',
+                                                                            'cmHandleWithTrustLevel')
     }
 
 }
index 6c45755..2b17e5d 100644 (file)
@@ -32,7 +32,7 @@ import spock.lang.Specification
 class DmiServiceUrlBuilderSpec extends Specification {
 
     static YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName',
-        'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'))
+        'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'),'')
 
     NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties()
 
index a58f22b..ca0015e 100644 (file)
@@ -47,7 +47,7 @@ class YangModelCmHandleSpec extends Specification {
                 .withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, 'some-sync-time').build()
             ncmpServiceCmHandle.setCompositeState(compositeState)
         when: 'it is converted to a yang model cm handle'
-            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle)
+            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('', '', '', ncmpServiceCmHandle,'')
         then: 'the result has the right size'
             assert objectUnderTest.dmiProperties.size() == 1
         and: 'the DMI property in the result has the correct name and value'
@@ -63,7 +63,8 @@ class YangModelCmHandleSpec extends Specification {
 
     def 'Resolve DMI service name: #scenario and #requiredService service require.'() {
         given: 'a yang model cm handle'
-            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'))
+            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName,
+                    dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'),'')
         expect:
             assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService
         where:
index ffdd672..a3a5efc 100644 (file)
 
 package org.onap.cps.ncmp.api.inventory
 
+import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel
+
 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.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 
+import com.hazelcast.map.IMap
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueriesImpl
 import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
 import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState
@@ -37,8 +40,9 @@ import spock.lang.Specification
 
 class CmHandleQueriesImplSpec extends Specification {
     def cpsDataPersistenceService = Mock(CpsDataPersistenceService)
+    def trustLevelPerCmHandle = [ 'my completed cm handle': TrustLevel.COMPLETE, 'my untrusted cm handle': TrustLevel.NONE ]
 
-    def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService)
+    def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService, trustLevelPerCmHandle)
 
     @Shared
     def static sampleDataNodes = [new DataNode()]
@@ -60,11 +64,21 @@ class CmHandleQueriesImplSpec extends Specification {
             result.containsAll(expectedCmHandleIds)
             result.size() == expectedCmHandleIds.size()
         where: 'the following data is used'
-            scenario                         | publicPropertyPairs                                                                           || expectedCmHandleIds
-            'single property matches'        | ['Contact' : 'newemailforstore@bookstore.com']                                                || ['PNFDemo', 'PNFDemo2', 'PNFDemo4']
-            'public property does not match' | ['wont_match' : 'wont_match']                                                                 || []
-            '2 properties, only one match'   | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': 'newemailforstore2@bookstore.com'] || ['PNFDemo4']
-            '2 properties, no matches'       | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': '']                                || []
+            scenario                         | publicPropertyPairs                                                                      || expectedCmHandleIds
+            'single property matches'        | [Contact: 'newemailforstore@bookstore.com']                                              || ['PNFDemo', 'PNFDemo2', 'PNFDemo4']
+            'public property does not match' | [wont_match: 'wont_match']                                                               || []
+            '2 properties, only one match'   | [Contact: 'newemailforstore@bookstore.com', Contact2: 'newemailforstore2@bookstore.com'] || ['PNFDemo4']
+            '2 properties, no matches'       | [Contact: 'newemailforstore@bookstore.com', Contact2: '']                                || []
+    }
+
+    def 'Query cm handles on trust level'() {
+        given: 'query properties for trustlevel COMPLETE'
+            def trustLevelPropertyQueryPairs = ['trustLevel' : TrustLevel.COMPLETE.toString()]
+        when: 'the query is executed'
+            def result = objectUnderTest.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs)
+        then: 'the result only contains the completed cm handle'
+            assert result.size() == 1
+            assert result[0] == 'my completed cm handle'
     }
 
     def 'Query CmHandles using empty public properties query pair.'() {
@@ -153,7 +167,7 @@ class CmHandleQueriesImplSpec extends Specification {
                 cpsPath + '/ancestor::cm-handles', INCLUDE_ALL_DESCENDANTS)
                 >> Arrays.asList(cmHandleDataNode)
         when: 'get cm handles by cps path is invoked'
-            def result = objectUnderTest.queryCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS)
+            def result = objectUnderTest.queryCmHandleAncestorsByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS)
         then: 'the returned result is a list of data nodes returned by cps data service'
             assert result.contains(cmHandleDataNode)
     }
index e961055..b547da7 100644 (file)
 
 package org.onap.cps.ncmp.api.inventory.sync
 
+import org.onap.cps.spi.FetchDescendantsOption
+
 import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
+import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE
 
 import org.onap.cps.api.CpsAdminService
+import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleSyncService
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries
+import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.CascadeDeleteAllowed
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.spi.model.ModuleReference
+import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Specification
 
 class ModuleSyncServiceSpec extends Specification {
@@ -38,17 +45,23 @@ class ModuleSyncServiceSpec extends Specification {
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockDmiModelOperations = Mock(DmiModelOperations)
     def mockCpsAdminService = Mock(CpsAdminService)
+    def mockCmHandleQueries = Mock(CmHandleQueries)
+    def mockCpsDataService = Mock(CpsDataService)
+    def mockJsonObjectMapper = Mock(JsonObjectMapper)
 
-    def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService)
+    def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService,
+            mockCmHandleQueries, mockCpsDataService, mockJsonObjectMapper)
 
     def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
 
     def 'Sync model for a (new) cm handle with #scenario'() {
-        given: 'a cm handle'
+        given: 'a cm handle having lock reason : MODULE_UPGRADE'
             def ncmpServiceCmHandle = new NcmpServiceCmHandle()
+            ncmpServiceCmHandle.setCompositeState(new CompositeStateBuilder()
+                .withLockReason(MODULE_UPGRADE, 'new moduleSetTag: someModuleSetTag').build())
             def dmiServiceName = 'some service name'
             ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1'
-            def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle)
+            def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle,'someModuleSetTag')
         and: 'DMI operations returns some module references'
             def moduleReferences =  [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ]
             mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
@@ -56,11 +69,14 @@ class ModuleSyncServiceSpec extends Specification {
             mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps)
         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
             mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap
+        and: 'empty data node list is returned by cps path a query'
+        mockCmHandleQueries.queryNcmpRegistryByCpsPath("//cm-handles[@module-set-tag='someModuleSetTag']",
+                FetchDescendantsOption.OMIT_DESCENDANTS) >> Collections.emptyList()
         when: 'module sync is triggered'
             mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences)
-            objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
+            objectUnderTest.syncAndCreateOrUpgradeSchemaSetAndAnchor(yangModelCmHandle)
         then: 'create schema set from module is invoked with correct parameters'
-            1 * mockCpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences)
+            1 * mockCpsModuleService.createOrUpgradeSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', newModuleNameContentToMap, moduleReferences)
         and: 'anchor is created with the correct parameters'
             1 * mockCpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'cmHandleId-1', 'cmHandleId-1')
         where: 'the following parameters are used'
index 231e34a..a11f148 100644 (file)
@@ -69,8 +69,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.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
-            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') }
+            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
+            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, '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)
@@ -86,7 +86,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.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
+            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
         when: 'module sync is executed'
             objectUnderTest.performModuleSync([cmHandle], batchCount)
         then: 'update lock reason, details and attempts is invoked'
@@ -112,7 +112,7 @@ class ModuleSyncTasksSpec extends Specification {
             moduleSyncStartedOnCmHandles.put('cm-handle-1', 'started')
             moduleSyncStartedOnCmHandles.put('cm-handle-2', 'started')
         and: 'sync utils retry locked cm handle returns #isReadyForRetry'
-            mockSyncUtils.needsModuleSyncRetry(lockedState) >>> isReadyForRetry
+            mockSyncUtils.needsModuleSyncRetryOrUpgrade(lockedState) >>> isReadyForRetry
         when: 'resetting failed cm handles'
             objectUnderTest.resetFailedCmHandles([yangModelCmHandle1, yangModelCmHandle2])
         then: 'updated to state "ADVISED" from "READY" is called as often as there are cm handles ready for retry'
@@ -135,7 +135,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.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
+            1 * mockModuleSyncService.syncAndCreateOrUpgradeSchemaSetAndAnchor(_) >> { 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
     }
index d85686a..390e88b 100644 (file)
@@ -111,7 +111,7 @@ class ModuleSyncWatchdogSpec extends Specification {
     def 'Reset failed cm handles.'() {
         given: 'sync utilities returns failed cm handles'
             def failedCmHandles = [new YangModelCmHandle()]
-            mockSyncUtils.getModuleSyncFailedCmHandles() >> failedCmHandles
+            mockSyncUtils.getCmHandlesThatFailedModelSyncOrUpgrade() >> failedCmHandles
         when: 'reset failed cm handles is started'
             objectUnderTest.resetPreviouslyFailedCmHandles()
         then: 'it is delegated to the module sync task (service)'
index 8fdbb6f..00d14cd 100644 (file)
@@ -115,11 +115,11 @@ class SyncUtilsSpec extends Specification{
 
     def 'Get all locked Cm-Handle where Lock Reason is MODULE_SYNC_FAILED cm handle #scenario'() {
         given: 'the cps (persistence service) returns a collection of data nodes'
-            mockCmHandleQueries.queryCmHandleDataNodesByCpsPath(
-                '//lock-reason[@reason="MODULE_SYNC_FAILED"]',
+            mockCmHandleQueries.queryCmHandleAncestorsByCpsPath(
+                    '//lock-reason[@reason="MODULE_SYNC_FAILED" or @reason="MODULE_UPGRADE"]',
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> [dataNode]
         when: 'get locked Misbehaving cm handle is called'
-            def result = objectUnderTest.getModuleSyncFailedCmHandles()
+            def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade()
         then: 'the returned cm handle collection is the correct size'
             result.size() == 1
         and: 'the correct cm handle is returned'
@@ -133,7 +133,7 @@ class SyncUtilsSpec extends Specification{
                 lastUpdatedTime = neverUpdatedBefore
             }
         when: 'checking to see if cm handle is ready for retry'
-         def result = objectUnderTest.needsModuleSyncRetry(new CompositeStateBuilder()
+         def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder()
                 .withLockReason(MODULE_SYNC_FAILED, lockDetails)
                 .withLastUpdatedTime(lastUpdatedTime).build())
         then: 'retry is only attempted when expected'
@@ -151,16 +151,18 @@ class SyncUtilsSpec extends Specification{
 
     def 'Retry Locked Cm-Handle with other lock reasons (category) #lockReasonCategory'() {
         when: 'checking to see if cm handle is ready for retry'
-            def result = objectUnderTest.needsModuleSyncRetry(new CompositeStateBuilder()
+        def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder()
                 .withLockReason(lockReasonCategory, 'some details')
                 .withLastUpdatedTime(nowAsString).build())
-        then: 'retry attempt is never triggered'
-            assert result == false
+        then: 'verify retry attempts'
+        assert result == retryAttempt
         and: 'logs contain related information'
-            def logs = loggingListAppender.list.toString()
-            assert logs.contains('Locked for other reason')
+        def logs = loggingListAppender.list.toString()
+        assert logs.contains(logReason)
         where: 'the following lock reasons occurred'
-            lockReasonCategory  << [MODULE_UPGRADE, MODULE_UPGRADE_FAILED]
+        scenario             | lockReasonCategory || logReason                    | retryAttempt
+        'module upgrade'     | MODULE_UPGRADE     || 'Locked for module upgrade.' | true
+        'module sync failed' | MODULE_SYNC_FAILED || 'First Attempt:'             | false
     }
 
     def 'Get a Cm-Handle where #scenario'() {
index dba2934..d76f912 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
+ *  Modifications 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.
 
 package org.onap.cps.ncmp.api.models
 
-import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationError
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
+
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
 import spock.lang.Specification
-
 import java.util.stream.Collectors
 
 class CmHandleRegistrationResponseSpec extends Specification {
@@ -37,7 +39,7 @@ class CmHandleRegistrationResponseSpec extends Specification {
                 assert it.status == Status.SUCCESS
             }
         and: 'error details are null'
-            cmHandleRegistrationResponse.registrationError == null
+            cmHandleRegistrationResponse.ncmpResponseStatus == null
             cmHandleRegistrationResponse.errorText == null
     }
 
@@ -47,33 +49,29 @@ class CmHandleRegistrationResponseSpec extends Specification {
                 CmHandleRegistrationResponse.createFailureResponse('cmHandle', new Exception('unexpected error'))
         then: 'the response is created with expected value'
             with(cmHandleRegistrationResponse) {
-                assert it.registrationError == RegistrationError.UNKNOWN_ERROR
+                assert it.ncmpResponseStatus == UNKNOWN_ERROR
                 assert it.cmHandle == 'cmHandle'
                 assert errorText == 'unexpected error'
             }
     }
 
-    def 'Failed cm-handle Registration Response: for #scenario'() {
-        when: 'cm-handle failure response is created for #scenario'
-            def cmHandleRegistrationResponse =
-                CmHandleRegistrationResponse.createFailureResponse(cmHandleId, registrationError)
+    def 'Failed cm-handle Registration Response'() {
+        when: 'cm-handle failure response is created'
+        def cmHandleRegistrationResponse =
+                CmHandleRegistrationResponse.createFailureResponse('cmHandle', CM_HANDLE_ALREADY_EXIST)
         then: 'the response is created with expected value'
-            with(cmHandleRegistrationResponse) {
-                assert it.registrationError == registrationError
-                assert it.cmHandle == cmHandleId
-                assert it.status == Status.FAILURE
-                assert errorText == registrationError.errorText
-            }
-        where:
-            scenario                   | cmHandleId  | registrationError
-            'cm-handle already exists' | 'cmHandle'  | RegistrationError.CM_HANDLE_ALREADY_EXIST
-            'cm-handle id is invalid'  | 'cm handle' | RegistrationError.CM_HANDLE_INVALID_ID
+        with(cmHandleRegistrationResponse) {
+            assert it.ncmpResponseStatus == CM_HANDLE_ALREADY_EXIST
+            assert it.cmHandle == 'cmHandle'
+            assert it.status == Status.FAILURE
+            assert errorText == CM_HANDLE_ALREADY_EXIST.message
+        }
     }
 
     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']"], RegistrationError.CM_HANDLE_ALREADY_EXIST)
+                CmHandleRegistrationResponse.createFailureResponses(["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())
@@ -83,12 +81,9 @@ 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']"], RegistrationError.CM_HANDLE_ALREADY_EXIST)
+                CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], CM_HANDLE_ALREADY_EXIST)
         then: 'the response has only one entry'
             assert cmHandleRegistrationResponses.size() == 1
     }
 
-
-
-
 }
index 4121556..37c9ad3 100755 (executable)
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.3.8-SNAPSHOT</version>
+    <version>3.3.9-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
index 8ce3fa5..4fc7d40 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index cd5bc0d..f99e48f 100755 (executable)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
@@ -39,6 +39,7 @@
             <groupId>${project.groupId}</groupId>
             <artifactId>cps-service</artifactId>
         </dependency>
+        <!-- S P R I N G   D E P E N D E N C I E S -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
             <groupId>org.springframework</groupId>
             <artifactId>spring-aspects</artifactId>
         </dependency>
+        <!-- A L P H A B E T I C A L   D E P E N D E N C I E S -->
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.gsonfire</groupId>
+            <artifactId>gson-fire</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.swagger.core.v3</groupId>
             <artifactId>swagger-annotations</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>jakarta.validation</groupId>
-            <artifactId>jakarta.validation-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.gsonfire</groupId>
-            <artifactId>gson-fire</artifactId>
-        </dependency>
     </dependencies>
 
     <build>
index f8fd730..63a6ab2 100644 (file)
@@ -26,7 +26,7 @@
     <parent>\r
         <groupId>org.onap.cps</groupId>\r
         <artifactId>cps-parent</artifactId>\r
-        <version>3.3.8-SNAPSHOT</version>\r
+        <version>3.3.9-SNAPSHOT</version>\r
         <relativePath>../cps-parent/pom.xml</relativePath>\r
     </parent>\r
 \r
index ca88a4d..0d77530 100755 (executable)
@@ -46,7 +46,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.hibernate.exception.ConstraintViolationException;
-import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.CpsModulePersistenceService;
 import org.onap.cps.spi.entities.DataspaceEntity;
 import org.onap.cps.spi.entities.SchemaSetEntity;
@@ -89,8 +88,6 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
 
     private final DataspaceRepository dataspaceRepository;
 
-    private final CpsAdminPersistenceService cpsAdminPersistenceService;
-
     private final ModuleReferenceRepository moduleReferenceRepository;
 
     @Override
index b8503a7..19646c5 100755 (executable)
@@ -70,13 +70,25 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> {
 
     Integer countByDataspace(DataspaceEntity dataspaceEntity);
 
-    @Query(value = "SELECT anchor.* FROM yang_resource\n"
-        + "JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id\n"
-        + "JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id\n"
-        + "JOIN anchor ON anchor.schema_set_id = schema_set.id\n"
-        + "WHERE schema_set.dataspace_id = :dataspaceId AND module_name = ANY (:moduleNames)\n"
-        + "GROUP BY anchor.id, anchor.name, anchor.dataspace_id, anchor.schema_set_id\n"
-        + "HAVING COUNT(DISTINCT module_name) = :sizeOfModuleNames", nativeQuery = true)
+    @Query(value = """
+            SELECT
+                anchor.*
+            FROM
+                     yang_resource
+                JOIN schema_set_yang_resources ON schema_set_yang_resources.yang_resource_id = yang_resource.id
+                JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id
+                JOIN anchor ON anchor.schema_set_id = schema_set.id
+            WHERE
+                    schema_set.dataspace_id = :dataspaceId
+                AND module_name = ANY ( :moduleNames )
+            GROUP BY
+                anchor.id,
+                anchor.name,
+                anchor.dataspace_id,
+                anchor.schema_set_id
+            HAVING
+                COUNT(DISTINCT module_name) = :sizeOfModuleNames
+            """, nativeQuery = true)
     Collection<AnchorEntity> getAnchorsByDataspaceIdAndModuleNames(@Param("dataspaceId") int dataspaceId,
                                                                    @Param("moduleNames") String[] moduleNames,
                                                                    @Param("sizeOfModuleNames") int sizeOfModuleNames);
index df21ccc..e833796 100644 (file)
@@ -41,56 +41,65 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
         return findAllByChecksumIn(checksums.toArray(new String[0]));
     }
 
-    @Query(value = "SELECT DISTINCT\n"
-        + "yang_resource.module_name AS module_name,\n"
-        + "yang_resource.revision AS revision\n"
-        + "FROM\n"
-        + "dataspace\n"
-        + "JOIN schema_set ON schema_set.dataspace_id = dataspace.id\n"
-        + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = "
-        + "schema_set.id\n"
-        + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
-        + "WHERE\n"
-        + "dataspace.name = :dataspaceName", nativeQuery = true)
+    @Query(value = """
+            SELECT DISTINCT
+                yang_resource.module_name AS module_name,
+                yang_resource.revision    AS revision
+            FROM
+                     dataspace
+                JOIN schema_set ON schema_set.dataspace_id = dataspace.id
+                JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id
+                JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id
+            WHERE
+                dataspace.name = :dataspaceName
+            """, nativeQuery = true)
     Set<YangResourceModuleReference> findAllModuleReferencesByDataspace(@Param("dataspaceName") String dataspaceName);
 
-    @Query(value = "SELECT DISTINCT\n"
-        + "yang_resource.module_Name AS module_name,\n"
-        + "yang_resource.revision AS revision\n"
-        + "FROM\n"
-        + "dataspace\n"
-        + "JOIN anchor ON anchor.dataspace_id = dataspace.id\n"
-        + "JOIN schema_set ON schema_set.id = anchor.schema_set_id\n"
-        + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = "
-        + "schema_set.id\n"
-        + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
-        + "WHERE\n"
-        + "dataspace.name = :dataspaceName AND\n"
-        + "anchor.name =:anchorName", nativeQuery = true)
+    @Query(value = """
+            SELECT DISTINCT
+                yang_resource.module_name AS module_name,
+                yang_resource.revision    AS revision
+            FROM
+                     dataspace
+                JOIN anchor ON anchor.dataspace_id = dataspace.id
+                JOIN schema_set ON schema_set.id = anchor.schema_set_id
+                JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id
+                JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id
+            WHERE
+                    dataspace.name = :dataspaceName
+                AND anchor.name = :anchorName
+            """, nativeQuery = true)
     Set<YangResourceModuleReference> findAllModuleReferencesByDataspaceAndAnchor(
         @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName);
 
-    @Query(value = "SELECT DISTINCT yang_resource.*\n"
-            + "FROM dataspace\n"
-            + "JOIN anchor ON anchor.dataspace_id = dataspace.id\n"
-            + "JOIN schema_set ON schema_set.id = anchor.schema_set_id\n"
-            + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id\n"
-            + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
-            + "WHERE dataspace.name = :dataspaceName "
-            + "AND anchor.name =:anchorName", nativeQuery = true)
+    @Query(value = """
+            SELECT DISTINCT
+                yang_resource.*
+            FROM
+                     dataspace
+                JOIN anchor ON anchor.dataspace_id = dataspace.id
+                JOIN schema_set ON schema_set.id = anchor.schema_set_id
+                JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id
+                JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id
+            WHERE
+                    dataspace.name = :dataspaceName
+                AND anchor.name = :anchorName
+            """, nativeQuery = true)
     Set<YangResourceEntity> findAllModuleDefinitionsByDataspaceAndAnchor(
             @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName);
 
-    @Query(value = "SELECT DISTINCT\n"
-        + "yang_resource.*\n"
-        + "FROM\n"
-        + "dataspace\n"
-        + "JOIN schema_set ON schema_set.dataspace_id = dataspace.id\n"
-        + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = "
-        + "schema_set.id\n"
-        + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
-        + "WHERE\n"
-        + "dataspace.name = :dataspaceName and yang_resource.module_Name = ANY (:moduleNames)", nativeQuery = true)
+    @Query(value = """
+            SELECT DISTINCT
+                yang_resource.*
+            FROM
+                     dataspace
+                JOIN schema_set ON schema_set.dataspace_id = dataspace.id
+                JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = schema_set.id
+                JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id
+            WHERE
+                    dataspace.name = :dataspaceName
+                AND yang_resource.module_name = ANY ( :moduleNames )
+            """, nativeQuery = true)
     Set<YangResourceModuleReference> findAllModuleReferencesByDataspaceAndModuleNames(
             @Param("dataspaceName") String dataspaceName, @Param("moduleNames") String[] moduleNames);
 
index 52651c6..9696b28 100644 (file)
@@ -69,7 +69,7 @@ class CpsModulePersistenceServiceSpec extends Specification {
 
     def setup() {
         objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock,
-            dataspaceRepositoryMock, cpsAdminPersistenceServiceMock, moduleReferenceRepositoryMock)
+            dataspaceRepositoryMock, moduleReferenceRepositoryMock)
     }
 
     def 'Store schema set error scenario: #scenario.'() {
index 4a3c90e..0787409 100644 (file)
   <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.3.8-SNAPSHOT</version>
+    <version>3.3.9-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
   </parent>
 
   <artifactId>cps-service</artifactId>
 
   <dependencies>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-messaging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-cache</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-aop</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-validation</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.kafka</groupId>
+      <artifactId>spring-kafka</artifactId>
+    </dependency>
     <dependency>
       <groupId>com.github.ben-manes.caffeine</groupId>
       <artifactId>caffeine</artifactId>
       <groupId>io.micrometer</groupId>
       <artifactId>micrometer-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>jakarta.validation</groupId>
+      <artifactId>jakarta.validation-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>net.logstash.logback</groupId>
       <artifactId>logstash-logback-encoder</artifactId>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-messaging</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-cache</artifactId>
-      <version>3.0.0</version>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-aop</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-validation</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.kafka</groupId>
-      <artifactId>spring-kafka</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>
-    <!-- T E S T   D E P E N D E N C I E S -->
     <dependency>
       <groupId>org.codehaus.groovy</groupId>
       <artifactId>groovy</artifactId>
       <artifactId>aspectjrt</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>jakarta.validation</groupId>
-      <artifactId>jakarta.validation-api</artifactId>
-    </dependency>
   </dependencies>
 </project>
index 5ff08c9..e8c3e77 100644 (file)
@@ -47,13 +47,13 @@ public interface CpsModuleService {
                          Map<String, String> yangResourcesNameToContentMap);
 
     /**
-     * Create a schema set from new modules and existing modules.
+     * Create or upgrade a schema set from new modules and existing modules or only existing modules.
      * @param dataspaceName             Dataspace name
      * @param schemaSetName             schema set name
      * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
      * @param allModuleReferences       All YANG resource module references
      */
-    void createSchemaSetFromModules(String dataspaceName, String schemaSetName,
+    void createOrUpgradeSchemaSetFromModules(String dataspaceName, String schemaSetName,
                                     Map<String, String> newModuleNameToContentMap,
                                     Collection<ModuleReference> allModuleReferences);
 
index 7db87e8..1d68450 100755 (executable)
@@ -31,6 +31,7 @@ import static org.onap.cps.notification.Operation.UPDATE;
 import io.micrometer.core.annotation.Timed;
 import java.io.Serializable;
 import java.time.OffsetDateTime;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
@@ -321,28 +322,12 @@ public class CpsDataServiceImpl implements CpsDataService {
         processDataUpdatedEventAsync(anchor, listNodeXpath, DELETE, observedTimestamp);
     }
 
-    private DataNode buildDataNode(final Anchor anchor, final String parentNodeXpath, final String nodeData,
-                                   final ContentType contentType) {
-        final SchemaContext schemaContext = getSchemaContext(anchor);
-
-        if (ROOT_NODE_XPATH.equals(parentNodeXpath)) {
-            final ContainerNode containerNode = timedYangParser.parseData(contentType, nodeData, schemaContext);
-            return new DataNodeBuilder().withContainerNode(containerNode).build();
-        }
-
-        final ContainerNode containerNode =
-            timedYangParser.parseData(contentType, nodeData, schemaContext, parentNodeXpath);
-
-        return new DataNodeBuilder()
-                .withParentNodeXpath(parentNodeXpath)
-                .withContainerNode(containerNode)
-                .build();
-    }
-
     private Collection<DataNode> buildDataNodes(final Anchor anchor, final Map<String, String> nodesJsonData) {
-        return nodesJsonData.entrySet().stream().map(nodeJsonData ->
-            buildDataNode(anchor, nodeJsonData.getKey(),
-                nodeJsonData.getValue(), ContentType.JSON)).collect(Collectors.toList());
+        final Collection<DataNode> dataNodes = new ArrayList<>();
+        for (final Map.Entry<String, String> nodeJsonData : nodesJsonData.entrySet()) {
+            dataNodes.addAll(buildDataNodes(anchor, nodeJsonData.getKey(), nodeJsonData.getValue(), ContentType.JSON));
+        }
+        return dataNodes;
     }
 
     private Collection<DataNode> buildDataNodes(final Anchor anchor, final String parentNodeXpath,
index 7d9c472..444c895 100644 (file)
@@ -66,7 +66,7 @@ public class CpsModuleServiceImpl implements CpsModuleService {
     }
 
     @Override
-    public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
+    public void createOrUpgradeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
         final Map<String, String> newModuleNameToContentMap,
         final Collection<ModuleReference> allModuleReferences) {
         cpsValidator.validateNameCharacters(dataspaceName, schemaSetName);
index b4ac7a6..e1d15d6 100644 (file)
@@ -307,15 +307,16 @@ class CpsDataServiceImplSpec extends Specification {
             objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, parentNodeXpath, jsonData, observedTimestamp)
         then: 'the persistence service method is invoked with correct parameters'
             1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
-                { dataNode -> dataNode.xpath[0] == expectedNodeXpath })
+                    { dataNode -> dataNode.xpath == expectedNodeXpath})
         and: 'data updated event is sent to notification service'
             1 * mockNotificationService.processDataUpdatedEvent(anchor, parentNodeXpath, Operation.UPDATE, observedTimestamp)
         and: 'the CpsValidator is called on the dataspaceName and AnchorName'
             1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
         where: 'following parameters were used'
-            scenario         | parentNodeXpath | jsonData                        || expectedNodeXpath
-            'top level node' | '/'             | '{"test-tree": {"branch": []}}' || '/test-tree'
-            'level 2 node'   | '/test-tree'    | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']'
+            scenario         | parentNodeXpath | jsonData                                           || expectedNodeXpath
+            'top level node' | '/'             | '{"test-tree": {"branch": []}}'                    || ['/test-tree']
+            'level 2 node'   | '/test-tree'    | '{"branch": [{"name":"Name"}]}'                    || ['/test-tree/branch[@name=\'Name\']']
+            'json list'      | '/test-tree'    | '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}' || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
     }
 
     def 'Replace data node using multiple data nodes: #scenario.'() {
@@ -327,14 +328,16 @@ class CpsDataServiceImplSpec extends Specification {
             1 * mockCpsDataPersistenceService.updateDataNodesAndDescendants(dataspaceName, anchorName,
                 { dataNode -> dataNode.xpath == expectedNodeXpath})
         and: 'data updated event is sent to notification service'
-            1 * mockNotificationService.processDataUpdatedEvent(anchor, nodesJsonData.keySet()[0], Operation.UPDATE, observedTimestamp)
-            1 * mockNotificationService.processDataUpdatedEvent(anchor, nodesJsonData.keySet()[1], Operation.UPDATE, observedTimestamp)
+            nodesJsonData.keySet().each {
+                1 * mockNotificationService.processDataUpdatedEvent(anchor, it, Operation.UPDATE, observedTimestamp)
+            }
         and: 'the CpsValidator is called on the dataspaceName and AnchorName'
             1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
         where: 'following parameters were used'
             scenario         | nodesJsonData                                                                                                        || expectedNodeXpath
             'top level node' | ['/' : '{"test-tree": {"branch": []}}', '/test-tree' : '{"branch": [{"name":"Name"}]}']                              || ["/test-tree", "/test-tree/branch[@name='Name']"]
             'level 2 node'   | ['/test-tree' : '{"branch": [{"name":"Name"}]}', '/test-tree/branch[@name=\'Name\']':'{"nest":{"name":"nestName"}}'] || ["/test-tree/branch[@name='Name']", "/test-tree/branch[@name='Name']/nest"]
+            'json list'      | ['/test-tree' : '{"branch": [{"name":"Name1"}, {"name":"Name2"}]}']                                                  || ["/test-tree/branch[@name='Name1']", "/test-tree/branch[@name='Name2']"]
     }
 
     def 'Replace data node with concurrency exception in persistence layer.'() {
index a794c58..61f6741 100644 (file)
@@ -65,7 +65,7 @@ class CpsModuleServiceImplSpec extends Specification {
             def moduleReferenceForExistingModule = new ModuleReference('test',  '2021-10-12','test.org')
             def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
         when: 'create schema set from modules method is invoked'
-            objectUnderTest.createSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
+            objectUnderTest.createOrUpgradeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
         then: 'processing is delegated to persistence service'
             1 * mockCpsModulePersistenceService.storeSchemaSetFromModules('someDataspaceName', 'someSchemaSetName', [newModule: 'newContent'], listOfExistingModulesModuleReference)
         and: 'the CpsValidator is called on the dataspaceName and schemaSetName'
index 583268a..97f1bdc 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
     </parent>
 
     <artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
-            <version>11.0.14</version>
+            <version>11.0.16</version>
         </dependency>
         <dependency>
             <groupId>jakarta.servlet</groupId>
index 8daddba..3a46e43 100644 (file)
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
     </parent>
     <artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
 
index 987436f..3f9731d 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.onap.cps.ncmp.dmi.rest.stub.controller;
 
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESS;
+
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.cloudevents.CloudEvent;
@@ -28,12 +30,10 @@ import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
-import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.json.simple.parser.JSONParser;
 import org.json.simple.parser.ParseException;
-import org.onap.cps.ncmp.api.NcmpEventResponseCode;
 import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter;
 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.CmHandle;
 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
@@ -161,9 +161,9 @@ public class DmiRestStubController {
     private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
         final Response response = new Response();
         response.setOperationId(dataOperationRequest.getOperationId());
-        response.setStatusCode(NcmpEventResponseCode.SUCCESS.getStatusCode());
-        response.setStatusMessage(NcmpEventResponseCode.SUCCESS.getStatusMessage());
-        response.setIds(dataOperationRequest.getCmHandles().stream().map(CmHandle::getId).collect(Collectors.toList()));
+        response.setStatusCode(SUCCESS.getCode());
+        response.setStatusMessage(SUCCESS.getMessage());
+        response.setIds(dataOperationRequest.getCmHandles().stream().map(CmHandle::getId).toList());
         response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier());
         response.setOptions(dataOperationRequest.getOptions());
         final String ietfNetworkTopologySample = ResourceFileReaderUtil
@@ -176,7 +176,7 @@ public class DmiRestStubController {
         } catch (final ParseException parseException) {
             log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage());
         }
-        final List<Response> responseList = new ArrayList<>();
+        final List<Response> responseList = new ArrayList<>(1);
         responseList.add(response);
         final Data data = new Data();
         data.setResponses(responseList);
index 4304769..88f11c7 100644 (file)
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index bb286fd..2607385 100644 (file)
@@ -38,6 +38,7 @@ services:
     ports:
       - ${CPS_CORE_PORT:-8883}:8080
       - ${CPS_CORE_MANAGEMENT_PORT:-8887}:8081
+      - ${CPS_CORE_DEBUG_PORT:-5005}:5005
     environment:
       CPS_USERNAME: ${CPS_CORE_USERNAME:-cpsuser}
       CPS_PASSWORD: ${CPS_CORE_PASSWORD:-cpsr0cks!}
@@ -52,6 +53,7 @@ services:
       NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'
       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:
       - dbpostgresql
index 1b74153..20a5ae3 100644 (file)
@@ -10,15 +10,15 @@ CPS-NCMP Message Status Codes
 #############################
 
     +-----------------+------------------------------------------------------+-----------------------------------+
-    | Status Code     | Status Message                                       | Feature                           |
+    | Status Code     | Status Message                                       | Feature(s)                        |
     +=================+======================================================+===================================+
     | 0               | Successfully applied changes                         | Data Operation                    |
     +-----------------+------------------------------------------------------+-----------------------------------+
     | 1               | successfully applied subscription                    | CM Data Notification Subscription |
     +-----------------+------------------------------------------------------+-----------------------------------+
-    | 100             | cm handle id(s) is(are) not found                    | Data Operation                    |
+    | 100             | cm handle id(s) is(are) not found                    | Data Operation, Inventory         |
     +-----------------+------------------------------------------------------+-----------------------------------+
-    | 101             | cm handle id(s) is(are) in non ready state           | Data Operation                    |
+    | 101             | cm handle(s) not ready                               | Data Operation                    |
     +-----------------+------------------------------------------------------+-----------------------------------+
     | 102             | dmi plugin service is not responding                 | Data Operation                    |
     +-----------------+------------------------------------------------------+-----------------------------------+
@@ -32,6 +32,12 @@ CPS-NCMP Message Status Codes
     +-----------------+------------------------------------------------------+-----------------------------------+
     | 107             | southbound system is busy                            | Data Operation                    |
     +-----------------+------------------------------------------------------+-----------------------------------+
+    | 108             | Unknown error                                        | Inventory                         |
+    +-----------------+------------------------------------------------------+-----------------------------------+
+    | 109             | cm-handle already exists                             | Inventory                         |
+    +-----------------+------------------------------------------------------+-----------------------------------+
+    | 110             | cm-handle has an invalid character(s) in id          | Inventory                         |
+    +-----------------+------------------------------------------------------+-----------------------------------+
 
 .. note::
 
index 6611789..eb203d8 100644 (file)
@@ -247,7 +247,7 @@ leaf-conditions
   - Using comparative operators with string values will lead to an error at runtime. This error can't be validated earlier as the datatype is unknown until the execution phase.
   - The key should be supplied with correct data type for it to be queried from DB. In the last example above the attribute code is of type
     Integer so the cps query will not work if the value is passed as string.
-    eg: ``//categories[@code="1"]`` or ``//categories[@code='1']`` will not work because the key attribute code is treated a string.
+    e.g.: ``//categories[@code="1"]`` or ``//categories[@code='1']`` will not work because the key attribute code is treated a string.
 
 **Notes**
   - For performance reasons it does not make sense to query using key leaf as attribute. If the key value is known it is better to execute a get request with the complete xpath.
@@ -260,7 +260,7 @@ The text()-condition  can be added to any CPS path query.
 **Syntax**: ``<cps-path> ( '/' <leaf-name> '[text()=' <string-value> ']' )?``
   - ``cps-path``: Any CPS path query.
   - ``leaf-name``: The name of the leaf or leaf-list which value needs to be compared.
-  - ``string-value``: The required value of the leaf or leaf-list element as a string wrapped in quotation marks (U+0022) or apostrophes (U+0027). This wil still match integer values.
+  - ``string-value``: The required value of the leaf or leaf-list element as a string wrapped in quotation marks (U+0022) or apostrophes (U+0027). This will still match integer values.
 
 **Examples**
   - ``//book/label[text()="classic"]``
diff --git a/docs/cps-stubs.rst b/docs/cps-stubs.rst
new file mode 100644 (file)
index 0000000..00577eb
--- /dev/null
@@ -0,0 +1,120 @@
+.. 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
+
+.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
+
+.. _cpsStubs:
+
+
+CPS Stubs
+#########
+
+.. toctree::
+   :maxdepth: 1
+
+NCMP Stubs
+==========
+
+The CPS NCMP stub module provides the capability to create dynamic and customizable stubs, offering control over the responses generated for each endpoint. This capability ensures that client interactions adhere to a specified NCMP interface, enabling comprehensive testing and validation of your applications.
+
+The NCMP stub RestController is an extended implementation of the actual NCMP interface. It can be deployed as part of the application JAR or within a SpringBootTest JUnit environment, allowing you to define dynamic responses for each endpoint and  allowing testing against real stub interfaces.
+
+Prerequisites
+=============
+
+Ensure you meet the following prerequisites:
+
+1. **Required Java Installation:**
+   
+   Ensure that you have the required Java installed on your system. 
+
+2. **Access to Gerrit and Maven Installation (for building CPS project locally):**
+
+   - Ensure you have access to the ONAP Gerrit repository.
+   
+   - If you plan to build the CPS project locally, make sure you have Maven installed. 
+
+Method 1: Running Stubs as an Application
+=========================================
+
+Follow these steps to run the CPS-NCMP stub application:
+
+1. **Download Application Jar:**
+
+   You can obtain the CPS-NCMP stub application jar in one of the following ways:
+
+   - **Option 1: Download from Nexus Repository:**
+
+     Download the application jar from the Nexus repository at `https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/`_.
+
+   - **Option 2: Build Locally:**
+
+     To build the CPS project locally, navigate to the project's root directory. Once there, you can build the project using :code:`mvn clean install`, and the application CPS-NCMP stub application jar can be found in the following location:
+
+     ::
+
+       cps/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/target/
+
+2. **Run the Application:**
+
+   After obtaining the application jar, use the following command to run it:
+
+   .. code-block:: bash
+
+      java -jar ./cps-ncmp-rest-stub-app-<VERSION>.jar
+
+   Replace ``<VERSION>`` with the actual version number of the application jar.
+
+This will start the CPS-NCMP stub application, and you can interact with it as needed.
+
+.. _`https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/`: https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/
+
+Method 2: Using Stubs in Unit Tests
+===================================
+1. **Add Dependency to pom.xml:**
+
+   To include the required module in your project, add the following dependency to your `pom.xml` file:
+
+   .. code-block:: xml
+
+      <dependency>
+        <groupId>org.onap.cps</groupId>
+        <artifactId>cps-ncmp-rest-stub-service</artifactId>
+        <version>VERSION</version>
+     </dependency>
+
+   Replace ``VERSION`` with the actual version number.
+
+2. **Using Custom Response Objects:**
+
+   If you prefer to use custom response objects instead of the built-in ones, follow these steps:
+
+   Modify the `application.yaml` file located in your project's test resources directory (`src/test/resources`).
+
+   Add the following property to the `application.yaml` file, specifying the directory that contains your custom response objects:
+   
+   .. code-block:: yaml
+
+      stub:
+          path: "/my_custom_stubs/"
+
+   **Note:** Custom response objects can be placed in the `src/test/resources` directory of your project under the directory defined in above property. Refer to the `examples <https://github.com/onap/cps/tree/master/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/resources/stubs>`_ included in the CPS source repository for reference.
+
+3. **Simple Test Code:**
+
+   You may refer to the sample test code 'SampleCpsNcmpClientSpec.groovy' in the local CPS project under the following directory:
+
+   ::
+
+     /cps/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/
+
+   Alternatively, you can refer to the `example <https://github.com/onap/cps/tree/master/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub>`_ included in the CPS source repository.
+
+**Custom Responses for Supported Endpoints**
+
+  Only the following endpoints are supported for the first draft. To use your custom response objects for these endpoints, create the corresponding JSON files:
+
+  - For RequestMethod.GET /v1/ch/{cm-handle}/data/ds/{datastore-name}, create "passthrough-operational-example.json".
+
+  - For RequestMethod.POST /v1/ch/searches, create "cmHandlesSearch.json".
index 0642e6a..0fee559 100644 (file)
@@ -197,8 +197,8 @@ Any spring supported property can be configured by providing in ``config.additio
 | logging.level                         | Logging level set in cps-core                                                                           | info                          |
 |                                       |                                                                                                         |                               |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
-| config.useStrimziKafka                | If targeting a custom kafka cluster, ie useStrimziKafka: false, the config.eventPublisher.spring.kafka  | true                          |
-|                                       | values below must be set.                                                                               |                               |
+| config.useStrimziKafka                | If targeting a custom kafka cluster, i.e. useStrimziKafka: false, the                                   | true                          |
+|                                       | config.eventPublisher.spring.kafka values below must be set.                                            |                               |
 +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+
 | config.eventPublisher.                | Kafka hostname and port                                                                                 | ``<kafka-bootstrap>:9092``    |
 | spring.kafka.bootstrap-servers        |                                                                                                         |                               |
index c6413c2..80eb5f1 100755 (executable)
@@ -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) 2021-2022 Nordix Foundation
+.. Copyright (C) 2021-2023 Nordix Foundation
 
 .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
 .. _design:
@@ -83,3 +83,34 @@ NCMP Inventory CM Handle Querying
 
 The CM Handle searches ncmp inventory endpoints can be used to query for CM Handles or CM Handle IDs.
 This endpoint is described in detail in :doc:`ncmp-inventory-querying`.
+
+Common NCMP Response Codes
+==========================
+
+NCMP uses common responses codes in REST responses and events. Also the DMI plugin interface uses these codes which are defined here:
+
+.. toctree::
+   :maxdepth: 1
+
+   cps-ncmp-message-status-codes.rst
+
+Contract Testing (stubs)
+========================
+
+The CPS team is committed to supporting consumers of our APIs through contract testing.
+Obviously we test our own contracts on a continuous basis as part of the build and delivery process.
+CPS uses a contract-first approach. That means we write our OpenAPi contracts first and then generate the interface code from that.
+This means our interface implementation simply cannot deviate from the OpenApi contracts we deliver.
+
+Another advantage is that we can also generate 'stubs'. Stubs are a basic implementation of the same interface for testing purposes.
+These stubs can be used by clients for unit testing but also for more higher level integration-like testing where the real service is replaced by a stub.
+This can be useful for faster feedback loops where deployment of a full stack is difficult and not strictly needed for the purpose of the tests.
+
+Stubs for contract testing typically always return the same response which is sufficient for the strict definition of a contract test.
+However it is often useful to allow more variation in the responses so different clients or the same client can test different scenarios without having to mock the service.
+CPS has implemented what we call 'extended stubs' that allow clients to provide alternate responses.implementation
+
+The available stubs and how to use them are described in :doc:'cps-stubs'.
+
+
+
index c1427ad..573bd54 100755 (executable)
@@ -25,6 +25,7 @@ CPS
    cps-events.rst
    deployment.rst
    release-notes.rst
+   cps-stubs.rst
 
 DMI-Plugin
 ==========
index 26a7a55..e6b81fc 100755 (executable)
@@ -16,6 +16,33 @@ CPS Release Notes
 ..      * * *   MONTREAL   * * *
 ..      ========================
 
+Version: 3.3.9
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project**                      |                                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images**                    | onap/cps-and-ncmp:3.3.9                                |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation**              | 3.3.9 Montreal                                         |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release date**                     | Not yet released                                       |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.3.9
+
+Features
+--------
+
 Version: 3.3.8
 ==============
 
@@ -32,7 +59,7 @@ Release Data
 | **Release designation**              | 3.3.8 Montreal                                         |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
-| **Release date**                     | Not yet released                                       |
+| **Release date**                     | 2023 September 29                                      |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
 
@@ -42,6 +69,7 @@ Bug Fixes
 
 Features
 --------
+    - `CPS-1888 <https://jira.onap.org/browse/CPS-1888>`_ Uplift Spring Boot to 3.1.2.
 
 Version: 3.3.7
 ==============
@@ -70,7 +98,7 @@ Bug Fixes
 
 Features
 --------
-- `CPS-1789 <https://jira.onap.org/browse/CPS-1789>`_ CPS Upgrade to Springboot 3.0.
+    - `CPS-1789 <https://jira.onap.org/browse/CPS-1789>`_ CPS Upgrade to Springboot 3.0.
 
 Note
 ----
@@ -487,7 +515,7 @@ Bug Fixes
    - `CPS-1289 <https://jira.onap.org/browse/CPS-1289>`_  Getting wrong error code for create node api
    - `CPS-1326 <https://jira.onap.org/browse/CPS-1326>`_  Creation of DataNodeBuilder with module name prefix is very slow
    - `CPS-1344 <https://jira.onap.org/browse/CPS-1344>`_  Top level container (prefix) is not always the first module
-   - `CPS-1350 <https://jira.onap.org/browse/CPS-1350>`_  Add Basic Auth to CPS/NCMP OpenAPI Definitions.
+   - `CPS-1350 <https://jira.onap.org/browse/CPS-1350>`_  Add Basic Authentication to CPS/NCMP OpenAPI Definitions.
    - `CPS-1352 <https://jira.onap.org/browse/CPS-1352>`_  Handle YangChoiceNode in right format.
    - `CPS-1409 <https://jira.onap.org/browse/CPS-1409>`_  Fix Delete uses case with '/' in path.
    - `CPS-1433 <https://jira.onap.org/browse/CPS-1433>`_  Fix to allow posting data with '/' key fields.
@@ -673,7 +701,7 @@ Bug Fixes
    - `CPS-957 <https://jira.onap.org/browse/CPS-957>`_  NCMP: fix getResourceDataForPassthroughOperational endpoint
    - `CPS-1020 <https://jira.onap.org/browse/CPS-1020>`_  DuplicatedYangResourceException error at parallel cmHandle registration
    - `CPS-1056 <https://jira.onap.org/browse/CPS-1056>`_  Wrong error response format in case of Dmi plugin error
-   - `CPS-1067 <https://jira.onap.org/browse/CPS-1067>`_  NCMP returns 500 error on searches endpoint when No DMi Handles registered
+   - `CPS-1067 <https://jira.onap.org/browse/CPS-1067>`_  NCMP returns 500 error on searches endpoint when No DMI Handles registered
    - `CPS-1085 <https://jira.onap.org/browse/CPS-1085>`_  Performance degradation on ncmp/v1/ch/searches endpoint
    - `CPS-1088 <https://jira.onap.org/browse/CPS-1088>`_  Kafka consumer can not be turned off
    - `CPS-1097 <https://jira.onap.org/browse/CPS-1097>`_  Unable to change state from LOCKED to ADVISED
@@ -785,7 +813,7 @@ Bug Fixes
    - `CPS-886 <https://jira.onap.org/browse/CPS-886>`_ Fragment handling decreasing performance for large number of cmHandles
    - `CPS-887 <https://jira.onap.org/browse/CPS-887>`_ Increase performance of cmHandle registration for large number of schema sets in DB
    - `CPS-892 <https://jira.onap.org/browse/CPS-892>`_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT
-   - `CPS-893 <https://jira.onap.org/browse/CPS-893>`_ NCMP Java API depends on NCM-Rest-API (cyclic) through json properties on Java API
+   - `CPS-893 <https://jira.onap.org/browse/CPS-893>`_ NCMP Java API depends on NCMP-Rest-API (cyclic) through json properties on Java API
 
 Known Limitations, Issues and Workarounds
 -----------------------------------------
@@ -1240,7 +1268,7 @@ Security Notes
 
 *Known Security Issues*
 
-    * Weak Crytography using md5
+    * Weak Cryptography using md5
     * Risk seen in Zip file expansion
 
 *Known Vulnerabilities in Used Modules*
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
new file mode 100644 (file)
index 0000000..3bfa9a0
--- /dev/null
@@ -0,0 +1,67 @@
+api
+async
+authorization
+boolean
+behavior
+camelCasing
+cmHandle
+cmHandles
+config
+color
+cps
+cpsPath
+cpsPaths
+csit
+customizations
+dataschema
+dataspace
+dataspaces
+datasource
+datastore
+datastores
+deliverables
+dmi
+dmiPlugin
+dmiPlugins
+rnv
+hazelcast
+hostname
+jira
+json
+kafka
+kibana
+kubernetes
+kohn
+lifecycle
+liquibase
+logback
+modeled
+modeling
+normalized
+ncmp
+onap
+openapi
+optimized
+passthrough
+performant
+postgres
+queryable
+repo
+runtime
+schemas
+sql
+ssl
+standardized
+synchronize
+synchronization
+undeploying
+utilizes
+xml
+xNF
+xNFs
+xpath
+xpaths
+xPath
+XPath
+XPaths
+yaml
\ No newline at end of file
index 8684276..7020752 100644 (file)
@@ -21,23 +21,23 @@ minversion = 1.6
 envlist = docs,docs-linkcheck,docs-spellcheck
 skipsdist = true
 [testenv:docs]
-basepython = python3.8
+basepython = python3.10
 deps =
     -r{toxinidir}/requirements-docs.txt
     -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt
     -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master
 commands =
-    sphinx-build -W -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html
+    sphinx-build -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html
 [testenv:docs-linkcheck]
-basepython = python3.8
+basepython = python3.10
 deps =
     -r{toxinidir}/requirements-docs.txt
     -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt
     -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master
 commands =
-    sphinx-build -W -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck
+    sphinx-build -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck
 [testenv:docs-spellcheck]
-basepython = python3.8
+basepython = python3.10
 deps =
     -r{toxinidir}/requirements-docs.txt
     -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt
index af67a5f..b2bdce7 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index e39e114..f4cc8b7 100644 (file)
@@ -90,7 +90,7 @@ class TestConfig extends Specification{
 
     @Bean
     CpsModulePersistenceService cpsModulePersistenceService() {
-        return (CpsModulePersistenceService) new CpsModulePersistenceServiceImpl(yangResourceRepository, schemaSetRepository, dataspaceRepository, cpsAdminPersistenceService(), moduleReferenceRepository)
+        return (CpsModulePersistenceService) new CpsModulePersistenceServiceImpl(yangResourceRepository, schemaSetRepository, dataspaceRepository, moduleReferenceRepository)
     }
 
     @Bean
index cfc8ab7..d33a774 100644 (file)
@@ -94,7 +94,7 @@ class CpsModuleServiceIntegrationSpec extends FunctionalSpecBase {
             moduleReferences.addAll(existingModuleReferences)
         when: 'the new schema set is created'
             def schemaSetName = "NewSchemaWith${numberOfNewModules}Modules"
-            objectUnderTest.createSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences)
+            objectUnderTest.createOrUpgradeSchemaSetFromModules(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, newYangResourcesNameToContentMap, moduleReferences)
         and: 'associated with a new anchor'
             cpsAdminService.createAnchor(FUNCTIONAL_TEST_DATASPACE_1, schemaSetName, 'newAnchor')
         then: 'the new anchor has the correct number of modules'
index 22e071f..020cbc9 100644 (file)
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.8-SNAPSHOT</version>
+        <version>3.3.9-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/pom.xml b/pom.xml
index 71310fb..9efdef2 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.3.8-SNAPSHOT</version>\r
+    <version>3.3.9-SNAPSHOT</version>\r
     <packaging>pom</packaging>\r
 \r
     <name>cps</name>\r
diff --git a/releases/3.3.8-container.yaml b/releases/3.3.8-container.yaml
new file mode 100644 (file)
index 0000000..810f328
--- /dev/null
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.3.8
+project: cps
+log_dir: cps-maven-docker-stage-master/929/
+ref: cb9d15022ca5e7696e474ffc8c1b10fa053d6b40
+containers:
+  - name: 'cps-and-ncmp'
+    version: '3.3.8-20230928T113343Z'
\ No newline at end of file
diff --git a/releases/3.3.8.yaml b/releases/3.3.8.yaml
new file mode 100644 (file)
index 0000000..e28b453
--- /dev/null
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/937/
+project: cps
+version: 3.3.8
\ No newline at end of file
index d511c0f..743d3b7 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>3.3.8-SNAPSHOT</version>
+    <version>3.3.9-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
index 4709bf1..482fe36 100755 (executable)
@@ -22,7 +22,7 @@
 
 major=3
 minor=3
-patch=8
+patch=9
 
 base_version=${major}.${minor}.${patch}