diff options
60 files changed, 732 insertions, 832 deletions
diff --git a/checkstyle/pom.xml b/checkstyle/pom.xml index cc07fce027..2129244dfa 100644 --- a/checkstyle/pom.xml +++ b/checkstyle/pom.xml @@ -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> diff --git a/cps-application/pom.xml b/cps-application/pom.xml index f6a56ce222..2e7b12dfb9 100755 --- a/cps-application/pom.xml +++ b/cps-application/pom.xml @@ -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> diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index 802da9e87c..ed71339f93 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -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: diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml index 4c99fcb86d..a87b34ba51 100644 --- a/cps-bom/pom.xml +++ b/cps-bom/pom.xml @@ -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> diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml index e06bbd7a1f..8003d30a4a 100755 --- a/cps-dependencies/pom.xml +++ b/cps-dependencies/pom.xml @@ -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> @@ -147,6 +147,13 @@ <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> <version>3.11</version> diff --git a/cps-events/pom.xml b/cps-events/pom.xml index b8ddb20095..66c4fe53db 100644 --- a/cps-events/pom.xml +++ b/cps-events/pom.xml @@ -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> diff --git a/cps-ncmp-events/pom.xml b/cps-ncmp-events/pom.xml index 52ca77e936..1dfb7eba83 100644 --- a/cps-ncmp-events/pom.xml +++ b/cps-ncmp-events/pom.xml @@ -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> diff --git a/cps-ncmp-rest-stub/pom.xml b/cps-ncmp-rest-stub/pom.xml index f434863c7e..0a6684fc0b 100644 --- a/cps-ncmp-rest-stub/pom.xml +++ b/cps-ncmp-rest-stub/pom.xml @@ -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> diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml index b3ac659782..8c84546b23 100644 --- a/cps-ncmp-rest/pom.xml +++ b/cps-ncmp-rest/pom.xml @@ -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> diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index b87fe64366..19ef988d30 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -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> @@ -42,6 +42,18 @@ <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> </dependency> @@ -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 index 0000000000..b76f86ebeb --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfig.java @@ -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; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java index d92316dc58..7b28b4cd5f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java @@ -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)); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventResponseOutcome.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventResponseOutcome.java index a74682571b..1bfc4ab28b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventResponseOutcome.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventResponseOutcome.java @@ -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; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistence.java index f240c4510d..27d4266566 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistence.java @@ -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); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceImpl.java index 9a063d6dfd..d2b1237a4d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceImpl.java @@ -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; + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionStatus.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionStatus.java index 0b4f91fac3..ce3b88ba03 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionStatus.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionStatus.java @@ -20,8 +20,36 @@ 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); + } + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java index 1648ac4fbb..8d44592ae2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java @@ -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; + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy index bcf75a29b2..fe7b3f11cb 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy @@ -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' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy index 28464bb91c..02071cd8cf 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumerSpec.groovy @@ -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 index 0000000000..ed5f161258 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaTemplateConfigSpec.groovy @@ -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 + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy index 5f54bbe3dd..3dffac714b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy @@ -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' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy index 93741261f6..4c6880421b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsPublisherSpec.groovy @@ -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' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceSpec.groovy index a372abe6ff..ec54e8917a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/subscriptions/SubscriptionPersistenceSpec.groovy @@ -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) } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DataNodeHelperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DataNodeHelperSpec.groovy index e527ae12bb..ee726a908e 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DataNodeHelperSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DataNodeHelperSpec.groovy @@ -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] } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/kafka/MessagingBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/kafka/MessagingBaseSpec.groovy index 337178e128..603b8cdda6 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/kafka/MessagingBaseSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/kafka/MessagingBaseSpec.groovy @@ -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) - } } diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml index 1016f2b033..197bfda19c 100644 --- a/cps-ncmp-service/src/test/resources/application.yml +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -16,6 +16,14 @@ # 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: diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml index 17c96c577c..0f58fbba76 100755 --- a/cps-parent/pom.xml +++ b/cps-parent/pom.xml @@ -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> diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml index c388c36e79..bd8b47ca94 100644 --- a/cps-path-parser/pom.xml +++ b/cps-path-parser/pom.xml @@ -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> diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml index c9d07973d1..f6f81006ed 100755 --- a/cps-rest/pom.xml +++ b/cps-rest/pom.xml @@ -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> diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml index 504fce560b..89e60dbfbe 100644 --- a/cps-ri/pom.xml +++ b/cps-ri/pom.xml @@ -26,15 +26,15 @@ <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>
<artifactId>cps-ri</artifactId>
<properties>
- <minimum-coverage>0.32</minimum-coverage>
- <!-- Additional coverage is provided by the integration-test module -->
+ <minimum-coverage>0.29</minimum-coverage>
+ <!-- Additional coverage is provided by integration-test module -->
</properties>
<dependencies>
@@ -125,16 +125,6 @@ </exclusion>
</exclusions>
</dependency>
- <dependency>
- <groupId>org.testcontainers</groupId>
- <artifactId>postgresql</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.testcontainers</groupId>
- <artifactId>spock</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<profiles>
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy index 65d63dfe3b..2e4dba2e9b 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy @@ -20,81 +20,126 @@ */ 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(_) >> [] } + } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy index 5e42ce04e7..52651c6b18 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy @@ -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 index 34a040e604..0000000000 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy +++ /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 index 61a5c042a6..0000000000 --- a/cps-ri/src/test/java/org/onap/cps/DatabaseTestContainer.java +++ /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 index 075a241fc7..0000000000 --- a/cps-ri/src/test/java/org/onap/cps/TestApplication.java +++ /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 index 4f40aeaa06..0000000000 --- a/cps-ri/src/test/resources/application.yml +++ /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 index a15d5aed21..0000000000 --- a/cps-ri/src/test/resources/data/anchor.sql +++ /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 index 65b3a48ca5..0000000000 --- a/cps-ri/src/test/resources/data/anchors-schemaset-modules.sql +++ /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 index 07c8a7aab5..0000000000 --- a/cps-ri/src/test/resources/data/clear-all.sql +++ /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 index 4980073433..0000000000 --- a/cps-ri/src/test/resources/data/fragment.sql +++ /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 index 48e8b1fbb5..0000000000 --- a/cps-ri/src/test/resources/data/perf-test.sql +++ /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 index e5bf63b701..0000000000 --- a/cps-ri/src/test/resources/data/schemaset.sql +++ /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 index fae9275ddc..0000000000 --- a/cps-ri/src/test/resources/hibernate.cfg.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE hibernate-configuration PUBLIC
- "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
- "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
-
-<hibernate-configuration>
- <session-factory>
- <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
- <property name="hibernate.connection.url">${DB_URL}</property>
- <property name="hibernate.connection.username">${DB_USERNAME}</property>
- <property name="hibernate.connection.password">${DB_PASSWORD}</property>
- <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>
- <property name="show_sql">true</property>
- <property name="hibernate.hbm2ddl.auto">none</property>
- </session-factory>
-</hibernate-configuration>
\ No newline at end of file diff --git a/cps-service/pom.xml b/cps-service/pom.xml index 45c4d303e4..158eebfd6b 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -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> diff --git a/docs/api/swagger/ncmp/openapi.yaml b/docs/api/swagger/ncmp/openapi.yaml index 5cc443b062..ea9786ef7f 100644 --- a/docs/api/swagger/ncmp/openapi.yaml +++ b/docs/api/swagger/ncmp/openapi.yaml @@ -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 diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 0b2d2a4612..60050c1a3c 100755 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -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 ============== diff --git a/integration-test/pom.xml b/integration-test/pom.xml index fa403a9789..5dd64cbb2f 100644 --- a/integration-test/pom.xml +++ b/integration-test/pom.xml @@ -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> diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index b942a43af2..a1e03529c3 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -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' diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy index 18a2941615..e39e114405 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/TestConfig.groovy @@ -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 @@ -103,11 +104,6 @@ class TestConfig extends Specification{ } @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() + } + } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/SessionManagerIntegrationSpec.groovy index ceb9dd4cf3..e0a2602b23 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/SessionManagerIntegrationSpec.groovy @@ -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. @@ -18,28 +18,21 @@ * ============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() } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy index d20da46cc5..eee87dd7c0 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/GetPerfTest.groovy @@ -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 } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy index 885f1c2038..eafd16f346 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/QueryPerfTest.groovy @@ -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 } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy index c281908653..a02d21c41a 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/UpdatePerfTest.groovy @@ -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) } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy index 5d7c9de51e..bcb2d2fe5d 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/ncmp/CmHandleQueryPerfTest.groovy @@ -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' diff --git a/jacoco-report/pom.xml b/jacoco-report/pom.xml index 375c7fcffb..6c8fdcf290 100644 --- a/jacoco-report/pom.xml +++ b/jacoco-report/pom.xml @@ -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> @@ -32,7 +32,7 @@ <groupId>org.onap.cps</groupId>
<artifactId>cps-aggregator</artifactId>
- <version>3.3.2-SNAPSHOT</version>
+ <version>3.3.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>cps</name>
diff --git a/releases/3.3.2-container.yaml b/releases/3.3.2-container.yaml new file mode 100644 index 0000000000..a02cd39f60 --- /dev/null +++ b/releases/3.3.2-container.yaml @@ -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 index 0000000000..b80be72223 --- /dev/null +++ b/releases/3.3.2.yaml @@ -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 diff --git a/spotbugs/pom.xml b/spotbugs/pom.xml index 19d8b430cf..874aa04c68 100644 --- a/spotbugs/pom.xml +++ b/spotbugs/pom.xml @@ -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> diff --git a/version.properties b/version.properties index 20a0ce4b81..f6c96da8b9 100755 --- a/version.properties +++ b/version.properties @@ -22,7 +22,7 @@ major=3 minor=3 -patch=2 +patch=3 base_version=${major}.${minor}.${patch} |