aboutsummaryrefslogtreecommitdiffstats
path: root/cps-ncmp-service
diff options
context:
space:
mode:
Diffstat (limited to 'cps-ncmp-service')
-rw-r--r--cps-ncmp-service/pom.xml28
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java38
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java27
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/DataJobService.java)8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobMetadata.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobMetadata.java)2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobReadRequest.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobReadRequest.java)2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobWriteRequest.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobWriteRequest.java)2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ReadOperation.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/ReadOperation.java)2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/WriteOperation.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/WriteOperation.java)2
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java16
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java5
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java158
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiProperties.java55
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java125
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java57
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java114
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfig.java111
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java35
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java13
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java30
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java54
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java20
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java10
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java11
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java21
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java68
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java44
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiClientRequestException.java54
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidDmiResourceUrlException.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/HttpClientRequestException.java)22
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java10
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java20
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java4
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java223
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java34
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperationCmHandle.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java)15
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java49
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java17
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java179
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java72
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NoAlternateIdMatchFoundException.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NoAlternateIdParentFoundException.java)9
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/DataJobServiceImpl.java)10
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java6
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java8
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java63
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy11
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy10
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy7
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumerSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy151
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiPropertiesSpec.groovy37
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfigurationSpec.groovy68
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy45
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy70
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy81
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfigSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy11
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy7
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy29
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy31
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy60
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy45
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy36
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DatastoreTypeSpec.groovy46
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy149
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy24
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy14
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/OperationTypeSpec.groovy48
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy109
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/EventDateTimeFormatterSpec.groovy45
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy5
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContextSpec.groovy20
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy79
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy14
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy6
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy17
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy66
-rw-r--r--cps-ncmp-service/src/test/resources/application.yml13
-rw-r--r--cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json4
84 files changed, 2065 insertions, 1180 deletions
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index fc41da3ae..55abffc9b 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -38,6 +38,22 @@
</properties>
<dependencies>
<dependency>
+ <groupId>io.opentelemetry</groupId>
+ <artifactId>opentelemetry-exporter-otlp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.opentelemetry</groupId>
+ <artifactId>opentelemetry-sdk-extension-jaeger-remote-sampler</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.opentelemetry</groupId>
+ <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.opentelemetry.instrumentation</groupId>
+ <artifactId>opentelemetry-kafka-clients-2.6</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
@@ -70,8 +86,8 @@
<artifactId>mapstruct-processor</artifactId>
</dependency>
<dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- T E S T - D E P E N D E N C I E S -->
<dependency>
@@ -104,5 +120,13 @@
<artifactId>spock</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-actuator-autoconfigure</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java
index bdc3dee77..8cfad7dbf 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java
@@ -26,14 +26,12 @@ import lombok.Getter;
public enum NcmpResponseStatus {
SUCCESS("0", "Successfully applied changes"),
- SUCCESSFULLY_APPLIED_SUBSCRIPTION("1", "successfully applied subscription"),
+ CM_DATA_SUBSCRIPTION_ACCEPTED("1", "ACCEPTED"),
CM_HANDLES_NOT_FOUND("100", "cm handle id(s) not found"),
CM_HANDLES_NOT_READY("101", "cm handle(s) not ready"),
DMI_SERVICE_NOT_RESPONDING("102", "dmi plugin service is not responding"),
UNABLE_TO_READ_RESOURCE_DATA("103", "dmi plugin service is not able to read resource data"),
- PARTIALLY_APPLIED_SUBSCRIPTION("104", "partially applied subscription"),
- SUBSCRIPTION_NOT_APPLICABLE("105", "subscription not applicable for all cm handles"),
- SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"),
+ CM_DATA_SUBSCRIPTION_REJECTED("104", "REJECTED"),
UNKNOWN_ERROR("108", "Unknown error"),
CM_HANDLE_ALREADY_EXIST("109", "cm-handle already exists"),
CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"),
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
index 20545d711..73c8d9609 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
@@ -37,6 +37,7 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
+import reactor.core.publisher.Mono;
/*
* Datastore interface for handling CPS data.
@@ -52,20 +53,29 @@ public interface NetworkCmProxyDataService {
DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
/**
- * Get resource data for given data store using dmi.
- *
- * @param cmResourceAddress target datastore, cm handle and resource identifier
- * @param optionsParamInQuery options query
- * @param topicParamInQuery topic name for (triggering) async responses
- * @param requestId unique requestId for async request
- * @param authorization contents of Authorization header, or null if not present
- * @return {@code Object} resource data
- */
- Object getResourceDataForCmHandle(CmResourceAddress cmResourceAddress,
- String optionsParamInQuery,
- String topicParamInQuery,
- String requestId,
- String authorization);
+ * Fetches resource data for a given data store using DMI (Data Management Interface).
+ * This method retrieves data based on the provided CmResourceAddress and additional query parameters.
+ * It supports asynchronous processing and handles authorization if required.
+ *
+ * @param cmResourceAddress The target data store, including the CM handle and resource identifier.
+ * This parameter must not be null.
+ * @param optionsParamInQuery Additional query parameters that may influence the data retrieval process,
+ * such as filters or limits. This parameter can be null.
+ * @param topicParamInQuery The topic name for triggering asynchronous responses. If specified,
+ * the response will be sent to this topic. This parameter can be null.
+ * @param requestId A unique identifier for the request, used for tracking and correlating
+ * asynchronous operations. This parameter must not be null.
+ * @param authorization The contents of the Authorization header. This parameter can be null
+ * if authorization is not required.
+ * @return {@code Mono<Object>} A reactive Mono that emits the resource data on successful retrieval
+ * or an error signal if the operation fails. The Mono represents a single asynchronous
+ * computation result.
+ */
+ Mono<Object> getResourceDataForCmHandle(CmResourceAddress cmResourceAddress,
+ String optionsParamInQuery,
+ String topicParamInQuery,
+ String requestId,
+ String authorization);
/**
* Get resource data for operational.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java
index 340806b89..39d497217 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,9 @@
package org.onap.cps.ncmp.api;
+import java.util.Collection;
import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.model.DataNode;
/*
* Datastore interface for handling cached CPS data query requests.
@@ -28,14 +30,21 @@ import org.onap.cps.spi.FetchDescendantsOption;
public interface NetworkCmProxyQueryService {
/**
- * Get resource data for operational.
+ * Fetches operational resource data based on the provided CM handle identifier and CPS path.
+ * This method retrieves data nodes from the specified path within the context of a given CM handle.
+ * It supports options for fetching descendant nodes.
*
- * @param cmHandleId cm handle identifier
- * @param cpsPath cps path
- * @Link FetchDescendantsOption fetch descendants option
- * @return {@code Object} resource data
+ * @param cmHandleId The CM handle identifier, which uniquely identifies the CM handle.
+ * This parameter must not be null.
+ * @param cpsPath The CPS (Control Plane Service) path specifying the location of the
+ * resource data within the CM handle. This parameter must not be null.
+ * @param fetchDescendantsOption The option specifying whether to fetch descendant nodes along with the specified
+ * resource data.
+ * @return {@code Collection<DataNode>} A collection of DataNode objects representing the resource data
+ * retrieved from the specified path. The collection may include descendant nodes based on the
+ * fetchDescendantsOption.
*/
- Object queryResourceDataOperational(String cmHandleId,
- String cpsPath,
- FetchDescendantsOption fetchDescendantsOption);
+ Collection<DataNode> queryResourceDataOperational(String cmHandleId,
+ String cpsPath,
+ FetchDescendantsOption fetchDescendantsOption);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/DataJobService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
index 6122afc80..f22124593 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/DataJobService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java
@@ -18,11 +18,11 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api;
+package org.onap.cps.ncmp.api.datajobs;
-import org.onap.cps.ncmp.api.models.datajob.DataJobMetadata;
-import org.onap.cps.ncmp.api.models.datajob.DataJobReadRequest;
-import org.onap.cps.ncmp.api.models.datajob.DataJobWriteRequest;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest;
public interface DataJobService {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobMetadata.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobMetadata.java
index dc8037b86..564352d8d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobMetadata.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobMetadata.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.models.datajob;
+package org.onap.cps.ncmp.api.datajobs.models;
/**
* Metadata of read/write data job request.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobReadRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobReadRequest.java
index f861c3d49..19408b1da 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobReadRequest.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobReadRequest.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.models.datajob;
+package org.onap.cps.ncmp.api.datajobs.models;
import java.util.List;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobWriteRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobWriteRequest.java
index 254e198b8..d8961b17c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobWriteRequest.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobWriteRequest.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.models.datajob;
+package org.onap.cps.ncmp.api.datajobs.models;
import java.util.List;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/ReadOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ReadOperation.java
index d2b073896..2459e4cc2 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/ReadOperation.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ReadOperation.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.models.datajob;
+package org.onap.cps.ncmp.api.datajobs.models;
import java.util.List;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/WriteOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/WriteOperation.java
index c2f6504ce..807e03f06 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/WriteOperation.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/WriteOperation.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.models.datajob;
+package org.onap.cps.ncmp.api.datajobs.models;
/**
* Holds information of write data job operation.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
index 6aa09767b..754050947 100755
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
@@ -87,8 +87,8 @@ import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
@Slf4j
@Service
@@ -133,17 +133,14 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
@Override
- public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress,
+ public Mono<Object> getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress,
final String optionsParamInQuery,
final String topicParamInQuery,
final String requestId,
final String authorization) {
- final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmResourceAddress,
- optionsParamInQuery,
- topicParamInQuery,
- requestId,
- authorization);
- return responseEntity.getBody();
+ return dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, optionsParamInQuery, topicParamInQuery,
+ requestId, authorization)
+ .flatMap(responseEntity -> Mono.justOrEmpty(responseEntity.getBody()));
}
@Override
@@ -160,8 +157,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
final DataOperationRequest dataOperationRequest,
final String requestId,
final String authorization) {
- dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, dataOperationRequest, requestId,
- authorization);
+ dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, dataOperationRequest, requestId, authorization);
}
@Override
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
index f86191002..11c58235e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2022-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.onap.cps.spi.exceptions.DataValidationException;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.DataNodeBuilder;
+import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -222,7 +223,7 @@ public class NetworkCmProxyDataServicePropertyHandler {
cmHandleData.put(fieldName, newFieldValue);
dmiRegistryData.put("cm-handles", cmHandleData);
cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- jsonObjectMapper.asJsonString(dmiRegistryData), OffsetDateTime.now());
+ jsonObjectMapper.asJsonString(dmiRegistryData), OffsetDateTime.now(), ContentType.JSON);
log.debug("Updating {} for cmHandle {} with value : {})", fieldName, cmHandleIdToUpdate, newFieldValue);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java
index d8353f302..8d3b6ed8f 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java
@@ -22,11 +22,13 @@ package org.onap.cps.ncmp.api.impl;
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
+import java.util.Collection;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsQueryService;
import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.model.DataNode;
import org.springframework.stereotype.Service;
@Slf4j
@@ -37,9 +39,9 @@ public class NetworkCmProxyQueryServiceImpl implements NetworkCmProxyQueryServic
private final CpsQueryService cpsQueryService;
@Override
- public Object queryResourceDataOperational(final String cmHandleId,
- final String cpsPath,
- final FetchDescendantsOption fetchDescendantsOption) {
+ public Collection<DataNode> queryResourceDataOperational(final String cmHandleId,
+ final String cpsPath,
+ final FetchDescendantsOption fetchDescendantsOption) {
return cpsQueryService.queryDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cpsPath,
fetchDescendantsOption);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
index 798a280c8..5811cf97d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,20 +21,36 @@
package org.onap.cps.ncmp.api.impl.client;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
+import static org.springframework.http.HttpStatus.REQUEST_TIMEOUT;
+
import com.fasterxml.jackson.databind.JsonNode;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties;
-import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
+import org.onap.cps.ncmp.api.impl.config.DmiProperties;
+import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException;
+import org.onap.cps.ncmp.api.impl.exception.InvalidDmiResourceUrlException;
import org.onap.cps.ncmp.api.impl.operations.OperationType;
-import org.springframework.http.HttpEntity;
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
-import org.springframework.web.client.HttpStatusCodeException;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.web.client.HttpServerErrorException;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClientRequestException;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+import reactor.core.publisher.Mono;
@Component
@RequiredArgsConstructor
@@ -43,60 +59,134 @@ public class DmiRestClient {
private static final String HEALTH_CHECK_URL_EXTENSION = "/actuator/health";
private static final String NOT_SPECIFIED = "";
- private final RestTemplate restTemplate;
+ private static final String NO_AUTHORIZATION = null;
+
private final DmiProperties dmiProperties;
+ private final JsonObjectMapper jsonObjectMapper;
+ @Qualifier("dataServicesWebClient")
+ private final WebClient dataServicesWebClient;
+ @Qualifier("modelServicesWebClient")
+ private final WebClient modelServicesWebClient;
+ @Qualifier("healthChecksWebClient")
+ private final WebClient healthChecksWebClient;
/**
- * Sends POST operation to DMI with json body containing module references.
+ * Sends a POST operation to the DMI with a JSON body containing module references.
*
- * @param dmiResourceUrl dmi resource url
- * @param requestBodyAsJsonString json data body
- * @param operationType the type of operation being executed (for error reporting only)
- * @param authorization contents of Authorization header, or null if not present
- * @return response entity of type String
+ * @param requiredDmiService Determines if the required service is for a data or model operation.
+ * @param dmiUrl The DMI resource URL.
+ * @param requestBodyAsJsonString JSON data body.
+ * @param operationType The type of operation being executed (for error reporting only).
+ * @param authorization Contents of the Authorization header, or null if not present.
+ * @return ResponseEntity containing the response from the DMI.
+ * @throws DmiClientRequestException If there is an error during the DMI request.
*/
- public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl,
+ public ResponseEntity<Object> postOperationWithJsonData(final RequiredDmiService requiredDmiService,
+ final String dmiUrl,
final String requestBodyAsJsonString,
final OperationType operationType,
final String authorization) {
- final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders(),
- authorization));
try {
- return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
- } catch (final HttpStatusCodeException httpStatusCodeException) {
- final String exceptionMessage = "Unable to " + operationType.toString() + " resource data.";
- throw new HttpClientRequestException(exceptionMessage, httpStatusCodeException.getResponseBodyAsString(),
- httpStatusCodeException.getStatusCode().value());
+ return postOperationWithJsonDataAsync(requiredDmiService, dmiUrl, requestBodyAsJsonString, operationType,
+ authorization).block();
+ } catch (final HttpServerErrorException e) {
+ throw handleDmiClientException(e, operationType.getOperationName());
}
}
/**
+ * Asynchronously performs an HTTP POST operation with the given JSON data.
+ *
+ * @param requiredDmiService The service object required for retrieving or configuring the WebClient.
+ * @param dmiUrl The URL to which the POST request is sent.
+ * @param requestBodyAsJsonString The JSON string that will be sent as the request body.
+ * @param operationType An enumeration or object that holds information about the type of operation
+ * being performed.
+ * @param authorization The authorization token to be added to the request headers.
+ * @return A Mono emitting the response entity containing the server's response.
+ */
+ public Mono<ResponseEntity<Object>> postOperationWithJsonDataAsync(final RequiredDmiService requiredDmiService,
+ final String dmiUrl,
+ final String requestBodyAsJsonString,
+ final OperationType operationType,
+ final String authorization) {
+ final WebClient webClient = getWebClient(requiredDmiService);
+ return webClient.post()
+ .uri(toUri(dmiUrl))
+ .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization))
+ .body(BodyInserters.fromValue(requestBodyAsJsonString))
+ .retrieve()
+ .toEntity(Object.class)
+ .onErrorMap(throwable -> handleDmiClientException(throwable, operationType.getOperationName()));
+ }
+
+ /**
* Get DMI plugin health status.
*
- * @param dmiPluginBaseUrl the base URL of the dmi-plugin
- * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception)
+ * @param dmiUrl the base URL of the dmi-plugin
+ * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception)
*/
- public String getDmiHealthStatus(final String dmiPluginBaseUrl) {
- final HttpEntity<Object> httpHeaders = new HttpEntity<>(configureHttpHeaders(new HttpHeaders(), null));
+ public String getDmiHealthStatus(final String dmiUrl) {
try {
- final JsonNode responseHealthStatus =
- restTemplate.getForObject(dmiPluginBaseUrl + HEALTH_CHECK_URL_EXTENSION,
- JsonNode.class, httpHeaders);
+ final URI dmiHealthCheckUri = toUri(dmiUrl + HEALTH_CHECK_URL_EXTENSION);
+ final JsonNode responseHealthStatus = healthChecksWebClient.get()
+ .uri(dmiHealthCheckUri)
+ .headers(httpHeaders -> configureHttpHeaders(httpHeaders, NO_AUTHORIZATION))
+ .retrieve()
+ .bodyToMono(JsonNode.class).block();
return responseHealthStatus == null ? NOT_SPECIFIED :
- responseHealthStatus.get("status").asText();
+ responseHealthStatus.path("status").asText();
} catch (final Exception e) {
- log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiPluginBaseUrl, e.getMessage());
+ log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiUrl, e.getMessage());
return NOT_SPECIFIED;
}
}
- private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) {
+ private WebClient getWebClient(final RequiredDmiService requiredDmiService) {
+ return requiredDmiService.equals(RequiredDmiService.DATA) ? dataServicesWebClient : modelServicesWebClient;
+ }
+
+ private void configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) {
if (dmiProperties.isDmiBasicAuthEnabled()) {
httpHeaders.setBasicAuth(dmiProperties.getAuthUsername(), dmiProperties.getAuthPassword());
} else if (authorization != null && authorization.toLowerCase(Locale.getDefault()).startsWith("bearer ")) {
httpHeaders.add(HttpHeaders.AUTHORIZATION, authorization);
}
- httpHeaders.setContentType(MediaType.APPLICATION_JSON);
- return httpHeaders;
+ }
+
+ private static URI toUri(final String dmiResourceUrl) {
+ try {
+ return new URI(dmiResourceUrl);
+ } catch (final URISyntaxException e) {
+ throw new InvalidDmiResourceUrlException(dmiResourceUrl, BAD_REQUEST.value());
+ }
+ }
+
+ private DmiClientRequestException handleDmiClientException(final Throwable throwable, final String operationType) {
+ if (throwable instanceof WebClientResponseException webClientResponseException) {
+ if (webClientResponseException.getStatusCode().isSameCodeAs(REQUEST_TIMEOUT)) {
+ throw new DmiClientRequestException(webClientResponseException.getStatusCode().value(),
+ webClientResponseException.getMessage(),
+ jsonObjectMapper.asJsonString(webClientResponseException.getResponseBodyAsString()),
+ DMI_SERVICE_NOT_RESPONDING);
+ }
+ throw new DmiClientRequestException(webClientResponseException.getStatusCode().value(),
+ webClientResponseException.getMessage(),
+ jsonObjectMapper.asJsonString(webClientResponseException.getResponseBodyAsString()),
+ UNABLE_TO_READ_RESOURCE_DATA);
+
+ }
+ final String exceptionMessage = "Unable to " + operationType + " resource data.";
+ if (throwable instanceof WebClientRequestException webClientRequestException) {
+ throw new DmiClientRequestException(HttpStatus.SERVICE_UNAVAILABLE.value(),
+ webClientRequestException.getMessage(),
+ exceptionMessage, DMI_SERVICE_NOT_RESPONDING);
+ }
+ if (throwable instanceof HttpServerErrorException httpServerErrorException) {
+ throw new DmiClientRequestException(httpServerErrorException.getStatusCode().value(), exceptionMessage,
+ httpServerErrorException.getResponseBodyAsString(), DMI_SERVICE_NOT_RESPONDING);
+ }
+ throw new DmiClientRequestException(INTERNAL_SERVER_ERROR.value(), exceptionMessage, throwable.getMessage(),
+ UNKNOWN_ERROR);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiProperties.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiProperties.java
new file mode 100644
index 000000000..5453efecd
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiProperties.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Getter
+@Component
+public class DmiProperties {
+ @Value("${ncmp.dmi.auth.username}")
+ private String authUsername;
+ @Value("${ncmp.dmi.auth.password}")
+ private String authPassword;
+ @Getter(AccessLevel.NONE)
+ @Value("${ncmp.dmi.api.base-path}")
+ private String dmiBasePath;
+ @Value("${ncmp.dmi.auth.enabled}")
+ private boolean dmiBasicAuthEnabled;
+
+ /**
+ * Removes both leading and trailing slashes if they are present.
+ *
+ * @return dmi base path without any slashes ("/")
+ */
+ public String getDmiBasePath() {
+ if (dmiBasePath.startsWith("/")) {
+ dmiBasePath = dmiBasePath.substring(1);
+ }
+ if (dmiBasePath.endsWith("/")) {
+ dmiBasePath = dmiBasePath.substring(0, dmiBasePath.length() - 1);
+ }
+ return dmiBasePath;
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java
new file mode 100644
index 000000000..08885a9e0
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfiguration.java
@@ -0,0 +1,125 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config;
+
+import io.netty.channel.ChannelOption;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.handler.timeout.WriteTimeoutHandler;
+import java.util.concurrent.TimeUnit;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.resources.ConnectionProvider;
+
+/**
+ * Configures and creates a WebClient bean that triggers an initialization (warmup) of the host name resolver and
+ * loads the necessary native libraries to avoid the extra time needed to load resources for first request.
+ */
+@Configuration
+@RequiredArgsConstructor
+public class DmiWebClientConfiguration {
+
+ private final HttpClientConfiguration httpClientConfiguration;
+
+ /**
+ * Configures and create a WebClient bean for DMI data service.
+ *
+ * @return a WebClient instance for data services.
+ */
+ @Bean
+ public WebClient dataServicesWebClient() {
+ final HttpClientConfiguration.DataServices httpClientConfiguration
+ = this.httpClientConfiguration.getDataServices();
+
+ final HttpClient httpClient = createHttpClient("dataConnectionPool",
+ httpClientConfiguration.getMaximumConnectionsTotal(),
+ httpClientConfiguration.getConnectionTimeoutInSeconds(),
+ httpClientConfiguration.getReadTimeoutInSeconds(),
+ httpClientConfiguration.getWriteTimeoutInSeconds());
+ return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+ }
+
+ /**
+ * Configures and creates a WebClient bean for DMI model service.
+ *
+ * @return a WebClient instance for model services.
+ */
+ @Bean
+ public WebClient modelServicesWebClient() {
+ final HttpClientConfiguration.ModelServices httpClientConfiguration
+ = this.httpClientConfiguration.getModelServices();
+
+ final HttpClient httpClient = createHttpClient("modelConnectionPool",
+ httpClientConfiguration.getMaximumConnectionsTotal(),
+ httpClientConfiguration.getConnectionTimeoutInSeconds(),
+ httpClientConfiguration.getReadTimeoutInSeconds(),
+ httpClientConfiguration.getWriteTimeoutInSeconds());
+ return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+ }
+
+ /**
+ * Configures and creates a WebClient bean for DMI health service.
+ *
+ * @return a WebClient instance for health checks.
+ */
+ @Bean
+ public WebClient healthChecksWebClient() {
+ final HttpClientConfiguration.HealthCheckServices httpClientConfiguration
+ = this.httpClientConfiguration.getHealthCheckServices();
+
+ final HttpClient httpClient = createHttpClient("healthConnectionPool",
+ httpClientConfiguration.getMaximumConnectionsTotal(),
+ httpClientConfiguration.getConnectionTimeoutInSeconds(),
+ httpClientConfiguration.getReadTimeoutInSeconds(),
+ httpClientConfiguration.getWriteTimeoutInSeconds());
+ return buildAndGetWebClient(httpClient, httpClientConfiguration.getMaximumInMemorySizeInMegabytes());
+ }
+
+ private static HttpClient createHttpClient(final String connectionProviderName,
+ final Integer maximumConnectionsTotal,
+ final Integer connectionTimeoutInSeconds,
+ final Integer readTimeoutInSeconds,
+ final Integer writeTimeoutInSeconds) {
+ final ConnectionProvider dmiWebClientConnectionProvider = ConnectionProvider.create(connectionProviderName,
+ maximumConnectionsTotal);
+
+ return HttpClient.create(dmiWebClientConnectionProvider)
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutInSeconds * 1000)
+ .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(readTimeoutInSeconds,
+ TimeUnit.SECONDS)).addHandlerLast(new WriteTimeoutHandler(writeTimeoutInSeconds,
+ TimeUnit.SECONDS)));
+ }
+
+ private static WebClient buildAndGetWebClient(final HttpClient httpClient,
+ final Integer maximumInMemorySizeInMegabytes) {
+ return WebClient.builder()
+ .defaultHeaders(header -> header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
+ .defaultHeaders(header -> header.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE))
+ .clientConnector(new ReactorClientHttpConnector(httpClient))
+ .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(
+ maximumInMemorySizeInMegabytes * 1024 * 1024)).build();
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java
index d547e31c6..62432f6ca 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation.
+ * Copyright (C) 2023-2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,38 +20,47 @@
package org.onap.cps.ncmp.api.impl.config;
-import java.time.Duration;
-import java.time.temporal.ChronoUnit;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.convert.DurationUnit;
+import org.springframework.stereotype.Component;
@Getter
@Setter
-@ConfigurationProperties(prefix = "ncmp.dmi.httpclient", ignoreUnknownFields = true)
+@Component
+@ConfigurationProperties(prefix = "ncmp.dmi.httpclient")
public class HttpClientConfiguration {
- /**
- * The maximum time to establish a connection.
- */
- @DurationUnit(ChronoUnit.SECONDS)
- private Duration connectionTimeoutInSeconds = Duration.ofSeconds(180);
+ private final DataServices dataServices = new DataServices();
+ private final ModelServices modelServices = new ModelServices();
+ private final HealthCheckServices healthCheckServices = new HealthCheckServices();
- /**
- * The maximum number of open connections per route.
- */
- private int maximumConnectionsPerRoute = 50;
+ @Getter
+ @Setter
+ public static class DataServices {
+ private Integer maximumConnectionsTotal = 100;
+ private Integer connectionTimeoutInSeconds = 30;
+ private Integer readTimeoutInSeconds = 30;
+ private Integer writeTimeoutInSeconds = 30;
+ private Integer maximumInMemorySizeInMegabytes = 1;
+ }
- /**
- * The maximum total number of open connections.
- */
- private int maximumConnectionsTotal = maximumConnectionsPerRoute * 2;
-
- /**
- * The duration after which idle connections are evicted.
- */
- @DurationUnit(ChronoUnit.SECONDS)
- private Duration idleConnectionEvictionThresholdInSeconds = Duration.ofSeconds(5);
+ @Getter
+ @Setter
+ public static class ModelServices {
+ private Integer maximumConnectionsTotal = 100;
+ private Integer connectionTimeoutInSeconds = 30;
+ private Integer readTimeoutInSeconds = 30;
+ private Integer writeTimeoutInSeconds = 30;
+ private Integer maximumInMemorySizeInMegabytes = 1;
+ }
+ @Getter
+ public static class HealthCheckServices {
+ private final Integer maximumConnectionsTotal = 10;
+ private final Integer connectionTimeoutInSeconds = 30;
+ private final Integer readTimeoutInSeconds = 30;
+ private final Integer writeTimeoutInSeconds = 30;
+ private final Integer maximumInMemorySizeInMegabytes = 1;
+ }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java
deleted file mode 100644
index c6ff116a7..000000000
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-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;
-
-import java.util.Arrays;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.hc.client5.http.config.ConnectionConfig;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
-import org.apache.hc.core5.util.TimeValue;
-import org.apache.hc.core5.util.Timeout;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.web.client.RestTemplateBuilder;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Scope;
-import org.springframework.http.MediaType;
-import org.springframework.http.client.ClientHttpRequestFactory;
-import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.RestTemplate;
-
-@Configuration
-@EnableConfigurationProperties(HttpClientConfiguration.class)
-@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
-public class NcmpConfiguration {
-
- @Getter
- @Component
- public static class DmiProperties {
- @Value("${ncmp.dmi.auth.username}")
- private String authUsername;
- @Value("${ncmp.dmi.auth.password}")
- private String authPassword;
- @Value("${ncmp.dmi.api.base-path}")
- private String dmiBasePath;
- @Value("${ncmp.dmi.auth.enabled}")
- private boolean dmiBasicAuthEnabled;
- }
-
- /**
- * Rest template bean.
- *
- * @param restTemplateBuilder the rest template builder
- * @param httpClientConfiguration the http client configuration
- * @return rest template instance
- */
- @Bean
- @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
- public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder,
- final HttpClientConfiguration httpClientConfiguration) {
-
- final ConnectionConfig connectionConfig = ConnectionConfig.copy(ConnectionConfig.DEFAULT)
- .setConnectTimeout(Timeout.of(httpClientConfiguration.getConnectionTimeoutInSeconds()))
- .build();
-
- final PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
- .setDefaultConnectionConfig(connectionConfig)
- .setMaxConnTotal(httpClientConfiguration.getMaximumConnectionsTotal())
- .setMaxConnPerRoute(httpClientConfiguration.getMaximumConnectionsPerRoute())
- .build();
-
- final CloseableHttpClient httpClient = HttpClients.custom()
- .setConnectionManager(connectionManager)
- .evictExpiredConnections()
- .evictIdleConnections(
- TimeValue.of(httpClientConfiguration.getIdleConnectionEvictionThresholdInSeconds()))
- .build();
-
- final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
-
- final RestTemplate restTemplate = restTemplateBuilder
- .requestFactory(() -> requestFactory)
- .setConnectTimeout(httpClientConfiguration.getConnectionTimeoutInSeconds())
- .build();
-
- setRestTemplateMessageConverters(restTemplate);
- return restTemplate;
- }
-
- private static void setRestTemplateMessageConverters(final RestTemplate restTemplate) {
- final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =
- new MappingJackson2HttpMessageConverter();
- mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
- Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
- restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
- }
-
-}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfig.java
new file mode 100644
index 000000000..bcbacbd42
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfig.java
@@ -0,0 +1,111 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config;
+
+import io.micrometer.observation.ObservationPredicate;
+import io.micrometer.observation.ObservationRegistry;
+import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
+import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler;
+import io.opentelemetry.sdk.trace.samplers.Sampler;
+import java.time.Duration;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.server.observation.ServerRequestObservationContext;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+
+@Configuration
+public class OpenTelemetryConfig {
+
+ public static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND = 30;
+
+ @Value("${spring.application.name:cps-application}")
+ private String serviceId;
+
+ @Value("${cps.tracing.exporter.endpoint:http://onap-otel-collector:4317}")
+ private String tracingExporterEndpointUrl;
+
+ @Value("${cps.tracing.sampler.jaeger_remote.endpoint:http://onap-otel-collector:14250}")
+ private String jaegerRemoteSamplerUrl;
+
+ /**
+ * OTLP Exporter with Grpc exporter protocol.
+ */
+ @Bean
+ @ConditionalOnExpression(
+ "${cps.tracing.enabled} && 'grpc'.equals('${cps.tracing.exporter.protocol}')")
+ public OtlpGrpcSpanExporter createOtlpExporterGrpc() {
+ return OtlpGrpcSpanExporter.builder().setEndpoint(tracingExporterEndpointUrl).build();
+ }
+
+ /**
+ * OTLP Exporter with HTTP exporter protocol.
+ */
+ @Bean
+ @ConditionalOnExpression(
+ "${cps.tracing.enabled} && 'http'.equals('${cps.tracing.exporter.protocol}')")
+ public OtlpHttpSpanExporter createOtlpExporterHttp() {
+ return OtlpHttpSpanExporter.builder().setEndpoint(tracingExporterEndpointUrl).build();
+ }
+
+ /**
+ * Jaeger Remote Sampler.
+ */
+ @Bean
+ @ConditionalOnProperty("cps.tracing.enabled")
+ public JaegerRemoteSampler createJaegerRemoteSampler() {
+ return JaegerRemoteSampler.builder()
+ .setEndpoint(jaegerRemoteSamplerUrl)
+ .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND))
+ .setInitialSampler(Sampler.alwaysOff())
+ .setServiceName(serviceId)
+ .build();
+ }
+
+ /**
+ * Excluding /actuator/** endpoints.
+ */
+ @Bean
+ @ConditionalOnProperty("cps.tracing.enabled")
+ ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() {
+ final PathMatcher pathMatcher = new AntPathMatcher("/");
+ return registry ->
+ registry.observationConfig().observationPredicate(observationPredicate(pathMatcher));
+ }
+
+ /**
+ * Excluding /actuator/** endpoints.
+ */
+ static ObservationPredicate observationPredicate(final PathMatcher pathMatcher) {
+ return (name, context) -> {
+ if (context instanceof ServerRequestObservationContext observationContext) {
+ return !pathMatcher.match("/actuator/**", observationContext.getCarrier().getRequestURI());
+ } else {
+ return true;
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java
index 167df5a98..cf6f1c5b1 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java
@@ -21,10 +21,14 @@
package org.onap.cps.ncmp.api.impl.config.kafka;
import io.cloudevents.CloudEvent;
+import io.opentelemetry.instrumentation.kafkaclients.v2_6.TracingConsumerInterceptor;
+import io.opentelemetry.instrumentation.kafkaclients.v2_6.TracingProducerInterceptor;
import java.time.Duration;
import java.util.Map;
import lombok.RequiredArgsConstructor;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
@@ -52,6 +56,9 @@ public class KafkaConfig<T> {
private final KafkaProperties kafkaProperties;
+ @Value("${cps.tracing.enabled:false}")
+ private boolean tracingEnabled;
+
private static final SslBundles NO_SSL = null;
/**
@@ -64,6 +71,10 @@ public class KafkaConfig<T> {
public ProducerFactory<String, T> legacyEventProducerFactory() {
final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties(NO_SSL);
producerConfigProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
+ if (tracingEnabled) {
+ producerConfigProperties.put(
+ ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName());
+ }
return new DefaultKafkaProducerFactory<>(producerConfigProperties);
}
@@ -77,6 +88,10 @@ public class KafkaConfig<T> {
public ConsumerFactory<String, T> legacyEventConsumerFactory() {
final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties(NO_SSL);
consumerConfigProperties.put("spring.deserializer.value.delegate.class", JsonDeserializer.class);
+ if (tracingEnabled) {
+ consumerConfigProperties.put(
+ ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName());
+ }
return new DefaultKafkaConsumerFactory<>(consumerConfigProperties);
}
@@ -90,6 +105,9 @@ public class KafkaConfig<T> {
public KafkaTemplate<String, T> legacyEventKafkaTemplate() {
final KafkaTemplate<String, T> kafkaTemplate = new KafkaTemplate<>(legacyEventProducerFactory());
kafkaTemplate.setConsumerFactory(legacyEventConsumerFactory());
+ if (tracingEnabled) {
+ kafkaTemplate.setObservationEnabled(true);
+ }
return kafkaTemplate;
}
@@ -104,6 +122,9 @@ public class KafkaConfig<T> {
new ConcurrentKafkaListenerContainerFactory<>();
containerFactory.setConsumerFactory(legacyEventConsumerFactory());
containerFactory.getContainerProperties().setAuthExceptionRetryInterval(Duration.ofSeconds(10));
+ if (tracingEnabled) {
+ containerFactory.getContainerProperties().setObservationEnabled(true);
+ }
return containerFactory;
}
@@ -116,6 +137,10 @@ public class KafkaConfig<T> {
@Bean
public ProducerFactory<String, CloudEvent> cloudEventProducerFactory() {
final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties(NO_SSL);
+ if (tracingEnabled) {
+ producerConfigProperties.put(
+ ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName());
+ }
return new DefaultKafkaProducerFactory<>(producerConfigProperties);
}
@@ -128,6 +153,10 @@ public class KafkaConfig<T> {
@Bean
public ConsumerFactory<String, CloudEvent> cloudEventConsumerFactory() {
final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties(NO_SSL);
+ if (tracingEnabled) {
+ consumerConfigProperties.put(
+ ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName());
+ }
return new DefaultKafkaConsumerFactory<>(consumerConfigProperties);
}
@@ -142,6 +171,9 @@ public class KafkaConfig<T> {
final KafkaTemplate<String, CloudEvent> kafkaTemplate =
new KafkaTemplate<>(cloudEventProducerFactory());
kafkaTemplate.setConsumerFactory(cloudEventConsumerFactory());
+ if (tracingEnabled) {
+ kafkaTemplate.setObservationEnabled(true);
+ }
return kafkaTemplate;
}
@@ -157,6 +189,9 @@ public class KafkaConfig<T> {
new ConcurrentKafkaListenerContainerFactory<>();
containerFactory.setConsumerFactory(cloudEventConsumerFactory());
containerFactory.getContainerProperties().setAuthExceptionRetryInterval(Duration.ofSeconds(10));
+ if (tracingEnabled) {
+ containerFactory.getContainerProperties().setObservationEnabled(true);
+ }
return containerFactory;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java
index 8a4beb956..4e2062fed 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java
@@ -64,12 +64,19 @@ public class CmNotificationSubscriptionDelta {
}
}
- final DmiCmNotificationSubscriptionPredicate predicateDelta =
- new DmiCmNotificationSubscriptionPredicate(targetCmHandleIds, datastoreType, xpaths);
+ populateValidDmiCmNotificationSubscriptionPredicateDelta(targetCmHandleIds, xpaths, datastoreType, delta);
+ }
+ return delta;
+ }
+ private void populateValidDmiCmNotificationSubscriptionPredicateDelta(final Set<String> targetCmHandleIds,
+ final Set<String> xpaths, final DatastoreType datastoreType,
+ final List<DmiCmNotificationSubscriptionPredicate> delta) {
+ if (!(targetCmHandleIds.isEmpty() || xpaths.isEmpty())) {
+ final DmiCmNotificationSubscriptionPredicate predicateDelta =
+ new DmiCmNotificationSubscriptionPredicate(targetCmHandleIds, datastoreType, xpaths);
delta.add(predicateDelta);
}
- return delta;
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java
index b5370bf1e..368e27a7d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java
@@ -123,12 +123,12 @@ public class DmiCmNotificationSubscriptionCacheHandler {
*
*/
public void updateDmiCmNotificationSubscriptionStatusPerDmi(final String subscriptionId,
- final String dmiServiceName, final CmNotificationSubscriptionStatus status) {
+ final String dmiServiceName,
+ final CmNotificationSubscriptionStatus status) {
final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi =
cmNotificationSubscriptionCache.get(subscriptionId);
dmiCmNotificationSubscriptionDetailsPerDmi.get(dmiServiceName).setCmNotificationSubscriptionStatus(status);
cmNotificationSubscriptionCache.put(subscriptionId, dmiCmNotificationSubscriptionDetailsPerDmi);
-
}
/**
@@ -157,6 +157,32 @@ public class DmiCmNotificationSubscriptionCacheHandler {
}
}
+ /**
+ * Remove subscription from database per DMI service name.
+ *
+ * @param subscriptionId String of subscription id
+ * @param dmiServiceName String of dmiServiceName
+ *
+ */
+ public void removeFromDatabasePerDmi(final String subscriptionId, final String dmiServiceName) {
+ final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicateList =
+ cmNotificationSubscriptionCache.get(subscriptionId).get(dmiServiceName)
+ .getDmiCmNotificationSubscriptionPredicates();
+ for (final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate:
+ dmiCmNotificationSubscriptionPredicateList) {
+ final DatastoreType datastoreType = dmiCmNotificationSubscriptionPredicate.getDatastoreType();
+ final Set<String> cmHandles = dmiCmNotificationSubscriptionPredicate.getTargetCmHandleIds();
+ final Set<String> xpaths = dmiCmNotificationSubscriptionPredicate.getXpaths();
+
+ for (final String cmHandle: cmHandles) {
+ for (final String xpath: xpaths) {
+ cmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(datastoreType,
+ cmHandle, xpath, subscriptionId);
+ }
+ }
+ }
+ }
+
private void updateDmiCmNotificationSubscriptionDetailsPerDmi(
final String dmiServiceName,
final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate,
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java
index fb89aae3f..051949c5e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java
@@ -20,6 +20,8 @@
package org.onap.cps.ncmp.api.impl.events.cmsubscription.consumer;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_DATA_SUBSCRIPTION_ACCEPTED;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_DATA_SUBSCRIPTION_REJECTED;
import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
import io.cloudevents.CloudEvent;
@@ -27,12 +29,14 @@ import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionEventsHandler;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.Data;
import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@@ -46,12 +50,14 @@ public class CmNotificationSubscriptionDmiOutEventConsumer {
private final CmNotificationSubscriptionEventsHandler cmNotificationSubscriptionEventsHandler;
private final CmNotificationSubscriptionMappersHandler cmNotificationSubscriptionMappersHandler;
+ private static final String CM_DATA_SUBSCRIPTION_CORRELATION_ID_SEPARATOR = "#";
+
/**
* Consume the Cm Notification Subscription event from the dmi-plugin.
*
* @param cmNotificationSubscriptionDmiOutEventConsumerRecord the event to be consumed
*/
- @KafkaListener(topics = "${app.ncmp.avc.subscription-response-topic}",
+ @KafkaListener(topics = "${app.ncmp.avc.cm-subscription-dmi-out}",
containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
public void consumeCmNotificationSubscriptionDmiOutEvent(
final ConsumerRecord<String, CloudEvent> cmNotificationSubscriptionDmiOutEventConsumerRecord) {
@@ -59,26 +65,33 @@ public class CmNotificationSubscriptionDmiOutEventConsumer {
final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent =
toTargetEvent(cloudEvent, CmNotificationSubscriptionDmiOutEvent.class);
final String correlationId = String.valueOf(cloudEvent.getExtension("correlationid"));
- if ("subscriptionCreateResponse".equals(cloudEvent.getType()) && cmNotificationSubscriptionDmiOutEvent != null
- && correlationId != null) {
- handleCmSubscriptionCreate(correlationId, cmNotificationSubscriptionDmiOutEvent);
+ if (cmNotificationSubscriptionDmiOutEvent != null && correlationId != null) {
+ final String eventType = cloudEvent.getType();
+ handleCmSubscriptionDmiOutEvent(correlationId, eventType, cmNotificationSubscriptionDmiOutEvent);
}
}
- private void handleCmSubscriptionCreate(final String correlationId,
- final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent) {
- final String subscriptionId = correlationId.split("#")[0];
- final String dmiPluginName = correlationId.split("#")[1];
+ private void handleCmSubscriptionDmiOutEvent(final String correlationId,
+ final String eventType,
+ final CmNotificationSubscriptionDmiOutEvent
+ cmNotificationSubscriptionDmiOutEvent) {
+ final String subscriptionId = correlationId.split(CM_DATA_SUBSCRIPTION_CORRELATION_ID_SEPARATOR)[0];
+ final String dmiPluginName = correlationId.split(CM_DATA_SUBSCRIPTION_CORRELATION_ID_SEPARATOR)[1];
- if ("ACCEPTED".equals(cmNotificationSubscriptionDmiOutEvent.getData().getStatusMessage())) {
+ if (checkStatusCodeAndMessage(CM_DATA_SUBSCRIPTION_ACCEPTED, cmNotificationSubscriptionDmiOutEvent.getData())) {
handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmNotificationSubscriptionStatus.ACCEPTED);
- dmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
- handleEventsStatusPerDmi(subscriptionId);
+ if (eventType.equals("subscriptionCreateResponse")) {
+ dmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
+ }
+ if (eventType.equals("subscriptionDeleteResponse")) {
+ dmiCmNotificationSubscriptionCacheHandler.removeFromDatabasePerDmi(subscriptionId, dmiPluginName);
+ }
+ handleEventsStatusPerDmi(subscriptionId, eventType);
}
- if ("REJECTED".equals(cmNotificationSubscriptionDmiOutEvent.getData().getStatusMessage())) {
+ if (checkStatusCodeAndMessage(CM_DATA_SUBSCRIPTION_REJECTED, cmNotificationSubscriptionDmiOutEvent.getData())) {
handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmNotificationSubscriptionStatus.REJECTED);
- handleEventsStatusPerDmi(subscriptionId);
+ handleEventsStatusPerDmi(subscriptionId, eventType);
}
log.info("Cm Subscription with id : {} handled by the dmi-plugin : {} has the status : {}", subscriptionId,
@@ -86,18 +99,25 @@ public class CmNotificationSubscriptionDmiOutEventConsumer {
}
private void handleCacheStatusPerDmi(final String subscriptionId, final String dmiPluginName,
- final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus) {
+ final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus) {
dmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi(subscriptionId,
dmiPluginName, cmNotificationSubscriptionStatus);
}
- private void handleEventsStatusPerDmi(final String subscriptionId) {
+ private void handleEventsStatusPerDmi(final String subscriptionId, final String eventType) {
final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi =
dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId);
final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent =
cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEvent(subscriptionId,
dmiCmNotificationSubscriptionDetailsPerDmi);
cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId,
- "subscriptionCreateResponse", cmNotificationSubscriptionNcmpOutEvent, false);
+ eventType, cmNotificationSubscriptionNcmpOutEvent, false);
+ }
+
+ private boolean checkStatusCodeAndMessage(final NcmpResponseStatus ncmpResponseStatus,
+ final Data cmNotificationSubscriptionDmiOutData) {
+ return ncmpResponseStatus.getCode().equals(cmNotificationSubscriptionDmiOutData.getStatusCode())
+ && ncmpResponseStatus.getMessage()
+ .equals(cmNotificationSubscriptionDmiOutData.getStatusMessage());
}
-}
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java
index 70135b307..fb3388c11 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java
@@ -23,12 +23,13 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.consumer;
import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent;
import io.cloudevents.CloudEvent;
+import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionHandlerService;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent;
-import org.springframework.beans.factory.annotation.Value;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@@ -39,15 +40,12 @@ public class CmNotificationSubscriptionNcmpInEventConsumer {
private final CmNotificationSubscriptionHandlerService cmNotificationSubscriptionHandlerService;
- @Value("${notification.enabled:true}")
- private boolean notificationFeatureEnabled;
-
/**
* Consume the specified event.
*
* @param subscriptionEventConsumerRecord the event to be consumed
*/
- @KafkaListener(topics = "${app.ncmp.avc.subscription-topic}",
+ @KafkaListener(topics = "${app.ncmp.avc.cm-subscription-ncmp-in}",
containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory")
public void consumeSubscriptionEvent(final ConsumerRecord<String, CloudEvent> subscriptionEventConsumerRecord) {
final CloudEvent cloudEvent = subscriptionEventConsumerRecord.value();
@@ -57,10 +55,16 @@ public class CmNotificationSubscriptionNcmpInEventConsumer {
cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId());
final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId();
+ final List<Predicate> predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates();
if ("subscriptionCreateRequest".equals(cloudEvent.getType())) {
- log.info("Subscription for source {} with subscription id {} ...", cloudEvent.getSource(), subscriptionId);
- cmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest(
- cmNotificationSubscriptionNcmpInEvent);
+ log.info("Subscription create request for source {} with subscription id {} ...",
+ cloudEvent.getSource(), subscriptionId);
+ cmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest(subscriptionId, predicates);
+ }
+ if ("subscriptionDeleteRequest".equals(cloudEvent.getType())) {
+ log.info("Subscription delete request for source {} with subscription id {} ...",
+ cloudEvent.getSource(), subscriptionId);
+ cmNotificationSubscriptionHandlerService.processSubscriptionDeleteRequest(subscriptionId, predicates);
}
}
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java
index 489401f26..761068748 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java
@@ -29,8 +29,8 @@ import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate;
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmHandle;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent;
-import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Cmhandle;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Data;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Predicate;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.ScopeFilter;
@@ -54,7 +54,7 @@ public class CmNotificationSubscriptionDmiInEventMapper {
new CmNotificationSubscriptionDmiInEvent();
final Data cmSubscriptionData = new Data();
cmSubscriptionData.setPredicates(mapToDmiInEventPredicates(dmiCmNotificationSubscriptionPredicates));
- cmSubscriptionData.setCmhandles(mapToCmSubscriptionCmhandleWithPrivateProperties(
+ cmSubscriptionData.setCmHandles(mapToCmSubscriptionCmhandleWithPrivateProperties(
extractUniqueCmHandleIds(dmiCmNotificationSubscriptionPredicates)));
cmNotificationSubscriptionDmiInEvent.setData(cmSubscriptionData);
return cmNotificationSubscriptionDmiInEvent;
@@ -81,12 +81,12 @@ public class CmNotificationSubscriptionDmiInEventMapper {
}
- private List<Cmhandle> mapToCmSubscriptionCmhandleWithPrivateProperties(final Set<String> cmHandleIds) {
+ private List<CmHandle> mapToCmSubscriptionCmhandleWithPrivateProperties(final Set<String> cmHandleIds) {
- final List<Cmhandle> cmSubscriptionCmHandles = new ArrayList<>();
+ final List<CmHandle> cmSubscriptionCmHandles = new ArrayList<>();
inventoryPersistence.getYangModelCmHandles(cmHandleIds).forEach(yangModelCmHandle -> {
- final Cmhandle cmhandle = new Cmhandle();
+ final CmHandle cmhandle = new CmHandle();
final Map<String, String> cmhandleDmiProperties = new LinkedHashMap<>();
yangModelCmHandle.getDmiProperties()
.forEach(dmiProperty -> cmhandleDmiProperties.put(dmiProperty.getName(), dmiProperty.getValue()));
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java
index 9fbe26848..3273c556c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java
@@ -25,7 +25,6 @@ import io.cloudevents.core.builder.CloudEventBuilder;
import java.net.URI;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
import org.onap.cps.events.EventsPublisher;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent;
import org.onap.cps.utils.JsonObjectMapper;
@@ -34,7 +33,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@Component
-@Slf4j
@RequiredArgsConstructor
@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true)
public class CmNotificationSubscriptionDmiInEventProducer {
@@ -42,7 +40,7 @@ public class CmNotificationSubscriptionDmiInEventProducer {
private final EventsPublisher<CloudEvent> eventsPublisher;
private final JsonObjectMapper jsonObjectMapper;
- @Value("${app.ncmp.avc.subscription-forward-topic-prefix}")
+ @Value("${app.ncmp.avc.cm-subscription-dmi-in}")
private String cmNotificationSubscriptionDmiInEventTopic;
/**
@@ -65,9 +63,10 @@ public class CmNotificationSubscriptionDmiInEventProducer {
final String dmiPluginName, final String eventType,
final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) {
return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType)
- .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.dmi.cm.subscription:1.0.0"))
- .withExtension("correlationid", subscriptionId.concat("#").concat(dmiPluginName))
- .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionDmiInEvent)).build();
+ .withSource(URI.create("NCMP"))
+ .withDataSchema(URI.create("org.onap.ncmp.dmi.cm.subscription:1.0.0"))
+ .withExtension("correlationid", subscriptionId.concat("#").concat(dmiPluginName))
+ .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionDmiInEvent)).build();
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java
index ac5de07f0..ed7ed2a0b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java
@@ -48,7 +48,7 @@ import org.springframework.stereotype.Component;
@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true)
public class CmNotificationSubscriptionNcmpOutEventProducer {
- @Value("${app.ncmp.avc.subscription-outcome-topic}")
+ @Value("${app.ncmp.avc.cm-subscription-ncmp-out}")
private String cmNotificationSubscriptionNcmpOutEventTopic;
@Value("${ncmp.timers.subscription-forwarding.dmi-response-timeout-ms}")
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java
index 536693ee4..1c52ffa79 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java
@@ -20,16 +20,25 @@
package org.onap.cps.ncmp.api.impl.events.cmsubscription.service;
-import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent;
+import java.util.List;
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate;
public interface CmNotificationSubscriptionHandlerService {
/**
- * Process cm notification subscription request.
+ * Process cm notification subscription create request.
*
- * @param cmNotificationSubscriptionNcmpInEvent CM Notification Subscription event
+ * @param subscriptionId subscription id
+ * @param predicates subscription predicates
*/
- void processSubscriptionCreateRequest(
- final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent);
+ void processSubscriptionCreateRequest(final String subscriptionId, final List<Predicate> predicates);
-}
+ /**
+ * Process cm notification subscription delete request.
+ *
+ * @param subscriptionId subscription id
+ * @param predicates subscription predicates
+ */
+ void processSubscriptionDeleteRequest(final String subscriptionId, final List<Predicate> predicates);
+
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java
index 7872ba0a3..08e3c9552 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java
@@ -30,9 +30,9 @@ import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscripti
import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionEventsHandler;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler;
+import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails;
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate;
-import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate;
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent;
import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent;
@@ -49,28 +49,32 @@ public class CmNotificationSubscriptionHandlerServiceImpl implements CmNotificat
private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler;
@Override
- public void processSubscriptionCreateRequest(
- final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent) {
- final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId();
- final List<Predicate> predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates();
-
+ public void processSubscriptionCreateRequest(final String subscriptionId, final List<Predicate> predicates) {
if (cmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId(subscriptionId)) {
dmiCmNotificationSubscriptionCacheHandler.add(subscriptionId, predicates);
- sendSubscriptionCreateRequestToDmi(subscriptionId);
- scheduleCmNotificationSubscriptionNcmpOutEventResponse(subscriptionId);
+ handleCmNotificationSubscriptionDelta(subscriptionId);
+ scheduleCmNotificationSubscriptionNcmpOutEventResponse(subscriptionId,
+ "subscriptionCreateResponse");
} else {
- rejectAndPublishCmNotificationSubscriptionCreateRequest(subscriptionId,
- predicates);
+ rejectAndPublishCmNotificationSubscriptionCreateRequest(subscriptionId, predicates);
}
}
- private void scheduleCmNotificationSubscriptionNcmpOutEventResponse(final String subscriptionId) {
+ @Override
+ public void processSubscriptionDeleteRequest(final String subscriptionId, final List<Predicate> predicates) {
+ dmiCmNotificationSubscriptionCacheHandler.add(subscriptionId, predicates);
+ sendSubscriptionDeleteRequestToDmi(subscriptionId);
+ scheduleCmNotificationSubscriptionNcmpOutEventResponse(subscriptionId, "subscriptionDeleteResponse");
+ }
+
+ private void scheduleCmNotificationSubscriptionNcmpOutEventResponse(final String subscriptionId,
+ final String eventType) {
cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId,
- "subscriptionCreateResponse", null, true);
+ eventType, null, true);
}
private void rejectAndPublishCmNotificationSubscriptionCreateRequest(final String subscriptionId,
- final List<Predicate> predicates) {
+ final List<Predicate> predicates) {
final Set<String> subscriptionTargetFilters =
predicates.stream().flatMap(predicate -> predicate.getTargetFilter().stream())
.collect(Collectors.toSet());
@@ -81,18 +85,50 @@ public class CmNotificationSubscriptionHandlerServiceImpl implements CmNotificat
"subscriptionCreateResponse", cmNotificationSubscriptionNcmpOutEvent, false);
}
- private void sendSubscriptionCreateRequestToDmi(final String subscriptionId) {
+ private void handleCmNotificationSubscriptionDelta(final String subscriptionId) {
final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap =
dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId);
dmiCmNotificationSubscriptionDetailsMap.forEach((dmiPluginName, dmiCmNotificationSubscriptionDetails) -> {
final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates =
cmNotificationSubscriptionDelta.getDelta(
dmiCmNotificationSubscriptionDetails.getDmiCmNotificationSubscriptionPredicates());
+
+ if (dmiCmNotificationSubscriptionPredicates.isEmpty()) {
+ acceptAndPublishCmNotificationSubscriptionNcmpOutEventPerDmi(subscriptionId, dmiPluginName);
+ } else {
+ publishCmNotificationSubscriptionDmiInEventPerDmi(subscriptionId, dmiPluginName,
+ dmiCmNotificationSubscriptionPredicates);
+ }
+ });
+ }
+
+ private void publishCmNotificationSubscriptionDmiInEventPerDmi(final String subscriptionId,
+ final String dmiPluginName,
+ final List<DmiCmNotificationSubscriptionPredicate>
+ dmiCmNotificationSubscriptionPredicates) {
+ final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent =
+ cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionDmiInEvent(
+ dmiCmNotificationSubscriptionPredicates);
+ cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent(subscriptionId,
+ dmiPluginName, "subscriptionCreateRequest", cmNotificationSubscriptionDmiInEvent);
+ }
+
+ private void acceptAndPublishCmNotificationSubscriptionNcmpOutEventPerDmi(final String subscriptionId,
+ final String dmiPluginName) {
+ dmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi(subscriptionId,
+ dmiPluginName, CmNotificationSubscriptionStatus.ACCEPTED);
+ dmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName);
+ }
+
+ private void sendSubscriptionDeleteRequestToDmi(final String subscriptionId) {
+ final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap =
+ dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId);
+ dmiCmNotificationSubscriptionDetailsMap.forEach((dmiPluginName, dmiCmNotificationSubscriptionDetails) -> {
final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent =
cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionDmiInEvent(
- dmiCmNotificationSubscriptionPredicates);
+ dmiCmNotificationSubscriptionDetails.getDmiCmNotificationSubscriptionPredicates());
cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent(subscriptionId,
- dmiPluginName, "subscriptionCreateRequest", cmNotificationSubscriptionDmiInEvent);
+ dmiPluginName, "subscriptionDeleteRequest", cmNotificationSubscriptionDmiInEvent);
});
}
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java
index 0adf225fe..a9b1e26f5 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +21,7 @@
package org.onap.cps.ncmp.api.impl.events.cmsubscription.service;
+import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY;
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
import java.io.Serializable;
@@ -45,10 +47,14 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions";
private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE = """
- /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters
+ /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']
""".trim();
+ private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE =
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filters";
+
private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH =
- CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']";
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']";
+
private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID = """
//filter/subscriptionIds[text()='%s']
@@ -106,12 +112,11 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType,
cmHandleId, xpath);
if (subscriptionIds.remove(subscriptionId)) {
- if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)) {
- saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds);
- log.info("There are subscribers left for the following cps path {} :",
- CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
- datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)));
- } else {
+ saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds);
+ log.info("There are subscribers left for the following cps path {} :",
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
+ datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)));
+ if (subscriptionIds.isEmpty()) {
log.info("No subscribers left for the following cps path {} :",
CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)));
@@ -126,11 +131,25 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(
datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)),
OffsetDateTime.now());
+ final Collection<DataNode> existingFiltersForCmHandle =
+ cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
+ datastoreType.getDatastoreName(), cmHandleId),
+ DIRECT_CHILDREN_ONLY).iterator().next().getChildDataNodes();
+ if (existingFiltersForCmHandle.isEmpty()) {
+ removeCmHandleFromDatastore(datastoreType.getDatastoreName(), cmHandleId);
+ }
+ }
+
+ private void removeCmHandleFromDatastore(final String datastoreName, final String cmHandleId) {
+ cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME,
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+ datastoreName, cmHandleId), OffsetDateTime.now());
}
private boolean isFirstSubscriptionForCmHandle(final DatastoreType datastoreType, final String cmHandleId) {
return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
- CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
datastoreType.getDatastoreName(), cmHandleId),
OMIT_DESCENDANTS).isEmpty();
}
@@ -150,7 +169,7 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
OffsetDateTime.now(), ContentType.JSON);
} else {
cpsDataService.saveListElements(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
- CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
datastoreType.getDatastoreName(), cmHandleId),
subscriptionDetailsAsJson, OffsetDateTime.now());
}
@@ -161,8 +180,9 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif
final Collection<String> subscriptionIds) {
final String subscriptionDetailsAsJson = getSubscriptionDetailsAsJson(xpath, subscriptionIds);
cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
- CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(
- datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson, OffsetDateTime.now());
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
+ datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson,
+ OffsetDateTime.now(), ContentType.JSON);
}
private String getSubscriptionDetailsAsJson(final String xpath, final Collection<String> subscriptionIds) {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiClientRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiClientRequestException.java
new file mode 100644
index 000000000..ab0fa6893
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiClientRequestException.java
@@ -0,0 +1,54 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022-2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.exception;
+
+import lombok.Getter;
+import org.onap.cps.ncmp.api.NcmpResponseStatus;
+
+/**
+ * Http Client Request exception from dmi service.
+ */
+@Getter
+public class DmiClientRequestException extends NcmpException {
+
+ private static final long serialVersionUID = 6659897770659834797L;
+ final NcmpResponseStatus ncmpResponseStatus;
+ final String message;
+ final String responseBodyAsString;
+ final int httpStatusCode;
+
+ /**
+ * Constructor to form exception for dmi service response.
+ *
+ * @param httpStatusCode http response code from the client
+ * @param message response message from the client
+ * @param responseBodyAsString response body from the client
+ * @param ncmpResponseStatus ncmp status message and code
+ */
+ public DmiClientRequestException(final int httpStatusCode, final String message, final String responseBodyAsString,
+ final NcmpResponseStatus ncmpResponseStatus) {
+ super(message, responseBodyAsString);
+ this.httpStatusCode = httpStatusCode;
+ this.message = message;
+ this.responseBodyAsString = responseBodyAsString;
+ this.ncmpResponseStatus = ncmpResponseStatus;
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/HttpClientRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidDmiResourceUrlException.java
index 9d307e5d2..270988b63 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/HttpClientRequestException.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidDmiResourceUrlException.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
+ * Copyright (C) 2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,24 +22,16 @@ package org.onap.cps.ncmp.api.impl.exception;
import lombok.Getter;
-/**
- * Http Client Request exception for passthrough scenarios.
- */
@Getter
-public class HttpClientRequestException extends NcmpException {
+public class InvalidDmiResourceUrlException extends RuntimeException {
+
+ private static final long serialVersionUID = 2928476384584894968L;
- private static final long serialVersionUID = 6659897770659834797L;
+ private static final String INVALID_DMI_URL = "Invalid dmi resource url";
final Integer httpStatus;
- /**
- * Constructor to form exception for passthrough scenarios.
- *
- * @param message message details from NCMP
- * @param details response body from the client available as details
- * @param httpStatus http status code from the client
- */
- public HttpClientRequestException(final String message, final String details, final Integer httpStatus) {
- super(message, details);
+ public InvalidDmiResourceUrlException(final String details, final Integer httpStatus) {
+ super(String.format(INVALID_DMI_URL + ": %s", details));
this.httpStatus = httpStatus;
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java
index 184b12570..e230b3fcb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java
@@ -130,16 +130,6 @@ public interface InventoryPersistence extends NcmpPersistence {
DataNode getCmHandleDataNodeByAlternateId(String alternateId);
/**
- * Get data node that matches longest alternate id by removing elements (as defined by the separator string)
- * from right to left.
- *
- * @param alternateId alternate ID
- * @param separator a string that separates each element from the next.
- * @return data node
- */
- DataNode getCmHandleDataNodeByLongestMatchAlternateId(final String alternateId, final String separator);
-
- /**
* Get collection of data nodes of given cm handles.
*
* @param cmHandleIds collection of cmHandle IDs
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java
index bf54fe5d9..c4cab31ab 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java
@@ -33,11 +33,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
-import org.onap.cps.ncmp.api.impl.exception.NoAlternateIdParentFoundException;
import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.spi.FetchDescendantsOption;
@@ -182,19 +180,6 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
}
@Override
- public DataNode getCmHandleDataNodeByLongestMatchAlternateId(final String alternateId, final String separator) {
- String bestMatch = alternateId;
- while (StringUtils.isNotEmpty(bestMatch)) {
- try {
- return getCmHandleDataNodeByAlternateId(bestMatch);
- } catch (final DataNodeNotFoundException ignored) {
- bestMatch = getParentPath(bestMatch, separator);
- }
- }
- throw new NoAlternateIdParentFoundException(alternateId);
- }
-
- @Override
public Collection<DataNode> getCmHandleDataNodes(final Collection<String> cmHandleIds) {
final Collection<String> xpaths = new ArrayList<>(cmHandleIds.size());
cmHandleIds.forEach(cmHandleId -> xpaths.add(getXPathForCmHandleById(cmHandleId)));
@@ -221,9 +206,4 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
private String createCmHandlesJsonData(final List<YangModelCmHandle> yangModelCmHandles) {
return "{\"cm-handles\":" + jsonObjectMapper.asJsonString(yangModelCmHandles) + "}";
}
-
- private static String getParentPath(final String path, final String separator) {
- final int lastSeparatorIndex = path.lastIndexOf(separator);
- return lastSeparatorIndex < 0 ? "" : path.substring(0, lastSeparatorIndex);
- }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java
index e257112fc..45156ce88 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2022-2024 Nordix Foundation
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,6 +48,7 @@ import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.ModuleReference;
+import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Service;
@@ -146,6 +148,6 @@ public class ModuleSyncService {
final String jsonForUpdate = jsonObjectMapper.asJsonString(Map.of(
"cm-handles", Map.of("id", yangModelCmHandle.getId(), "module-set-tag", newModuleSetTag)));
cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- jsonForUpdate, OffsetDateTime.now());
+ jsonForUpdate, OffsetDateTime.now(), ContentType.JSON);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java
index 2e66ac0bf..7baac34b1 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java
@@ -40,7 +40,7 @@ public class DmiDataOperation {
private String options;
private String resourceIdentifier;
- private final List<CmHandle> cmHandles = new ArrayList<>();
+ private final List<DmiOperationCmHandle> cmHandles = new ArrayList<>();
/**
* Create and initialise a (outgoing) DMI data operation.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
index a9ec1241b..3db84556e 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
@@ -21,10 +21,9 @@
package org.onap.cps.ncmp.api.impl.operations;
-import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING;
-import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA;
import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING;
import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ;
+import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA;
import io.micrometer.core.annotation.Timed;
import java.util.Collection;
@@ -32,11 +31,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
-import lombok.extern.slf4j.Slf4j;
+import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.NcmpResponseStatus;
import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
-import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
+import org.onap.cps.ncmp.api.impl.config.DmiProperties;
+import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException;
import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
@@ -47,41 +46,40 @@ import org.onap.cps.ncmp.api.models.DataOperationRequest;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
/**
* Operations class for DMI data.
*/
-@Component
-@Slf4j
-public class DmiDataOperations extends DmiOperations {
+@RequiredArgsConstructor
+@Service
+public class DmiDataOperations {
- public DmiDataOperations(final InventoryPersistence inventoryPersistence,
- final JsonObjectMapper jsonObjectMapper,
- final NcmpConfiguration.DmiProperties dmiProperties,
- final DmiRestClient dmiRestClient,
- final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
- super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
- }
+ private final InventoryPersistence inventoryPersistence;
+ private final JsonObjectMapper jsonObjectMapper;
+ private final DmiProperties dmiProperties;
+ private final DmiRestClient dmiRestClient;
/**
- * This method fetches the resource data from operational data store for given cm handle
- * identifier on given resource using dmi client.
+ * This method fetches the resource data from the operational data store for a given CM handle
+ * identifier on the specified resource using the DMI client.
*
- * @param cmResourceAddress target datastore, cm handle and resource identifier
- * @param optionsParamInQuery options query
- * @param topicParamInQuery topic name for (triggering) async responses
- * @param requestId requestId for async responses
- * @param authorization contents of Authorization header, or null if not present
- * @return {@code ResponseEntity} response entity
+ * @param cmResourceAddress Target datastore, CM handle, and resource identifier.
+ * @param optionsParamInQuery Options query string.
+ * @param topicParamInQuery Topic name for triggering asynchronous responses.
+ * @param requestId Request ID for asynchronous responses.
+ * @param authorization Contents of the Authorization header, or null if not present.
+ * @return {@code Mono<ResponseEntity<Object>>} A reactive type representing the response entity.
*/
@Timed(value = "cps.ncmp.dmi.get",
description = "Time taken to fetch the resource data from operational data store for given cm handle "
+ "identifier on given resource using dmi client")
- public ResponseEntity<Object> getResourceDataFromDmi(final CmResourceAddress cmResourceAddress,
+ public Mono<ResponseEntity<Object>> getResourceDataFromDmi(final CmResourceAddress cmResourceAddress,
final String optionsParamInQuery,
final String topicParamInQuery,
final String requestId,
@@ -90,33 +88,30 @@ public class DmiDataOperations extends DmiOperations {
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
- final String dmiResourceDataUrl = getDmiRequestUrl(cmResourceAddress.datastoreName(),
- cmResourceAddress.cmHandleId(), cmResourceAddress.resourceIdentifier(), optionsParamInQuery,
- topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ, authorization);
+ final String dmiUrl = getDmiResourceDataUrl(cmResourceAddress.datastoreName(), yangModelCmHandle,
+ cmResourceAddress.resourceIdentifier(), optionsParamInQuery, topicParamInQuery);
+ return dmiRestClient.postOperationWithJsonDataAsync(DATA, dmiUrl, jsonRequestBody, READ, authorization);
}
/**
* This method fetches all the resource data from operational data store for given cm handle
* identifier using dmi client.
*
- * @param dataStoreName data store name
+ * @param datastoreName data store name
* @param cmHandleId network resource identifier
* @param requestId requestId for async responses
* @return {@code ResponseEntity} response entity
*/
- public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+ public ResponseEntity<Object> getResourceDataFromDmi(final String datastoreName,
final String cmHandleId,
final String requestId) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
- final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
- yangModelCmHandle);
- final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/",
- null, null,
- yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ, null);
+
+ final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
+ final String dmiUrl = getDmiResourceDataUrl(datastoreName, yangModelCmHandle, "/", null, null);
+ return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, READ, null);
}
/**
@@ -137,13 +132,13 @@ public class DmiDataOperations extends DmiOperations {
= getDistinctCmHandleIdsFromDataOperationRequest(dataOperationRequest);
final Collection<YangModelCmHandle> yangModelCmHandles
- = inventoryPersistence.getYangModelCmHandles(cmHandlesIds);
+ = inventoryPersistence.getYangModelCmHandles(cmHandlesIds);
final Map<String, List<DmiDataOperation>> operationsOutPerDmiServiceName
= ResourceDataOperationRequestUtils.processPerDefinitionInDataOperationsRequest(topicParamInQuery,
requestId, dataOperationRequest, yangModelCmHandles);
- buildDataOperationRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName,
+ buildDataOperationRequestUrlAndSendToDmiService(requestId, topicParamInQuery, operationsOutPerDmiServiceName,
authorization);
}
@@ -166,14 +161,14 @@ public class DmiDataOperations extends DmiOperations {
final String dataType,
final String authorization) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
- final String jsonRequestBody = getDmiRequestBody(operationType, null, requestData, dataType,
- yangModelCmHandle);
- final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId,
- null, null,
- yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
- return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operationType, authorization);
+
+ final String jsonRequestBody = getDmiRequestBody(operationType, null, requestData, dataType,
+ yangModelCmHandle);
+ final String dmiUrl = getDmiResourceDataUrl(PASSTHROUGH_RUNNING.getDatastoreName(),
+ yangModelCmHandle, resourceId, null, null);
+ return dmiRestClient.postOperationWithJsonData(DATA, dmiUrl, jsonRequestBody, operationType, authorization);
}
private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
@@ -190,30 +185,28 @@ public class DmiDataOperations extends DmiOperations {
.requestId(requestId)
.data(requestData)
.dataType(dataType)
+ .moduleSetTag(yangModelCmHandle.getModuleSetTag())
.build();
dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
return jsonObjectMapper.asJsonString(dmiRequestBody);
}
- private String getDmiRequestUrl(final String dataStoreName,
- final String cmHandleId,
- final String resourceId,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String dmiServiceName) {
- return dmiServiceUrlBuilder.getDmiDatastoreUrl(
- dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
- topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
- cmHandleId));
- }
-
- private String getDmiServiceDataOperationRequestUrl(final String dmiServiceName,
- final String topicParamInQuery,
- final String requestId) {
- final MultiValueMap<String, String> dataOperationRequestQueryParams = dmiServiceUrlBuilder
- .getDataOperationRequestQueryParams(topicParamInQuery, requestId);
- return dmiServiceUrlBuilder.getDataOperationRequestUrl(dataOperationRequestQueryParams,
- dmiServiceUrlBuilder.populateDataOperationRequestUriVariables(dmiServiceName));
+ private String getDmiResourceDataUrl(final String datastoreName,
+ final YangModelCmHandle yangModelCmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery) {
+ final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
+ return DmiServiceUrlBuilder.newInstance()
+ .pathSegment("ch")
+ .variablePathSegment("cmHandleId", yangModelCmHandle.getId())
+ .pathSegment("data")
+ .pathSegment("ds")
+ .variablePathSegment("datastore", datastoreName)
+ .queryParameter("resourceIdentifier", resourceIdentifier)
+ .queryParameter("options", optionsParamInQuery)
+ .queryParameter("topic", topicParamInQuery)
+ .build(dmiServiceName, dmiProperties.getDmiBasePath());
}
private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle,
@@ -226,66 +219,78 @@ public class DmiDataOperations extends DmiOperations {
}
private static Set<String> getDistinctCmHandleIdsFromDataOperationRequest(final DataOperationRequest
- dataOperationRequest) {
+ dataOperationRequest) {
return dataOperationRequest.getDataOperationDefinitions().stream()
.flatMap(dataOperationDefinition ->
dataOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet());
}
- private void buildDataOperationRequestUrlAndSendToDmiService(final String topicParamInQuery,
- final String requestId,
+ private void buildDataOperationRequestUrlAndSendToDmiService(final String requestId,
+ final String topicParamInQuery,
final Map<String, List<DmiDataOperation>>
- groupsOutPerDmiServiceName,
+ groupsOutPerDmiServiceName,
final String authorization) {
- groupsOutPerDmiServiceName.forEach((dmiServiceName, dmiDataOperationRequestBodies) -> {
- final String dmiDataOperationResourceUrl =
- getDmiServiceDataOperationRequestUrl(dmiServiceName, topicParamInQuery, requestId);
- sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies,
- authorization);
- });
+ Flux.fromIterable(groupsOutPerDmiServiceName.entrySet())
+ .flatMap(dmiDataOperationsByDmiServiceName -> {
+ final String dmiServiceName = dmiDataOperationsByDmiServiceName.getKey();
+ final String dmiUrl = buildDmiServiceUrl(dmiServiceName, requestId, topicParamInQuery);
+ final List<DmiDataOperation> dmiDataOperationRequestBodies
+ = dmiDataOperationsByDmiServiceName.getValue();
+ return sendDataOperationRequestToDmiService(dmiUrl, dmiDataOperationRequestBodies, authorization);
+ })
+ .subscribe();
}
- private void sendDataOperationRequestToDmiService(final String dataOperationResourceUrl,
- final List<DmiDataOperation> dmiDataOperationRequestBodies,
- final String authorization) {
+ private String buildDmiServiceUrl(final String dmiServiceName, final String requestId,
+ final String topicParamInQuery) {
+ return DmiServiceUrlBuilder.newInstance()
+ .pathSegment("data")
+ .queryParameter("requestId", requestId)
+ .queryParameter("topic", topicParamInQuery)
+ .build(dmiServiceName, dmiProperties.getDmiBasePath());
+ }
+
+ private Mono<Void> sendDataOperationRequestToDmiService(final String dmiUrl,
+ final List<DmiDataOperation> dmiDataOperationRequestBodies,
+ final String authorization) {
+ final String dmiDataOperationRequestAsJsonString
+ = createDmiDataOperationRequestAsJsonString(dmiDataOperationRequestBodies);
+ return dmiRestClient.postOperationWithJsonDataAsync(DATA, dmiUrl, dmiDataOperationRequestAsJsonString,
+ READ, authorization)
+ .then()
+ .onErrorResume(DmiClientRequestException.class, dmiClientRequestException -> {
+ handleTaskCompletionException(dmiClientRequestException, dmiUrl, dmiDataOperationRequestBodies);
+ return Mono.empty();
+ });
+ }
+
+ private String createDmiDataOperationRequestAsJsonString(
+ final List<DmiDataOperation> dmiDataOperationRequestBodies) {
final DmiDataOperationRequest dmiDataOperationRequest = DmiDataOperationRequest.builder()
- .operations(dmiDataOperationRequestBodies).build();
- final String dmiDataOperationRequestAsJsonString =
- jsonObjectMapper.asJsonString(dmiDataOperationRequest);
- try {
- dmiRestClient.postOperationWithJsonData(dataOperationResourceUrl, dmiDataOperationRequestAsJsonString, READ,
- authorization);
- } catch (final Exception exception) {
- handleTaskCompletionException(exception, dataOperationResourceUrl, dmiDataOperationRequestBodies);
- }
+ .operations(dmiDataOperationRequestBodies)
+ .build();
+ return jsonObjectMapper.asJsonString(dmiDataOperationRequest);
}
- private void handleTaskCompletionException(final Throwable throwable,
+ private void handleTaskCompletionException(final DmiClientRequestException dmiClientRequestException,
final String dataOperationResourceUrl,
final List<DmiDataOperation> dmiDataOperationRequestBodies) {
- if (throwable != null) {
- final MultiValueMap<String, String> dataOperationResourceUrlParameters =
- UriComponentsBuilder.fromUriString(dataOperationResourceUrl).build().getQueryParams();
- final String topicName = dataOperationResourceUrlParameters.get("topic").get(0);
- final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0);
+ final MultiValueMap<String, String> dataOperationResourceUrlParameters =
+ UriComponentsBuilder.fromUriString(dataOperationResourceUrl).build().getQueryParams();
+ final String topicName = dataOperationResourceUrlParameters.get("topic").get(0);
+ final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0);
- final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>>
- cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
+ final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>>
+ cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
- dmiDataOperationRequestBodies.forEach(dmiDataOperationRequestBody -> {
- final List<String> cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream()
- .map(CmHandle::getId).toList();
- if (throwable.getCause() instanceof HttpClientRequestException) {
- cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
- Map.of(UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds));
- } else {
- cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
- Map.of(DMI_SERVICE_NOT_RESPONDING, cmHandleIds));
- }
- });
- ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId,
- cmHandleIdsPerResponseCodesPerOperation);
- }
+ dmiDataOperationRequestBodies.forEach(dmiDataOperationRequestBody -> {
+ final List<String> cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream()
+ .map(DmiOperationCmHandle::getId).toList();
+ cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody,
+ Map.of(dmiClientRequestException.getNcmpResponseStatus(), cmHandleIds));
+ });
+ ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId,
+ cmHandleIdsPerResponseCodesPerOperation);
}
-}
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
index 798f6de81..77dfcb7a2 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
@@ -33,34 +33,27 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
-import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.impl.config.DmiProperties;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.models.YangResource;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
/**
* Operations class for DMI Model.
*/
-@Component
-public class DmiModelOperations extends DmiOperations {
+@RequiredArgsConstructor
+@Service
+public class DmiModelOperations {
- /**
- * Constructor for {@code DmiOperations}. This method also manipulates url properties.
- *
- * @param dmiRestClient {@code DmiRestClient}
- */
- public DmiModelOperations(final InventoryPersistence inventoryPersistence,
- final JsonObjectMapper jsonObjectMapper,
- final NcmpConfiguration.DmiProperties dmiProperties,
- final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
- super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
- }
+ private final JsonObjectMapper jsonObjectMapper;
+ private final DmiProperties dmiProperties;
+ private final DmiRestClient dmiRestClient;
/**
* Retrieves module references.
@@ -113,9 +106,12 @@ public class DmiModelOperations extends DmiOperations {
final String jsonRequestBody,
final String cmHandle,
final String resourceName) {
- final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
- OperationType.READ, null);
+ final String dmiUrl = DmiServiceUrlBuilder.newInstance()
+ .pathSegment("ch")
+ .variablePathSegment("cmHandleId", cmHandle)
+ .variablePathSegment("resourceName", resourceName)
+ .build(dmiServiceName, dmiProperties.getDmiBasePath());
+ return dmiRestClient.postOperationWithJsonData(MODEL, dmiUrl, jsonRequestBody, OperationType.READ, null);
}
private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences,
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperationCmHandle.java
index 618da7454..1bf2b77dc 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperationCmHandle.java
@@ -29,14 +29,21 @@ import lombok.Getter;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Builder
-public class CmHandle {
+public class DmiOperationCmHandle {
private String id;
@JsonProperty("cmHandleProperties")
private Map<String, String> dmiProperties;
+ private String moduleSetTag;
- public static CmHandle buildCmHandleWithProperties(final String cmHandleId,
- final Map<String, String> dmiProperties) {
- return CmHandle.builder().id(cmHandleId).dmiProperties(dmiProperties).build();
+ /**
+ * Builds Dmi Operation Cm Handle object with all its associated properties.
+ */
+ public static DmiOperationCmHandle buildDmiOperationCmHandle(final String cmHandleId,
+ final Map<String, String> dmiProperties,
+ final String moduleSetTag) {
+ return DmiOperationCmHandle.builder().id(cmHandleId)
+ .dmiProperties(dmiProperties).moduleSetTag(moduleSetTag)
+ .build();
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java
deleted file mode 100644
index c8d73eac6..000000000
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
- * Modifications Copyright (C) 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=========================================================
- */
-
-package org.onap.cps.ncmp.api.impl.operations;
-
-import lombok.RequiredArgsConstructor;
-import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
-import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
-import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
-import org.onap.cps.utils.JsonObjectMapper;
-import org.springframework.stereotype.Service;
-
-@RequiredArgsConstructor
-@Service
-public class DmiOperations {
-
- protected final InventoryPersistence inventoryPersistence;
- protected final JsonObjectMapper jsonObjectMapper;
- protected final NcmpConfiguration.DmiProperties dmiProperties;
- protected final DmiRestClient dmiRestClient;
- protected final DmiServiceUrlBuilder dmiServiceUrlBuilder;
-
- String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) {
- return dmiServiceUrlBuilder.getResourceDataBasePathUriBuilder()
- .pathSegment("{resourceName}")
- .buildAndExpand(dmiServiceName, dmiProperties.getDmiBasePath(), cmHandle, resourceName).toUriString();
- }
-
-
-}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java
index fa00d1a15..e863228ed 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java
@@ -21,9 +21,7 @@
package org.onap.cps.ncmp.api.impl.operations;
import com.fasterxml.jackson.annotation.JsonValue;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Locale;
import lombok.Getter;
import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException;
@@ -48,13 +46,6 @@ public enum OperationType {
return String.valueOf(operationName);
}
- private static final Map<String, OperationType> operationNameToOperationEnum = new HashMap<>();
-
- static {
- Arrays.stream(OperationType.values()).forEach(
- operationType -> operationNameToOperationEnum.put(operationType.getOperationName(), operationType));
- }
-
/**
* From operation name get operation enum type.
*
@@ -62,10 +53,10 @@ public enum OperationType {
* @return the operation enum type
*/
public static OperationType fromOperationName(final String operationName) {
- final OperationType operationType = operationNameToOperationEnum.get(operationName);
- if (null == operationType) {
+ try {
+ return OperationType.valueOf(operationName.toUpperCase(Locale.ENGLISH));
+ } catch (final IllegalArgumentException e) {
throw new InvalidOperationException(operationName + " is an invalid operation name");
}
- return operationType;
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
index 04acaa5e9..aeeeb6430 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
@@ -21,166 +21,91 @@
package org.onap.cps.ncmp.api.impl.utils;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
-import lombok.RequiredArgsConstructor;
+import lombok.NoArgsConstructor;
import org.apache.logging.log4j.util.Strings;
-import org.apache.logging.log4j.util.TriConsumer;
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
-import org.onap.cps.spi.utils.CpsValidator;
-import org.springframework.stereotype.Component;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
-@Component
-@RequiredArgsConstructor
+@NoArgsConstructor
public class DmiServiceUrlBuilder {
- private final NcmpConfiguration.DmiProperties dmiProperties;
- private final CpsValidator cpsValidator;
+ private static final String FIXED_PATH_SEGMENT = null;
- /**
- * This method creates the dmi service url.
- *
- * @param queryParams query param map as key,value pair
- * @param uriVariables uri param map as key (placeholder),value pair
- * @return {@code String} dmi service url as string
- */
- public String getDmiDatastoreUrl(final MultiValueMap<String, String> queryParams,
- final Map<String, Object> uriVariables) {
- return getUriComponentsBuilder(getResourceDataBasePathUriBuilder(), queryParams, uriVariables)
- .buildAndExpand().toUriString();
- }
+ final UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance();
+ final Map<String, Object> pathSegments = new LinkedHashMap<>();
- /**
- * This method builds data operation request url.
- *
- * @param dataoperationRequestQueryParams query param map as key, value pair
- * @param dataoperationRequestUriVariables uri param map as key (placeholder), value pair
- * @return {@code String} data operation request url as string
- */
- public String getDataOperationRequestUrl(final MultiValueMap<String, String> dataoperationRequestQueryParams,
- final Map<String, Object> dataoperationRequestUriVariables) {
- return getDataOperationResourceDataBasePathUriBuilder()
- .queryParams(dataoperationRequestQueryParams)
- .uriVariables(dataoperationRequestUriVariables)
- .buildAndExpand().toUriString();
+ public static DmiServiceUrlBuilder newInstance() {
+ return new DmiServiceUrlBuilder();
}
/**
- * This method creates the dmi service url builder object with path variables.
+ * Add a fixed pathSegment to the URI.
*
- * @return {@code UriComponentsBuilder} dmi service url builder object
+ * @param pathSegment the path segment
+ * @return this builder
*/
- public UriComponentsBuilder getResourceDataBasePathUriBuilder() {
- return UriComponentsBuilder.newInstance()
- .path("{dmiServiceName}")
- .pathSegment("{dmiBasePath}")
- .pathSegment("v1")
- .pathSegment("ch")
- .pathSegment("{cmHandleId}");
+ public DmiServiceUrlBuilder pathSegment(final String pathSegment) {
+ pathSegments.put(pathSegment, FIXED_PATH_SEGMENT);
+ return this;
}
/**
- * This method creates the dmi service url builder object with path variables for data operation request.
+ * Add a variable pathSegment to the URI.
+ * Do NOT add { } braces. the builder will take care of that
*
- * @return {@code UriComponentsBuilder} dmi service url builder object
+ * @param pathSegment the name of the variable path segment (with { and }
+ * @param value the value to be insert in teh URI for the given variable path segment
+ * @return this builder
*/
- public UriComponentsBuilder getDataOperationResourceDataBasePathUriBuilder() {
- return UriComponentsBuilder.newInstance()
- .path("{dmiServiceName}")
- .pathSegment("{dmiBasePath}")
- .pathSegment("v1")
- .pathSegment("data");
+ public DmiServiceUrlBuilder variablePathSegment(final String pathSegment, final Object value) {
+ pathSegments.put(pathSegment, value);
+ return this;
}
/**
- * This method populates uri variables.
+ * Add a query parameter to the URI.
+ * Do NOT encode as the builder wil take care of encoding
*
- * @param dataStoreName data store name
- * @param dmiServiceName dmi service name
- * @param cmHandleId cm handle id for dmi registration
- * @return {@code String} dmi service url as string
+ * @param name the name of the variable
+ * @param value the value of the variable (only Strings are supported).
+ *
+ * @return this builder
*/
- public Map<String, Object> populateUriVariables(final String dataStoreName,
- final String dmiServiceName,
- final String cmHandleId) {
- cpsValidator.validateNameCharacters(cmHandleId);
- final Map<String, Object> uriVariables = new HashMap<>();
- final String dmiBasePath = dmiProperties.getDmiBasePath();
- uriVariables.put("dmiServiceName", dmiServiceName);
- uriVariables.put("dmiBasePath", dmiBasePath);
- uriVariables.put("cmHandleId", cmHandleId);
- uriVariables.put("dataStore", dataStoreName);
- return uriVariables;
+ public DmiServiceUrlBuilder queryParameter(final String name, final String value) {
+ if (Strings.isNotBlank(value)) {
+ uriComponentsBuilder.queryParam(name, value);
+ }
+ return this;
}
/**
- * This method populates uri variables for data operation request.
+ * Build the URI as a correctly percentage-encoded String.
+ *
+ * @param dmiServiceName the name of the dmi service
+ * @param dmiBasePath the base path of the dmi service
*
- * @param dmiServiceName dmi service name
- * @return {@code Map<String, Object>} uri variables as map
+ * @return URI as a string
*/
- public Map<String, Object> populateDataOperationRequestUriVariables(final String dmiServiceName) {
+ public String build(final String dmiServiceName, final String dmiBasePath) {
+ uriComponentsBuilder
+ .path("{dmiServiceName}")
+ .pathSegment("{dmiBasePath}")
+ .pathSegment("v1");
+
final Map<String, Object> uriVariables = new HashMap<>();
- final String dmiBasePath = dmiProperties.getDmiBasePath();
uriVariables.put("dmiServiceName", dmiServiceName);
uriVariables.put("dmiBasePath", dmiBasePath);
- return uriVariables;
- }
-
- /**
- * This method is used to populate map from query params.
- *
- * @param resourceId unique id of response for valid topic
- * @param optionsParamInQuery options into url param
- * @param topicParamInQuery topic into url param
- * @return all valid query params as map
- */
- public MultiValueMap<String, String> populateQueryParams(final String resourceId,
- final String optionsParamInQuery,
- final String topicParamInQuery) {
- final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
- getQueryParamConsumer().accept("resourceIdentifier",
- resourceId, queryParams);
- getQueryParamConsumer().accept("options", optionsParamInQuery, queryParams);
- if (Strings.isNotEmpty(topicParamInQuery)) {
- getQueryParamConsumer().accept("topic", topicParamInQuery, queryParams);
- }
- return queryParams;
- }
- /**
- * This method is used to populate map from query params for data operation request.
- *
- * @param topicParamInQuery topic into url param
- * @param requestId unique id of response for valid topic
- * @return all valid query params as map
- */
- public MultiValueMap<String, String> getDataOperationRequestQueryParams(final String topicParamInQuery,
- final String requestId) {
- final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
- getQueryParamConsumer().accept("topic", topicParamInQuery, queryParams);
- getQueryParamConsumer().accept("requestId", requestId, queryParams);
- return queryParams;
- }
-
- private TriConsumer<String, String, MultiValueMap<String, String>> getQueryParamConsumer() {
- return (paramName, paramValue, paramMap) -> {
- if (Strings.isNotEmpty(paramValue)) {
- paramMap.add(paramName, paramValue);
+ pathSegments.forEach((pathSegment, variablePathValue) -> {
+ if (variablePathValue == FIXED_PATH_SEGMENT) {
+ uriComponentsBuilder.pathSegment(pathSegment);
+ } else {
+ uriComponentsBuilder.pathSegment("{" + pathSegment + "}");
+ uriVariables.put(pathSegment, variablePathValue);
}
- };
+ });
+ return uriComponentsBuilder.buildAndExpand(uriVariables).encode().toUriString();
}
- private UriComponentsBuilder getUriComponentsBuilder(final UriComponentsBuilder uriComponentsBuilder,
- final MultiValueMap<String, String> queryParams,
- final Map<String, Object> uriVariables) {
- return uriComponentsBuilder
- .pathSegment("data")
- .pathSegment("ds")
- .pathSegment("{dataStore}")
- .queryParams(queryParams)
- .uriVariables(uriVariables);
- }
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java
index 4b016b37d..407fcf034 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java
@@ -31,7 +31,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@@ -39,8 +38,8 @@ import lombok.extern.slf4j.Slf4j;
import org.onap.cps.events.EventsPublisher;
import org.onap.cps.ncmp.api.NcmpResponseStatus;
import org.onap.cps.ncmp.api.impl.inventory.CmHandleState;
-import org.onap.cps.ncmp.api.impl.operations.CmHandle;
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation;
+import org.onap.cps.ncmp.api.impl.operations.DmiOperationCmHandle;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
@@ -49,8 +48,8 @@ import org.onap.cps.ncmp.api.models.DataOperationRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
-@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Slf4j
public class ResourceDataOperationRequestUtils {
private static final String UNKNOWN_SERVICE_NAME = null;
@@ -81,6 +80,8 @@ public class ResourceDataOperationRequestUtils {
final Map<String, String> dmiServiceNamesPerCmHandleId =
getDmiServiceNamesPerCmHandleId(dmiPropertiesPerCmHandleIdPerServiceName);
+ final Map<String, String> moduleSetTagPerCmHandle = getModuleSetTagPerCmHandleId(yangModelCmHandles);
+
for (final DataOperationDefinition dataOperationDefinitionIn :
dataOperationRequestIn.getDataOperationDefinitions()) {
final List<String> nonExistingCmHandleIds = new ArrayList<>();
@@ -97,9 +98,10 @@ public class ResourceDataOperationRequestUtils {
} else {
final DmiDataOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName,
dataOperationDefinitionIn, dmiDataOperationsOutPerDmiServiceName);
- final CmHandle cmHandle = CmHandle.buildCmHandleWithProperties(cmHandleId,
- cmHandleIdProperties);
- dmiBatchOperationOut.getCmHandles().add(cmHandle);
+ final DmiOperationCmHandle dmiOperationCmHandle = DmiOperationCmHandle
+ .buildDmiOperationCmHandle(cmHandleId, cmHandleIdProperties,
+ moduleSetTagPerCmHandle.get(cmHandleId));
+ dmiBatchOperationOut.getCmHandles().add(dmiOperationCmHandle);
}
}
}
@@ -114,56 +116,12 @@ public class ResourceDataOperationRequestUtils {
return dmiDataOperationsOutPerDmiServiceName;
}
- /**
- * Handles the async task completion for an entire data, publishing errors to client topic on task failure.
- *
- * @param topicParamInQuery client given topic
- * @param requestId unique identifier per request
- * @param dataOperationRequest incoming data operation request details
- * @param throwable error cause, or null if task completed with no exception
- */
- public static void handleAsyncTaskCompletionForDataOperationsRequest(
- final String topicParamInQuery,
- final String requestId,
- final DataOperationRequest dataOperationRequest,
- final Throwable throwable) {
- if (throwable == null) {
- log.info("Data operations request {} completed.", requestId);
- } else if (throwable instanceof TimeoutException) {
- log.error("Data operations request {} timed out.", requestId);
- ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery,
- requestId, dataOperationRequest, NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING);
- } else {
- log.error("Data operations request {} failed.", requestId, throwable);
- ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery,
- requestId, dataOperationRequest, NcmpResponseStatus.UNKNOWN_ERROR);
- }
- }
-
- /**
- * Creates data operation cloud event for when the entire data operation fails and publishes it to client topic.
- *
- * @param topicParamInQuery client given topic
- * @param requestId unique identifier per request
- * @param dataOperationRequestIn incoming data operation request details
- * @param ncmpResponseStatus response code to be sent for all cm handle ids in all operations
- */
- private static void publishErrorMessageToClientTopicForEntireOperation(
- final String topicParamInQuery,
- final String requestId,
- final DataOperationRequest dataOperationRequestIn,
- final NcmpResponseStatus ncmpResponseStatus) {
-
- final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>>
- cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
-
- for (final DataOperationDefinition dataOperationDefinitionIn :
- dataOperationRequestIn.getDataOperationDefinitions()) {
- cmHandleIdsPerResponseCodesPerOperation.add(
- DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
- Map.of(ncmpResponseStatus, dataOperationDefinitionIn.getCmHandleIds()));
- }
- publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
+ private static Map<String, String> getModuleSetTagPerCmHandleId(
+ final Collection<YangModelCmHandle> yangModelCmHandles) {
+ final Map<String, String> moduleSetTagPerCmHandle = new HashMap<>(yangModelCmHandles.size());
+ yangModelCmHandles.forEach(yangModelCmHandle ->
+ moduleSetTagPerCmHandle.put(yangModelCmHandle.getId(), yangModelCmHandle.getModuleSetTag()));
+ return moduleSetTagPerCmHandle;
}
/**
@@ -182,6 +140,8 @@ public class ResourceDataOperationRequestUtils {
final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic,
requestId, cmHandleIdsPerResponseCodesPerOperation);
final EventsPublisher<CloudEvent> eventsPublisher = CpsApplicationContext.getCpsBean(EventsPublisher.class);
+ log.warn("publishing error message to client topic: {} ,requestId: {}, data operation cloud event id: {}",
+ clientTopic, requestId, dataOperationCloudEvent.getId());
eventsPublisher.publishCloudEvent(clientTopic, requestId, dataOperationCloudEvent);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NoAlternateIdParentFoundException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NoAlternateIdMatchFoundException.java
index 2e6cd3308..510a6f51a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NoAlternateIdParentFoundException.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NoAlternateIdMatchFoundException.java
@@ -18,22 +18,23 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.exception;
+package org.onap.cps.ncmp.exceptions;
import java.io.Serial;
+import org.onap.cps.ncmp.api.impl.exception.NcmpException;
-public class NoAlternateIdParentFoundException extends NcmpException {
+public class NoAlternateIdMatchFoundException extends NcmpException {
@Serial
private static final long serialVersionUID = -2412915490233422945L;
- private static final String ALTERNATE_ID_NOT_FOUND = "No matching (parent) cm handle found using alternate ids";
+ private static final String ALTERNATE_ID_NOT_FOUND = "No matching cm handle found using alternate ids";
/**
* Constructor.
*
* @param cpsPath datanode cpsPath
*/
- public NoAlternateIdParentFoundException(final String cpsPath) {
+ public NoAlternateIdMatchFoundException(final String cpsPath) {
super(ALTERNATE_ID_NOT_FOUND, String.format("cannot find a datanode with alternate id %s", cpsPath));
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/DataJobServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
index b4377b84f..7db6c5c27 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/DataJobServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java
@@ -18,13 +18,13 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl;
+package org.onap.cps.ncmp.impl.datajobs;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.DataJobService;
-import org.onap.cps.ncmp.api.models.datajob.DataJobMetadata;
-import org.onap.cps.ncmp.api.models.datajob.DataJobReadRequest;
-import org.onap.cps.ncmp.api.models.datajob.DataJobWriteRequest;
+import org.onap.cps.ncmp.api.datajobs.DataJobService;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest;
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest;
@Slf4j
public class DataJobServiceImpl implements DataJobService {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java
index 4cc8cdaa6..554501127 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java
@@ -39,7 +39,7 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
@Slf4j
@RequiredArgsConstructor
@@ -61,12 +61,12 @@ abstract class AbstractModelLoader implements ModelLoader {
long retryTimeMs;
@Override
- public void onApplicationEvent(@NonNull final ApplicationReadyEvent applicationReadyEvent) {
+ public void onApplicationEvent(@NonNull final ApplicationStartedEvent applicationStartedEvent) {
try {
onboardOrUpgradeModel();
} catch (final NcmpStartUpException ncmpStartUpException) {
log.error("Onboarding model for NCMP failed: {} ", ncmpStartUpException.getMessage());
- SpringApplication.exit(applicationReadyEvent.getApplicationContext(), () -> EXIT_CODE_ON_ERROR);
+ SpringApplication.exit(applicationStartedEvent.getApplicationContext(), () -> EXIT_CODE_ON_ERROR);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java
index c61bf1c9b..9832ba3f9 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
+ * Copyright (C) 2023-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,13 +21,13 @@
package org.onap.cps.ncmp.init;
import lombok.NonNull;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
-public interface ModelLoader extends ApplicationListener<ApplicationReadyEvent> {
+public interface ModelLoader extends ApplicationListener<ApplicationStartedEvent> {
@Override
- void onApplicationEvent(@NonNull ApplicationReadyEvent applicationReadyEvent);
+ void onApplicationEvent(@NonNull ApplicationStartedEvent applicationStartedEvent);
void onboardOrUpgradeModel();
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java
new file mode 100644
index 000000000..8385f19f7
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/AlternateIdMatcher.java
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.utils;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
+import org.onap.cps.spi.model.DataNode;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AlternateIdMatcher {
+
+ private final InventoryPersistence inventoryPersistence;
+
+ /**
+ * Get data node that matches longest alternate id by removing elements (as defined by the separator string)
+ * from right to left.
+ *
+ * @param alternateId alternate ID
+ * @param separator a string that separates each element from the next.
+ * @return data node
+ */
+ public DataNode getCmHandleDataNodeByLongestMatchAlternateId(final String alternateId, final String separator) {
+ String bestMatch = alternateId;
+ while (StringUtils.isNotEmpty(bestMatch)) {
+ try {
+ return inventoryPersistence.getCmHandleDataNodeByAlternateId(bestMatch);
+ } catch (final DataNodeNotFoundException ignored) {
+ bestMatch = getParentPath(bestMatch, separator);
+ }
+ }
+ throw new NoAlternateIdMatchFoundException(alternateId);
+ }
+
+ private String getParentPath(final String path, final String separator) {
+ final int lastSeparatorIndex = path.lastIndexOf(separator);
+ return lastSeparatorIndex < 0 ? "" : path.substring(0, lastSeparatorIndex);
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy
index 43787640a..bef0adc9c 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/DataJobServiceImplSpec.groovy
@@ -24,12 +24,13 @@ import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.read.ListAppender
+import org.onap.cps.ncmp.impl.datajobs.DataJobServiceImpl
import org.slf4j.LoggerFactory
-import org.onap.cps.ncmp.api.models.datajob.DataJobReadRequest
-import org.onap.cps.ncmp.api.models.datajob.DataJobWriteRequest
-import org.onap.cps.ncmp.api.models.datajob.DataJobMetadata
-import org.onap.cps.ncmp.api.models.datajob.ReadOperation
-import org.onap.cps.ncmp.api.models.datajob.WriteOperation
+import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest
+import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest
+import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata
+import org.onap.cps.ncmp.api.datajobs.models.ReadOperation
+import org.onap.cps.ncmp.api.datajobs.models.WriteOperation
import spock.lang.Specification
class DataJobServiceImplSpec extends Specification{
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
index 4d0af6f49..d91c79d33 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
@@ -23,6 +23,8 @@
package org.onap.cps.ncmp.api.impl
+import reactor.core.publisher.Mono
+
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -120,16 +122,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
>> { new ResponseEntity<>(HttpStatus.CREATED) }
}
- def 'Get resource data for from DMI.'() {
+ def 'Get resource data from DMI.'() {
given: 'cpsDataService returns valid data node'
mockDataNode()
and: 'some cm resource address'
- def cmResourceAddress = new CmResourceAddress('some datastore','some CM Handle', 'some resource Id')
+ def cmResourceAddress = new CmResourceAddress('some datastore', 'some CM Handle', 'some resource Id')
and: 'get resource data from DMI is called'
mockDmiDataOperations.getResourceDataFromDmi(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >>
- new ResponseEntity<>('dmi-response', HttpStatus.OK)
+ Mono.just(new ResponseEntity<>('dmi-response', HttpStatus.OK))
when: 'get resource data operational for the given cm resource address is called'
- def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER)
+ def response = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block()
then: 'DMI returns a json response'
assert response == 'dmi-response'
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
index 260772714..b0024b19b 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandlerSpec.groovy
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2022-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
+import org.onap.cps.utils.ContentType
import org.onap.cps.utils.JsonObjectMapper
import org.slf4j.LoggerFactory
import spock.lang.Specification
@@ -209,7 +210,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
when: 'cm handle properties is updated'
def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
then: 'the update is delegated to cps data service with correct parameters'
- 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >>
+ 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >>
{ args ->
assert args[3].contains('alt-1')
}
@@ -245,7 +246,7 @@ class NetworkCmProxyDataServicePropertyHandlerSpec extends Specification {
when: 'data producer identifier updated'
objectUnderTest.updateDataProducerIdentifier(existingCmHandleDataNode, ncmpServiceCmHandle)
then: 'the update node leaves method is invoked once'
- 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >> { args ->
+ 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _, ContentType.JSON) >> { args ->
assert args[3].contains('someDataProducerIdentifier')
}
and: 'correct information is logged'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumerSpec.groovy
index 369b496ca..b095f904a 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumerSpec.groovy
@@ -20,6 +20,8 @@
package org.onap.cps.ncmp.api.impl.async
+import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
+
import com.fasterxml.jackson.databind.ObjectMapper
import io.cloudevents.CloudEvent
import io.cloudevents.kafka.CloudEventDeserializer
@@ -42,8 +44,6 @@ import org.springframework.test.annotation.DirtiesContext
import org.testcontainers.spock.Testcontainers
import java.time.Duration
-import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
-
@SpringBootTest(classes = [EventsPublisher, DataOperationEventConsumer, RecordFilterStrategies, JsonObjectMapper, ObjectMapper])
@Testcontainers
@DirtiesContext
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
index c8e34b1a5..2c2212773 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,92 +21,133 @@
package org.onap.cps.ncmp.api.impl.client
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
+import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA
+import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA
+
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties;
-import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
+import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException
+import org.onap.cps.ncmp.api.impl.exception.InvalidDmiResourceUrlException
+import org.onap.cps.ncmp.api.impl.config.DmiProperties
import org.onap.cps.ncmp.utils.TestUtils
-import org.spockframework.spring.SpringBean
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.http.HttpEntity
+import org.onap.cps.utils.JsonObjectMapper
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
-import org.springframework.test.context.ContextConfiguration
import org.springframework.web.client.HttpServerErrorException
-import org.springframework.web.client.RestTemplate
+import org.springframework.web.reactive.function.client.WebClient
+import org.springframework.web.reactive.function.client.WebClientResponseException
+import org.springframework.web.reactive.function.client.WebClientRequestException
+import reactor.core.publisher.Mono
import spock.lang.Specification
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.PATCH
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
-
-@SpringBootTest
-@ContextConfiguration(classes = [DmiProperties, DmiRestClient, ObjectMapper])
class DmiRestClientSpec extends Specification {
static final NO_AUTH_HEADER = null
- static final BASIC_AUTH_HEADER = 'Basic c29tZS11c2VyOnNvbWUtcGFzc3dvcmQ='
+ static final BASIC_AUTH_HEADER = 'Basic c29tZSB1c2VyOnNvbWUgcGFzc3dvcmQ='
static final BEARER_AUTH_HEADER = 'Bearer my-bearer-token'
- @SpringBean
- RestTemplate mockRestTemplate = Mock(RestTemplate)
+ def mockDataServicesWebClient = Mock(WebClient)
+ def mockModelServicesWebClient = Mock(WebClient)
+ def mockHealthChecksWebClient = Mock(WebClient)
- @Autowired
- NcmpConfiguration.DmiProperties dmiProperties
+ def mockRequestBody = Mock(WebClient.RequestBodyUriSpec)
+ def mockResponse = Mock(WebClient.ResponseSpec)
- @Autowired
- DmiRestClient objectUnderTest
+ def responseBody = [message: 'Success']
+ def mockDmiProperties = Mock(DmiProperties)
- @Autowired
- ObjectMapper objectMapper
+ JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
- def responseFromRestTemplate = Mock(ResponseEntity)
+ DmiRestClient objectUnderTest = new DmiRestClient(mockDmiProperties, jsonObjectMapper, mockDataServicesWebClient, mockModelServicesWebClient, mockHealthChecksWebClient)
+
+ def setup() {
+ mockRequestBody.uri(_) >> mockRequestBody
+ mockRequestBody.headers(_) >> mockRequestBody
+ mockRequestBody.body(_) >> mockRequestBody
+ mockRequestBody.retrieve() >> mockResponse
+ }
- def 'DMI POST operation with JSON.'() {
- given: 'the rest template returns a valid response entity for the expected parameters'
- mockRestTemplate.postForEntity('my url', _ as HttpEntity, Object.class) >> responseFromRestTemplate
+ def 'DMI POST Operation with JSON for status #httpStatusCode'() {
+ given: 'the web client returns a valid response entity for the expected parameters'
+ mockDataServicesWebClient.post() >> mockRequestBody
+ mockResponse.toEntity(Object.class) >> Mono.just(new ResponseEntity<>(responseBody, httpStatusCode))
when: 'POST operation is invoked'
- def result = objectUnderTest.postOperationWithJsonData('my url', 'some json', READ, null)
+ def response = objectUnderTest.postOperationWithJsonData(DATA, '/my/url', 'some json', READ, NO_AUTH_HEADER)
then: 'the output of the method is equal to the output from the test template'
- result == responseFromRestTemplate
+ assert response.statusCode == httpStatusCode
+ assert response.body == responseBody
+ where: 'the following status codes are used'
+ httpStatusCode << [HttpStatus.OK, HttpStatus.CREATED, HttpStatus.ACCEPTED]
}
- def 'Failing DMI POST operation.'() {
- given: 'the rest template returns a valid response entity'
- def serverResponse = 'server response'.getBytes()
- def httpServerErrorException = new HttpServerErrorException(HttpStatus.FORBIDDEN, 'status text', serverResponse, null)
- mockRestTemplate.postForEntity(*_) >> { throw httpServerErrorException }
+ def 'Failing DMI POST operation for server error'() {
+ given: 'the web client throws an exception'
+ mockDataServicesWebClient.post() >> { throw new HttpServerErrorException(SERVICE_UNAVAILABLE, null, null, null) }
when: 'POST operation is invoked'
- def result = objectUnderTest.postOperationWithJsonData('some url', 'some json', operation, null)
- then: 'a Http Client Exception is thrown'
- def thrown = thrown(HttpClientRequestException)
+ objectUnderTest.postOperationWithJsonData(DATA, '/some', 'some json', READ, NO_AUTH_HEADER)
+ then: 'a http client exception is thrown'
+ def thrown = thrown(DmiClientRequestException)
and: 'the exception has the relevant details from the error response'
- assert thrown.httpStatus == 403
- assert thrown.message == "Unable to ${operation} resource data."
- assert thrown.details == 'server response'
- where: 'the following operation is executed'
+ thrown.ncmpResponseStatus.code == '102'
+ thrown.httpStatusCode == 503
+ }
+
+ def 'Failing DMI POST operation due to invalid dmi resource url.'() {
+ when: 'POST operation is invoked with invalid dmi resource url'
+ objectUnderTest.postOperationWithJsonData(DATA, '/invalid dmi url', null, null, NO_AUTH_HEADER)
+ then: 'invalid dmi resource url exception is thrown'
+ def thrown = thrown(InvalidDmiResourceUrlException)
+ and: 'the exception has the relevant details from the error response'
+ thrown.httpStatus == 400
+ thrown.message == 'Invalid dmi resource url: /invalid dmi url'
+ where: 'the following operations are executed'
operation << [CREATE, READ, PATCH]
}
+ def 'Dmi service sends client error response when #scenario'() {
+ given: 'the web client unable to return response entity but error'
+ mockDataServicesWebClient.post() >> mockRequestBody
+ mockResponse.toEntity(Object.class) >> Mono.error(exceptionType)
+ when: 'POST operation is invoked'
+ objectUnderTest.postOperationWithJsonData(DATA, '/my/url', 'some json', READ, NO_AUTH_HEADER)
+ then: 'a http client exception is thrown'
+ def thrown = thrown(DmiClientRequestException)
+ and: 'the exception has the relevant details from the error response'
+ assert thrown.ncmpResponseStatus == expectedNcmpResponseStatusCode
+ assert thrown.httpStatusCode == httpStatusCode
+ where: 'the following errors occur'
+ scenario | httpStatusCode | exceptionType || expectedNcmpResponseStatusCode
+ 'dmi service unavailable' | 503 | new WebClientRequestException(new RuntimeException('some-error'), null, null, new HttpHeaders()) || DMI_SERVICE_NOT_RESPONDING
+ 'dmi request timeout' | 408 | new WebClientResponseException('message', httpStatusCode, 'statusText', null, null, null) || DMI_SERVICE_NOT_RESPONDING
+ 'dmi server error' | 500 | new WebClientResponseException('message', httpStatusCode, 'statusText', null, null, null) || UNABLE_TO_READ_RESOURCE_DATA
+ 'unknown error' | 500 | new Throwable('message') || UNKNOWN_ERROR
+ }
+
def 'Dmi trust level is determined by spring boot health status'() {
given: 'a health check response'
def dmiPluginHealthCheckResponseJsonData = TestUtils.getResourceFileContent('dmiPluginHealthCheckResponse.json')
- def jsonNode = objectMapper.readValue(dmiPluginHealthCheckResponseJsonData, JsonNode.class)
+ def jsonNode = jsonObjectMapper.convertJsonString(dmiPluginHealthCheckResponseJsonData, JsonNode.class)
((ObjectNode) jsonNode).put('status', 'my status')
- mockRestTemplate.getForObject(*_) >> {jsonNode}
+ mockHealthChecksWebClient.get() >> mockRequestBody
+ mockResponse.bodyToMono(JsonNode.class) >> Mono.just(jsonNode)
when: 'get trust level of the dmi plugin'
- def result = objectUnderTest.getDmiHealthStatus('some url')
- then: 'the status value from the json is return'
+ def result = objectUnderTest.getDmiHealthStatus('some/url')
+ then: 'the status value from the json is returned'
assert result == 'my status'
}
def 'Failing to get dmi plugin health status #scenario'() {
given: 'rest template with #scenario'
- mockRestTemplate.getForObject(*_) >> healthStatusResponse
+ mockHealthChecksWebClient.get() >> mockRequestBody
+ mockResponse.bodyToMono(_) >> healthStatusResponse
when: 'attempt to get health status of the dmi plugin'
def result = objectUnderTest.getDmiHealthStatus('some url')
then: 'result will be empty'
@@ -114,15 +155,18 @@ class DmiRestClientSpec extends Specification {
where: 'the following responses are used'
scenario | healthStatusResponse
'null' | null
- 'exception' | {throw new Exception()}
+ 'exception' | { throw new Exception() }
}
def 'DMI auth header #scenario'() {
when: 'Specific dmi properties are provided'
- dmiProperties.dmiBasicAuthEnabled = authEnabled
+ mockDmiProperties.dmiBasicAuthEnabled >> authEnabled
+ mockDmiProperties.authUsername >> 'some user'
+ mockDmiProperties.authPassword >> 'some password'
then: 'http headers to conditionally have Authorization header'
- def authHeaderValues = objectUnderTest.configureHttpHeaders(new HttpHeaders(), ncmpAuthHeader).getOrEmpty('Authorization')
- def outputAuthHeader = (authHeaderValues == null ? null : authHeaderValues[0])
+ def httpHeaders = new HttpHeaders()
+ objectUnderTest.configureHttpHeaders(httpHeaders, ncmpAuthHeader)
+ def outputAuthHeader = (httpHeaders.Authorization == null ? null : httpHeaders.Authorization[0])
assert outputAuthHeader == expectedAuthHeader
where: 'the following configurations are used'
scenario | authEnabled | ncmpAuthHeader || expectedAuthHeader
@@ -132,5 +176,4 @@ class DmiRestClientSpec extends Specification {
'DMI basic auth disabled, with NCMP bearer token' | false | BEARER_AUTH_HEADER || BEARER_AUTH_HEADER
'DMI basic auth disabled, with NCMP basic auth' | false | BASIC_AUTH_HEADER || NO_AUTH_HEADER
}
-
-}
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiPropertiesSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiPropertiesSpec.groovy
new file mode 100644
index 000000000..c763c522c
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiPropertiesSpec.groovy
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config
+
+import spock.lang.Specification
+
+class DmiPropertiesSpec extends Specification {
+
+ def objectUnderTest = new DmiProperties()
+
+ def 'Geting dmi base path.'() {
+ given: 'base path of #dmiBasePath'
+ objectUnderTest.dmiBasePath = dmiBasePath
+ expect: 'Preceding and trailing slash wil be removed'
+ assert objectUnderTest.getDmiBasePath() == 'test'
+ where: 'the following dmi base paths are used'
+ dmiBasePath << [ 'test' , '/test', 'test/', '/test/' ]
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfigurationSpec.groovy
new file mode 100644
index 000000000..05ecaa11b
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/DmiWebClientConfigurationSpec.groovy
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.test.context.TestPropertySource
+import org.springframework.web.reactive.function.client.WebClient
+import spock.lang.Specification
+
+@SpringBootTest
+@ContextConfiguration(classes = [HttpClientConfiguration])
+@TestPropertySource(properties = ['ncmp.dmi.httpclient.data-services.connectionTimeoutInSeconds=1', 'ncmp.dmi.httpclient.model-services.maximumInMemorySizeInMegabytes=1'])
+@EnableConfigurationProperties
+class DmiWebClientConfigurationSpec extends Specification {
+
+ def httpClientConfiguration = Spy(HttpClientConfiguration.class)
+
+ def objectUnderTest = new DmiWebClientConfiguration(httpClientConfiguration)
+
+ def 'Web Client Configuration construction.'() {
+ expect: 'the system can create an instance'
+ new DmiWebClientConfiguration(httpClientConfiguration) != null
+ }
+
+ def 'Creating a web client instance data service.'() {
+ given: 'Web client configuration is invoked'
+ def dataServicesWebClient = objectUnderTest.dataServicesWebClient()
+ expect: 'the system can create an instance for data service'
+ assert dataServicesWebClient != null
+ assert dataServicesWebClient instanceof WebClient
+ }
+
+ def 'Creating a web client instance model service.'() {
+ given: 'Web client configuration invoked'
+ def modelServicesWebClient = objectUnderTest.modelServicesWebClient()
+ expect: 'the system can create an instance for model service'
+ assert modelServicesWebClient != null
+ assert modelServicesWebClient instanceof WebClient
+ }
+
+ def 'Creating a web client instance health service.'() {
+ given: 'Web client configuration invoked'
+ def healthChecksWebClient = objectUnderTest.healthChecksWebClient()
+ expect: 'the system can create an instance for health service'
+ assert healthChecksWebClient != null
+ assert healthChecksWebClient instanceof WebClient
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy
index 2c76b5bb4..b7ced2382 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/HttpClientConfigurationSpec.groovy
@@ -1,6 +1,6 @@
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation.
+ * Copyright (C) 2023-2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,32 +17,55 @@
* SPDX-License-Identifier: Apache-2.0
* ============LICENSE_END=========================================================
*/
+
package org.onap.cps.ncmp.api.impl.config
-import java.time.Duration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.TestPropertySource
-import org.springframework.test.context.support.AnnotationConfigContextLoader
import spock.lang.Specification
@SpringBootTest
@ContextConfiguration(classes = [HttpClientConfiguration])
@EnableConfigurationProperties(HttpClientConfiguration.class)
-@TestPropertySource(properties = ["ncmp.dmi.httpclient.connectionTimeoutInSeconds=1", "ncmp.dmi.httpclient.maximumConnectionsTotal=200"])
+@TestPropertySource(properties = ["ncmp.dmi.httpclient.data-services.readTimeoutInSeconds=789", "ncmp.dmi.httpclient.model-services.maximumConnectionsTotal=111"])
class HttpClientConfigurationSpec extends Specification {
@Autowired
private HttpClientConfiguration httpClientConfiguration
- def 'Test HttpClientConfiguration properties with custom and default values'() {
- expect: 'custom property values'
- assert httpClientConfiguration.getConnectionTimeoutInSeconds() == Duration.ofSeconds(1)
- assert httpClientConfiguration.getMaximumConnectionsTotal() == 200
- and: 'default property values'
- assert httpClientConfiguration.getMaximumConnectionsPerRoute() == 50
- assert httpClientConfiguration.getIdleConnectionEvictionThresholdInSeconds() == Duration.ofSeconds(5)
+ def 'Test http client configuration properties of data with custom and default values'() {
+ expect: 'properties are populated correctly for data'
+ with(httpClientConfiguration.dataServices) {
+ assert connectionTimeoutInSeconds == 123
+ assert readTimeoutInSeconds == 789
+ assert writeTimeoutInSeconds == 30
+ assert maximumConnectionsTotal == 100
+ assert maximumInMemorySizeInMegabytes == 7
+ }
+ }
+
+ def 'Test http client configuration properties of model with custom and default values'() {
+ expect: 'properties are populated correctly for model'
+ with(httpClientConfiguration.modelServices) {
+ assert connectionTimeoutInSeconds == 456
+ assert readTimeoutInSeconds == 30
+ assert writeTimeoutInSeconds == 30
+ assert maximumConnectionsTotal == 111
+ assert maximumInMemorySizeInMegabytes == 8
+ }
+ }
+
+ def 'Test http client configuration properties of health with default values'() {
+ expect: 'properties are populated correctly for health'
+ with(httpClientConfiguration.healthCheckServices) {
+ assert connectionTimeoutInSeconds == 30
+ assert readTimeoutInSeconds == 30
+ assert writeTimeoutInSeconds == 30
+ assert maximumConnectionsTotal == 10
+ assert maximumInMemorySizeInMegabytes == 1
+ }
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy
deleted file mode 100644
index 74e342405..000000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-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
-
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.boot.web.client.RestTemplateBuilder
-import org.springframework.http.MediaType
-import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
-import org.springframework.test.context.ContextConfiguration
-import org.springframework.web.client.RestTemplate
-import spock.lang.Specification
-
-@SpringBootTest
-@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, HttpClientConfiguration])
-class NcmpConfigurationSpec extends Specification{
-
- @Autowired
- NcmpConfiguration.DmiProperties dmiProperties
-
- @Autowired
- HttpClientConfiguration httpClientConfiguration
-
- def mockRestTemplateBuilder = new RestTemplateBuilder()
-
- def 'NcmpConfiguration Construction.'() {
- expect: 'the system can create an instance'
- new NcmpConfiguration() != null
- }
-
- def 'DMI Properties.'() {
- expect: 'properties are set to values in test configuration yaml file'
- dmiProperties.authUsername == 'some-user'
- dmiProperties.authPassword == 'some-password'
- }
-
- def 'Rest Template creation with CloseableHttpClient and MappingJackson2HttpMessageConverter.'() {
- when: 'a rest template is created'
- def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder, httpClientConfiguration)
- then: 'the rest template is returned'
- assert result instanceof RestTemplate
- and: 'the rest template is created with httpclient5'
- assert result.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory
- assert ((HttpComponentsClientHttpRequestFactory) result.getRequestFactory()).getHttpClient() instanceof CloseableHttpClient;
- and: 'a jackson media converter has been added'
- def lastMessageConverter = result.getMessageConverters().get(result.getMessageConverters().size()-1)
- lastMessageConverter instanceof MappingJackson2HttpMessageConverter
- and: 'the jackson media converters supports the expected media types'
- lastMessageConverter.getSupportedMediaTypes() == [MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN];
- }
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy
new file mode 100644
index 000000000..07395cf5b
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy
@@ -0,0 +1,81 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.config
+
+import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
+import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler
+import org.spockframework.spring.SpringBean
+import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer
+import spock.lang.Shared
+import spock.lang.Specification
+
+class OpenTelemetryConfigSpec extends Specification{
+
+ @Shared
+ @SpringBean
+ OpenTelemetryConfig openTelemetryConfig = new OpenTelemetryConfig()
+
+ def setupSpec() {
+ openTelemetryConfig.tracingExporterEndpointUrl="http://tracingExporterEndpointUrl"
+ openTelemetryConfig.jaegerRemoteSamplerUrl="http://jaegerremotesamplerurl"
+ openTelemetryConfig.serviceId ="cps-application"
+ }
+
+ def 'OpenTelemetryConfig Construction.'() {
+ expect: 'the system can create an instance'
+ new OpenTelemetryConfig() != null
+ }
+
+ def 'OTLP Exporter creation with Grpc protocol'(){
+ when: 'an OTLP exporter is created'
+ def result = openTelemetryConfig.createOtlpExporterGrpc()
+ then: 'an OTLP Exporter is created'
+ assert result instanceof OtlpGrpcSpanExporter
+ }
+
+ def 'OTLP Exporter creation with HTTP protocol'(){
+ when: 'an OTLP exporter is created'
+ def result = openTelemetryConfig.createOtlpExporterHttp()
+ then: 'an OTLP Exporter is created'
+ assert result instanceof OtlpHttpSpanExporter
+ and:
+ assert result.builder.endpoint=="http://tracingExporterEndpointUrl"
+ }
+
+ def 'Jaeger Remote Sampler Creation'(){
+ when: 'an OTLP exporter is created'
+ def result = openTelemetryConfig.createJaegerRemoteSampler()
+ then: 'an OTLP Exporter is created'
+ assert result instanceof JaegerRemoteSampler
+ and:
+ assert result.delegate.type=="remoteSampling"
+ and:
+ assert result.delegate.url.toString().startsWith("http://jaegerremotesamplerurl")
+ }
+
+ def 'Skipping Acutator endpoints'(){
+ when: 'an OTLP exporter is created'
+ def result = openTelemetryConfig.skipActuatorEndpointsFromObservation()
+ then: 'an OTLP Exporter is created'
+ assert result instanceof ObservationRegistryCustomizer
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfigSpec.groovy
index 16f27d081..4d3fd6616 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfigSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfigSpec.groovy
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.config.kafka;
+package org.onap.cps.ncmp.api.impl.config.kafka
import io.cloudevents.CloudEvent
import io.cloudevents.kafka.CloudEventDeserializer
@@ -31,12 +31,14 @@ 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 org.springframework.test.context.TestPropertySource
import spock.lang.Shared
import spock.lang.Specification
@SpringBootTest(classes = [KafkaProperties, KafkaConfig])
@EnableSharedInjection
@EnableConfigurationProperties
+@TestPropertySource(properties = ["cps.tracing.enabled=true"])
class KafkaConfigSpec extends Specification {
@Shared
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy
index e50652689..75db0bfe5 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy
@@ -46,4 +46,15 @@ class CmNotificationSubscriptionDeltaSpec extends Specification {
}
+ def 'Find Delta of given list of predicates when it is an ongoing Cm Subscription'() {
+ given: 'A list of predicates'
+ def predicateList = [new DmiCmNotificationSubscriptionPredicate(['ch-1'].toSet(), DatastoreType.PASSTHROUGH_OPERATIONAL, ['a/1/'].toSet())]
+ and: 'its already present'
+ mockCmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_OPERATIONAL, 'ch-1', 'a/1/') >>> true
+ when: 'getDelta is called'
+ def result = objectUnderTest.getDelta(predicateList)
+ then: 'verify correct delta is returned'
+ assert result.size() == 0
+ }
+
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy
index cfb28a0ad..039a18949 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy
@@ -25,8 +25,8 @@ import io.cloudevents.CloudEvent
import org.onap.cps.events.EventsPublisher
import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionDmiInEventProducer
import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper
+import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmHandle
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent
-import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Cmhandle
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Data
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Specification
@@ -43,7 +43,7 @@ class CmNotificationSubscriptionDmiInEventProducerSpec extends Specification {
def subscriptionId = 'test-subscription-id'
def dmiPluginName = 'test-dmiplugin'
def eventType = 'subscriptionCreateRequest'
- def cmNotificationSubscriptionDmiInEvent = new CmNotificationSubscriptionDmiInEvent(data: new Data(cmhandles: [new Cmhandle(cmhandleId: 'test-1', privateProperties: [:])]))
+ def cmNotificationSubscriptionDmiInEvent = new CmNotificationSubscriptionDmiInEvent(data: new Data(cmHandles: [new CmHandle(cmhandleId: 'test-1', privateProperties: [:])]))
and: 'also we have target topic for dmiPlugin'
objectUnderTest.cmNotificationSubscriptionDmiInEventTopic = 'dmiplugin-test-topic'
when: 'the event is published'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy
index 488879db7..9b0a48d93 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy
@@ -36,7 +36,6 @@ import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncm
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
import org.slf4j.LoggerFactory
-import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
@@ -108,9 +107,9 @@ class CmNotificationSubscriptionDmiOutEventConsumerSpec extends MessagingBaseSpe
and: 'correct number of calls to publish the ncmp out event to client'
1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent('sub-1', 'subscriptionCreateResponse', _, false)
where: 'the following parameters are used'
- scenario | subscriptionStatus | statusCode || expectedCacheCalls | expectedPersistenceCalls
- 'Accepted Status' | CmNotificationSubscriptionStatus.ACCEPTED | '1' || 1 | 1
- 'Rejected Status' | CmNotificationSubscriptionStatus.REJECTED | '2' || 1 | 0
+ scenario | subscriptionStatus | statusCode || expectedCacheCalls | expectedPersistenceCalls
+ 'Accepted Status' | CmNotificationSubscriptionStatus.ACCEPTED | '1' || 1 | 1
+ 'Rejected Status' | CmNotificationSubscriptionStatus.REJECTED | '104' || 1 | 0
}
def getLoggingEvent() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy
index 9c84c51b2..01a92c02f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy
@@ -72,19 +72,40 @@ class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpe
.withSource(URI.create('some-resource'))
.withExtension('correlationid', 'test-cmhandle1').build()
def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
- and: 'notifications are enabled'
- objectUnderTest.notificationFeatureEnabled = true
when: 'the valid event is consumed'
objectUnderTest.consumeSubscriptionEvent(consumerRecord)
then: 'an event is logged with level INFO'
def loggingEvent = getLoggingEvent()
assert loggingEvent.level == Level.INFO
and: 'the log indicates the task completed successfully'
- assert loggingEvent.formattedMessage == 'Subscription for source some-resource with subscription id test-id ...'
+ assert loggingEvent.formattedMessage == 'Subscription create request for source some-resource with subscription id test-id ...'
and: 'the subscription handler service is called once'
- 1 * mockCmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest(_)
+ 1 * mockCmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest('test-id',_)
}
+ def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() {
+ given: 'a cmNotificationSubscription event'
+ def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
+ def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
+ def testCloudEventSent = CloudEventBuilder.v1()
+ .withData(objectMapper.writeValueAsBytes(testEventSent))
+ .withId('sub-id')
+ .withType('subscriptionDeleteRequest')
+ .withSource(URI.create('some-resource'))
+ .withExtension('correlationid', 'test-cmhandle1').build()
+ def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
+ when: 'the valid event is consumed'
+ objectUnderTest.consumeSubscriptionEvent(consumerRecord)
+ then: 'an event is logged with level INFO'
+ def loggingEvent = getLoggingEvent()
+ assert loggingEvent.level == Level.INFO
+ and: 'the log indicates the task completed successfully'
+ assert loggingEvent.formattedMessage == 'Subscription delete request for source some-resource with subscription id test-id ...'
+ and: 'the subscription handler service is called once'
+ 1 * mockCmNotificationSubscriptionHandlerService.processSubscriptionDeleteRequest('test-id',_)
+ }
+
+
def getLoggingEvent() {
return logger.list[1]
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy
index 43568be50..8d7a4b9cf 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy
@@ -133,10 +133,10 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].targetCmHandleIds == ['ch4'].toSet()
and: 'the list of xpath for each is correct'
assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[0].xpaths
- && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].xpaths == ['/x1/y1','x2/y2'].toSet()
+ && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].xpaths == ['/x1/y1','x2/y2'].toSet()
assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[1].xpaths
- && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].xpaths == ['/x3/y3','x4/y4'].toSet()
+ && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].xpaths == ['/x3/y3','x4/y4'].toSet()
}
def 'Get map for cm handle IDs by DMI service name'() {
@@ -164,7 +164,7 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
}
def 'Persist Cache into database per dmi'() {
- given: 'populate cache'
+ given: 'populated cache'
def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates()
def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()
objectUnderTest.add(subscriptionId, predicates)
@@ -174,15 +174,26 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
4 * mockCmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(_,_,_,subscriptionId)
}
+ def 'Remove subscription from database per dmi'() {
+ given: 'populated cache'
+ def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates()
+ def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()
+ objectUnderTest.add(subscriptionId, predicates)
+ when: 'subscription is persisted in database'
+ objectUnderTest.removeFromDatabasePerDmi(subscriptionId,'dmi-1')
+ then: 'persistence service is called the correct number of times per dmi'
+ 4 * mockCmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(_,_,_,subscriptionId)
+ }
+
def setUpTestEvent(){
def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
def testCloudEventSent = CloudEventBuilder.v1()
- .withData(objectMapper.writeValueAsBytes(testEventSent))
- .withId('subscriptionCreated')
- .withType('subscriptionCreated')
- .withSource(URI.create('some-resource'))
- .withExtension('correlationid', 'test-cmhandle1').build()
+ .withData(objectMapper.writeValueAsBytes(testEventSent))
+ .withId('subscriptionCreated')
+ .withType('subscriptionCreated')
+ .withSource(URI.create('some-resource'))
+ .withExtension('correlationid', 'test-cmhandle1').build()
def consumerRecord = new ConsumerRecord<String, CloudEvent>('topic-name', 0, 0, 'event-key', testCloudEventSent)
def cloudEvent = consumerRecord.value()
@@ -191,10 +202,10 @@ class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec {
def initialiseMockInventoryPersistenceResponses(){
mockInventoryPersistence.getYangModelCmHandles(['ch1','ch2'])
- >> [yangModelCmHandle1, yangModelCmHandle2]
+ >> [yangModelCmHandle1, yangModelCmHandle2]
mockInventoryPersistence.getYangModelCmHandles(['ch3','ch4'])
- >> [yangModelCmHandle3, yangModelCmHandle4]
+ >> [yangModelCmHandle3, yangModelCmHandle4]
}
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy
index 763aedaa0..cf72b2925 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy
@@ -48,8 +48,8 @@ class CmNotificationSubscriptionDmiInEventMapperSpec extends Specification {
when: 'we try to map the values'
def result = objectUnderTest.toCmNotificationSubscriptionDmiInEvent(dmiCmNotificationSubscriptionPredicates)
then: 'it contains correct cm notification subscription cmhandle object'
- assert result.data.cmhandles.cmhandleId.containsAll(['ch-1', 'ch-2'])
- assert result.data.cmhandles.privateProperties.containsAll([['k1': 'v1'], ['k2': 'v2']])
+ assert result.data.cmHandles.cmhandleId.containsAll(['ch-1', 'ch-2'])
+ assert result.data.cmHandles.privateProperties.containsAll([['k1': 'v1'], ['k2': 'v2']])
and: 'also has the correct dmi cm notification subscription predicates'
assert result.data.predicates.targetFilter.containsAll([['ch-1'], ['ch-2']])
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy
index 98b4ee267..982150ec0 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy
@@ -25,10 +25,10 @@ import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscripti
import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionEventsHandler
import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler
import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionDmiInEventMapper
-import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionNcmpOutEventMapper
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus
import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails
+import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate
+import org.onap.cps.ncmp.api.impl.operations.DatastoreType
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent
import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent
import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent
@@ -50,13 +50,16 @@ class CmNotificationSubscriptionHandlerServiceImplSpec extends Specification{
mockCmNotificationSubscriptionEventsHandler, mockDmiCmNotificationSubscriptionCacheHandler)
def testSubscriptionDetailsMap = ["dmi-1":new DmiCmNotificationSubscriptionDetails([], CmNotificationSubscriptionStatus.PENDING)]
- def testListOfDeltaPredicates = []
def 'Consume valid and unique CmNotificationSubscriptionNcmpInEvent create message'() {
given: 'a cmNotificationSubscriptionNcmp in event with unique subscription id'
def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
+ def testListOfDeltaPredicates = [new DmiCmNotificationSubscriptionPredicate(['ch1'].toSet(), DatastoreType.PASSTHROUGH_OPERATIONAL, ['/a/b'].toSet())]
mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId("test-id") >> true
+ and: 'relevant details is extracted from the event'
+ def subscriptionId = testEventConsumed.getData().getSubscriptionId()
+ def predicates = testEventConsumed.getData().getPredicates()
and: 'the cache handler returns for relevant subscription id'
1 * mockDmiCmNotificationSubscriptionCacheHandler.get("test-id") >> testSubscriptionDetailsMap
and: 'the delta predicates is returned'
@@ -66,7 +69,7 @@ class CmNotificationSubscriptionHandlerServiceImplSpec extends Specification{
1 * mockCmNotificationSubscriptionMappersHandler
.toCmNotificationSubscriptionDmiInEvent(testListOfDeltaPredicates) >> testDmiInEvent
when: 'the valid and unique event is consumed'
- objectUnderTest.processSubscriptionCreateRequest(testEventConsumed)
+ objectUnderTest.processSubscriptionCreateRequest(subscriptionId, predicates)
then: 'the subscription cache handler is called once'
1 * mockDmiCmNotificationSubscriptionCacheHandler.add('test-id',_)
and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters'
@@ -77,21 +80,68 @@ class CmNotificationSubscriptionHandlerServiceImplSpec extends Specification{
"test-id", "subscriptionCreateResponse", null, true)
}
+ def 'Consume valid and Overlapping Cm Notification Subscription NcmpIn Event'() {
+ given: 'a cmNotificationSubscriptionNcmp in event with unique subscription id'
+ def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
+ def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
+ def noDeltaPredicates = []
+ mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId("test-id") >> true
+ and: 'the cache handler returns for relevant subscription id'
+ 1 * mockDmiCmNotificationSubscriptionCacheHandler.get("test-id") >> testSubscriptionDetailsMap
+ and: 'the delta predicates is returned'
+ 1 * mockCmNotificationSubscriptionDelta.getDelta(_) >> noDeltaPredicates
+ when: 'the valid and unique event is consumed'
+ objectUnderTest.processSubscriptionCreateRequest('test-id', noDeltaPredicates)
+ then: 'the subscription cache handler is called once'
+ 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('test-id', _)
+ and: 'the subscription details are updated in the cache'
+ 1 * mockDmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi('test-id', _, CmNotificationSubscriptionStatus.ACCEPTED)
+ and: 'we schedule to send the response after configured time from the cache'
+ 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(
+ "test-id", "subscriptionCreateResponse", null, true)
+ }
+
def 'Consume valid and but non-unique CmNotificationSubscription create message'() {
given: 'a cmNotificationSubscriptionNcmp in event'
def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId('test-id') >> false
+ and: 'relevant details is extracted from the event'
+ def subscriptionId = testEventConsumed.getData().getSubscriptionId()
+ def predicates = testEventConsumed.getData().getPredicates()
and: 'the NCMP out in event mapper returns an event for rejected request'
def testNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent()
1 * mockCmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest(
"test-id",_) >> testNcmpOutEvent
when: 'the valid but non-unique event is consumed'
- objectUnderTest.processSubscriptionCreateRequest(testEventConsumed)
+ objectUnderTest.processSubscriptionCreateRequest(subscriptionId, predicates)
then: 'the events handler method to publish DMI event is never called'
0 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent(_,_,_,_)
and: 'the events handler method to publish NCMP out event is called once'
1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(
'test-id', 'subscriptionCreateResponse', testNcmpOutEvent, false)
}
+
+ def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() {
+ given: 'a cmNotificationSubscriptionNcmp in event for delete'
+ def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json')
+ def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class)
+ and: 'relevant details is extracted from the event'
+ def subscriptionId = testEventConsumed.getData().getSubscriptionId()
+ def predicates = testEventConsumed.getData().getPredicates()
+ and: 'the cache handler returns for relevant subscription id'
+ 1 * mockDmiCmNotificationSubscriptionCacheHandler.get('test-id') >> testSubscriptionDetailsMap
+ when: 'the valid and unique event is consumed'
+ objectUnderTest.processSubscriptionDeleteRequest(subscriptionId, predicates)
+ then: 'the subscription cache handler is called once'
+ 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('test-id', predicates)
+ and: 'the mapper handler to get DMI in event is called once'
+ 1 * mockCmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionDmiInEvent(_)
+ and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters'
+ testSubscriptionDetailsMap.size() * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent(
+ 'test-id', 'dmi-1', 'subscriptionDeleteRequest', _)
+ and: 'we schedule to send the response after configured time from the cache'
+ 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(
+ 'test-id', 'subscriptionDeleteResponse', null, true)
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy
index b51ecb0cf..281ec4f7e 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (c) 2024 Nordix Foundation.
+ * Modifications Copyright (C) 2024 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,9 +24,8 @@ package org.onap.cps.ncmp.api.impl.events.cmsubscription.service
import org.onap.cps.utils.ContentType
import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID;
-import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE;
+import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE;
import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH;
-
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsQueryService
import org.onap.cps.ncmp.api.impl.operations.DatastoreType
@@ -48,7 +48,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']";
and: 'datanodes optionally returned'
1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
- cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode
+ cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode
when: 'we check for an ongoing cm subscription'
def response = objectUnderTest.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/cps/path')
then: 'we get expected response'
@@ -64,7 +64,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted('some-sub')
and: 'relevant datanodes are returned'
1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >>
- dataNodes
+ dataNodes
when: 'a subscription ID is tested for uniqueness'
def result = objectUnderTest.isUniqueSubscriptionId('some-sub')
then: 'result is as expected'
@@ -79,7 +79,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
given: 'a valid cm subscription path query'
def cpsPathQuery =CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y')
and: 'a dataNode exists for the given cps path query'
- mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
+ mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
when: 'the method to add/update cm notification subscription is called'
objectUnderTest.addCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1','/x/y', 'newSubId')
@@ -88,18 +88,18 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
'NCMP-Admin',
'cm-data-subscriptions',
'/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters',
- objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-1','newSubId']), _)
+ objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-1','newSubId']), _,ContentType.JSON)
}
def 'Add new cm notification subscription for #datastoreType'() {
given: 'a valid cm subscription path query'
def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y')
- def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
+ def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
and: 'a parent node xpath for the cm subscription path above'
def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles'
and: 'a datanode does not exist for cm subscription path query'
mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
- cmSubscriptionCpsPathQuery,
+ cmSubscriptionCpsPathQuery,
FetchDescendantsOption.OMIT_DESCENDANTS) >> []
and: 'a datanode does not exist for the given cm handle subscription path query'
mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
@@ -124,7 +124,7 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
def 'Add new cm notification subscription when xpath does not exist for existing subscription cm handle'() {
given: 'a valid cm subscription path query'
def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y')
- def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
+ def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1')
and: 'a parent node xpath for given cm handle for subscription path above'
def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles/cm-handle[@id=\'%s\']/filters'
and: 'a datanode does not exist for cm subscription path query'
@@ -157,19 +157,36 @@ class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification
then: 'the list of subscribers is updated'
1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'cm-data-subscriptions',
'/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters',
- objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _)
+ objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _, ContentType.JSON)
}
- def 'Removing last ongoing subscription for datastore, cmhandle and xpath'(){
+ def 'Removing last ongoing subscription for datastore and cmhandle and xpath'(){
given: 'a subscription exists when queried but has only 1 subscriber'
- def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y')
- mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions',
- cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
+ mockCpsQueryService.queryDataNodes(
+ 'NCMP-Admin',
+ 'cm-data-subscriptions',
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y'),
+ FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])]
+ and: 'the #scenario'
+ mockCpsQueryService.queryDataNodes(
+ 'NCMP-Admin',
+ 'cm-data-subscriptions',
+ CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted('ncmp-datastore:passthrough-running', 'ch-1'),
+ FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [new DataNode(childDataNodes: listOfChildNodes)]
when: 'that last ongoing subscription is removed'
objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1')
then: 'the subscription with empty subscriber list is removed'
1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions',
'/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters/filter[@xpath=\'/x/y\']',
_)
+ and: 'method call to delete the cm handle is called the correct number of times'
+ numberOfCallsToDeleteCmHandle * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions',
+ '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']',
+ _)
+ where:
+ scenario | listOfChildNodes || numberOfCallsToDeleteCmHandle
+ 'cm handle in same datastore is used for other subscriptions' | [new DataNode()] || 0
+ 'cm handle in same datastore is NOT used for other subscriptions' | [] || 1
}
+
} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy
index 9907e9ab2..66fd7d88e 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImplSpec.groovy
@@ -37,7 +37,6 @@ import org.onap.cps.api.CpsModuleService
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.spi.CascadeDeleteAllowed
import org.onap.cps.spi.FetchDescendantsOption
-import org.onap.cps.ncmp.api.impl.exception.NoAlternateIdParentFoundException
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.ModuleDefinition
@@ -303,41 +302,6 @@ class InventoryPersistenceImplSpec extends Specification {
assert objectUnderTest.getCmHandleDataNodeByAlternateId('alternate id') == new DataNode()
}
- def 'Find cm handle parent data node using alternate ids'() {
- given: 'cm handle in the registry with alternateId /a/b'
- def matchingCpsPath = "/dmi-registry/cm-handles[@alternate-id='/a/b']"
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(matchingCpsPath, OMIT_DESCENDANTS) >> [new DataNode()]
- and: 'no other cm handle'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> []
- expect: 'querying for alternate id a matching result found'
- assert objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(alternateId, '/') != null
- where: 'the following parameters are used'
- scenario | alternateId
- 'exact match' | '/a/b'
- 'exact match with trailing separator' | '/a/b/'
- 'child match' | '/a/b/c'
- }
-
- def 'Find cm handle parent data node using alternate ids mismatches'() {
- given: 'cm handle in the registry with alternateId'
- def matchingCpsPath = "/dmi-registry/cm-handles[@alternate-id='${cpsPath}]"
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(matchingCpsPath, OMIT_DESCENDANTS) >> [new DataNode()]
- and: 'no other cm handle'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> []
- when: 'attempt to find alternateId'
- objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(alternateId, '/')
- then: 'no alternate id found exception thrown'
- def thrown = thrown(NoAlternateIdParentFoundException)
- and: 'the exception has the relevant details from the error response'
- assert thrown.message == 'No matching (parent) cm handle found using alternate ids'
- assert thrown.details == 'cannot find a datanode with alternate id ' + alternateId
- where: 'the following parameters are used'
- scenario | alternateId | cpsPath
- 'no match for parent only' | '/a' | '/a/b'
- 'no match at all' | '/x/y/z' | '/a/b'
- 'no match with trailing separator' | '/c/d/' | '/c/d'
- }
-
def 'Attempt to get non existing cm handle data node by alternate id'() {
given: 'query service is invoked and returns empty collection of data nodes'
mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> []
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DatastoreTypeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DatastoreTypeSpec.groovy
new file mode 100644
index 000000000..7e364c97c
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DatastoreTypeSpec.groovy
@@ -0,0 +1,46 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations
+
+import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException
+import spock.lang.Specification
+
+class DatastoreTypeSpec extends Specification {
+
+ def 'Converting string to enum.'() {
+ expect: 'converting string to enum results in the correct enum value'
+ DatastoreType.fromDatastoreName(datastoreName) == expectedEnum
+ where: 'the following datastore names are used'
+ datastoreName || expectedEnum
+ 'ncmp-datastore:operational' || DatastoreType.OPERATIONAL
+ 'ncmp-datastore:passthrough-running' || DatastoreType.PASSTHROUGH_RUNNING
+ 'ncmp-datastore:passthrough-operational' || DatastoreType.PASSTHROUGH_OPERATIONAL
+ }
+
+ def 'Converting unknown name string to enum.'() {
+ when: 'attempt converting unknown datastore name'
+ DatastoreType.fromDatastoreName('unknown')
+ then: 'an invalid datastore exception is thrown'
+ def thrown = thrown(InvalidDatastoreException)
+ assert thrown.message.contains('unknown is an invalid datastore')
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
index eb6c7a0f4..b286e9fb1 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
@@ -21,16 +21,25 @@
package org.onap.cps.ncmp.api.impl.operations
+import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR
+
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.events.EventsPublisher
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
-import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
-import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder
+import org.onap.cps.ncmp.api.impl.config.DmiProperties
+import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException
import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext
import org.onap.cps.ncmp.api.models.DataOperationRequest
import org.onap.cps.ncmp.api.models.CmResourceAddress
import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent
import org.onap.cps.ncmp.utils.TestUtils
+import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
@@ -39,28 +48,17 @@ import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
import spock.lang.Shared
-
-import java.util.concurrent.TimeoutException
-
-import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING
-import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA
-import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.UPDATE
+import reactor.core.publisher.Mono
@SpringBootTest
-@ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, NcmpConfiguration.DmiProperties, DmiDataOperations])
+@ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, DmiProperties, DmiDataOperations])
class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
- @SpringBean
- DmiServiceUrlBuilder dmiServiceUrlBuilder = Mock()
def dmiServiceBaseUrl = "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/data/ds/ncmp-datastore:"
def NO_TOPIC = null
def NO_REQUEST_ID = null
def NO_AUTH_HEADER = null
+
@Shared
def OPTIONS_PARAM = '(a=1,b=2)'
@@ -77,23 +75,24 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
given: 'a cm handle for #cmHandleId'
mockYangModelCmHandleRetrieval(dmiProperties)
and: 'a positive response from DMI service when it is called with the expected parameters'
- def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
- def expectedUrl = dmiServiceBaseUrl + "${expectedDatastoreInUrl}?resourceIdentifier=${resourceIdentifier}${expectedOptionsInUrl}"
- mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi
- dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
+ def responseFromDmi = Mono.just(new ResponseEntity<Object>('{some-key:some-value}', HttpStatus.OK))
+ def expectedUrl = "${dmiServiceBaseUrl}${expectedDatastoreInUrl}?resourceIdentifier=${resourceIdentifier}${expectedOptionsInUrl}"
+ def expectedJson = '{"operation":"read","cmHandleProperties":' + expectedProperties + ',"moduleSetTag":""}'
+ mockDmiRestClient.postOperationWithJsonDataAsync(DATA, expectedUrl, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'get resource data is invoked'
def cmResourceAddress = new CmResourceAddress(dataStore.datastoreName, cmHandleId, resourceIdentifier)
- def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, options, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER)
+ def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, options, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block()
then: 'the result is the response from the DMI service'
- assert result == responseFromDmi
+ assert result.body == '{some-key:some-value}'
+ assert result.statusCode.'2xxSuccessful'
where: 'the following parameters are used'
- scenario | dmiProperties | dataStore | options || expectedJson | expectedDatastoreInUrl | expectedOptionsInUrl
- 'without properties' | [] | PASSTHROUGH_OPERATIONAL | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{}}' | 'passthrough-operational' | '&options=(a=1,b=2)'
- 'with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_OPERATIONAL | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-operational' | '&options=(a=1,b=2)'
- 'null options' | [yangModelCmHandleProperty] | PASSTHROUGH_OPERATIONAL | null || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-operational' | ''
- 'empty options' | [yangModelCmHandleProperty] | PASSTHROUGH_OPERATIONAL | '' || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-operational' | ''
- 'datastore running without properties' | [] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{}}' | 'passthrough-running' | '&options=(a=1,b=2)'
- 'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running' | '&options=(a=1,b=2)'
+ scenario | dmiProperties | dataStore | options || expectedProperties | expectedDatastoreInUrl | expectedOptionsInUrl
+ 'without properties' | [] | PASSTHROUGH_OPERATIONAL | OPTIONS_PARAM || '{}' | 'passthrough-operational' | '&options=(a%3D1,b%3D2)'
+ 'with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_OPERATIONAL | OPTIONS_PARAM || '{"prop1":"val1"}' | 'passthrough-operational' | '&options=(a%3D1,b%3D2)'
+ 'null options' | [yangModelCmHandleProperty] | PASSTHROUGH_OPERATIONAL | null || '{"prop1":"val1"}' | 'passthrough-operational' | ''
+ 'empty options' | [yangModelCmHandleProperty] | PASSTHROUGH_OPERATIONAL | '' || '{"prop1":"val1"}' | 'passthrough-operational' | ''
+ 'datastore running without properties' | [] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{}' | 'passthrough-running' | '&options=(a%3D1,b%3D2)'
+ 'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"prop1":"val1"}' | 'passthrough-running' | '&options=(a%3D1,b%3D2)'
}
def 'Execute (async) data operation from DMI service.'() {
@@ -103,47 +102,46 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def dataOperationRequest = spiedJsonObjectMapper.convertJsonString(dataOperationBatchRequestJsonData, DataOperationRequest.class)
dataOperationRequest.dataOperationDefinitions[0].cmHandleIds = [cmHandleId]
and: 'a positive response from DMI service when it is called with valid request parameters'
- def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED)
- def expectedDmiBatchResourceDataUrl = "ncmp/v1/data/topic=my-topic-name"
- def expectedBatchRequestAsJson = '{"operations":[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","cmHandleProperties":{"prop1":"val1"}}]}]}'
- mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, _, READ.operationName, NO_AUTH_HEADER) >> responseFromDmi
- dmiServiceUrlBuilder.getDataOperationRequestUrl(_, _) >> expectedDmiBatchResourceDataUrl
- when: 'get resource data for group of cm handles are invoked'
+ def responseFromDmi = Mono.just(new ResponseEntity<Object>(HttpStatus.ACCEPTED))
+ def expectedDmiBatchResourceDataUrl = "someServiceName/dmi/v1/data?requestId=requestId&topic=my-topic-name"
+ def expectedBatchRequestAsJson = '{"operations":[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","moduleSetTag":"","cmHandleProperties":{"prop1":"val1"}}]}]}'
+ mockDmiRestClient.postOperationWithJsonDataAsync(DATA, expectedDmiBatchResourceDataUrl, _, READ, NO_AUTH_HEADER) >> responseFromDmi
+ when: 'get resource data for group of cm handles is invoked'
objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
- then: 'the post operation was called and ncmp generated dmi request body json args'
- 1 * mockDmiRestClient.postOperationWithJsonData(expectedDmiBatchResourceDataUrl, expectedBatchRequestAsJson, READ, NO_AUTH_HEADER)
+ then: 'the post operation was called with the expected URL and JSON request body'
+ 1 * mockDmiRestClient.postOperationWithJsonDataAsync(DATA, expectedDmiBatchResourceDataUrl, expectedBatchRequestAsJson, READ, NO_AUTH_HEADER)
}
- def 'Execute (async) data operation from DMI service for #scenario.'() {
- given: 'data operation request body and dmi resource url'
- def dmiDataOperation = DmiDataOperation.builder().operationId('some-operation-id').build()
- dmiDataOperation.getCmHandles().add(CmHandle.builder().id('some-cm-handle-id').build())
- def dmiDataOperationResourceDataUrl = "http://dmi-service-name:dmi-port/dmi/v1/data?topic=my-topic-name&requestId=some-request-id"
+ def 'Execute (async) data operation from DMI service with Exception.'() {
+ given: 'collection of yang model cm Handles and data operation request'
+ mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty])
+ def dataOperationBatchRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
+ def dataOperationRequest = spiedJsonObjectMapper.convertJsonString(dataOperationBatchRequestJsonData, DataOperationRequest.class)
+ dataOperationRequest.dataOperationDefinitions[0].cmHandleIds = [cmHandleId]
+ and: 'the published cloud event will be captured'
def actualDataOperationCloudEvent = null
- when: 'exception occurs after sending request to dmi service'
- objectUnderTest.handleTaskCompletionException(new Throwable(exception), dmiDataOperationResourceDataUrl, List.of(dmiDataOperation))
- then: 'a cloud event is published'
- eventsPublisher.publishCloudEvent('my-topic-name', 'some-request-id', _) >> { args -> actualDataOperationCloudEvent = args[2] }
- and: 'the event contains the expected error details'
+ eventsPublisher.publishCloudEvent('my-topic-name', 'my-request-id', _) >> { args -> actualDataOperationCloudEvent = args[2] }
+ and: 'a DMI client request exception is thrown when DMI service is called'
+ mockDmiRestClient.postOperationWithJsonDataAsync(*_) >> { Mono.error(new DmiClientRequestException(123, '', '', UNKNOWN_ERROR)) }
+ when: 'attempt to get resource data for group of cm handles is invoked'
+ objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'my-request-id', NO_AUTH_HEADER)
+ then: 'the event contains the expected error details'
def eventDataValue = extractDataValue(actualDataOperationCloudEvent)
- assert eventDataValue.operationId == dmiDataOperation.operationId
- assert eventDataValue.ids == dmiDataOperation.cmHandles.id
- assert eventDataValue.statusCode == responseCode.code
- assert eventDataValue.statusMessage == responseCode.message
- where: 'the following exceptions are occurred'
- scenario | exception || responseCode
- 'http client request exception' | new HttpClientRequestException('error-message', 'error-details', HttpStatus.SERVICE_UNAVAILABLE.value()) || UNABLE_TO_READ_RESOURCE_DATA
- 'timeout exception' | new TimeoutException() || DMI_SERVICE_NOT_RESPONDING
+ assert eventDataValue.statusCode == '108'
+ assert eventDataValue.statusMessage == UNKNOWN_ERROR.message
+ and: 'the event contains the correct operation details'
+ assert eventDataValue.operationId == dataOperationRequest.dataOperationDefinitions[0].operationId
+ assert eventDataValue.ids == dataOperationRequest.dataOperationDefinitions[0].cmHandleIds
}
def 'call get all resource data.'() {
- given: 'the system returns a cm handle with a sample property'
- mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
+ given: 'the system returns a cm handle with a sample property and sample module set tag'
+ mockYangModelCmHandleRetrieval([yangModelCmHandleProperty], 'my-module-set-tag')
and: 'a positive response from DMI service when it is called with the expected parameters'
def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
def expectedUrl = dmiServiceBaseUrl + "passthrough-operational?resourceIdentifier=/"
- mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}', READ, null) >> responseFromDmi
- dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
+ def expectedJson = '{"operation":"read","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":"my-module-set-tag"}'
+ mockDmiRestClient.postOperationWithJsonData(DATA, expectedUrl, expectedJson, READ, null) >> responseFromDmi
when: 'get resource data is invoked'
def result = objectUnderTest.getResourceDataFromDmi( PASSTHROUGH_OPERATIONAL.datastoreName, cmHandleId, NO_REQUEST_ID)
then: 'the result is the response from the DMI service'
@@ -154,11 +152,10 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
given: 'a cm handle for #cmHandleId'
mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
and: 'a positive response from DMI service when it is called with the expected parameters'
- def expectedUrl = dmiServiceBaseUrl + "passthrough-running?resourceIdentifier=${resourceIdentifier}"
- def expectedJson = '{"operation":"' + expectedOperationInUrl + '","dataType":"some data type","data":"requestData","cmHandleProperties":{"prop1":"val1"}}'
+ def expectedUrl = "${dmiServiceBaseUrl}passthrough-running?resourceIdentifier=${resourceIdentifier}"
+ def expectedJson = '{"operation":"' + expectedOperationInUrl + '","dataType":"some data type","data":"requestData","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":""}'
def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
- dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
- mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, operation, NO_AUTH_HEADER) >> responseFromDmi
+ mockDmiRestClient.postOperationWithJsonData(DATA, expectedUrl, expectedJson, operation, NO_AUTH_HEADER) >> responseFromDmi
when: 'write resource method is invoked'
def result = objectUnderTest.writeResourceDataPassThroughRunningFromDmi(cmHandleId, 'parent/child', operation, 'requestData', 'some data type', NO_AUTH_HEADER)
then: 'the result is the response from the DMI service'
@@ -169,7 +166,29 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
UPDATE || 'update'
}
+ def 'State Ready validation'() {
+ given: ' a yang model cm handle'
+ populateYangModelCmHandle([] ,'')
+ when: 'Validating State of #cmHandleState'
+ def caughtException = null
+ try {
+ objectUnderTest.validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState)
+ } catch (Exception e) {
+ caughtException = e
+ }
+ then: 'only when not ready a exception is thrown'
+ if (expecteException) {
+ assert caughtException.details.contains('not in READY state')
+ } else {
+ assert caughtException == null
+ }
+ where: ' the following states are used'
+ cmHandleState || expecteException
+ CmHandleState.READY || false
+ CmHandleState.ADVISED || true
+ }
+
def extractDataValue(actualDataOperationCloudEvent) {
- return toTargetEvent(actualDataOperationCloudEvent, DataOperationEvent.class).data.responses[0]
+ return toTargetEvent(actualDataOperationCloudEvent, DataOperationEvent).data.responses[0]
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
index 9aab46747..db7f26f5f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
@@ -21,9 +21,12 @@
package org.onap.cps.ncmp.api.impl.operations
+import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
+import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL
+
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
+import org.onap.cps.ncmp.api.impl.config.DmiProperties
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
@@ -34,10 +37,8 @@ import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
import spock.lang.Shared
-import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ
-
@SpringBootTest
-@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations])
+@ContextConfiguration(classes = [DmiProperties, DmiModelOperations])
class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
@Shared
@@ -58,8 +59,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
def moduleReferencesAsLisOfMaps = [[moduleName: 'mod1', revision: 'A'], [moduleName: 'mod2', revision: 'X']]
def expectedUrl = "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules"
def responseFromDmi = new ResponseEntity([schemas: moduleReferencesAsLisOfMaps], HttpStatus.OK)
- mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"cmHandleProperties":{},"moduleSetTag":""}', READ, NO_AUTH_HEADER)
- >> responseFromDmi
+ mockDmiRestClient.postOperationWithJsonData(MODEL, expectedUrl, '{"cmHandleProperties":{},"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'get module references is called'
def result = objectUnderTest.getModuleReferences(yangModelCmHandle)
then: 'the result consists of expected module references'
@@ -90,7 +90,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
mockYangModelCmHandleRetrieval(dmiProperties)
and: 'a positive response from DMI service when it is called with tha expected parameters'
def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK)
- mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules",
+ mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules",
'{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + ',"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'a get module references is called'
def result = objectUnderTest.getModuleReferences(yangModelCmHandle)
@@ -109,7 +109,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
def responseFromDmi = new ResponseEntity([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source'],
[moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK)
def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
- mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
+ mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
'{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'get new yang resources from DMI service'
def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences)
@@ -141,7 +141,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
mockYangModelCmHandleRetrieval(dmiProperties)
and: 'a positive response from DMI service when it is called with the expected moduleSetTag, modules and properties'
def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
- mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
+ mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
'{"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}',
READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'get new yang resources from DMI service'
@@ -159,9 +159,9 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
mockYangModelCmHandleRetrieval([], moduleSetTag)
and: 'a positive response from DMI service when it is called with the expected moduleSetTag'
def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
- mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
- '{' + expectedModuleSetTagInRequest + '"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":{}}',
- READ, NO_AUTH_HEADER) >> responseFromDmi
+ mockDmiRestClient.postOperationWithJsonData(MODEL, "${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
+ '{' + expectedModuleSetTagInRequest + '"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":{}}',
+ READ, NO_AUTH_HEADER) >> responseFromDmi
when: 'get new yang resources from DMI service'
def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences)
then: 'the result is the response from DMI service'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
index 72a0f2f11..136ff7832 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation
+ * Copyright (C) 2021-2024 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,13 +22,10 @@ package org.onap.cps.ncmp.api.impl.operations
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.ncmp.api.impl.client.DmiRestClient
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
-import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder
import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
import org.onap.cps.ncmp.api.impl.inventory.CompositeState
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
-import org.onap.cps.spi.utils.CpsValidator
import org.spockframework.spring.SpringBean
import spock.lang.Shared
import spock.lang.Specification
@@ -44,16 +41,11 @@ abstract class DmiOperationsBaseSpec extends Specification {
@SpringBean
InventoryPersistence mockInventoryPersistence = Mock()
- def mockCpsValidator = Mock(CpsValidator)
-
@SpringBean
ObjectMapper spyObjectMapper = Spy()
- @SpringBean
- DmiServiceUrlBuilder dmiServiceUrlBuilder = new DmiServiceUrlBuilder(new NcmpConfiguration.DmiProperties(), mockCpsValidator)
-
def yangModelCmHandle = new YangModelCmHandle()
- def static dmiServiceName = 'some service name'
+ def static dmiServiceName = 'someServiceName'
def static cmHandleId = 'some-cm-handle'
def static resourceIdentifier = 'parent/child'
@@ -68,7 +60,7 @@ abstract class DmiOperationsBaseSpec extends Specification {
}
def mockYangModelCmHandleCollectionRetrieval(dmiProperties) {
- populateYangModelCmHandle(dmiProperties, "")
+ populateYangModelCmHandle(dmiProperties, '')
mockInventoryPersistence.getYangModelCmHandles(_) >> [yangModelCmHandle]
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/OperationTypeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/OperationTypeSpec.groovy
new file mode 100644
index 000000000..d31b8d4fd
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/OperationTypeSpec.groovy
@@ -0,0 +1,48 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations
+
+import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException
+import spock.lang.Specification
+
+class OperationTypeSpec extends Specification {
+
+ def 'Converting string to enum.'() {
+ expect: 'converting string to enum results in the correct enum value'
+ OperationType.fromOperationName(operationName) == expectedEnum
+ where: 'the following datastore names are used'
+ operationName || expectedEnum
+ 'read' || OperationType.READ
+ 'create' || OperationType.CREATE
+ 'update' || OperationType.UPDATE
+ 'patch' || OperationType.PATCH
+ 'delete' || OperationType.DELETE
+ }
+
+ def 'Converting unknown name string to enum.'() {
+ when: 'attempt converting unknown datastore name'
+ OperationType.fromOperationName('unknown')
+ then: 'an invalid operation exception is thrown'
+ def thrown = thrown(InvalidOperationException)
+ assert thrown.message.contains('unknown is an invalid operation')
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
index fbf2c3d78..69d08e3de 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
@@ -20,76 +20,67 @@
package org.onap.cps.ncmp.api.impl.utils
-import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING
-
-import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService
-import org.onap.cps.spi.utils.CpsValidator
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
-import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
-import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
import spock.lang.Specification
class DmiServiceUrlBuilderSpec extends Specification {
- static YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName',
- 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'),'my-module-set-tag', 'my-alternate-id', 'my-data-producer-identifier')
-
- NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties()
-
- def mockCpsValidator = Mock(CpsValidator)
+ def objectUnderTest = new DmiServiceUrlBuilder()
- def objectUnderTest = new DmiServiceUrlBuilder(dmiProperties, mockCpsValidator)
-
- def setup() {
- dmiProperties.dmiBasePath = 'dmi'
+ def 'Build URI with (variable) path segments and parameters.'() {
+ given: 'the URI details are given to the builder'
+ objectUnderTest.pathSegment(segment1)
+ objectUnderTest.variablePathSegment('myVariableSegment','someValue')
+ objectUnderTest.pathSegment(segment2)
+ objectUnderTest.queryParameter('param1', paramValue1)
+ objectUnderTest.queryParameter('param2', paramValue2)
+ objectUnderTest.queryParameter('param3', null)
+ objectUnderTest.queryParameter('param4', '')
+ when: 'the URI (string) is build'
+ def result = objectUnderTest.build('myDmiServer', 'myBasePath')
+ then: 'the URI is correct (segments are in correct order) '
+ assert result == expectedUri
+ where: 'following URI details are used'
+ segment1 | segment2 | paramValue1 | paramValue2 || expectedUri
+ 'segment1' | 'segment2' | '123' | 'abc' || 'myDmiServer/myBasePath/v1/segment1/someValue/segment2?param1=123&param2=abc'
+ 'segment2' | 'segment1' | 'abc' | '123' || 'myDmiServer/myBasePath/v1/segment2/someValue/segment1?param1=abc&param2=123'
}
- def 'Create the dmi service url with #scenario.'() {
- given: 'uri variables'
- def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.datastoreName, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA), 'cmHandle')
- and: 'query params'
- def uriQueries = objectUnderTest.populateQueryParams(resourceId, 'optionsParamInQuery', topic)
- when: 'a dmi datastore service url is generated'
- def dmiServiceUrl = objectUnderTest.getDmiDatastoreUrl(uriQueries, uriVars)
- then: 'service url is generated as expected'
- assert dmiServiceUrl == expectedDmiServiceUrl
- where: 'the following parameters are used'
- scenario | topic | resourceId || expectedDmiServiceUrl
- 'With valid resourceId' | 'topicParamInQuery' | 'resourceId' || 'dmiServiceName/dmi/v1/ch/cmHandle/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=resourceId&options=optionsParamInQuery&topic=topicParamInQuery'
- 'With Empty resourceId' | 'topicParamInQuery' | '' || 'dmiServiceName/dmi/v1/ch/cmHandle/data/ds/ncmp-datastore:passthrough-running?options=optionsParamInQuery&topic=topicParamInQuery'
- 'With Empty dmi base path' | 'topicParamInQuery' | 'resourceId' || 'dmiServiceName/dmi/v1/ch/cmHandle/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=resourceId&options=optionsParamInQuery&topic=topicParamInQuery'
- 'With Empty topicParamInQuery' | '' | 'resourceId' || 'dmiServiceName/dmi/v1/ch/cmHandle/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=resourceId&options=optionsParamInQuery'
+ def 'Build URI with special characters in path segments.'() {
+ given: 'the path segments are given to the builder'
+ objectUnderTest.pathSegment(segment)
+ objectUnderTest.variablePathSegment('myVariableSegment', variableSegmentValue)
+ when: 'the URI (string) is build'
+ def result = objectUnderTest.build('myDmiServer', 'myBasePath')
+ then: 'Only teh characters that cause issues in path segments issues are encoded'
+ assert result == expectedUri
+ where: 'following variable path segments are used'
+ segment | variableSegmentValue || expectedUri
+ 'some/special?characters=are\\encoded' | 'my/variable/segment' || 'myDmiServer/myBasePath/v1/some%2Fspecial%3Fcharacters=are%5Cencoded/my%2Fvariable%2Fsegment'
+ 'but=some&are:not-!' | 'my&variable:segment' || 'myDmiServer/myBasePath/v1/but=some&are:not-!/my&variable:segment'
}
- def 'Populate dmi data store url #scenario.'() {
- given: 'uri variables are created'
- dmiProperties.dmiBasePath = dmiBasePath
- def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.datastoreName, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA), 'cmHandle')
- and: 'null query params'
- def uriQueries = objectUnderTest.populateQueryParams(null, null, null)
- when: 'a dmi datastore service url is generated'
- def dmiServiceUrl = objectUnderTest.getDmiDatastoreUrl(uriQueries, uriVars)
- then: 'the created dmi service url matches the expected'
- assert dmiServiceUrl == expectedDmiServiceUrl
- where: 'the following parameters are used'
- scenario | decription | dmiBasePath || expectedDmiServiceUrl
- 'with base path / ' | 'Invalid base path as it starts with /' | '/dmi' || 'dmiServiceName//dmi/v1/ch/cmHandle/data/ds/ncmp-datastore:passthrough-running'
- 'without base path / ' | 'Valid path as it does not starts with /' | 'dmi' || 'dmiServiceName/dmi/v1/ch/cmHandle/data/ds/ncmp-datastore:passthrough-running'
+ def 'Build URI with special characters in query parameters.'() {
+ given: 'the query parameter is given to the builder'
+ objectUnderTest.queryParameter(paramName, value)
+ when: 'the URI (string) is build'
+ def result = objectUnderTest.build('myDmiServer', 'myBasePath')
+ then: 'Only the characters (in the name and value) that cause issues in query parameters are encoded'
+ assert result == expectedUri
+ where: 'the following query parameters are used'
+ paramName | value || expectedUri
+ 'my&param' | 'some?special&characters=are\\encoded' || 'myDmiServer/myBasePath/v1?my%26param=some?special%26characters%3Dare%5Cencoded'
+ 'my-param' | 'but/some:are-not-!' || 'myDmiServer/myBasePath/v1?my-param=but/some:are-not-!'
}
- def 'Bath request Url creation.'() {
- given: 'the required path parameters'
- def batchRequestUriVariables = [dmiServiceName: 'some-service', dmiBasePath: 'testBase', cmHandleId: '123']
- and: 'the relevant query parameters'
- def batchRequestQueryParams = objectUnderTest.getDataOperationRequestQueryParams('some topic', 'some id')
- when: 'a URL is created'
- def result = objectUnderTest.getDataOperationRequestUrl(batchRequestQueryParams, batchRequestUriVariables)
- then: 'it is formed correctly'
- assert result.toString() == 'some-service/testBase/v1/data?topic=some topic&requestId=some id'
+ def 'Build URI with empty query parameters.'() {
+ when: 'the query parameter is given to the builder'
+ objectUnderTest.queryParameter('param', value)
+ and: 'the URI (string) is build'
+ def result = objectUnderTest.build('myDmiServer', 'myBasePath')
+ then: 'no parameter gets added'
+ assert result == 'myDmiServer/myBasePath/v1'
+ where: 'the following parameter values are used'
+ value << [ null, '', ' ' ]
}
- def 'Populate batch uri variables.'() {
- expect: 'Populate batch uri variables returns a map with given service name and base path from setup'
- assert objectUnderTest.populateDataOperationRequestUriVariables('some service') == [dmiServiceName: 'some service', dmiBasePath: 'dmi' ]
- }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/EventDateTimeFormatterSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/EventDateTimeFormatterSpec.groovy
new file mode 100644
index 000000000..c72eb9e4c
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/EventDateTimeFormatterSpec.groovy
@@ -0,0 +1,45 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils
+
+import spock.lang.Specification
+import java.time.Year
+
+class EventDateTimeFormatterSpec extends Specification {
+
+ def 'Get ISO formatted date and time.' () {
+ expect: 'iso formatted date and time starts with current year'
+ assert EventDateTimeFormatter.getCurrentIsoFormattedDateTime().startsWith(String.valueOf(Year.now()))
+ }
+
+ def 'Convert date time from string to OffsetDateTime type.'() {
+ when: 'date time as a string is converted to OffsetDateTime type'
+ def result = EventDateTimeFormatter.toIsoOffsetDateTime('2024-05-28T18:28:02.869+0100')
+ then: 'the result convert back back to a string is the same as the original timestamp (except the format of timezone offset)'
+ assert result.toString() == '2024-05-28T18:28:02.869+01:00'
+ }
+
+ def 'Convert blank string.' () {
+ expect: 'converting a blank string result in null'
+ assert EventDateTimeFormatter.toIsoOffsetDateTime(' ') == null
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy
index dc471e64f..54befb446 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidatorSpec.groovy
@@ -27,7 +27,6 @@ import spock.lang.Specification
class RestQueryParametersValidatorSpec extends Specification {
-
def 'CM Handle Query validation: empty query.'() {
given: 'a cm handle query'
def cmHandleQueryParameters = new CmHandleQueryServiceParameters()
@@ -62,13 +61,13 @@ class RestQueryParametersValidatorSpec extends Specification {
then: 'a data validation exception is thrown'
def thrown = thrown(DataValidationException)
and: 'the exception details contain the correct significant term '
- thrown.details.contains(expectedWordInDetails)
+ assert thrown.details.contains(expectedWordInDetails)
where:
scenario | conditionName | conditionParameters || expectedWordInDetails
'unknown condition name' | 'unknownCondition' | [['key': 'value']] || 'conditionName'
'no condition name' | '' | [['key': 'value']] || 'conditionName'
+ 'empty conditions' | 'validConditionName' | [] || 'conditionsParameters'
'empty properties' | 'validConditionName' | [[:]] || 'conditionsParameter'
- 'empty conditions' | 'validConditionName' | [[:]] || 'conditionsParameter'
'too many properties' | 'validConditionName' | [[key1: 'value1', key2: 'value2']] || 'conditionsParameter'
'empty key' | 'validConditionName' | [['': 'wrong']] || 'conditionsParameter'
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContextSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContextSpec.groovy
index b7fa44925..ee117160c 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContextSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContextSpec.groovy
@@ -1,3 +1,23 @@
+/*
+ * ============LICENSE_START========================================================
+ * Copyright (c) 2023-2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
package org.onap.cps.ncmp.api.impl.utils.context
import com.fasterxml.jackson.databind.ObjectMapper
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy
index 8df27bb62..653068592 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtilsSpec.groovy
@@ -20,16 +20,20 @@
package org.onap.cps.ncmp.api.impl.utils.data.operation
+import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
+import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.ADVISED
+import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.READY
+
import com.fasterxml.jackson.databind.ObjectMapper
import io.cloudevents.CloudEvent
import io.cloudevents.kafka.CloudEventDeserializer
import io.cloudevents.kafka.impl.KafkaHeaders
import org.apache.kafka.clients.consumer.KafkaConsumer
import org.onap.cps.events.EventsPublisher
-import org.onap.cps.ncmp.api.NcmpResponseStatus
+import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation
+import org.onap.cps.ncmp.api.impl.operations.OperationType
import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
-import org.onap.cps.ncmp.api.impl.inventory.CmHandleState
import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder
import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec
import org.onap.cps.ncmp.api.models.DataOperationRequest
@@ -37,15 +41,11 @@ import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent
import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
-import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
-
+import org.springframework.util.LinkedMultiValueMap
import java.time.Duration
-import java.util.concurrent.TimeoutException
-
-import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent
-@ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, ObjectMapper])
+@ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext])
class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
def static clientTopic = 'my-topic-name'
@@ -57,9 +57,6 @@ class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
@SpringBean
EventsPublisher eventPublisher = new EventsPublisher<CloudEvent>(legacyEventKafkaTemplate, cloudEventKafkaTemplate)
- @Autowired
- ObjectMapper objectMapper
-
def 'Process per data operation request with #serviceName.'() {
given: 'data operation request with 3 operations'
def dataOperationRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
@@ -90,6 +87,23 @@ class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
'dmi2' | 2 || 'operational-15' | 'ncmp-datastore:passthrough-operational' | ['ch4-dmi2']
}
+ def 'Process one data operation request with #serviceName and Module Set Tag set.'() {
+ given: 'data operation request'
+ def dataOperationRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
+ def dataOperationRequest = jsonObjectMapper.convertJsonString(dataOperationRequestJsonData, DataOperationRequest.class)
+ and: '1 known cm handles: ch1-dmi1'
+ def yangModelCmHandles = getYangModelCmHandlesForOneCmHandle()
+ when: 'data operation request is processed'
+ def operationsOutPerDmiServiceName = ResourceDataOperationRequestUtils.processPerDefinitionInDataOperationsRequest(clientTopic,'request-id', dataOperationRequest, yangModelCmHandles)
+ and: 'converted to a json node'
+ def dmiDataOperationRequestBody = operationsOutPerDmiServiceName['dmi1']
+ def cmHandlesInRequestBody = dmiDataOperationRequestBody[0].cmHandles
+ then: 'it contains the correct operation details'
+ assert cmHandlesInRequestBody.size() == 1
+ assert cmHandlesInRequestBody[0].id == 'ch1-dmi1'
+ assert cmHandlesInRequestBody[0].moduleSetTag == 'module-set-tag1'
+ }
+
def 'Process per data operation request with non-ready, non-existing cm handle and publish event to client specified topic'() {
given: 'consumer subscribing to client topic'
def cloudEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('test-1', CloudEventDeserializer))
@@ -118,34 +132,10 @@ class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
jsonObjectMapper.asJsonString(dataOperationResponseEvent.data.responses) == dataOperationResponseEventJson
}
- def 'Publish error response for entire data operations request when async task fails'() {
- given: 'consumer subscribing to client topic'
- def cloudEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties(consumerGroupId, CloudEventDeserializer))
- cloudEventKafkaConsumer.subscribe([clientTopic])
- and: 'data operation request having non-ready and non-existing cm handle ids'
- def dataOperationRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
- def dataOperationRequest = jsonObjectMapper.convertJsonString(dataOperationRequestJsonData, DataOperationRequest.class)
- when: 'an error occurs for the entire data operations request'
- ResourceDataOperationRequestUtils.handleAsyncTaskCompletionForDataOperationsRequest(clientTopic, 'request-id', dataOperationRequest, exceptionThrown)
- and: 'subscribed client specified topic is polled and first record is selected'
- def consumerRecordOut = cloudEventKafkaConsumer.poll(Duration.ofMillis(1500)).last()
- def dataOperationResponseEvent = toTargetEvent(consumerRecordOut.value(), DataOperationEvent.class)
- then: 'data operation response event response size is 3'
- dataOperationResponseEvent.data.responses.size() == 3
- and: 'all 3 have the expected error code'
- dataOperationResponseEvent.data.responses.each {
- assert it.statusCode == errorReportedToClientTopic.code
- }
- where:
- scenario | exceptionThrown | consumerGroupId || errorReportedToClientTopic
- 'task timed out' | new TimeoutException() | 'test-2' || NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING
- 'unspecified error' | new RuntimeException() | 'test-3' || NcmpResponseStatus.UNKNOWN_ERROR
- }
-
static def getYangModelCmHandles() {
def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
- def readyState = new CompositeStateBuilder().withCmHandleState(CmHandleState.READY).withLastUpdatedTimeNow().build()
- def advisedState = new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED).withLastUpdatedTimeNow().build()
+ def readyState = new CompositeStateBuilder().withCmHandleState(READY).withLastUpdatedTimeNow().build()
+ def advisedState = new CompositeStateBuilder().withCmHandleState(ADVISED).withLastUpdatedTimeNow().build()
return [new YangModelCmHandle(id: 'ch1-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
new YangModelCmHandle(id: 'ch2-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
new YangModelCmHandle(id: 'ch6-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
@@ -156,4 +146,19 @@ class ResourceDataOperationRequestUtilsSpec extends MessagingBaseSpec {
new YangModelCmHandle(id: 'non-ready-cm-handle', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: advisedState)
]
}
+
+ static def getYangModelCmHandlesForOneCmHandle() {
+ def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
+ def readyState = new CompositeStateBuilder().withCmHandleState(READY).withLastUpdatedTimeNow().build()
+ return [new YangModelCmHandle(id: 'ch1-dmi1', dmiServiceName: 'dmi1', moduleSetTag: 'module-set-tag1', dmiProperties: dmiProperties, compositeState: readyState)]
+ }
+
+ def mockAndPopulateErrorMap(errorReportedToClientTopic) {
+ def dmiDataOperation = DmiDataOperation.builder().operation(OperationType.fromOperationName('read'))
+ .operationId('some-op-id').datastore('ncmp-datastore:passthrough-operational')
+ .options('some-option').resourceIdentifier('some-resource-identifier').build()
+ def cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>()
+ cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperation, Map.of(errorReportedToClientTopic, ['some-cm-handle-id']))
+ return cmHandleIdsPerResponseCodesPerOperation
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy
index b0be29d93..162a9831c 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/AbstractModelLoaderSpec.groovy
@@ -32,7 +32,7 @@ import org.onap.cps.spi.CascadeDeleteAllowed
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.springframework.boot.SpringApplication
import org.slf4j.LoggerFactory
-import org.springframework.boot.context.event.ApplicationReadyEvent
+import org.springframework.boot.context.event.ApplicationStartedEvent
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import spock.lang.Specification
@@ -64,18 +64,18 @@ class AbstractModelLoaderSpec extends Specification {
applicationContext.close()
}
- def 'Application ready event'() {
- when: 'Application (ready) event is triggered'
- objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
+ def 'Application started event'() {
+ when: 'Application (started) event is triggered'
+ objectUnderTest.onApplicationEvent(Mock(ApplicationStartedEvent))
then: 'the onboard/upgrade method is executed'
1 * objectUnderTest.onboardOrUpgradeModel()
}
- def 'Application ready event with start up exception'() {
+ def 'Application started event with start up exception'() {
given: 'a start up exception is thrown doing model onboarding'
objectUnderTest.onboardOrUpgradeModel() >> { throw new NcmpStartUpException('test message','details are not logged') }
- when: 'Application (ready) event is triggered'
- objectUnderTest.onApplicationEvent(new ApplicationReadyEvent(new SpringApplication(), null, applicationContext, null))
+ when: 'Application (started) event is triggered'
+ objectUnderTest.onApplicationEvent(new ApplicationStartedEvent(new SpringApplication(), null, applicationContext, null))
then: 'the exception message is logged'
def logs = loggingListAppender.list.toString()
assert logs.contains('test message')
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy
index f3b405b11..3d490c861 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoaderSpec.groovy
@@ -31,7 +31,7 @@ import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException
import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.model.Dataspace
import org.slf4j.LoggerFactory
-import org.springframework.boot.context.event.ApplicationReadyEvent
+import org.springframework.boot.context.event.ApplicationStartedEvent
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import spock.lang.Specification
@@ -65,11 +65,11 @@ class CmDataSubscriptionModelLoaderSpec extends Specification {
applicationContext.close()
}
- def 'Onboard subscription model via application ready event.'() {
+ def 'Onboard subscription model via application started event.'() {
given: 'dataspace is ready for use'
mockCpsDataspaceService.getDataspace(NCMP_DATASPACE_NAME) >> new Dataspace('')
when: 'the application is ready'
- objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
+ objectUnderTest.onApplicationEvent(Mock(ApplicationStartedEvent))
then: 'the module service to create schema set is called once'
1 * mockCpsModuleService.createSchemaSet(NCMP_DATASPACE_NAME, 'cm-data-subscriptions', expectedYangResourcesToContentMap)
and: 'the admin service to create an anchor set is called once'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy
index cd659bb52..d43288688 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/init/InventoryModelLoaderSpec.groovy
@@ -20,23 +20,22 @@
package org.onap.cps.ncmp.init
-import org.onap.cps.api.CpsAnchorService
-
-import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
-import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
-
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
-import org.onap.cps.api.CpsDataspaceService
+import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDataService
+import org.onap.cps.api.CpsDataspaceService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.spi.model.Dataspace
import org.slf4j.LoggerFactory
-import org.springframework.boot.context.event.ApplicationReadyEvent
+import org.springframework.boot.context.event.ApplicationStartedEvent
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import spock.lang.Specification
+import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME
+import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
+
class InventoryModelLoaderSpec extends Specification {
def mockCpsAdminService = Mock(CpsDataspaceService)
@@ -68,8 +67,8 @@ class InventoryModelLoaderSpec extends Specification {
def 'Onboard subscription model via application ready event.'() {
given: 'dataspace is ready for use'
mockCpsAdminService.getDataspace(NCMP_DATASPACE_NAME) >> new Dataspace('')
- when: 'the application is ready'
- objectUnderTest.onApplicationEvent(Mock(ApplicationReadyEvent))
+ when: 'the application is started'
+ objectUnderTest.onApplicationEvent(Mock(ApplicationStartedEvent))
then: 'the module service is used to create the new schema set from the correct resource'
1 * mockCpsModuleService.createSchemaSet(NCMP_DATASPACE_NAME, 'dmi-registry-2024-02-23', expectedYangResourceToContentMap)
and: 'the admin service is used to update the anchor'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy
new file mode 100644
index 000000000..720a7e7e9
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/utils/AlternateIdMatcherSpec.groovy
@@ -0,0 +1,66 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.utils
+
+import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
+import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException
+import org.onap.cps.spi.exceptions.DataNodeNotFoundException
+import org.onap.cps.spi.model.DataNode
+import spock.lang.Specification
+
+class AlternateIdMatcherSpec extends Specification {
+
+ def mockInventoryPersistence = Mock(InventoryPersistence)
+ def objectUnderTest = new AlternateIdMatcher(mockInventoryPersistence)
+
+ def setup() {
+ given: 'cm handle in the registry with alternate id /a/b'
+ mockInventoryPersistence.getCmHandleDataNodeByAlternateId('/a/b') >> new DataNode()
+ and: 'no other cm handle'
+ mockInventoryPersistence.getCmHandleDataNodeByAlternateId(_) >> { throw new DataNodeNotFoundException('', '') }
+ }
+
+ def 'Finding longest alternate id matches.'() {
+ expect: 'querying for alternate id a matching result found'
+ assert objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(targetAlternateId, '/') != null
+ where: 'the following parameters are used'
+ scenario | targetAlternateId
+ 'exact match' | '/a/b'
+ 'parent match' | '/a/b/c'
+ 'grand parent match' | '/a/b/c/d'
+ 'trailing separator match' | '/a/b/'
+ }
+
+ def 'Attempt to find longest alternate id match without any matches.'() {
+ when: 'attempt to find alternateId'
+ objectUnderTest.getCmHandleDataNodeByLongestMatchAlternateId(targetAlternateId, '/')
+ then: 'no alternate id match found exception thrown'
+ def thrown = thrown(NoAlternateIdMatchFoundException)
+ and: 'the exception has the relevant details from the error response'
+ assert thrown.message == 'No matching cm handle found using alternate ids'
+ assert thrown.details == 'cannot find a datanode with alternate id ' + targetAlternateId
+ where: 'the following parameters are used'
+ scenario | targetAlternateId
+ 'no match for parent only' | '/a'
+ 'no match for other child' | '/a/c'
+ 'no match at all' | '/x/y'
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
index 574b49982..e35f47100 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -1,5 +1,5 @@
# ============LICENSE_START=======================================================
-# Copyright (C) 2021-2023 Nordix Foundation
+# Copyright (C) 2021-2024 Nordix Foundation
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -30,14 +30,19 @@ app:
async-m2m:
topic: ncmp-async-m2m
avc:
- subscription-topic: subscription
+ cm-subscription-ncmp-in: subscription
cm-events-topic: cm-events
- subscription-forward-topic-prefix: ${NCMP_FORWARD_CM_AVC_SUBSCRIPTION:ncmp-dmi-cm-avc-subscription-}
+ cm-subscription-dmi-in: ${CM_SUBSCRIPTION_DMI_IN_TOPIC:ncmp-dmi-cm-avc-subscription}
ncmp:
dmi:
httpclient:
- connectionTimeoutInSeconds: 180
+ data-services:
+ connectionTimeoutInSeconds: 123
+ maximumInMemorySizeInMegabytes: 7
+ model-services:
+ connectionTimeoutInSeconds: 456
+ maximumInMemorySizeInMegabytes: 8
auth:
username: some-user
password: some-password
diff --git a/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json b/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json
index 6b665495c..04d37b8bb 100644
--- a/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json
+++ b/cps-ncmp-service/src/test/resources/cmSubscription/cmNotificationSubscriptionNcmpInEvent.json
@@ -6,14 +6,14 @@
"targetFilter": ["ch1","ch2"],
"scopeFilter": {
"datastore": "ncmp-datastore:passthrough-operational",
- "xpath-filter": ["/x1/y1","x2/y2"]
+ "xpathFilter": ["/x1/y1","x2/y2"]
}
},
{
"targetFilter": ["ch3","ch4"],
"scopeFilter": {
"datastore": "ncmp-datastore:passthrough-operational",
- "xpath-filter": ["/x3/y3","x4/y4"]
+ "xpathFilter": ["/x3/y3","x4/y4"]
}
}
]