Merge "Replace deprecated WebSecurityConfigurerAdapter"
authorToine Siebelink <toine.siebelink@est.tech>
Tue, 20 Jun 2023 07:30:56 +0000 (07:30 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 20 Jun 2023 07:30:56 +0000 (07:30 +0000)
60 files changed:
checkstyle/pom.xml
cps-application/pom.xml
cps-application/src/main/resources/application.yml
cps-bom/pom.xml
cps-dependencies/pom.xml
cps-events/pom.xml
cps-ncmp-events/pom.xml
cps-ncmp-rest-stub/pom.xml
cps-ncmp-rest/pom.xml
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfig.java [new file with mode: 0644]
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/avcsubscription/SubscriptionEventResponseOutcome.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistence.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionStatus.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfigSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DataNodeHelperSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/kafka/MessagingBaseSpec.groovy
cps-ncmp-service/src/test/resources/application.yml
cps-parent/pom.xml
cps-path-parser/pom.xml
cps-rest/pom.xml
cps-ri/pom.xml
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy [deleted file]
cps-ri/src/test/java/org/onap/cps/DatabaseTestContainer.java [deleted file]
cps-ri/src/test/java/org/onap/cps/TestApplication.java [deleted file]
cps-ri/src/test/resources/application.yml [deleted file]
cps-ri/src/test/resources/data/anchor.sql [deleted file]
cps-ri/src/test/resources/data/anchors-schemaset-modules.sql [deleted file]
cps-ri/src/test/resources/data/clear-all.sql [deleted file]
cps-ri/src/test/resources/data/fragment.sql [deleted file]
cps-ri/src/test/resources/data/perf-test.sql [deleted file]
cps-ri/src/test/resources/data/schemaset.sql [deleted file]
cps-ri/src/test/resources/hibernate.cfg.xml [deleted file]
cps-service/pom.xml
docs/api/swagger/ncmp/openapi.yaml
docs/release-notes.rst
integration-test/pom.xml
integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy
integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/SessionManagerIntegrationSpec.groovy [moved from cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy with 65% similarity]
integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy
integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy
jacoco-report/pom.xml
pom.xml
releases/3.3.2-container.yaml [new file with mode: 0644]
releases/3.3.2.yaml [new file with mode: 0644]
spotbugs/pom.xml
version.properties

index cc07fce..2129244 100644 (file)
@@ -26,7 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>3.3.2-SNAPSHOT</version>
+    <version>3.3.3-SNAPSHOT</version>
 
     <profiles>
         <profile>
index f6a56ce..2e7b12d 100755 (executable)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 802da9e..ed71339 100644 (file)
@@ -75,7 +75,7 @@ spring:
         security:
             protocol: PLAINTEXT
         producer:
-            value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
+            value-serializer: io.cloudevents.kafka.CloudEventSerializer
             client-id: cps-core
         consumer:
             group-id: ${NCMP_CONSUMER_GROUP_ID:ncmp-group}
@@ -83,7 +83,7 @@ spring:
             value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
             properties:
                 spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
-                spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
+                spring.deserializer.value.delegate.class: io.cloudevents.kafka.CloudEventDeserializer
                 spring.json.use.type.headers: false
 
     jackson:
index 4c99fcb..a87b34b 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-bom</artifactId>
-    <version>3.3.2-SNAPSHOT</version>
+    <version>3.3.3-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
index e06bbd7..8003d30 100755 (executable)
@@ -27,7 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-dependencies</artifactId>
-    <version>3.3.2-SNAPSHOT</version>
+    <version>3.3.3-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>${project.groupId}:${project.artifactId}</name>
                 <artifactId>cglib-nodep</artifactId>
                 <version>3.1</version>
             </dependency>
+            <dependency>
+                <groupId>io.cloudevents</groupId>
+                <artifactId>cloudevents-bom</artifactId>
+                <version>2.5.0</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
             <dependency>
                 <groupId>org.apache.commons</groupId>
                 <artifactId>commons-lang3</artifactId>
index b8ddb20..66c4fe5 100644 (file)
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 52ca77e..1dfb7eb 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index f434863..0a6684f 100644 (file)
@@ -26,7 +26,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index b3ac659..8c84546 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index b87fe64..19ef988 100644 (file)
@@ -27,7 +27,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.cloudevents</groupId>
+            <artifactId>cloudevents-json-jackson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.cloudevents</groupId>
+            <artifactId>cloudevents-kafka</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.cloudevents</groupId>
+            <artifactId>cloudevents-spring</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>cps-service</artifactId>
@@ -54,8 +66,8 @@
             <artifactId>cps-path-parser</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-web</artifactId>
+            <groupId>com.hazelcast</groupId>
+            <artifactId>hazelcast-spring</artifactId>
         </dependency>
         <dependency>
             <groupId>org.mapstruct</groupId>
@@ -66,8 +78,8 @@
             <artifactId>mapstruct-processor</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.hazelcast</groupId>
-            <artifactId>hazelcast-spring</artifactId>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
         </dependency>
         <!-- T E S T - D E P E N D E N C I E S -->
         <dependency>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfig.java
new file mode 100644 (file)
index 0000000..b76f86e
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config.kafka;
+
+import io.cloudevents.CloudEvent;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.core.ProducerFactory;
+import org.springframework.kafka.support.serializer.JsonDeserializer;
+import org.springframework.kafka.support.serializer.JsonSerializer;
+
+/**
+ * kafka Configuration for legacy and cloud events.
+ *
+ * @param <T> valid legacy event to be published over the wire.
+ */
+@Configuration
+@EnableKafka
+@RequiredArgsConstructor
+public class KafkaTemplateConfig<T> {
+
+    private final KafkaProperties kafkaProperties;
+
+    /**
+     * This sets the strategy for creating legacy Kafka producer instance from kafka properties defined into
+     * application.yml and replaces value-serializer by JsonSerializer.
+     *
+     * @return legacy event producer instance.
+     */
+    @Bean
+    public ProducerFactory<String, T> legacyEventProducerFactory() {
+        final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties();
+        producerConfigProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
+        return new DefaultKafkaProducerFactory<>(producerConfigProperties);
+    }
+
+    /**
+     * The ConsumerFactory implementation is to produce new legacy instance for provided kafka properties defined
+     * into application.yml and replaces deserializer-value by JsonDeserializer.
+     *
+     * @return an instance of legacy consumer factory.
+     */
+    @Bean
+    public ConsumerFactory<String, T> legacyEventConsumerFactory() {
+        final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties();
+        consumerConfigProperties.put("spring.deserializer.value.delegate.class", JsonDeserializer.class);
+        return new DefaultKafkaConsumerFactory<>(consumerConfigProperties);
+    }
+
+    /**
+     * This sets the strategy for creating cloud Kafka producer instance from kafka properties defined into
+     * application.yml with CloudEventSerializer.
+     *
+     * @return cloud event producer instance.
+     */
+    @Bean
+    public ProducerFactory<String, CloudEvent> cloudEventProducerFactory() {
+        final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties();
+        return new DefaultKafkaProducerFactory<>(producerConfigProperties);
+    }
+
+    /**
+     * The ConsumerFactory implementation to produce new legacy instance for provided kafka properties defined
+     * into application.yml having CloudEventDeserializer as deserializer-value.
+     *
+     * @return an instance of cloud consumer factory.
+     */
+    @Bean
+    public ConsumerFactory<String, CloudEvent> cloudEventConsumerFactory() {
+        final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties();
+        return new DefaultKafkaConsumerFactory<>(consumerConfigProperties);
+    }
+
+    /**
+     * A legacy Kafka event template for executing high-level operations. The legacy producer factory ensure this.
+     *
+     * @return an instance of legacy Kafka template.
+     */
+    @Bean
+    @Primary
+    public KafkaTemplate<String, T> legacyEventKafkaTemplate() {
+        final KafkaTemplate<String, T> kafkaTemplate = new KafkaTemplate<>(legacyEventProducerFactory());
+        kafkaTemplate.setConsumerFactory(legacyEventConsumerFactory());
+        return kafkaTemplate;
+    }
+
+    /**
+     * A cloud Kafka event template for executing high-level operations. The cloud producer factory ensure this.
+     *
+     * @return an instance of cloud Kafka template.
+     */
+    @Bean
+    public KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate() {
+        final KafkaTemplate<String, CloudEvent> kafkaTemplate = new KafkaTemplate<>(cloudEventProducerFactory());
+        kafkaTemplate.setConsumerFactory(cloudEventConsumerFactory());
+        return kafkaTemplate;
+    }
+
+}
index d92316d..7b28b4c 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.api.impl.events;
 
+import io.cloudevents.CloudEvent;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -42,7 +43,12 @@ import org.springframework.util.concurrent.ListenableFutureCallback;
 @RequiredArgsConstructor
 public class EventsPublisher<T> {
 
-    private final KafkaTemplate<String, T> eventKafkaTemplate;
+    /** Once all cps events will be modified to cloud compliant, will remove legacyKafkaEventTemplate with
+     it's java configuration file KafkaTemplateConfig. **/
+    @Deprecated(forRemoval = true)
+    private final KafkaTemplate<String, T> legacyKafkaEventTemplate;
+
+    private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
 
     /**
      * Generic Event publisher.
@@ -54,7 +60,8 @@ public class EventsPublisher<T> {
      */
     @Deprecated
     public void publishEvent(final String topicName, final String eventKey, final T event) {
-        final ListenableFuture<SendResult<String, T>> eventFuture = eventKafkaTemplate.send(topicName, eventKey, event);
+        final ListenableFuture<SendResult<String, T>> eventFuture
+                = legacyKafkaEventTemplate.send(topicName, eventKey, event);
         eventFuture.addCallback(handleCallback(topicName));
     }
 
@@ -70,7 +77,7 @@ public class EventsPublisher<T> {
 
         final ProducerRecord<String, T> producerRecord =
                 new ProducerRecord<>(topicName, null, eventKey, event, eventHeaders);
-        final ListenableFuture<SendResult<String, T>> eventFuture = eventKafkaTemplate.send(producerRecord);
+        final ListenableFuture<SendResult<String, T>> eventFuture = legacyKafkaEventTemplate.send(producerRecord);
         eventFuture.addCallback(handleCallback(topicName));
     }
 
index a746825..1bfc4ab 100644 (file)
@@ -22,8 +22,6 @@ package org.onap.cps.ncmp.api.impl.events.avcsubscription;
 
 import java.io.Serializable;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
@@ -104,29 +102,12 @@ public class SubscriptionEventResponseOutcome {
     private SubscriptionEventResponse toSubscriptionEventResponse(
             final List<Collection<Serializable>> cmHandleIdToStatus, final String subscriptionClientId,
             final String subscriptionName) {
-        final Map<String, SubscriptionStatus> cmHandleIdToStatusMap = new HashMap<>();
+        final Map<String, SubscriptionStatus> cmHandleIdToStatusMap =
+                DataNodeHelper.getCmHandleIdToStatusMap(cmHandleIdToStatus);
+
         final SubscriptionEventResponse subscriptionEventResponse = new SubscriptionEventResponse();
         subscriptionEventResponse.setClientId(subscriptionClientId);
         subscriptionEventResponse.setSubscriptionName(subscriptionName);
-
-        for (final Collection<Serializable> cmHandleToStatusBucket: cmHandleIdToStatus) {
-            final Iterator<Serializable> bucketIterator = cmHandleToStatusBucket.iterator();
-            while (bucketIterator.hasNext()) {
-                final String item = (String) bucketIterator.next();
-                if ("PENDING".equals(item)) {
-                    cmHandleIdToStatusMap.put((String) bucketIterator.next(),
-                            SubscriptionStatus.PENDING);
-                }
-                if ("REJECTED".equals(item)) {
-                    cmHandleIdToStatusMap.put((String) bucketIterator.next(),
-                            SubscriptionStatus.REJECTED);
-                }
-                if ("ACCEPTED".equals(item)) {
-                    cmHandleIdToStatusMap.put((String) bucketIterator.next(),
-                            SubscriptionStatus.ACCEPTED);
-                }
-            }
-        }
         subscriptionEventResponse.setCmHandleIdToStatus(cmHandleIdToStatusMap);
 
         return subscriptionEventResponse;
index f240c45..27d4266 100644 (file)
@@ -39,4 +39,11 @@ public interface SubscriptionPersistence {
      * @return the DataNode as collection.
      */
     Collection<DataNode> getDataNodesForSubscriptionEvent();
+
+    /**
+     * Get data nodes by xpath.
+     *
+     * @return the DataNode as collection.
+     */
+    Collection<DataNode> getCmHandlesForSubscriptionEvent(String clientId, String subscriptionName);
 }
index 9a063d6..d2b1237 100644 (file)
@@ -22,11 +22,18 @@ package org.onap.cps.ncmp.api.impl.subscriptions;
 
 import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
 
+import java.io.Serializable;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsDataService;
+import org.onap.cps.ncmp.api.impl.utils.DataNodeHelper;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelSubscriptionEvent;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
@@ -41,35 +48,86 @@ public class SubscriptionPersistenceImpl implements SubscriptionPersistence {
     private static final String SUBSCRIPTION_DATASPACE_NAME = "NCMP-Admin";
     private static final String SUBSCRIPTION_ANCHOR_NAME = "AVC-Subscriptions";
     private static final String SUBSCRIPTION_REGISTRY_PARENT = "/subscription-registry";
-
     private final JsonObjectMapper jsonObjectMapper;
     private final CpsDataService cpsDataService;
 
     @Override
     public void saveSubscriptionEvent(final YangModelSubscriptionEvent yangModelSubscriptionEvent) {
-        final String subscriptionEventJsonData =
-                createSubscriptionEventJsonData(jsonObjectMapper.asJsonString(yangModelSubscriptionEvent));
+        final String clientId = yangModelSubscriptionEvent.getClientId();
+        final String subscriptionName = yangModelSubscriptionEvent.getSubscriptionName();
+
         final Collection<DataNode> dataNodes = cpsDataService.getDataNodes(SUBSCRIPTION_DATASPACE_NAME,
                 SUBSCRIPTION_ANCHOR_NAME, SUBSCRIPTION_REGISTRY_PARENT, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+
+        if (isSubscriptionRegistryEmptyOrNonExist(dataNodes, clientId, subscriptionName)) {
+            saveSubscriptionEventYangModel(createSubscriptionEventJsonData(
+                    jsonObjectMapper.asJsonString(yangModelSubscriptionEvent)));
+        } else {
+            findDeltaCmHandlesAddOrUpdateInDatabase(yangModelSubscriptionEvent, clientId, subscriptionName, dataNodes);
+        }
+    }
+
+    private void findDeltaCmHandlesAddOrUpdateInDatabase(final YangModelSubscriptionEvent yangModelSubscriptionEvent,
+                                                         final String clientId, final String subscriptionName,
+                                                         final Collection<DataNode> dataNodes) {
+        final Map<String, SubscriptionStatus> cmHandleIdsFromYangModel =
+                extractCmHandleFromYangModelAsMap(yangModelSubscriptionEvent);
+        final Map<String, SubscriptionStatus> cmHandleIdsFromDatabase =
+                extractCmHandleFromDbAsMap(dataNodes);
+
+        final Map<String, SubscriptionStatus> newCmHandles =
+                mapDifference(cmHandleIdsFromYangModel, cmHandleIdsFromDatabase);
+        traverseCmHandleList(newCmHandles, clientId, subscriptionName, true);
+
+        final Map<String, SubscriptionStatus> existingCmHandles =
+                mapDifference(cmHandleIdsFromYangModel, newCmHandles);
+        traverseCmHandleList(existingCmHandles, clientId, subscriptionName, false);
+    }
+
+    private boolean isSubscriptionRegistryEmptyOrNonExist(final Collection<DataNode> dataNodes,
+                                                          final String clientId, final String subscriptionName) {
         final Optional<DataNode> dataNodeFirst = dataNodes.stream().findFirst();
-        final boolean isCreateOperation =
-                dataNodeFirst.isPresent() && dataNodeFirst.get().getChildDataNodes().isEmpty();
-        saveOrUpdateSubscriptionEventYangModel(subscriptionEventJsonData, isCreateOperation);
+        return ((dataNodeFirst.isPresent() && dataNodeFirst.get().getChildDataNodes().isEmpty())
+                || getCmHandlesForSubscriptionEvent(clientId, subscriptionName).isEmpty());
+    }
+
+    private void traverseCmHandleList(final Map<String, SubscriptionStatus> cmHandleMap,
+                                      final String clientId,
+                                      final String subscriptionName,
+                                      final boolean isAddListElementOperation) {
+        final List<YangModelSubscriptionEvent.TargetCmHandle> cmHandleList =
+                targetCmHandlesAsList(cmHandleMap);
+        for (final YangModelSubscriptionEvent.TargetCmHandle targetCmHandle : cmHandleList) {
+            final String targetCmHandleAsJson =
+                    createTargetCmHandleJsonData(jsonObjectMapper.asJsonString(targetCmHandle));
+            addOrReplaceCmHandlePredicateListElement(targetCmHandleAsJson, clientId, subscriptionName,
+                    isAddListElementOperation);
+        }
     }
 
-    private void saveOrUpdateSubscriptionEventYangModel(final String subscriptionEventJsonData,
-                                                        final boolean isCreateOperation) {
-        if (isCreateOperation) {
-            log.info("SubscriptionEventJsonData to be saved into DB {}", subscriptionEventJsonData);
-            cpsDataService.saveListElements(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
-                    SUBSCRIPTION_REGISTRY_PARENT, subscriptionEventJsonData, NO_TIMESTAMP);
+    private void addOrReplaceCmHandlePredicateListElement(final String targetCmHandleAsJson,
+                                                          final String clientId,
+                                                          final String subscriptionName,
+                                                          final boolean isAddListElementOperation) {
+        if (isAddListElementOperation) {
+            log.info("targetCmHandleAsJson to be added into DB {}", targetCmHandleAsJson);
+            cpsDataService.saveListElements(SUBSCRIPTION_DATASPACE_NAME,
+                    SUBSCRIPTION_ANCHOR_NAME, createCmHandleXpathPredicates(clientId, subscriptionName),
+                    targetCmHandleAsJson, NO_TIMESTAMP);
         } else {
-            log.info("SubscriptionEventJsonData to be updated into DB {}", subscriptionEventJsonData);
-            cpsDataService.updateDataNodeAndDescendants(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
-                    SUBSCRIPTION_REGISTRY_PARENT, subscriptionEventJsonData, NO_TIMESTAMP);
+            log.info("targetCmHandleAsJson to be updated into DB {}", targetCmHandleAsJson);
+            cpsDataService.updateNodeLeaves(SUBSCRIPTION_DATASPACE_NAME,
+                    SUBSCRIPTION_ANCHOR_NAME, createCmHandleXpathPredicates(clientId, subscriptionName),
+                    targetCmHandleAsJson, NO_TIMESTAMP);
         }
     }
 
+    private void saveSubscriptionEventYangModel(final String subscriptionEventJsonData) {
+        log.info("SubscriptionEventJsonData to be saved into DB {}", subscriptionEventJsonData);
+        cpsDataService.saveListElements(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+                SUBSCRIPTION_REGISTRY_PARENT, subscriptionEventJsonData, NO_TIMESTAMP);
+    }
+
     @Override
     public Collection<DataNode> getDataNodesForSubscriptionEvent() {
         return cpsDataService.getDataNodes(SUBSCRIPTION_DATASPACE_NAME,
@@ -77,7 +135,58 @@ public class SubscriptionPersistenceImpl implements SubscriptionPersistence {
                 FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
     }
 
+    @Override
+    public Collection<DataNode> getCmHandlesForSubscriptionEvent(final String clientId, final String subscriptionName) {
+        return cpsDataService.getDataNodesForMultipleXpaths(SUBSCRIPTION_DATASPACE_NAME,
+                SUBSCRIPTION_ANCHOR_NAME, Arrays.asList(createCmHandleXpath(clientId, subscriptionName)),
+                FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+    }
+
+    private static Map<String, SubscriptionStatus> extractCmHandleFromDbAsMap(final Collection<DataNode> dataNodes) {
+        final List<Map<String, Serializable>> dataNodeLeaves = DataNodeHelper.getDataNodeLeaves(dataNodes);
+        final List<Collection<Serializable>> cmHandleIdToStatus = DataNodeHelper.getCmHandleIdToStatus(dataNodeLeaves);
+        return DataNodeHelper.getCmHandleIdToStatusMap(cmHandleIdToStatus);
+    }
+
+    private static Map<String, SubscriptionStatus> extractCmHandleFromYangModelAsMap(
+            final YangModelSubscriptionEvent yangModelSubscriptionEvent) {
+        return yangModelSubscriptionEvent.getPredicates().getTargetCmHandles()
+                .stream().collect(Collectors.toMap(
+                        YangModelSubscriptionEvent.TargetCmHandle::getCmHandleId,
+                        YangModelSubscriptionEvent.TargetCmHandle::getStatus));
+    }
+
+    private static List<YangModelSubscriptionEvent.TargetCmHandle> targetCmHandlesAsList(
+            final Map<String, SubscriptionStatus> newCmHandles) {
+        return newCmHandles.entrySet().stream().map(entry ->
+                new YangModelSubscriptionEvent.TargetCmHandle(entry.getKey(),
+                        entry.getValue())).collect(Collectors.toList());
+    }
+
     private static String createSubscriptionEventJsonData(final String yangModelSubscriptionAsJson) {
         return "{\"subscription\":[" + yangModelSubscriptionAsJson + "]}";
     }
+
+    private static String createTargetCmHandleJsonData(final String targetCmHandleAsJson) {
+        return "{\"targetCmHandles\":[" + targetCmHandleAsJson + "]}";
+    }
+
+    private static String createCmHandleXpathPredicates(final String clientId, final String subscriptionName) {
+        return "/subscription-registry/subscription[@clientID='" + clientId
+                + "' and @subscriptionName='" + subscriptionName + "']/predicates";
+    }
+
+    private static String createCmHandleXpath(final String clientId, final String subscriptionName) {
+        return "/subscription-registry/subscription[@clientID='" + clientId
+                + "' and @subscriptionName='" + subscriptionName + "']";
+    }
+
+    private static <K, V> Map<K, V> mapDifference(final Map<? extends K, ? extends V> left,
+                                                  final Map<? extends K, ? extends V> right) {
+        final Map<K, V> difference = new HashMap<>();
+        difference.putAll(left);
+        difference.putAll(right);
+        difference.entrySet().removeAll(right.entrySet());
+        return difference;
+    }
 }
index 0b4f91f..ce3b88b 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.subscriptions;
 
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+
 public enum SubscriptionStatus {
     ACCEPTED,
     REJECTED,
-    PENDING
+    PENDING;
+
+
+    /**
+     * Populates a map with a key of cm handle id and a value of subscription status.
+     *
+     * @param resultMap the map is being populated
+     * @param bucketIterator to iterate over the collection
+     */
+    public static void populateCmHandleToSubscriptionStatusMap(final Map<String, SubscriptionStatus> resultMap,
+                                                          final Iterator<Serializable> bucketIterator) {
+        final String item = (String) bucketIterator.next();
+        if ("PENDING".equals(item)) {
+            resultMap.put((String) bucketIterator.next(),
+                    SubscriptionStatus.PENDING);
+        }
+        if ("REJECTED".equals(item)) {
+            resultMap.put((String) bucketIterator.next(),
+                    SubscriptionStatus.REJECTED);
+        }
+        if ("ACCEPTED".equals(item)) {
+            resultMap.put((String) bucketIterator.next(),
+                    SubscriptionStatus.ACCEPTED);
+        }
+    }
 }
index 1648ac4..8d44592 100644 (file)
@@ -22,12 +22,15 @@ package org.onap.cps.ncmp.api.impl.utils;
 
 import java.io.Serializable;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus;
 import org.onap.cps.spi.model.DataNode;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
@@ -72,4 +75,22 @@ public class DataNodeHelper {
                         || col.contains("REJECTED"))
                 .collect(Collectors.toList());
     }
+
+    /**
+     * The cm handle and status is returned as a map.
+     *
+     * @param cmHandleIdToStatus as a list of collection
+     * @return a map of cm handle id to status
+     */
+    public static Map<String, SubscriptionStatus> getCmHandleIdToStatusMap(
+            final List<Collection<Serializable>> cmHandleIdToStatus) {
+        final Map<String, SubscriptionStatus> resultMap = new HashMap<>();
+        for (final Collection<Serializable> cmHandleToStatusBucket: cmHandleIdToStatus) {
+            final Iterator<Serializable> bucketIterator = cmHandleToStatusBucket.iterator();
+            while (bucketIterator.hasNext()) {
+                SubscriptionStatus.populateCmHandleToSubscriptionStatusMap(resultMap, bucketIterator);
+            }
+        }
+        return resultMap;
+    }
 }
index bcf75a2..fe7b3f1 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.ncmp.api.impl.async
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.apache.kafka.clients.consumer.KafkaConsumer
+import org.apache.kafka.common.serialization.StringDeserializer
 import org.mapstruct.factory.Mappers
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher
 import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
@@ -34,7 +35,6 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.annotation.DirtiesContext
 import org.testcontainers.spock.Testcontainers
-
 import java.time.Duration
 
 @SpringBootTest(classes = [EventsPublisher, NcmpAsyncRequestResponseEventConsumer, ObjectMapper, JsonObjectMapper])
@@ -44,7 +44,7 @@ class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends MessagingBase
 
     @SpringBean
     EventsPublisher cpsAsyncRequestResponseEventPublisher =
-        new EventsPublisher<NcmpAsyncRequestResponseEvent>(kafkaTemplate);
+        new EventsPublisher<NcmpAsyncRequestResponseEvent>(legacyEventKafkaTemplate, cloudEventKafkaTemplate);
 
 
     @SpringBean
@@ -59,18 +59,18 @@ class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends MessagingBase
     @Autowired
     JsonObjectMapper jsonObjectMapper
 
-    def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('test'))
+    def legacyEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('test', StringDeserializer))
 
     def 'Consume and forward valid message'() {
         given: 'consumer has a subscription'
-            kafkaConsumer.subscribe(['test-topic'] as List<String>)
+            legacyEventKafkaConsumer.subscribe(['test-topic'] as List<String>)
         and: 'an event is sent'
             def jsonData = TestUtils.getResourceFileContent('dmiAsyncRequestResponseEvent.json')
             def testEventSent = jsonObjectMapper.convertJsonString(jsonData, DmiAsyncRequestResponseEvent.class)
         when: 'the event is consumed'
             ncmpAsyncRequestResponseEventConsumer.consumeAndForward(testEventSent)
         and: 'the topic is polled'
-            def records = kafkaConsumer.poll(Duration.ofMillis(1500))
+            def records = legacyEventKafkaConsumer.poll(Duration.ofMillis(1500))
         then: 'poll returns one record'
             assert records.size() == 1
         and: 'consumed forwarded event id is the same as sent event id'
index 28464bb..02071cd 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.commons.lang3.SerializationUtils
 import org.apache.kafka.clients.consumer.ConsumerRecord
 import org.apache.kafka.clients.consumer.KafkaConsumer
 import org.apache.kafka.common.header.internals.RecordHeader
+import org.apache.kafka.common.serialization.StringDeserializer
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher
 import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
 import org.onap.cps.ncmp.events.async.BatchDataResponseEventV1
@@ -46,7 +47,7 @@ import java.time.Duration
 class NcmpAsyncBatchEventConsumerSpec extends MessagingBaseSpec {
 
     @SpringBean
-    EventsPublisher asyncBatchEventPublisher = new EventsPublisher<BatchDataResponseEventV1>(kafkaTemplate)
+    EventsPublisher asyncBatchEventPublisher = new EventsPublisher<BatchDataResponseEventV1>(legacyEventKafkaTemplate, cloudEventKafkaTemplate)
 
     @SpringBean
     NcmpAsyncBatchEventConsumer asyncBatchEventConsumer = new NcmpAsyncBatchEventConsumer(asyncBatchEventPublisher)
@@ -57,19 +58,19 @@ class NcmpAsyncBatchEventConsumerSpec extends MessagingBaseSpec {
     @Autowired
     RecordFilterStrategy<String, BatchDataResponseEventV1> recordFilterStrategy
 
-    def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('test'))
+    def legacyEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('test', StringDeserializer))
     def static clientTopic = 'client-topic'
     def static batchEventType = 'org.onap.cps.ncmp.events.async.BatchDataResponseEventV1'
 
     def 'Consume and publish event to client specified topic'() {
         given: 'consumer subscribing to client topic'
-            kafkaConsumer.subscribe([clientTopic])
+            legacyEventKafkaConsumer.subscribe([clientTopic])
         and: 'consumer record for batch event'
             def consumerRecordIn = createConsumerRecord(batchEventType)
         when: 'the batch event is consumed and published to client specified topic'
             asyncBatchEventConsumer.consumeAndPublish(consumerRecordIn)
         and: 'the client specified topic is polled'
-            def consumerRecordOut = kafkaConsumer.poll(Duration.ofMillis(1500))[0]
+            def consumerRecordOut = legacyEventKafkaConsumer.poll(Duration.ofMillis(1500))[0]
         then: 'verifying consumed event operationID is same as published event operationID'
             def operationIdIn = consumerRecordIn.value.event.batchResponses[0].operationId
             def operationIdOut = jsonObjectMapper.convertJsonString((String)consumerRecordOut.value(), BatchDataResponseEventV1.class).event.batchResponses[0].operationId
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfigSpec.groovy
new file mode 100644 (file)
index 0000000..ed5f161
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config.kafka;
+
+import io.cloudevents.CloudEvent
+import io.cloudevents.kafka.CloudEventDeserializer
+import io.cloudevents.kafka.CloudEventSerializer
+import org.spockframework.spring.EnableSharedInjection
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties
+import org.springframework.boot.context.properties.EnableConfigurationProperties
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.kafka.core.KafkaTemplate
+import org.springframework.kafka.support.serializer.JsonDeserializer
+import org.springframework.kafka.support.serializer.JsonSerializer
+import spock.lang.Shared
+import spock.lang.Specification
+
+@SpringBootTest(classes = [KafkaProperties, KafkaTemplateConfig])
+@EnableSharedInjection
+@EnableConfigurationProperties
+class KafkaTemplateConfigSpec extends Specification {
+
+    @Shared
+    @Autowired
+    KafkaTemplate<String, String> legacyEventKafkaTemplate
+
+    @Shared
+    @Autowired
+    KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate
+
+    def 'Verify kafka template serializer and deserializer configuration for #eventType.'() {
+        expect: 'kafka template is instantiated'
+            assert kafkaTemplateInstance.properties['beanName'] == beanName
+        and: 'verify event key and value serializer'
+            assert kafkaTemplateInstance.properties['producerFactory'].configs['value.serializer'].asType(String.class).contains(valueSerializer.getCanonicalName())
+        and: 'verify event key and value deserializer'
+            assert kafkaTemplateInstance.properties['consumerFactory'].configs['spring.deserializer.value.delegate.class'].asType(String.class).contains(delegateDeserializer.getCanonicalName())
+        where: 'the following event type is used'
+            eventType      | kafkaTemplateInstance    || beanName                   | valueSerializer      | delegateDeserializer
+            'legacy event' | legacyEventKafkaTemplate || 'legacyEventKafkaTemplate' | JsonSerializer       | JsonDeserializer
+            'cloud event'  | cloudEventKafkaTemplate  || 'cloudEventKafkaTemplate'  | CloudEventSerializer | CloudEventDeserializer
+    }
+}
index 5f54bbe..3dffac7 100644 (file)
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.apache.kafka.clients.consumer.ConsumerRecord
 import org.apache.kafka.clients.consumer.KafkaConsumer
 import org.apache.kafka.common.header.internals.RecordHeader
+import org.apache.kafka.common.serialization.StringDeserializer
 import org.mapstruct.factory.Mappers
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher
 import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
@@ -48,7 +49,7 @@ class AvcEventConsumerSpec extends MessagingBaseSpec {
     AvcEventMapper avcEventMapper = Mappers.getMapper(AvcEventMapper.class)
 
     @SpringBean
-    EventsPublisher eventsPublisher = new EventsPublisher<AvcEvent>(kafkaTemplate)
+    EventsPublisher eventsPublisher = new EventsPublisher<AvcEvent>(legacyEventKafkaTemplate, cloudEventKafkaTemplate)
 
     @SpringBean
     AvcEventConsumer acvEventConsumer = new AvcEventConsumer(eventsPublisher, avcEventMapper)
@@ -56,13 +57,13 @@ class AvcEventConsumerSpec extends MessagingBaseSpec {
     @Autowired
     JsonObjectMapper jsonObjectMapper
 
-    def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('ncmp-group'))
+    def legacyEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('ncmp-group', StringDeserializer))
 
     def 'Consume and forward valid message'() {
         given: 'consumer has a subscription on a topic'
             def cmEventsTopicName = 'cm-events'
             acvEventConsumer.cmEventsTopicName = cmEventsTopicName
-            kafkaConsumer.subscribe([cmEventsTopicName] as List<String>)
+            legacyEventKafkaConsumer.subscribe([cmEventsTopicName] as List<String>)
         and: 'an event is sent'
             def jsonData = TestUtils.getResourceFileContent('sampleAvcInputEvent.json')
             def testEventSent = jsonObjectMapper.convertJsonString(jsonData, AvcEvent.class)
@@ -73,7 +74,7 @@ class AvcEventConsumerSpec extends MessagingBaseSpec {
         when: 'the event is consumed'
             acvEventConsumer.consumeAndForward(consumerRecord)
         and: 'the topic is polled'
-            def records = kafkaConsumer.poll(Duration.ofMillis(1500))
+            def records = legacyEventKafkaConsumer.poll(Duration.ofMillis(1500))
         then: 'poll returns one record'
             assert records.size() == 1
         and: 'record can be converted to AVC event'
index 9374126..4c68804 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.ncmp.api.impl.events.lcm
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.apache.kafka.clients.consumer.KafkaConsumer
+import org.apache.kafka.common.serialization.StringDeserializer
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher
 import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
 import org.onap.cps.ncmp.events.lcm.v1.Event
@@ -42,12 +43,12 @@ import java.time.Duration
 @DirtiesContext
 class LcmEventsPublisherSpec extends MessagingBaseSpec {
 
-    def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('ncmp-group'))
+    def legacyEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('ncmp-group', StringDeserializer))
 
     def testTopic = 'ncmp-events-test'
 
     @SpringBean
-    EventsPublisher<LcmEvent> lcmEventsPublisher = new EventsPublisher(kafkaTemplate)
+    EventsPublisher<LcmEvent> lcmEventsPublisher = new EventsPublisher(legacyEventKafkaTemplate, cloudEventKafkaTemplate)
 
     @Autowired
     JsonObjectMapper jsonObjectMapper
@@ -82,11 +83,11 @@ class LcmEventsPublisherSpec extends MessagingBaseSpec {
                 eventSchema       : eventSchema,
                 eventSchemaVersion: eventSchemaVersion]
         and: 'consumer has a subscription'
-            kafkaConsumer.subscribe([testTopic] as List<String>)
+            legacyEventKafkaConsumer.subscribe([testTopic] as List<String>)
         when: 'an event is published'
             lcmEventsPublisher.publishEvent(testTopic, eventKey, eventHeader, eventData)
         and: 'topic is polled'
-            def records = kafkaConsumer.poll(Duration.ofMillis(1500))
+            def records = legacyEventKafkaConsumer.poll(Duration.ofMillis(1500))
         then: 'poll returns one record'
             assert records.size() == 1
         and: 'record key matches the expected event key'
index a372abe..ec54e89 100644 (file)
@@ -34,6 +34,7 @@ class SubscriptionPersistenceSpec extends Specification {
     private static final String SUBSCRIPTION_DATASPACE_NAME = "NCMP-Admin";
     private static final String SUBSCRIPTION_ANCHOR_NAME = "AVC-Subscriptions";
     private static final String SUBSCRIPTION_REGISTRY_PARENT = "/subscription-registry";
+    private static final String SUBSCRIPTION_REGISTRY_PREDICATES_XPATH = "/subscription-registry/subscription[@clientID='some-client-id' and @subscriptionName='some-subscription-name']/predicates";
 
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
     def mockCpsDataService = Mock(CpsDataService)
@@ -45,11 +46,11 @@ class SubscriptionPersistenceSpec extends Specification {
     def yangModelSubscriptionEvent = new YangModelSubscriptionEvent(clientId: 'some-client-id',
         subscriptionName: 'some-subscription-name', tagged: true, topic: 'some-topic', predicates: predicates)
 
-   def 'save a subscription event' () {
-       given: 'a data node that does not exist in db'
+   def 'save a subscription event as yang model into db for the #scenarios' () {
+       given: 'a blank data node that exist in db'
            def blankDataNode = new DataNodeBuilder().withDataspace('NCMP-Admin')
                 .withAnchor('AVC-Subscriptions').withXpath('/subscription-registry').build()
-       and: 'cps data service return non existing data node'
+       and: 'cps data service return an empty data node'
             mockCpsDataService.getDataNodes(*_) >> [blankDataNode]
        when: 'the yangModelSubscriptionEvent is saved into db'
             objectUnderTest.saveSubscriptionEvent(yangModelSubscriptionEvent)
@@ -63,24 +64,28 @@ class SubscriptionPersistenceSpec extends Specification {
                 NO_TIMESTAMP)
    }
 
-    def 'update a subscription event' () {
-        given: 'a data node exist in db'
+    def 'add or replace cm handle list element into db' () {
+        given: 'a data node with child node exist in db'
+            def leaves1 = [status:'PENDING', cmHandleId:'cmhandle1'] as Map
             def childDataNode = new DataNodeBuilder().withDataspace('NCMP-Admin')
-                .withAnchor('AVC-Subscriptions').withXpath('/subscription-registry/subscription').build()
+                .withAnchor('AVC-Subscriptions').withXpath('/subscription-registry/subscription')
+                .withLeaves(leaves1).build()
             def engagedDataNode = new DataNodeBuilder().withDataspace('NCMP-Admin')
                 .withAnchor('AVC-Subscriptions').withXpath('/subscription-registry')
                 .withChildDataNodes([childDataNode]).build()
-        and: 'cps data service return existing data node'
+        and: 'cps data service return data node including a child data node'
             mockCpsDataService.getDataNodes(*_) >> [engagedDataNode]
-        when: 'the yangModelSubscriptionEvent is saved into db'
+        and: 'cps data service return data node for querying by xpaths'
+            mockCpsDataService.getDataNodesForMultipleXpaths(*_) >> [engagedDataNode]
+        when: 'the yang model subscription event is saved into db'
             objectUnderTest.saveSubscriptionEvent(yangModelSubscriptionEvent)
-        then: 'the cpsDataService update operation is called with the correct data'
-            1 * mockCpsDataService.updateDataNodeAndDescendants(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
-                SUBSCRIPTION_REGISTRY_PARENT,
-                '{"subscription":[{' +
-                    '"topic":"some-topic",' +
-                    '"predicates":{"datastore":"some-datastore","targetCmHandles":[{"cmHandleId":"cmhandle1","status":"PENDING"},{"cmHandleId":"cmhandle2","status":"PENDING"}]},' +
-                    '"clientID":"some-client-id","subscriptionName":"some-subscription-name","isTagged":true}]}',
+        then: 'the cpsDataService save non-existing cm handle with the correct data'
+            1 * mockCpsDataService.saveListElements(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+                SUBSCRIPTION_REGISTRY_PREDICATES_XPATH, '{"targetCmHandles":[{"cmHandleId":"cmhandle2","status":"PENDING"}]}',
+                NO_TIMESTAMP)
+        and: 'the cpsDataService update existing cm handle with the correct data'
+            1 * mockCpsDataService.updateNodeLeaves(SUBSCRIPTION_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+                SUBSCRIPTION_REGISTRY_PREDICATES_XPATH, '{"targetCmHandles":[{"cmHandleId":"cmhandle1","status":"PENDING"}]}',
                 NO_TIMESTAMP)
     }
 
index e527ae1..ee726a9 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.api.impl.utils
 
+import org.onap.cps.ncmp.api.impl.subscriptions.SubscriptionStatus
 import org.onap.cps.spi.model.DataNodeBuilder
 
 class DataNodeHelperSpec extends DataNodeBaseSpec {
@@ -51,8 +52,22 @@ class DataNodeHelperSpec extends DataNodeBaseSpec {
         and: 'the nested data node is flatten and retrieves the leaves '
             def leaves = DataNodeHelper.getDataNodeLeaves([dataNode])
         when:'cm handle id to status is retrieved'
-            def result = DataNodeHelper.getCmHandleIdToStatus(leaves);
+            def result = DataNodeHelper.getCmHandleIdToStatus(leaves)
         then: 'the result list size is 3'
             result.size() == 3
+        and: 'the result contains expected values'
+            result[0] as List == ['PENDING', 'CMHandle3']
+            result[1] as List == ['ACCEPTED', 'CMHandle2']
+            result[2] as List == ['REJECTED', 'CMHandle1']
+    }
+
+    def 'Get cm handle id to status map as expected from list of collection' () {
+        given: 'a list of collection'
+            def cmHandleCollection = [['PENDING', 'CMHandle3'], ['ACCEPTED', 'CMHandle2'], ['REJECTED', 'CMHandle1']]
+        when: 'the map is formed up with a method call'
+            def result = DataNodeHelper.getCmHandleIdToStatusMap(cmHandleCollection)
+        then: 'the map values are as expected'
+            result.keySet() == ['CMHandle3', 'CMHandle2', 'CMHandle1'] as Set
+            result.values() as List == [SubscriptionStatus.PENDING, SubscriptionStatus.ACCEPTED, SubscriptionStatus.REJECTED]
     }
 }
index 337178e..603b8cd 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.onap.cps.ncmp.api.kafka
 
+import io.cloudevents.CloudEvent
+import io.cloudevents.kafka.CloudEventSerializer
 import org.apache.kafka.common.serialization.StringDeserializer
 import org.apache.kafka.common.serialization.StringSerializer
 import org.spockframework.spring.SpringBean
@@ -44,30 +46,33 @@ class MessagingBaseSpec extends Specification {
 
     static kafkaTestContainer = new KafkaContainer(DockerImageName.parse('registry.nordix.org/onaptest/confluentinc/cp-kafka:6.2.1').asCompatibleSubstituteFor('confluentinc/cp-kafka'))
 
-    def producerConfigProperties() {
+    @SpringBean
+    KafkaTemplate legacyEventKafkaTemplate = new KafkaTemplate<>(new DefaultKafkaProducerFactory<Integer, String>(eventProducerConfigProperties(JsonSerializer)))
+
+    @SpringBean
+    KafkaTemplate cloudEventKafkaTemplate = new KafkaTemplate<>(new DefaultKafkaProducerFactory<String, CloudEvent>(eventProducerConfigProperties(CloudEventSerializer)))
+
+    @DynamicPropertySource
+    static void registerKafkaProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
+        dynamicPropertyRegistry.add('spring.kafka.bootstrap-servers', kafkaTestContainer::getBootstrapServers)
+    }
+
+    def eventProducerConfigProperties(valueSerializer) {
         return [('bootstrap.servers'): kafkaTestContainer.getBootstrapServers().split(',')[0],
                 ('retries')          : 0,
                 ('batch-size')       : 16384,
                 ('linger.ms')        : 1,
                 ('buffer.memory')    : 33554432,
                 ('key.serializer')   : StringSerializer,
-                ('value.serializer') : JsonSerializer]
+                ('value.serializer') : valueSerializer]
     }
 
-    def consumerConfigProperties(consumerGroupId) {
+    def eventConsumerConfigProperties(consumerGroupId, valueSerializer) {
         return [('bootstrap.servers') : kafkaTestContainer.getBootstrapServers().split(',')[0],
                 ('key.deserializer')  : StringDeserializer,
-                ('value.deserializer'): StringDeserializer,
+                ('value.deserializer'): valueSerializer,
                 ('auto.offset.reset') : 'earliest',
                 ('group.id')          : consumerGroupId
         ]
     }
-
-    @SpringBean
-    KafkaTemplate kafkaTemplate = new KafkaTemplate<>(new DefaultKafkaProducerFactory<Integer, String>(producerConfigProperties()))
-
-    @DynamicPropertySource
-    static void registerKafkaProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
-        dynamicPropertyRegistry.add('spring.kafka.bootstrap-servers', kafkaTestContainer::getBootstrapServers)
-    }
 }
index 1016f2b..197bfda 100644 (file)
 #  SPDX-License-Identifier: Apache-2.0
 #  ============LICENSE_END=========================================================
 
+spring:
+    kafka:
+        producer:
+            value-serializer: io.cloudevents.kafka.CloudEventSerializer
+        consumer:
+            properties:
+                spring.deserializer.value.delegate.class: io.cloudevents.kafka.CloudEventDeserializer
+
 app:
     ncmp:
         avc:
index 17c96c5..0f58fbb 100755 (executable)
@@ -32,7 +32,7 @@
 
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.3.2-SNAPSHOT</version>
+    <version>3.3.3-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <properties>
index c388c36..bd8b47c 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index c9d0797..f6f8100 100755 (executable)
@@ -28,7 +28,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index 504fce5..89e60db 100644 (file)
     <parent>\r
         <groupId>org.onap.cps</groupId>\r
         <artifactId>cps-parent</artifactId>\r
-        <version>3.3.2-SNAPSHOT</version>\r
+        <version>3.3.3-SNAPSHOT</version>\r
         <relativePath>../cps-parent/pom.xml</relativePath>\r
     </parent>\r
 \r
     <artifactId>cps-ri</artifactId>\r
 \r
     <properties>\r
-        <minimum-coverage>0.32</minimum-coverage>\r
-        <!-- Additional coverage is provided by the integration-test module -->\r
+        <minimum-coverage>0.29</minimum-coverage>\r
+        <!-- Additional coverage is provided by integration-test module -->\r
     </properties>\r
 \r
     <dependencies>\r
                 </exclusion>\r
             </exclusions>\r
         </dependency>\r
-        <dependency>\r
-            <groupId>org.testcontainers</groupId>\r
-            <artifactId>postgresql</artifactId>\r
-            <scope>test</scope>\r
-        </dependency>\r
-        <dependency>\r
-            <groupId>org.testcontainers</groupId>\r
-            <artifactId>spock</artifactId>\r
-            <scope>test</scope>\r
-        </dependency>\r
     </dependencies>\r
 \r
     <profiles>\r
index 65d63df..2e4dba2 100644 (file)
  */
 package org.onap.cps.spi.impl
 
-
 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
 import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.spi.repository.DataspaceRepository
+import org.onap.cps.spi.repository.ModuleReferenceRepository
+import org.onap.cps.spi.repository.SchemaSetRepository
 import org.onap.cps.spi.repository.YangResourceRepository
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.dao.DataIntegrityViolationException
-import spock.lang.Shared
+import org.springframework.retry.annotation.EnableRetry
+import spock.lang.Specification
 
 import java.sql.SQLException
 
-class CpsModulePersistenceServiceConcurrencySpec extends CpsPersistenceSpecBase {
+@SpringBootTest(classes=[CpsModulePersistenceServiceImpl])
+@EnableRetry
+class CpsModulePersistenceServiceConcurrencySpec extends Specification {
 
     @Autowired
     CpsModulePersistenceService objectUnderTest
 
     @SpringBean
-    YangResourceRepository yangResourceRepositoryMock = Mock()
+    DataspaceRepository dataspaceRepository = Mock()
+
+    @SpringBean
+    YangResourceRepository yangResourceRepository = Mock()
+
+    @SpringBean
+    SchemaSetRepository schemaSetRepository = Mock()
 
     @SpringBean
-    DataspaceRepository dataspaceRepositoryMock = Mock()
+    CpsAdminPersistenceService cpsAdminPersistenceService = Mock()
 
-    static final String DATASPACE_NAME = 'DATASPACE-001'
-    static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW'
-    static final String NEW_RESOURCE_NAME = 'some new resource'
-    static final String NEW_RESOURCE_CONTENT = 'module stores {\n' +
+    @SpringBean
+    ModuleReferenceRepository moduleReferenceRepository = Mock()
+
+    def NEW_RESOURCE_NAME = 'some new resource'
+    def NEW_RESOURCE_CONTENT = 'module stores {\n' +
             '    yang-version 1.1;\n' +
             '    namespace "org:onap:ccsdk:sample";\n' +
             '}'
 
     def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT]
 
-    @Shared
-    yangResourceChecksum = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539'
+    def yangResourceChecksum = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539'
 
-    @Shared
-    yangResourceChecksumDbConstraint = 'yang_resource_checksum_key'
+    def yangResourceChecksumDbConstraint = 'yang_resource_checksum_key'
 
-    @Shared
-    sqlExceptionMessage = String.format('(checksum)=(%s)', yangResourceChecksum)
+    def sqlExceptionMessage = String.format('(checksum)=(%s)', yangResourceChecksum)
 
-    @Shared
-    checksumIntegrityException =
-           new DataIntegrityViolationException("checksum integrity exception",
+    def checksumIntegrityException = new DataIntegrityViolationException("checksum integrity exception",
                  new ConstraintViolationException('', new SQLException(sqlExceptionMessage), yangResourceChecksumDbConstraint))
 
-    def 'Store new schema set, retry mechanism'() {
+    def 'Store new schema set, maximum retries.'() {
         given: 'no pre-existing schemaset in database'
-            dataspaceRepositoryMock.getByName(_) >> new DataspaceEntity()
-            yangResourceRepositoryMock.findAllByChecksumIn(_) >> Collections.emptyList()
+            dataspaceRepository.getByName(_) >> new DataspaceEntity()
+            yangResourceRepository.findAllByChecksumIn(_) >> Collections.emptyList()
         when: 'a new schemaset is stored'
-            objectUnderTest.storeSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NEW, newYangResourcesNameToContentMap)
-        then: ' duplicated yang resource exception is thrown '
-            def e = thrown(DuplicatedYangResourceException)
+            objectUnderTest.storeSchemaSet('some dataspace', 'some new schema set', newYangResourcesNameToContentMap)
+        then: 'a duplicated yang resource exception is thrown '
+            thrown(DuplicatedYangResourceException)
         and: 'the system will attempt to save the data 5 times (because checksum integrity exception is thrown each time)'
-            5 * yangResourceRepositoryMock.saveAll(_) >> { throw checksumIntegrityException }
+            5 * yangResourceRepository.saveAll(_) >> { throw checksumIntegrityException }
+    }
+
+    def 'Store new schema set, succeed on third attempt.'() {
+        given: 'no pre-existing schemaset in database'
+            dataspaceRepository.getByName(_) >> new DataspaceEntity()
+            yangResourceRepository.findAllByChecksumIn(_) >> Collections.emptyList()
+        when: 'a new schemaset is stored'
+            objectUnderTest.storeSchemaSet('some dataspace', 'some new schema set', newYangResourcesNameToContentMap)
+        then: 'no exception is thrown '
+            noExceptionThrown()
+        and: 'the system will attempt to save the data 2 times with checksum integrity exception but then succeed'
+            2 * yangResourceRepository.saveAll(_) >> { throw checksumIntegrityException }
+            1 * yangResourceRepository.saveAll(_) >> []
     }
 
-    def 'Store schema set using modules, retry mechanism'() {
+    def 'Store schema set using modules, maximum retries.'() {
         given: 'map of new modules, a list of existing modules, module reference'
             def mapOfNewModules = [newModule1: 'module newmodule { yang-version 1.1; revision "2021-10-12" { } }']
             def moduleReferenceForExistingModule = new ModuleReference("test","2021-10-12")
             def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
         and: 'no pre-existing schemaset in database'
-            dataspaceRepositoryMock.getByName(_) >> new DataspaceEntity()
-            yangResourceRepositoryMock.findAllByChecksumIn(_) >> Collections.emptyList()
+            dataspaceRepository.getByName(_) >> new DataspaceEntity()
+            yangResourceRepository.findAllByChecksumIn(_) >> Collections.emptyList()
         when: 'a new schemaset is stored from a module'
-            objectUnderTest.storeSchemaSetFromModules(DATASPACE_NAME, "newSchemaSetName" , mapOfNewModules, listOfExistingModulesModuleReference)
-        then: ' duplicated yang resource exception is thrown '
-            def e = thrown(DuplicatedYangResourceException)
+            objectUnderTest.storeSchemaSetFromModules('some dataspace', 'some new schema set' , mapOfNewModules, listOfExistingModulesModuleReference)
+        then: 'a duplicated yang resource exception is thrown '
+            thrown(DuplicatedYangResourceException)
         and: 'the system will attempt to save the data 5 times (because checksum integrity exception is thrown each time)'
-            5 * yangResourceRepositoryMock.saveAll(_) >> { throw checksumIntegrityException }
+            5 * yangResourceRepository.saveAll(_) >> { throw checksumIntegrityException }
+    }
+
+    def 'Store schema set using modules, succeed on third attempt.'() {
+        given: 'map of new modules, a list of existing modules, module reference'
+            def mapOfNewModules = [newModule1: 'module newmodule { yang-version 1.1; revision "2021-10-12" { } }']
+            def moduleReferenceForExistingModule = new ModuleReference("test","2021-10-12")
+            def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule]
+        and: 'no pre-existing schemaset in database'
+            def dataspaceEntity = new DataspaceEntity()
+            dataspaceRepository.getByName(_) >> new DataspaceEntity()
+            yangResourceRepository.findAllByChecksumIn(_) >> Collections.emptyList()
+            yangResourceRepository.getResourceIdsByModuleReferences(_) >> []
+        and: 'can retrieve schemaset details after storing it'
+            def schemaSetEntity = new SchemaSetEntity()
+            schemaSetRepository.getByDataspaceAndName(dataspaceEntity, 'new schema set') >> schemaSetEntity
+        when: 'a new schemaset is stored from a module'
+            objectUnderTest.storeSchemaSetFromModules('some dataspace', 'new schema set' , mapOfNewModules, listOfExistingModulesModuleReference)
+        then: 'no exception is thrown '
+            noExceptionThrown()
+        and: 'the system will attempt to save the data 2 times with checksum integrity exception but then succeed'
+            2 * yangResourceRepository.saveAll(_) >> { throw checksumIntegrityException }
+            1 * yangResourceRepository.saveAll(_) >> []
     }
+
 }
index 5e42ce0..52651c6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  * Copyright (c) 2021 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.
@@ -28,7 +28,6 @@ import org.onap.cps.spi.repository.ModuleReferenceRepository
 import org.onap.cps.spi.repository.SchemaSetRepository
 import org.onap.cps.spi.repository.YangResourceRepository
 import org.springframework.dao.DataIntegrityViolationException
-import spock.lang.Shared
 import spock.lang.Specification
 
 import java.sql.SQLException
@@ -38,17 +37,14 @@ import java.sql.SQLException
  */
 class CpsModulePersistenceServiceSpec extends Specification {
 
-    // Instance to test
     CpsModulePersistenceService objectUnderTest
 
-    // Mocks
     def dataspaceRepositoryMock = Mock(DataspaceRepository)
     def yangResourceRepositoryMock = Mock(YangResourceRepository)
     def schemaSetRepositoryMock = Mock(SchemaSetRepository)
     def cpsAdminPersistenceServiceMock = Mock(CpsAdminPersistenceService)
     def moduleReferenceRepositoryMock = Mock(ModuleReferenceRepository)
 
-    // Constants
     def yangResourceName = 'my-yang-resource-name'
     def yangResourceContent = 'module stores {\n' +
             '    yang-version 1.1;\n' +
@@ -62,17 +58,14 @@ class CpsModulePersistenceServiceSpec extends Specification {
             '    }' +
             '}'
 
-    // Scenario data
     static yangResourceChecksum = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539'
     static yangResourceChecksumDbConstraint = 'yang_resource_checksum_key'
     static sqlExceptionMessage = String.format('(checksum)=(%s)', yangResourceChecksum)
-    static checksumIntegrityException =  new DataIntegrityViolationException(
-                    "checksum integrity exception",
+    static checksumIntegrityException =  new DataIntegrityViolationException('checksum integrity exception',
                     new ConstraintViolationException('', new SQLException(sqlExceptionMessage), yangResourceChecksumDbConstraint))
-    static checksumIntegrityExceptionWithoutChecksum =  new DataIntegrityViolationException(
-                    "checksum integrity exception",
+    static checksumIntegrityExceptionWithoutChecksum =  new DataIntegrityViolationException('checksum integrity exception',
                     new ConstraintViolationException('', new SQLException('no checksum'), yangResourceChecksumDbConstraint))
-    static anotherIntegrityException = new DataIntegrityViolationException("another integrity exception")
+    static otherIntegrityException = new DataIntegrityViolationException('another integrity exception')
 
     def setup() {
         objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock,
@@ -94,7 +87,7 @@ class CpsModulePersistenceServiceSpec extends Specification {
             scenario                            | dbException                               || expectedThrownException         | expectedThrownExceptionMessage
             'checksum data failure'             | checksumIntegrityException                || DuplicatedYangResourceException | yangResourceChecksum
             'checksum failure without checksum' | checksumIntegrityExceptionWithoutChecksum || DuplicatedYangResourceException | 'no checksum found'
-            'other data failure'                | anotherIntegrityException                 || DataIntegrityViolationException | 'another integrity exception'
+            'other data failure'                | otherIntegrityException                   || DataIntegrityViolationException | 'another integrity exception'
     }
 
 }
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy
deleted file mode 100644 (file)
index 34a040e..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
- *  Modifications Copyright (C) 2021 Pantheon.tech
- *  Modifications Copyright (C) 2021 Bell Canada.
- *  Modifications Copyright (C) 2023 TechMahindra Ltd.
- *  ================================================================================
- *  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.spi.impl
-
-import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.DatabaseTestContainer
-import org.onap.cps.spi.repository.AnchorRepository
-import org.onap.cps.spi.repository.DataspaceRepository
-import org.onap.cps.spi.repository.FragmentRepository
-import org.onap.cps.spi.repository.YangResourceRepository
-import org.onap.cps.utils.JsonObjectMapper
-import org.spockframework.spring.SpringBean
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import org.testcontainers.spock.Testcontainers
-import spock.lang.Shared
-import spock.lang.Specification
-
-@SpringBootTest
-@Testcontainers
-class CpsPersistenceSpecBase extends Specification {
-
-    @Shared
-    DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance()
-
-    @Autowired
-    DataspaceRepository dataspaceRepository
-
-    @Autowired
-    YangResourceRepository yangResourceRepository
-
-    @Autowired
-    AnchorRepository anchorRepository
-
-    @Autowired
-    FragmentRepository fragmentRepository
-
-    @SpringBean
-    JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
-
-    protected static final String CLEAR_DATA = '/data/clear-all.sql'
-
-    static def DATASPACE_NAME = 'DATASPACE-001'
-    static def SCHEMA_SET_NAME1 = 'SCHEMA-SET-001'
-    static def SCHEMA_SET_NAME2 = 'SCHEMA-SET-002'
-    static def ANCHOR_NAME1 = 'ANCHOR-001'
-    static def ANCHOR_NAME2 = 'ANCHOR-002'
-    static def ANCHOR_NAME3 = 'ANCHOR-003'
-    static def ANCHOR_FOR_DATA_NODES_WITH_LEAVES = 'ANCHOR-003'
-    static def ANCHOR_FOR_SHOP_EXAMPLE = 'ANCHOR-004'
-    static def ANCHOR_HAVING_SINGLE_TOP_LEVEL_FRAGMENT = 'ANCHOR-005'
-    static def ANCHOR_WITH_MULTIPLE_TOP_LEVEL_FRAGMENTS = 'ANCHOR-006'
-}
diff --git a/cps-ri/src/test/java/org/onap/cps/DatabaseTestContainer.java b/cps-ri/src/test/java/org/onap/cps/DatabaseTestContainer.java
deleted file mode 100755 (executable)
index 61a5c04..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020 Pantheon.tech
- *  Modifications Copyright (C) 2022 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;
-
-import org.testcontainers.containers.PostgreSQLContainer;
-import org.testcontainers.utility.DockerImageName;
-
-/**
- * The Postgresql database test container wrapper.
- * Singleton implementation allows saving time on database initialization which otherwise would occur on each test.
- * for debugging/developing purposes you can suspend any test and connect to this database:
- *  docker exec -it {container-id} sh
- *  psql -d test -U test
- */
-public class DatabaseTestContainer extends PostgreSQLContainer<DatabaseTestContainer> {
-    private static final String IMAGE_VERSION = "registry.nordix.org/onaptest/postgres:14.1";
-    private static DatabaseTestContainer databaseTestContainer;
-
-    private DatabaseTestContainer() {
-        super(DockerImageName.parse(IMAGE_VERSION).asCompatibleSubstituteFor("postgres"));
-    }
-
-    /**
-     * Provides an instance of test container wrapper.
-     * The returned value expected to be assigned to static variable annotated with @ClassRule.
-     * This will allow to initialize DB connection env variables before DataSource object
-     * is initialized by Spring framework.
-     *
-     */
-    public static DatabaseTestContainer getInstance() {
-        if (databaseTestContainer == null) {
-            databaseTestContainer = new DatabaseTestContainer();
-            Runtime.getRuntime().addShutdownHook(new Thread(databaseTestContainer::terminate));
-        }
-        return databaseTestContainer;
-    }
-
-    @Override
-    public void start() {
-        super.start();
-        System.setProperty("DB_URL", databaseTestContainer.getJdbcUrl());
-        System.setProperty("DB_USERNAME", databaseTestContainer.getUsername());
-        System.setProperty("DB_PASSWORD", databaseTestContainer.getPassword());
-    }
-
-    @Override
-    public void stop() {
-        // do nothing on test completion, image removal will be performed via terminate() on JVM shutdown
-    }
-
-    private void terminate() {
-        super.stop();
-    }
-}
diff --git a/cps-ri/src/test/java/org/onap/cps/TestApplication.java b/cps-ri/src/test/java/org/onap/cps/TestApplication.java
deleted file mode 100644 (file)
index 075a241..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020 Pantheon.tech
- *  Modifications Copyright (C) 2021 Bell Canada.
- *  ================================================================================
- *  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;
-
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.retry.annotation.EnableRetry;
-
-/**
- * The @SpringBootApplication annotated class is required in order to run tests
- * marked with @SpringBootTest annotation.
- */
-@SpringBootApplication(scanBasePackages = "org.onap.cps.spi")
-@EnableRetry
-public class TestApplication {
-}
diff --git a/cps-ri/src/test/resources/application.yml b/cps-ri/src/test/resources/application.yml
deleted file mode 100644 (file)
index 4f40aea..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-# ============LICENSE_START=======================================================
-# Copyright (C) 2021 Pantheon.tech
-# Modifications Copyright (C) 2022 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=========================================================
-
-spring:
-  jpa:
-    ddl-auto: create
-    show-sql: false
-    properties:
-      hibernate:
-        enable_lazy_load_no_trans: true
-        dialect: org.hibernate.dialect.PostgreSQLDialect
-        format_sql: true
-        show_sql: false
-
-  datasource:
-    url: ${DB_URL}
-    username: ${DB_USERNAME}
-    password: ${DB_PASSWORD}
-    driverClassName: org.postgresql.Driver
-    initialization-mode: always
-
-  liquibase:
-    change-log: classpath:changelog/changelog-master.yaml
diff --git a/cps-ri/src/test/resources/data/anchor.sql b/cps-ri/src/test/resources/data/anchor.sql
deleted file mode 100644 (file)
index a15d5ae..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-   ============LICENSE_START=======================================================
-    Copyright (C) 2020 Pantheon.tech
-    Modifications Copyright (C) 2020-2023 Nordix Foundation.
-    Modifications Copyright (C) 2021-2022 Bell Canada.
-   ================================================================================
-   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=========================================================
-*/
-
-INSERT INTO DATASPACE (ID, NAME) VALUES
-    (1001, 'DATASPACE-001'),
-    (1002, 'DATASPACE-002-NO-DATA'),
-    (1003, 'DATASPACE-003');
-
-INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
-    (2001, 'SCHEMA-SET-001', 1001),
-    (2002, 'SCHEMA-SET-002', 1001),
-    (2003, 'SCHEMA-SET-002-NO-ANCHORS', 1003);
-
-INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
-    (3001, 'ANCHOR-001', 1001, 2001),
-    (3002, 'ANCHOR-002', 1001, 2002),
-    (3003, 'ANCHOR-003', 1001, 2002);
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
-    (4001, 3001, null, '/xpath', '{}');
diff --git a/cps-ri/src/test/resources/data/anchors-schemaset-modules.sql b/cps-ri/src/test/resources/data/anchors-schemaset-modules.sql
deleted file mode 100644 (file)
index 65b3a48..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-   ============LICENSE_START=======================================================
-    Copyright (C) 2021-2022 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=========================================================
-*/
-
-INSERT INTO DATASPACE (ID, NAME) VALUES
-    (1001, 'dataspace-1'), (1002, 'dataspace-2');
-
-INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
-    (2001, 'schema-set-1', 1001),
-    (2002, 'schema-set-2', 1001),
-    (2003, 'schema-set-3', 1001),
-    (2004, 'schema-set-4', 1002);
-
-INSERT INTO YANG_RESOURCE (ID, FILE_NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
-    (3001, 'module1@revA.yang', 'some-content', 'checksum1','module-name-1','revA'),
-    (3002, 'module2@revA.yang', 'some-content', 'checksum2','module-name-2','revA'),
-    (3003, 'module2@revB.yang', 'some-content', 'checksum3','module-name-2','revB'),
-    (3004, 'module3@revA.yang', 'some-content', 'checksum4','module-name-3','revA');
-
-INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
-    (2001, 3001), --schema-set-1(anchor-1) has modules module1@revA, module2@revA
-    (2001, 3002),
-    (2002, 3001), --schema-set-2(anchor-2) has modules module1@revA, module2@revB
-    (2002, 3003),
-    (2003, 3002), --schema-set-3(anchor-3) has modules module2@revA, module2@revB
-    (2003, 3003),
-    (2004, 3001); --schema-set-4(anchor-4) has module module1@revA but in other dataspace
-
-INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
-    (6001, 'anchor-1', 1001, 2001),
-    (6002, 'anchor-2', 1001, 2002),
-    (6003, 'anchor-3', 1001, 2003),
-    (6005, 'anchor-4', 1002, 2004);
diff --git a/cps-ri/src/test/resources/data/clear-all.sql b/cps-ri/src/test/resources/data/clear-all.sql
deleted file mode 100644 (file)
index 07c8a7a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-   ============LICENSE_START=======================================================
-    Copyright (C) 2020-2021 Pantheon.tech
-    Modifications Copyright (C) 2020,2022 Nordix Foundation.
-    Modifications Copyright (C) 2020 Bell Canada.
-   ================================================================================
-   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=========================================================
-*/
-
-DELETE FROM FRAGMENT;
-DELETE FROM ANCHOR;
-DELETE FROM DATASPACE;
-DELETE FROM YANG_RESOURCE
--- following tables are cleared by CASCADE constraint: SCHEMA_SET, SCHEMA_SET_YANG_RESOURCES
-
diff --git a/cps-ri/src/test/resources/data/fragment.sql b/cps-ri/src/test/resources/data/fragment.sql
deleted file mode 100755 (executable)
index 4980073..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-   ============LICENSE_START=======================================================
-    Copyright (C) 2021-2023 Nordix Foundation.
-    Modifications Copyright (C) 2021 Pantheon.tech
-    Modifications Copyright (C) 2021-2022 Bell Canada.
-   ================================================================================
-   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=========================================================
-*/
-
-INSERT INTO DATASPACE (ID, NAME) VALUES
-    (1001, 'DATASPACE-001'),
-    (1002, 'NCMP-Admin');
-
-INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
-    (2001, 'SCHEMA-SET-001', 1001);
-
-INSERT INTO YANG_RESOURCE (ID, FILE_NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
-    (4001, 'TEST','', 'SAMPLECHECKSUM','TESTMODULENAME', 'SAMPLEREVISION');
-
-UPDATE YANG_RESOURCE SET
-content = 'module stores {
-               yang-version 1.1;
-               namespace "org:onap:ccsdk:sample";
-
-               prefix book-store;
-
-               revision "2020-09-15" {
-                   description
-                   "Sample Model";
-               }
-           }
-'
-where ID = 4001;
-
-INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
-    (2001, 4001);
-
-INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
-    (3001, 'ANCHOR-001', 1001, 2001),
-    (3003, 'ANCHOR-003', 1001, 2001),
-    (3004, 'ncmp-dmi-registry', 1002, 2001),
-    (3005, 'ANCHOR-005', 1001, 2001),
-    (3006, 'ANCHOR-006', 1001, 2001);
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH) VALUES
-    (4001, 3001, null, '/parent-1'),
-    (4002, 3001, null, '/parent-2'),
-    (4003, 3001, null, '/parent-3'),
-    (4004, 3001, 4001, '/parent-1/child-1'),
-    (4005, 3001, 4002, '/parent-2/child-2'),
-    (4006, 3001, 4004, '/parent-1/child-1/grandchild-1');
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
-    (5009, 3005, null, '/parent-207', '{"parent-leaf": "parent-leaf value"}'),
-    (5010, 3005, 5009, '/parent-207/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
-    (5011, 3005, 5009, '/parent-207/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
-    (5012, 3005, 5011, '/parent-207/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}');
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
-    (5013, 3006, null, '/parent-208', '{"parent-leaf-1": "parent-leaf value-1"}'),
-    (5014, 3006, 5013, '/parent-208/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
-    (5015, 3006, 5013, '/parent-208/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
-    (5016, 3006, 5015, '/parent-208/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}'),
-    (5017, 3006, null, '/parent-209', '{"parent-leaf-2": "parent-leaf value-2"}'),
-    (5018, 3006, 5017, '/parent-209/child-001', '{"first-child-leaf": "first-child-leaf value"}'),
-    (5019, 3006, 5017, '/parent-209/child-002', '{"second-child-leaf": "second-child-leaf value"}'),
-    (5020, 3006, 5019, '/parent-209/child-002/grand-child', '{"grand-child-leaf": "grand-child-leaf value"}');
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
-    (4201, 3003, null, '/parent-200', '{"leaf-value": "original"}'),
-    (4202, 3003, 4201, '/parent-200/child-201', '{"leaf-value": "original"}'),
-    (4203, 3003, 4202, '/parent-200/child-201/grand-child', '{"leaf-value": "original"}'),
-    (4206, 3003, null, '/parent-201', '{"leaf-value": "original"}'),
-    (4207, 3003, 4206, '/parent-201/child-203', '{}'),
-    (4208, 3003, 4206, '/parent-201/child-204[@key=''A'']', '{"key": "A"}'),
-    (4209, 3003, 4206, '/parent-201/child-204[@key=''B'']', '{"key": "B"}'),
-    (4211, 3003, null, '/parent-202', '{"leaf-value": "original"}'),
-    (4212, 3003, 4211, '/parent-202/child-205[@key=''A'' and @key2=''B'']', '{"key": "A", "key2": "B"}'),
-    (4213, 3003, 4211, '/parent-202/child-206[@key=''A'']', '{"key": "A"}'),
-    (4214, 3003, null, '/parent-203', '{"leaf-value": "original"}'),
-    (4215, 3003, 4214, '/parent-203/child-203', '{}'),
-    (4216, 3003, 4214, '/parent-203/child-204[@key=''A'']', '{"key": "A"}'),
-    (4217, 3003, 4214, '/parent-203/child-204[@key=''B'']', '{"key": "B"}'),
-    (4218, 3003, 4217, '/parent-203/child-204[@key=''B'']/grand-child-204[@key2=''Y'']', '{"key": "B", "key2": "Y"}'),
-    (4226, 3003, null, '/parent-206', '{"leaf-value": "original"}'),
-    (4227, 3003, 4226, '/parent-206/child-206', '{}'),
-    (4228, 3003, 4227, '/parent-206/child-206/grand-child-206', '{}'),
-    (4229, 3003, 4227, '/parent-206/child-206/grand-child-206[@key=''A'']', '{"key": "A"}'),
-    (4230, 3003, 4227, '/parent-206/child-206/grand-child-206[@key=''X'']', '{"key": "X"}'),
-    (4231, 3003, null, '/parent-206[@key=''A'']', '{"key": "A"}'),
-    (4232, 3003, 4231, '/parent-206[@key=''A'']/child-206', '{}'),
-    (4233, 3003, null, '/parent-206[@key=''B'']', '{"key": "B"}');
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
-    (5000, 3004, null, '/dmi-registry/cm-handles[@id=''PNFDemo'']', '{"id": "PNFDemo", "dmi-service-name": "http://172.21.235.14:8783", "dmi-data-service-name": "", "dmi-model-service-name": ""}'),
-    (5001, 3004, null, '/dmi-registry/cm-handles[@id=''PNFDemo2'']', '{"id": "PNFDemo2", "dmi-service-name": "http://172.26.46.68:8783", "dmi-data-service-name": "", "dmi-model-service-name": ""}'),
-    (5002, 3004, null, '/dmi-registry/cm-handles[@id=''PNFDemo3'']', '{"id": "PNFDemo3", "dmi-service-name": "http://172.26.46.68:8783", "dmi-data-service-name": "", "dmi-model-service-name": ""}'),
-    (5003, 3004, null, '/dmi-registry/cm-handles[@id=''PNFDemo4'']', '{"id": "PNFDemo4", "dmi-service-name": "http://172.26.46.68:8783", "dmi-data-service-name": "", "dmi-model-service-name": ""}'),
-    (5004, 3004, 5000, '/dmi-registry/cm-handles[@id=''PNFDemo'']/public-properties[@name=''Contact'']', '{"name": "Contact", "value": "newemailforstore@bookstore.com"}'),
-    (5005, 3004, 5001, '/dmi-registry/cm-handles[@id=''PNFDemo2'']/public-properties[@name=''Contact'']', '{"name": "Contact", "value": "newemailforstore@bookstore.com"}'),
-    (5006, 3004, 5002, '/dmi-registry/cm-handles[@id=''PNFDemo3'']/public-properties[@name=''Contact'']', '{"name": "Contact3", "value": "PNF3@bookstore.com"}'),
-    (5007, 3004, 5003, '/dmi-registry/cm-handles[@id=''PNFDemo4'']/public-properties[@name=''Contact'']', '{"name": "Contact", "value": "newemailforstore@bookstore.com"}'),
-    (5008, 3004, 5004, '/dmi-registry/cm-handles[@id=''PNFDemo4'']/public-properties[@name=''Contact2'']', '{"name": "Contact2", "value": "newemailforstore2@bookstore.com"}');
diff --git a/cps-ri/src/test/resources/data/perf-test.sql b/cps-ri/src/test/resources/data/perf-test.sql
deleted file mode 100644 (file)
index 48e8b1f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-   ============LICENSE_START=======================================================
-    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.
-   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=========================================================
-*/
-
-INSERT INTO DATASPACE (ID, NAME) VALUES (9001, 'PERF-DATASPACE');
-
-INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES  (9002, 'PERF-SCHEMA-SET', 9001);
-
-INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES (9003, 'PERF-ANCHOR', 9001, 9002);
-
-INSERT INTO FRAGMENT (ID, ANCHOR_ID, PARENT_ID, XPATH) VALUES  (0, 9003, null, '/perf-parent-1');
-
diff --git a/cps-ri/src/test/resources/data/schemaset.sql b/cps-ri/src/test/resources/data/schemaset.sql
deleted file mode 100644 (file)
index e5bf63b..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-   ============LICENSE_START=======================================================
-    Copyright (C) 2020-2021 Pantheon.tech
-    Modifications Copyright (C) 2020-2023 Nordix Foundation.
-    Modifications Copyright (C) 2020-2021 Bell Canada.
-   ================================================================================
-   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=========================================================
-*/
-
-INSERT INTO DATASPACE (ID, NAME) VALUES
-    (1001, 'DATASPACE-001'), (1002, 'DATASPACE-002');
-
-INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
-    (2001, 'SCHEMA-SET-001', 1001),
-    (2002, 'SCHEMA-SET-002', 1001),
-    (2100, 'SCHEMA-SET-100', 1001), -- for removal, not referenced by anchors
-    (2101, 'SCHEMA-SET-101', 1001), -- for removal, having anchor and data associated
-    (2003, 'SCHEMA-SET-003', 1002),
-    (2004, 'SCHEMA-SET-004', 1002),
-    (2005, 'SCHEMA-SET-005', 1001);
-
-INSERT INTO YANG_RESOURCE (ID, FILE_NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
-    (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'e8bdda931099310de66532e08c3fafec391db29f55c81927b168f6aa8f81b73b',null,null),
-    (3002, 'module2@2020-02-02.yang', 'CONTENT-002', '7e7d48afbe066ed0a890a09081859046d3dde52300dfcdb13be5b20780353a11','MODULE-NAME-002','REVISION-002'),
-    (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'ca20c45fec8547633f05ff8905c48ffa7b02b94ec3ad4ed79922e6ba40779df3','MODULE-NAME-003','REVISION-002'),
-    (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'f6ed09d343562e4d4ae5140f3c6a55df9c53f6da8e30dda8cbd9eaf9cd449be0','MODULE-NAME-004','REVISION-004'),
-    (3100, 'orphan@2020-02-02.yang', 'ORPHAN', 'checksum',null,null), -- for auto-removal as orphan
-    (3005, 'module5@2020-02-02.yang', 'CONTENT-005', 'checksum-005','MODULE-NAME-005','REVISION-002'),
-    (3006, 'module6@2020-02-02.yang', 'CONTENT-006', 'checksum-006','MODULE-NAME-006','REVISION-006');
-
-INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
-    (2001, 3001), (2001, 3002),
-    (2002, 3003), (2005, 3004),
-    (2100, 3003), (2100, 3100), -- orphan removal case
-    (2101, 3003), (2101, 3004),
-    (2003, 3005), (2004, 3006);
-
-INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES -- anchors for removal
-    (6001, 'ANCHOR1', 1001, 2101),
-    (6002, 'ANCHOR2', 1001, 2101),
-    (6003, 'ANCHOR3', 1001, 2005);
-
-INSERT INTO FRAGMENT (ID, XPATH, ANCHOR_ID) VALUES
-    (7001, '/XPATH', 6001);
diff --git a/cps-ri/src/test/resources/hibernate.cfg.xml b/cps-ri/src/test/resources/hibernate.cfg.xml
deleted file mode 100644 (file)
index fae9275..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<!DOCTYPE hibernate-configuration PUBLIC\r
-        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"\r
-        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">\r
-\r
-<hibernate-configuration>\r
-    <session-factory>\r
-        <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>\r
-        <property name="hibernate.connection.url">${DB_URL}</property>\r
-        <property name="hibernate.connection.username">${DB_USERNAME}</property>\r
-        <property name="hibernate.connection.password">${DB_PASSWORD}</property>\r
-        <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>\r
-        <property name="show_sql">true</property>\r
-        <property name="hibernate.hbm2ddl.auto">none</property>\r
-    </session-factory>\r
-</hibernate-configuration>
\ No newline at end of file
index 45c4d30..158eebf 100644 (file)
@@ -29,7 +29,7 @@
   <parent>
     <groupId>org.onap.cps</groupId>
     <artifactId>cps-parent</artifactId>
-    <version>3.3.2-SNAPSHOT</version>
+    <version>3.3.3-SNAPSHOT</version>
     <relativePath>../cps-parent/pom.xml</relativePath>
   </parent>
 
index 5cc443b..ea9786e 100644 (file)
@@ -625,7 +625,7 @@ paths:
                 dmi-response:
                   http-code: 400
                   body: Bad Request
-  /v1/batch/data/ds/{datastore-name}:
+  /v1/data:
     post:
       tags:
       - network-cm-proxy
@@ -635,51 +635,6 @@ paths:
         to identify the relevant messages.
       operationId: getResourceDataForCmHandleBatch
       parameters:
-      - name: datastore-name
-        in: path
-        description: The type of the requested data
-        required: true
-        schema:
-          type: string
-          example: ncmp-datastore:running
-      - name: resourceIdentifier
-        in: query
-        description: The format of resource identifier depend on the associated DMI
-          Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but
-          it can really be anything.
-        required: true
-        allowReserved: true
-        schema:
-          type: string
-        examples:
-          sample 1:
-            value:
-              resourceIdentifier: \shops\bookstore
-          sample 2:
-            value:
-              resourceIdentifier: "\\shops\\bookstore\\categories[@code=1]"
-          sample 3:
-            value:
-              resourceIdentifier: "parent=shops,child=bookstore"
-      - name: options
-        in: query
-        description: "options parameter in query, it is mandatory to wrap key(s)=value(s)\
-          \ in parenthesis'()'. The format of options parameter depend on the associated\
-          \ DMI Plugin implementation."
-        required: false
-        allowReserved: true
-        schema:
-          type: string
-        examples:
-          sample 1:
-            value:
-              options: (depth=3)
-          sample 2:
-            value:
-              options: (fields=book)
-          sample 3:
-            value:
-              options: "(depth=2,fields=book/authors)"
       - name: topic
         in: query
         description: mandatory topic parameter in query.
@@ -691,18 +646,11 @@ paths:
           sample 1:
             value:
               topic: my-topic-name
-      - name: include-descendants
-        in: query
-        description: Determines if descendants are included in response
-        required: false
-        schema:
-          type: boolean
-          default: false
       requestBody:
         content:
           application/json:
             schema:
-              type: object
+              $ref: '#/components/schemas/ResourceDataBatchRequest'
         required: true
       responses:
         "200":
@@ -1485,6 +1433,43 @@ components:
           example: Bad Gateway Error Message NCMP
         dmi-response:
           $ref: '#/components/schemas/DmiErrorMessage_dmiresponse'
+    ResourceDataBatchRequest:
+      title: get resource data for given array of operations
+      type: object
+      properties:
+        operations:
+          type: array
+          description: contains batch request details
+          items:
+            $ref: '#/components/schemas/BatchOperationDefinition'
+    BatchOperationDefinition:
+      required:
+      - datastore
+      - operation
+      - operationId
+      properties:
+        operation:
+          type: string
+          example: read
+        operationId:
+          type: string
+          example: "12"
+        datastore:
+          type: string
+          example: ncmp-datastore:passthrough-operational
+        options:
+          type: string
+          example: (fields=schemas/schema)
+        resourceIdentifier:
+          type: string
+          example: parent/child
+        targetIds:
+          type: array
+          example:
+          - da310eecdb8d44c2acc0ddaae01174b1
+          - c748c58f8e0b438f9fd1f28370b17d47
+          items:
+            type: string
     RestModuleReference:
       title: Module reference details
       type: object
index 0b2d2a4..60050c1 100755 (executable)
@@ -16,6 +16,33 @@ CPS Release Notes
 ..      * * *   MONTREAL   * * *
 ..      ========================
 
+Version: 3.3.3
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project**                      |                                                        |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images**                    | onap/cps-and-ncmp:3.3.3                                |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation**              | 3.3.3 Montreal                                         |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+| **Release date**                     | Not yet released                                       |
+|                                      |                                                        |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.3.3
+
+Features
+--------
+
 Version: 3.3.2
 ==============
 
@@ -32,18 +59,22 @@ Release Data
 | **Release designation**              | 3.3.2 Montreal                                         |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
-| **Release date**                     | Not yet released                                       |
+| **Release date**                     | 2023 June 15                                           |
 |                                      |                                                        |
 +--------------------------------------+--------------------------------------------------------+
 
 Bug Fixes
 ---------
 3.3.2
-    - None
+    - `CPS-1716 <https://jira.onap.org/browse/CPS-1716>`_ NCMP: Java Heap OutOfMemory errors and slow registration in case of 20k cmhandles
 
 Features
 --------
     - `CPS-1006 <https://jira.onap.org/browse/CPS-1006>`_ Extend CPS PATCH API to allow update of leaves for multiple data nodes
+    - `CPS-1273 <https://jira.onap.org/browse/CPS-1273>`_ Add <,> operators support to cps-path
+    - `CPS-1664 <https://jira.onap.org/browse/CPS-1664>`_ Use recursive SQL to fetch descendants in CpsPath queries to improve query performance
+    - `CPS-1676 <https://jira.onap.org/browse/CPS-1676>`_ Entity ID types do not match types in database definition
+    - `CPS-1677 <https://jira.onap.org/browse/CPS-1677>`_ Remove dataspace_id column from Fragment table
 
 Version: 3.3.1
 ==============
index fa403a9..5dd64cb 100644 (file)
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
 
index b942a43..a1e0352 100644 (file)
@@ -25,10 +25,12 @@ import org.onap.cps.api.impl.CpsAdminServiceImpl
 import org.onap.cps.api.impl.CpsDataServiceImpl
 import org.onap.cps.api.impl.CpsModuleServiceImpl
 import org.onap.cps.integration.DatabaseTestContainer
+import org.onap.cps.spi.config.CpsSessionFactory
 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.repository.DataspaceRepository
 import org.onap.cps.spi.impl.utils.CpsValidatorImpl
+import org.onap.cps.spi.utils.SessionManager
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration
 import org.springframework.boot.autoconfigure.domain.EntityScan
@@ -42,12 +44,12 @@ import spock.lang.Specification
 
 import java.time.OffsetDateTime
 
-@SpringBootTest(classes = [TestConfig, CpsAdminServiceImpl, CpsValidatorImpl])
+@SpringBootTest(classes = [TestConfig, CpsAdminServiceImpl, CpsValidatorImpl, SessionManager, CpsSessionFactory])
 @Testcontainers
 @EnableAutoConfiguration
 @EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
-@ComponentScan(basePackages = ["org.onap.cps.api", "org.onap.cps.spi.repository"])
-@EntityScan("org.onap.cps.spi.entities")
+@ComponentScan(basePackages = ['org.onap.cps.api', 'org.onap.cps.spi.repository'])
+@EntityScan('org.onap.cps.spi.entities')
 class CpsIntegrationSpecBase extends Specification {
 
     @Shared
@@ -69,6 +71,10 @@ class CpsIntegrationSpecBase extends Specification {
     @Lazy
     CpsQueryService cpsQueryService
 
+    @Autowired
+    @Lazy
+    SessionManager sessionManager
+
     def static GENERAL_TEST_DATASPACE = 'generalTestDataspace'
     def static BOOKSTORE_SCHEMA_SET = 'bookstoreSchemaSet'
 
index 18a2941..e39e114 100644 (file)
@@ -34,6 +34,7 @@ import org.onap.cps.spi.repository.ModuleReferenceRepository
 import org.onap.cps.spi.repository.SchemaSetRepository
 import org.onap.cps.spi.repository.YangResourceRepository
 import org.onap.cps.spi.utils.SessionManager
+import org.onap.cps.spi.utils.TimeLimiterProvider
 import org.onap.cps.utils.JsonObjectMapper
 import org.onap.cps.utils.TimedYangParser
 import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
@@ -75,7 +76,7 @@ class TestConfig extends Specification{
 
     @Autowired
     @Lazy
-    SessionManager stubbedSessionManager
+    SessionManager sessionManager
 
     @Bean
     CpsAdminPersistenceServiceImpl cpsAdminPersistenceService() {
@@ -84,7 +85,7 @@ class TestConfig extends Specification{
 
     @Bean
     CpsDataPersistenceService cpsDataPersistenceService() {
-        return (CpsDataPersistenceService) new CpsDataPersistenceServiceImpl(dataspaceRepository, anchorRepository, fragmentRepository, jsonObjectMapper, stubbedSessionManager)
+        return (CpsDataPersistenceService) new CpsDataPersistenceServiceImpl(dataspaceRepository, anchorRepository, fragmentRepository, jsonObjectMapper, sessionManager)
     }
 
     @Bean
@@ -102,11 +103,6 @@ class TestConfig extends Specification{
         return Stub(NotificationService)
     }
 
-    @Bean
-    SessionManager sessionManager() {
-        return Stub(SessionManager)
-    }
-
     @Bean
     TimedYangParser timedYangParser() {
         return new TimedYangParser()
@@ -117,4 +113,9 @@ class TestConfig extends Specification{
         return new TimedYangTextSchemaSourceSetBuilder()
     }
 
+    @Bean
+    TimeLimiterProvider timeLimiterProvider() {
+        return new TimeLimiterProvider()
+    }
+
 }
@@ -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.
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.spi.utils
+package org.onap.cps.integration.functional
 
-import org.onap.cps.spi.config.CpsSessionFactory
+import org.onap.cps.integration.base.FunctionalSpecBase
 import org.onap.cps.spi.exceptions.SessionManagerException
-import org.onap.cps.spi.impl.CpsPersistenceSpecBase
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.test.context.jdbc.Sql
+import org.onap.cps.spi.utils.SessionManager
 
-class SessionManagerIntegrationSpec extends CpsPersistenceSpecBase{
+class SessionManagerIntegrationSpec extends FunctionalSpecBase {
 
-    final static String SET_DATA = '/data/anchor.sql'
-
-    @Autowired
     SessionManager objectUnderTest
 
-    @Autowired
-    CpsSessionFactory cpsSessionFactory
-
-    def sessionId
     def shortTimeoutForTesting = 300L
+    def sessionId
 
-    def setup(){
+    def setup() {
+        objectUnderTest = sessionManager
         sessionId = objectUnderTest.startSession()
     }
 
@@ -47,35 +40,32 @@ class SessionManagerIntegrationSpec extends CpsPersistenceSpecBase{
         objectUnderTest.closeSession(sessionId, objectUnderTest.WITH_COMMIT)
     }
 
-    @Sql([CLEAR_DATA, SET_DATA])
     def 'Lock anchor.'(){
         when: 'session tries to acquire anchor lock by passing anchor entity details'
-            objectUnderTest.lockAnchor(sessionId, DATASPACE_NAME, ANCHOR_NAME1, shortTimeoutForTesting)
+            objectUnderTest.lockAnchor(sessionId, FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, shortTimeoutForTesting)
         then: 'no exception is thrown'
             noExceptionThrown()
     }
 
-    @Sql([CLEAR_DATA, SET_DATA])
     def 'Attempt to lock anchor when another session is holding the lock.'(){
         given: 'another session that holds an anchor lock'
             def otherSessionId = objectUnderTest.startSession()
-            objectUnderTest.lockAnchor(otherSessionId,DATASPACE_NAME,ANCHOR_NAME1,shortTimeoutForTesting)
+            objectUnderTest.lockAnchor(otherSessionId,FUNCTIONAL_TEST_DATASPACE_1,BOOKSTORE_ANCHOR_1,shortTimeoutForTesting)
         when: 'a session tries to acquire the same anchor lock'
-            objectUnderTest.lockAnchor(sessionId,DATASPACE_NAME,ANCHOR_NAME1,shortTimeoutForTesting)
+            objectUnderTest.lockAnchor(sessionId,FUNCTIONAL_TEST_DATASPACE_1,BOOKSTORE_ANCHOR_1,shortTimeoutForTesting)
         then: 'a session manager exception is thrown specifying operation reached timeout'
             def thrown = thrown(SessionManagerException)
             thrown.message.contains('Timeout')
         then: 'when the other session holding the lock is closed, lock can finally be acquired'
             objectUnderTest.closeSession(otherSessionId, objectUnderTest.WITH_COMMIT)
-            objectUnderTest.lockAnchor(sessionId,DATASPACE_NAME,ANCHOR_NAME1,shortTimeoutForTesting)
+            objectUnderTest.lockAnchor(sessionId,FUNCTIONAL_TEST_DATASPACE_1,BOOKSTORE_ANCHOR_1,shortTimeoutForTesting)
     }
 
-    @Sql([CLEAR_DATA, SET_DATA])
     def 'Lock anchor twice using the same session.'(){
         given: 'session that already holds an anchor lock'
-            objectUnderTest.lockAnchor(sessionId, DATASPACE_NAME, ANCHOR_NAME1, shortTimeoutForTesting)
+            objectUnderTest.lockAnchor(sessionId, FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, shortTimeoutForTesting)
         when: 'same session tries to acquire same anchor lock'
-            objectUnderTest.lockAnchor(sessionId, DATASPACE_NAME, ANCHOR_NAME1, shortTimeoutForTesting)
+            objectUnderTest.lockAnchor(sessionId, FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, shortTimeoutForTesting)
         then: 'no exception is thrown'
             noExceptionThrown()
     }
index d20da46..eee87dd 100644 (file)
@@ -44,9 +44,9 @@ class GetPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Read datatrees with ${scenario}", durationLimit, durationInMillis)
         where: 'the following parameters are used'
             scenario             | fetchDescendantsOption  | anchor       || durationLimit | expectedNumberOfDataNodes
-            'no descendants'     | OMIT_DESCENDANTS        | 'openroadm1' || 100           | 1
-            'direct descendants' | DIRECT_CHILDREN_ONLY    | 'openroadm2' || 150           | 1 + 50
-            'all descendants'    | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 600           | 1 + 50 * 86
+            'no descendants'     | OMIT_DESCENDANTS        | 'openroadm1' || 50            | 1
+            'direct descendants' | DIRECT_CHILDREN_ONLY    | 'openroadm2' || 100           | 1 + 50
+            'all descendants'    | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 200           | 1 + 50 * 86
     }
 
     def 'Read data trees for multiple xpaths'() {
@@ -59,7 +59,7 @@ class GetPerfTest extends CpsPerfTestBase {
             assert countDataNodesInTree(result) == 50 * 86
             def durationInMillis = stopWatch.getTotalTimeMillis()
         then: 'all data is read within 500 ms'
-            recordAndAssertPerformance("Read datatrees for multiple xpaths", 500, durationInMillis)
+            recordAndAssertPerformance("Read datatrees for multiple xpaths", 200, durationInMillis)
     }
 
     def 'Read complete data trees using #scenario.'() {
@@ -75,10 +75,10 @@ class GetPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Read datatrees using ${scenario}", durationLimit, durationInMillis)
         where: 'the following xpaths are used'
             scenario                | anchorPrefix | xpath                || durationLimit | expectedNumberOfDataNodes
-            'bookstore root'        | 'bookstore'  | '/'                  || 300           | 78
-            'bookstore top element' | 'bookstore'  | '/bookstore'         || 300           | 78
-            'openroadm root'        | 'openroadm'  | '/'                  || 1200          | 1 + 50 * 86
-            'openroadm top element' | 'openroadm'  | '/openroadm-devices' || 1200          | 1 + 50 * 86
+            'bookstore root'        | 'bookstore'  | '/'                  || 200           | 78
+            'bookstore top element' | 'bookstore'  | '/bookstore'         || 200           | 78
+            'openroadm root'        | 'openroadm'  | '/'                  || 600           | 1 + 50 * 86
+            'openroadm top element' | 'openroadm'  | '/openroadm-devices' || 600           | 1 + 50 * 86
     }
 
 }
index 885f1c2..eafd16f 100644 (file)
@@ -45,10 +45,10 @@ class QueryPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Query 1 anchor ${scenario}", durationLimit, durationInMillis)
         where: 'the following parameters are used'
             scenario                     | anchor       | cpsPath                                                             || durationLimit | expectedNumberOfDataNodes
-            'top element'                | 'openroadm1' | '/openroadm-devices'                                                || 500           | 50 * 86 + 1
-            'leaf condition'             | 'openroadm2' | '//openroadm-device[@ne-state="inservice"]'                         || 500           | 50 * 86
-            'ancestors'                  | 'openroadm3' | '//openroadm-device/ancestor::openroadm-devices'                    || 500           | 50 * 86 + 1
-            'leaf condition + ancestors' | 'openroadm4' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 500           | 50 * 86 + 1
+            'top element'                | 'openroadm1' | '/openroadm-devices'                                                || 200           | 50 * 86 + 1
+            'leaf condition'             | 'openroadm2' | '//openroadm-device[@ne-state="inservice"]'                         || 250           | 50 * 86
+            'ancestors'                  | 'openroadm3' | '//openroadm-device/ancestor::openroadm-devices'                    || 200           | 50 * 86 + 1
+            'leaf condition + ancestors' | 'openroadm4' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 200           | 50 * 86 + 1
     }
 
     def 'Query complete data trees across all anchors with #scenario.'() {
@@ -63,10 +63,10 @@ class QueryPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Query across anchors ${scenario}", durationLimit, durationInMillis)
         where: 'the following parameters are used'
             scenario                     | cpspath                                                             || durationLimit | expectedNumberOfDataNodes
-            'top element'                | '/openroadm-devices'                                                || 2000          | 5 * (50 * 86 + 1)
-            'leaf condition'             | '//openroadm-device[@ne-state="inservice"]'                         || 2000          | 5 * (50 * 86)
-            'ancestors'                  | '//openroadm-device/ancestor::openroadm-devices'                    || 2000          | 5 * (50 * 86 + 1)
-            'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 2000          | 5 * (50 * 86 + 1)
+            'top element'                | '/openroadm-devices'                                                || 600           | 5 * (50 * 86 + 1)
+            'leaf condition'             | '//openroadm-device[@ne-state="inservice"]'                         || 1000          | 5 * (50 * 86)
+            'ancestors'                  | '//openroadm-device/ancestor::openroadm-devices'                    || 600           | 5 * (50 * 86 + 1)
+            'leaf condition + ancestors' | '//openroadm-device[@status="success"]/ancestor::openroadm-devices' || 600           | 5 * (50 * 86 + 1)
     }
 
     def 'Query with leaf condition and #scenario.'() {
@@ -81,9 +81,9 @@ class QueryPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Query with ${scenario}", durationLimit, durationInMillis)
         where: 'the following parameters are used'
             scenario             | fetchDescendantsOption  | anchor       || durationLimit | expectedNumberOfDataNodes
-            'no descendants'     | OMIT_DESCENDANTS        | 'openroadm1' || 100           | 50
-            'direct descendants' | DIRECT_CHILDREN_ONLY    | 'openroadm2' || 200           | 50 * 2
-            'all descendants'    | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 500           | 50 * 86
+            'no descendants'     | OMIT_DESCENDANTS        | 'openroadm1' || 60            | 50
+            'direct descendants' | DIRECT_CHILDREN_ONLY    | 'openroadm2' || 120           | 50 * 2
+            'all descendants'    | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 200           | 50 * 86
     }
 
     def 'Query ancestors with #scenario.'() {
@@ -98,9 +98,9 @@ class QueryPerfTest extends CpsPerfTestBase {
             recordAndAssertPerformance("Query ancestors with ${scenario}", durationLimit, durationInMillis)
         where: 'the following parameters are used'
             scenario             | fetchDescendantsOption  | anchor       || durationLimit | expectedNumberOfDataNodes
-            'no descendants'     | OMIT_DESCENDANTS        | 'openroadm1' || 100           | 1
-            'direct descendants' | DIRECT_CHILDREN_ONLY    | 'openroadm2' || 200           | 1 + 50
-            'all descendants'    | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 500           | 1 + 50 * 86
+            'no descendants'     | OMIT_DESCENDANTS        | 'openroadm1' || 60            | 1
+            'direct descendants' | DIRECT_CHILDREN_ONLY    | 'openroadm2' || 120           | 1 + 50
+            'all descendants'    | INCLUDE_ALL_DESCENDANTS | 'openroadm3' || 200           | 1 + 50 * 86
     }
 
 }
index c281908..a02d21c 100644 (file)
@@ -40,7 +40,7 @@ class UpdatePerfTest extends CpsPerfTestBase {
             stopWatch.stop()
             def updateDurationInMillis = stopWatch.getTotalTimeMillis()
         then: 'update duration is under 1000 milliseconds'
-            recordAndAssertPerformance('Update 1 data node', 1000, updateDurationInMillis)
+            recordAndAssertPerformance('Update 1 data node', 600, updateDurationInMillis)
     }
 
     def 'Batch update 10 data nodes with descendants'() {
@@ -56,7 +56,7 @@ class UpdatePerfTest extends CpsPerfTestBase {
             stopWatch.stop()
             def updateDurationInMillis = stopWatch.getTotalTimeMillis()
         then: 'update duration is under 5000 milliseconds'
-            recordAndAssertPerformance('Update 10 data nodes', 5000, updateDurationInMillis)
+            recordAndAssertPerformance('Update 10 data nodes', 4000, updateDurationInMillis)
     }
 
 }
index 5d7c9de..bcb2d2f 100644 (file)
@@ -44,7 +44,7 @@ class CmHandleQueryPerfTest extends NcmpRegistryPerfTestBase {
             stopWatch.stop()
             def durationInMillis = stopWatch.getTotalTimeMillis()
         then: 'the required operations are performed within 1200 ms'
-            recordAndAssertPerformance("CpsPath Registry attributes Query", 1200, durationInMillis)
+            recordAndAssertPerformance("CpsPath Registry attributes Query", 500, durationInMillis)
         and: 'all but 1 (other node) are returned'
             result.size() == 999
         and: 'the tree contains all the expected descendants too'
index 375c7fc..6c8fdcf 100644 (file)
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.onap.cps</groupId>
         <artifactId>cps-parent</artifactId>
-        <version>3.3.2-SNAPSHOT</version>
+        <version>3.3.3-SNAPSHOT</version>
         <relativePath>../cps-parent/pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/pom.xml b/pom.xml
index ae70b6c..9e4603d 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.2-SNAPSHOT</version>\r
+    <version>3.3.3-SNAPSHOT</version>\r
     <packaging>pom</packaging>\r
 \r
     <name>cps</name>\r
diff --git a/releases/3.3.2-container.yaml b/releases/3.3.2-container.yaml
new file mode 100644 (file)
index 0000000..a02cd39
--- /dev/null
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.3.2
+project: cps
+log_dir: cps-maven-docker-stage-master/921/
+ref: 5cec532ddc9079739d93ef10f1441f8c9fd75c22
+containers:
+  - name: 'cps-and-ncmp'
+    version: '3.3.2-20230615T111304Z'
diff --git a/releases/3.3.2.yaml b/releases/3.3.2.yaml
new file mode 100644 (file)
index 0000000..b80be72
--- /dev/null
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/929/
+project: cps
+version: 3.3.2
\ No newline at end of file
index 19d8b43..874aa04 100644 (file)
@@ -25,7 +25,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.cps</groupId>
     <artifactId>spotbugs</artifactId>
-    <version>3.3.2-SNAPSHOT</version>
+    <version>3.3.3-SNAPSHOT</version>
 
     <properties>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
index 20a0ce4..f6c96da 100755 (executable)
@@ -22,7 +22,7 @@
 
 major=3
 minor=3
-patch=2
+patch=3
 
 base_version=${major}.${minor}.${patch}