aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-application/src/main/resources/application.yml1
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp.yml2
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi-inventory.yml2
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi.yml2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java72
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java3
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy81
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy113
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy2
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy4
-rw-r--r--cps-ncmp-service/src/test/resources/application.yml9
-rw-r--r--cps-rest/docs/openapi/cpsData.yml11
-rw-r--r--cps-rest/docs/openapi/openapi.yml6
-rwxr-xr-xcps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java6
-rwxr-xr-xcps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy38
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/CpsDataService.java7
-rw-r--r--cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java7
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy22
-rw-r--r--docker-compose/docker-compose.yml13
-rw-r--r--docs/api/swagger/policy-executor/openapi.yaml64
-rw-r--r--docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json29
-rw-r--r--docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json25
-rw-r--r--docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json29
-rw-r--r--docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json29
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy12
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy6
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy5
-rw-r--r--policy-executor-stub/pom.xml19
-rw-r--r--policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java47
-rw-r--r--policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy82
-rw-r--r--postman-collections/CPS Environment.postman_environment.json24
-rw-r--r--postman-collections/DMI Stub.postman_collection.json161
33 files changed, 517 insertions, 418 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 6f0807113d..83494d6545 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -160,6 +160,7 @@ cps:
endpoint: ${ONAP_OTEL_EXPORTER_ENDPOINT:http://onap-otel-collector:4317}
protocol: ${ONAP_OTEL_EXPORTER_PROTOCOL:grpc}
enabled: ${ONAP_TRACING_ENABLED:false}
+ excluded-observation-names: ${ONAP_EXCLUDED_OBSERVATION_NAMES:tasks.scheduled.execution}
# Actuator
management:
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index d0b1f35eaa..6972463e3c 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -194,7 +194,7 @@ dataOperationForCmHandle:
tags:
- network-cm-proxy
summary: Execute a data operation for group of cm handle ids
- description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages. A maximum of 50 cm handles per operation is supported.
+ description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages. A maximum of 200 cm handles per operation is supported.
operationId: executeDataOperationForCmHandles
parameters:
- $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery'
diff --git a/cps-ncmp-rest/docs/openapi/openapi-inventory.yml b/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
index b794082875..d68cd8b141 100755
--- a/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
@@ -21,7 +21,7 @@ openapi: 3.0.3
info:
title: NCMP Inventory API
description: NCMP Inventory API
- version: "1.0"
+ version: "3.5.1"
servers:
- url: /ncmpInventory
components:
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index dd6d7c8baa..bf23e4af4e 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -22,7 +22,7 @@ openapi: 3.0.3
info:
title: NCMP to CPS Proxy API
description: NCMP to CPS Proxy API
- version: "1.0"
+ version: "3.5.1"
servers:
- url: /ncmp
components:
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java
index cff3187966..a6a82b7936 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java
@@ -26,7 +26,11 @@ 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 jakarta.annotation.PostConstruct;
import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -37,11 +41,14 @@ import org.springframework.http.server.observation.ServerRequestObservationConte
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
+/**
+ * Configuration class for setting up OpenTelemetry tracing in a Spring Boot application.
+ * This class provides beans for OTLP exporters (gRPC and HTTP), a Jaeger remote sampler,
+ * and customizes the ObservationRegistry to exclude certain endpoints from being observed.
+ */
@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;
@@ -51,9 +58,29 @@ public class OpenTelemetryConfig {
@Value("${cps.tracing.sampler.jaeger_remote.endpoint:http://onap-otel-collector:14250}")
private String jaegerRemoteSamplerUrl;
+ @Value("${cps.tracing.excluded-observation-names:tasks.scheduled.execution}")
+ private String excludedObservationNamesAsCsv;
+
+ private static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS = 30;
+
+ private List<String> excludedObservationNames;
+
/**
- * OTLP Exporter with Grpc exporter protocol.
- */
+ * Initializes the excludedObservationNames after the bean's properties have been set.
+ * This method is called by the Spring container during bean initialization.
+ */
+ @PostConstruct
+ public void init() {
+ excludedObservationNames = Arrays.stream(excludedObservationNamesAsCsv.split(","))
+ .map(String::trim)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Creates an OTLP Exporter with gRPC protocol.
+ *
+ * @return OtlpGrpcSpanExporter bean if tracing is enabled and the exporter protocol is gRPC
+ */
@Bean
@ConditionalOnExpression(
"${cps.tracing.enabled} && 'grpc'.equals('${cps.tracing.exporter.protocol}')")
@@ -62,7 +89,9 @@ public class OpenTelemetryConfig {
}
/**
- * OTLP Exporter with HTTP exporter protocol.
+ * Creates an OTLP Exporter with HTTP protocol.
+ *
+ * @return OtlpHttpSpanExporter bean if tracing is enabled and the exporter protocol is HTTP
*/
@Bean
@ConditionalOnExpression(
@@ -72,39 +101,40 @@ public class OpenTelemetryConfig {
}
/**
- * Jaeger Remote Sampler.
+ * Creates a Jaeger Remote Sampler.
+ *
+ * @return JaegerRemoteSampler bean if tracing is enabled
*/
@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();
+ .setEndpoint(jaegerRemoteSamplerUrl)
+ .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS))
+ .setInitialSampler(Sampler.alwaysOff())
+ .setServiceName(serviceId)
+ .build();
}
/**
- * Excluding /actuator/** endpoints.
- */
+ * Customizes the ObservationRegistry to exclude /actuator/** endpoints from being observed.
+ *
+ * @return ObservationRegistryCustomizer bean if tracing is enabled
+ */
@Bean
@ConditionalOnProperty("cps.tracing.enabled")
- ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() {
+ public ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() {
final PathMatcher pathMatcher = new AntPathMatcher("/");
return registry ->
- registry.observationConfig().observationPredicate(observationPredicate(pathMatcher));
+ registry.observationConfig().observationPredicate(observationPredicate(pathMatcher));
}
- /**
- * Excluding /actuator/** endpoints.
- */
- static ObservationPredicate observationPredicate(final PathMatcher pathMatcher) {
- return (name, context) -> {
+ private ObservationPredicate observationPredicate(final PathMatcher pathMatcher) {
+ return (observationName, context) -> {
if (context instanceof ServerRequestObservationContext observationContext) {
return !pathMatcher.match("/actuator/**", observationContext.getCarrier().getRequestURI());
} else {
- return true;
+ return !excludedObservationNames.contains(observationName);
}
};
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java
index c71109013a..6b5ed908b8 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.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.
@@ -216,7 +217,7 @@ public class CmSubscriptionPersistenceService {
cpsDataService.saveListElements(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME,
CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(
datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson,
- OffsetDateTime.now());
+ OffsetDateTime.now(), ContentType.JSON);
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
index cd1237b884..0ca2cd3407 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
@@ -162,7 +162,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv
Lists.partition(yangModelCmHandles, CMHANDLE_BATCH_SIZE)) {
final String cmHandlesJsonData = createCmHandlesJsonData(yangModelCmHandleBatch);
cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP);
+ NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP, ContentType.JSON);
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy
deleted file mode 100644
index 0f6906942f..0000000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2024 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.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/config/OpenTelemetryConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy
new file mode 100644
index 0000000000..cbff73113e
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/OpenTelemetryConfigSpec.groovy
@@ -0,0 +1,113 @@
+/*
+ * ============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.config
+
+import io.micrometer.observation.ObservationPredicate
+import io.micrometer.observation.ObservationRegistry
+import io.micrometer.observation.ObservationRegistry.ObservationConfig
+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.springframework.beans.factory.annotation.Value
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.server.observation.ServerRequestObservationContext
+import org.springframework.mock.web.MockHttpServletRequest
+import org.springframework.util.AntPathMatcher
+import spock.lang.Specification
+
+@SpringBootTest(classes = [OpenTelemetryConfig])
+class OpenTelemetryConfigSpec extends Specification {
+
+ def objectUnderTest
+
+ @Value('${cps.tracing.exporter.endpoint}')
+ def tracingExporterEndpointUrl
+
+ @Value('${cps.tracing.sampler.jaeger_remote.endpoint}')
+ def jaegerRemoteSamplerUrl
+
+ def setup() {
+ objectUnderTest = new OpenTelemetryConfig(
+ serviceId: 'sample-app',
+ tracingExporterEndpointUrl: tracingExporterEndpointUrl,
+ jaegerRemoteSamplerUrl: jaegerRemoteSamplerUrl,
+ excludedObservationNames: ['excluded-task-name'])
+ }
+
+ def 'OTLP exporter creation with Grpc protocol'() {
+ when: 'an OTLP exporter is created'
+ def result = objectUnderTest.createOtlpExporterGrpc()
+ then: 'expected an instance of OtlpGrpcSpanExporter'
+ assert result instanceof OtlpGrpcSpanExporter
+ }
+
+ def 'OTLP exporter creation with HTTP protocol'() {
+ when: 'an OTLP exporter is created'
+ def result = objectUnderTest.createOtlpExporterHttp()
+ then: 'an OTLP Exporter is created'
+ assert result instanceof OtlpHttpSpanExporter
+ and: 'the endpoint is correctly set'
+ assert result.builder.endpoint == 'http://exporter-test-url'
+ }
+
+ def 'Jaeger Remote Sampler Creation'() {
+ when: 'a Jaeger remote sampler is created'
+ def result = objectUnderTest.createJaegerRemoteSampler()
+ then: 'a Jaeger remote sampler is created'
+ assert result instanceof JaegerRemoteSampler
+ and: 'the sampler type is correct'
+ assert result.delegate.type == 'remoteSampling'
+ and: 'the sampler endpoint is correctly set'
+ assert result.delegate.url.toString().startsWith('http://jaeger-remote-test-url')
+ }
+
+ def 'Skipping actuator endpoints'() {
+ given: 'a mocked observation registry and config'
+ def observationRegistry = Mock(ObservationRegistry.class)
+ def observationConfig = Mock(ObservationConfig.class)
+ observationRegistry.observationConfig() >> observationConfig
+ when: 'an observation registry customizer is created and applied'
+ def result = objectUnderTest.skipActuatorEndpointsFromObservation()
+ result.customize(observationRegistry)
+ then: 'the observation predicate is set correctly'
+ 1 * observationConfig.observationPredicate(_) >> { ObservationPredicate observationPredicate ->
+ def mockedHttpServletRequest = new MockHttpServletRequest(_ as String, requestUrl)
+ def serverRequestObservationContext = new ServerRequestObservationContext(mockedHttpServletRequest, null)
+ and: 'expected predicate for endpoint'
+ assert observationPredicate.test('some-name', serverRequestObservationContext) == expectedPredicate
+ }
+ where: 'the following parameters are used'
+ scenario | requestUrl || expectedPredicate
+ 'an actuator' | '/actuator' || false
+ 'a non actuator' | '/some-api' || true
+ }
+
+ def 'Observation predicate is configured to filter out excluded tasks by name'() {
+ when: 'a path matcher and observation predicate'
+ def observationPredicate = objectUnderTest.observationPredicate(new AntPathMatcher('/'))
+ then: 'a task name is provided'
+ assert observationPredicate.test(taskName, null) == expectedPredicate
+ where: 'the following parameters are used'
+ taskName || expectedPredicate
+ 'excluded-task-name' || false
+ 'non-excluded-task-name' || true
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy
index 354e2af937..2b91065592 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy
@@ -142,7 +142,7 @@ class CmSubscriptionPersistenceServiceSpec extends Specification {
'NCMP-Admin',
'cm-data-subscriptions',
parentNodeXpath.formatted(datastoreName, 'ch-1'),
- objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']), _)
+ objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']), _, ContentType.JSON)
where:
scenario | datastoreType || datastoreName
'passthrough_running' | PASSTHROUGH_RUNNING || 'ncmp-datastore:passthrough-running'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
index e098fb81d7..e60bacbdc5 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
@@ -232,7 +232,7 @@ class InventoryPersistenceImplSpec extends Specification {
objectUnderTest.saveCmHandle(yangModelCmHandle)
then: 'the data service method to save list elements is called once'
1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- _,null) >> {
+ _,null, ContentType.JSON) >> {
args -> {
assert args[3].startsWith('{"cm-handles":[{"id":"cmhandle","additional-properties":[],"public-properties":[]}]}')
}
@@ -247,7 +247,7 @@ class InventoryPersistenceImplSpec extends Specification {
objectUnderTest.saveCmHandleBatch([yangModelCmHandle1, yangModelCmHandle2])
then: 'CPS Data Service persists both cm handles as a batch'
1 * mockCpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- NCMP_DMI_REGISTRY_PARENT, _,null) >> {
+ NCMP_DMI_REGISTRY_PARENT, _,null, ContentType.JSON) >> {
args -> {
def jsonData = (args[3] as String)
jsonData.contains('cmhandle1')
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
index f0790dda4b..759de834ab 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -16,6 +16,15 @@
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=========================================================
+cps:
+ tracing:
+ sampler:
+ jaeger_remote:
+ endpoint: http://jaeger-Remote-test-url
+ exporter:
+ endpoint: http://exporter-test-url
+ enabled: true
+
spring:
kafka:
producer:
diff --git a/cps-rest/docs/openapi/cpsData.yml b/cps-rest/docs/openapi/cpsData.yml
index 1e70ef60c8..4418a3b9b7 100644
--- a/cps-rest/docs/openapi/cpsData.yml
+++ b/cps-rest/docs/openapi/cpsData.yml
@@ -32,15 +32,24 @@ listElementByDataspaceAndAnchor:
- $ref: 'components.yml#/components/parameters/anchorNameInPath'
- $ref: 'components.yml#/components/parameters/requiredXpathInQuery'
- $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
+ - $ref: 'components.yml#/components/parameters/contentTypeInHeader'
requestBody:
required: true
content:
application/json:
schema:
- type: object
+ type: string
examples:
dataSample:
$ref: 'components.yml#/components/examples/dataSample'
+ application/xml:
+ schema:
+ type: object
+ xml:
+ name: stores
+ examples:
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSampleXml'
responses:
'201':
$ref: 'components.yml#/components/responses/Created'
diff --git a/cps-rest/docs/openapi/openapi.yml b/cps-rest/docs/openapi/openapi.yml
index b4e0b70408..ab841b280b 100644
--- a/cps-rest/docs/openapi/openapi.yml
+++ b/cps-rest/docs/openapi/openapi.yml
@@ -23,7 +23,7 @@ openapi: 3.0.3
info:
title: ONAP Open API v3 Configuration Persistence Service
description: Configuration Persistence Service is a Model Driven Generic Database
- version: "1.0.0"
+ version: "3.5.1"
contact:
name: ONAP
url: "https://onap.readthedocs.io"
@@ -31,10 +31,6 @@ info:
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0"
- x-planned-retirement-date: "202212"
- x-component: "Modeling"
- x-logo:
- url: "cps_logo.png"
servers:
- url: /cps/api
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
index 6100b7edd9..6015e0e3ae 100755
--- a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java
@@ -95,9 +95,11 @@ public class DataRestController implements CpsDataApi {
@Override
public ResponseEntity<String> addListElements(final String apiVersion, final String dataspaceName,
final String anchorName, final String parentNodeXpath,
- final Object jsonData, final String observedTimestamp) {
+ final String contentTypeInHeader, final String nodeData,
+ final String observedTimestamp) {
+ final ContentType contentType = getContentTypeFromHeader(contentTypeInHeader);
cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath,
- jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp));
+ nodeData, toOffsetDateTime(observedTimestamp), contentType);
return new ResponseEntity<>(HttpStatus.CREATED);
}
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
index 205d85dc26..d8ab0d10eb 100755
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
@@ -192,22 +192,25 @@ class DataRestControllerSpec extends Specification {
def rootNodeXpath = '/'
when: 'list-node endpoint is invoked with post (create) operation'
def postRequestBuilder = post("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes")
- .contentType(MediaType.APPLICATION_JSON)
+ .contentType(contentType)
.param('xpath', rootNodeXpath )
- .content(requestBodyJson)
+ .content(requestBody)
if (observedTimestamp != null)
postRequestBuilder.param('observed-timestamp', observedTimestamp)
def response = mvc.perform(postRequestBuilder).andReturn().response
then: 'a created response is returned'
response.status == expectedHttpStatus.value()
then: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, rootNodeXpath, expectedJsonData,
- { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
+ expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, rootNodeXpath, expectedData,
+ { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, expectedContentType)
where:
- scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
- 'with observed-timestamp' | '2021-03-03T23:59:59.999-0400' || 1 | HttpStatus.CREATED
- 'without observed-timestamp' | null || 1 | HttpStatus.CREATED
- 'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST
+ scenario | observedTimestamp | contentType | requestBody || expectedApiCount | expectedHttpStatus | expectedData | expectedContentType
+ 'Content type JSON with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON without observed-timestamp' | null | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_JSON | requestBodyJson || 0 | HttpStatus.BAD_REQUEST | expectedJsonData | ContentType.JSON
+ 'Content type XML with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML without observed-timestamp' | null | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_XML | requestBodyXml || 0 | HttpStatus.BAD_REQUEST | expectedXmlData | ContentType.XML
}
def 'Save list elements #scenario.'() {
@@ -215,22 +218,25 @@ class DataRestControllerSpec extends Specification {
def parentNodeXpath = 'parent node xpath'
when: 'list-node endpoint is invoked with post (create) operation'
def postRequestBuilder = post("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes")
- .contentType(MediaType.APPLICATION_JSON)
+ .contentType(contentType)
.param('xpath', parentNodeXpath)
- .content(requestBodyJson)
+ .content(requestBody)
if (observedTimestamp != null)
postRequestBuilder.param('observed-timestamp', observedTimestamp)
def response = mvc.perform(postRequestBuilder).andReturn().response
then: 'a created response is returned'
response.status == expectedHttpStatus.value()
then: 'the java API was called with the correct parameters'
- expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, expectedJsonData,
- { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
+ expectedApiCount * mockCpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, expectedData,
+ { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, expectedContentType)
where:
- scenario | observedTimestamp || expectedApiCount | expectedHttpStatus
- 'with observed-timestamp' | '2021-03-03T23:59:59.999-0400' || 1 | HttpStatus.CREATED
- 'without observed-timestamp' | null || 1 | HttpStatus.CREATED
- 'with invalid observed-timestamp' | 'invalid' || 0 | HttpStatus.BAD_REQUEST
+ scenario | observedTimestamp | contentType | requestBody || expectedApiCount | expectedHttpStatus | expectedData | expectedContentType
+ 'Content type JSON with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON without observed-timestamp' | null | MediaType.APPLICATION_JSON | requestBodyJson || 1 | HttpStatus.CREATED | expectedJsonData | ContentType.JSON
+ 'Content type JSON with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_JSON | requestBodyJson || 0 | HttpStatus.BAD_REQUEST | expectedJsonData | ContentType.JSON
+ 'Content type XML with observed-timestamp' | '2021-03-03T23:59:59.999-0400' | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML without observed-timestamp' | null | MediaType.APPLICATION_XML | requestBodyXml || 1 | HttpStatus.CREATED | expectedXmlData | ContentType.XML
+ 'Content type XML with invalid observed-timestamp' | 'invalid' | MediaType.APPLICATION_XML | requestBodyXml || 0 | HttpStatus.BAD_REQUEST | expectedXmlData | ContentType.XML
}
def 'Get data node with leaves'() {
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
index cfa5f2de20..68e1880d77 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
@@ -93,11 +93,12 @@ public interface CpsDataService {
* @param dataspaceName dataspace name
* @param anchorName anchor name
* @param parentNodeXpath parent node xpath
- * @param jsonData json data representing list element(s)
+ * @param nodeData node data representing list element(s)
* @param observedTimestamp observedTimestamp
+ * @param contentType JSON/XML content type
*/
- void saveListElements(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData,
- OffsetDateTime observedTimestamp);
+ void saveListElements(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
+ OffsetDateTime observedTimestamp, ContentType contentType);
/**
* Retrieves all the datanodes by XPath for given dataspace and anchor.
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
index c65bc5e0e9..165d62cede 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
@@ -119,12 +119,13 @@ public class CpsDataServiceImpl implements CpsDataService {
@Override
@Timed(value = "cps.data.service.list.element.save",
description = "Time taken to save list elements")
- public void saveListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final String jsonData, final OffsetDateTime observedTimestamp) {
+ public void saveListElements(final String dataspaceName, final String anchorName,
+ final String parentNodeXpath, final String nodeData,
+ final OffsetDateTime observedTimestamp, final ContentType contentType) {
cpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
final Collection<DataNode> listElementDataNodeCollection =
- buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
+ buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
if (isRootNodeXpath(parentNodeXpath)) {
cpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName, listElementDataNodeCollection);
} else {
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
index 0d3c5aa834..a296716b59 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
@@ -141,7 +141,7 @@ class CpsDataServiceImplSpec extends Specification {
setupSchemaSetMocks('bookstore.yang')
when: 'save data method is invoked with list element json data'
def jsonData = '{"bookstore-address":[{"bookstore-name":"Easons","address":"Dublin,Ireland","postal-code":"D02HA21"}]}'
- objectUnderTest.saveListElements(dataspaceName, anchorName, '/', jsonData, observedTimestamp)
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/', jsonData, observedTimestamp, ContentType.JSON)
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.storeDataNodes(dataspaceName, anchorName,
{ dataNodeCollection ->
@@ -169,12 +169,11 @@ class CpsDataServiceImplSpec extends Specification {
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
}
- def 'Saving list element data fragment under existing node.'() {
+ def 'Saving list element data fragment under existing JSON/XML node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
- when: 'save data method is invoked with list element json data'
- def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
- objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
+ when: 'save data method is invoked with list element data'
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', data, observedTimestamp, contentType)
then: 'the persistence service method is invoked with correct parameters'
1 * mockCpsDataPersistenceService.addListElements(dataspaceName, anchorName, '/test-tree',
{ dataNodeCollection ->
@@ -187,16 +186,23 @@ class CpsDataServiceImplSpec extends Specification {
)
and: 'the CpsValidator is called on the dataspaceName and AnchorName'
1 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
+ where:
+ data | contentType
+ '{"branch": [{"name": "A"}, {"name": "B"}]}' | ContentType.JSON
+ '<test-tree xmlns="org:onap:cps:test:test-tree"><branch><name>A</name></branch><branch><name>B</name></branch></test-tree>' | ContentType.XML
}
- def 'Saving empty list element data fragment.'() {
+ def 'Saving empty list element data fragment for JSON/XML data.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
when: 'save data method is invoked with an empty list'
- def jsonData = '{"branch": []}'
- objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', data, observedTimestamp, contentType)
then: 'invalid data exception is thrown'
thrown(DataValidationException)
+ where:
+ data | contentType
+ '{"branch": []}' | ContentType.JSON
+ '<test-tree><branch></branch></test-tree>' | ContentType.XML
}
def 'Get all data nodes #scenario.'() {
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
index f537cfe43c..5af325a50b 100644
--- a/docker-compose/docker-compose.yml
+++ b/docker-compose/docker-compose.yml
@@ -20,6 +20,7 @@ services:
### docker-compose --profile dmi-service up -d -> run CPS services incl. dmi-plugin ###
### docker-compose --profile dmi-stub --profile monitoring up -d -> run CPS with stubbed dmi-plugin (for registration performance testing)
+ ### docker-compose --profile dmi-stub --profile tracing up -d -> run CPS with stubbed dmi-plugin (for open telemetry tracing testing make ONAP_TRACING_ENABLED "true" later "http://localhost:16686" can be accessed from browser)
### to disable notifications make notification.enabled to false & comment out kafka/zookeeper services ###
dbpostgresql:
@@ -54,6 +55,9 @@ services:
DMI_PASSWORD: ${DMI_PASSWORD:-cpsr0cks!}
KAFKA_BOOTSTRAP_SERVER: kafka:29092
notification.enabled: 'true'
+ ONAP_TRACING_ENABLED: 'false'
+ ONAP_OTEL_SAMPLER_JAEGER_REMOTE_ENDPOINT: http://jaeger-service:14250
+ ONAP_OTEL_EXPORTER_ENDPOINT: http://jaeger-service:4317
restart: unless-stopped
depends_on:
- dbpostgresql
@@ -193,5 +197,14 @@ services:
profiles:
- monitoring
+ jaeger-service:
+ container_name: jaeger-service
+ image: jaegertracing/all-in-one:latest
+ ports:
+ - 16686:16686
+ restart: unless-stopped
+ profiles:
+ - tracing
+
volumes:
grafana:
diff --git a/docs/api/swagger/policy-executor/openapi.yaml b/docs/api/swagger/policy-executor/openapi.yaml
index 98c5b1e79a..f80b998369 100644
--- a/docs/api/swagger/policy-executor/openapi.yaml
+++ b/docs/api/swagger/policy-executor/openapi.yaml
@@ -52,6 +52,8 @@ paths:
$ref: '#/components/schemas/PolicyExecutionResponse'
'400':
$ref: '#/components/responses/BadRequest'
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
@@ -75,48 +77,34 @@ components:
details:
type: string
- Payload:
+ Request:
type: object
properties:
- targetFdn:
+ schema:
type: string
- description: "The complete FDN (Fully Distinguished Name) for the element to be changed"
- example: "/Subnetwork=Ireland/MeContext=Athlone/ManagedElement=Athlone/SomeFunction=1/Cell=12"
- cmHandleId:
- type: string
- description: "The CM handle ID (optional)"
- example: "F811AF64F5146DFC545EC60B73DE948E"
- resourceIdentifier:
- type: string
- description: "The resource identifier (optional)"
- example: "ManagedElement=Athlone/SomeFunction=1/Cell=12"
- cmChangeRequest:
+ description: "The schema for the data in this request. The schema name should include the type of operation"
+ example: "org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0"
+ data:
type: object
- description: "The content of the change to be made"
- example: '{"Cell":[{"id":"Cell-id","attributes":{"administrativeState":"UNLOCKED"}}]}'
+ description: "The data related to the request. The format of the object is determined by the schema"
required:
- - targetFdn
- - cmChangeRequest
+ - schema
+ - data
PolicyExecutionRequest:
type: object
properties:
- payloadType:
- type: string
- description: "The type of payload. Currently supported options: 'cm_write'"
- example: "cm_write"
decisionType:
type: string
- description: "The type of decision. Currently supported options: 'permit'"
- example: "permit"
- payload:
+ description: "The type of decision. Currently supported options: 'allow'"
+ example: "allow"
+ requests:
type: array
items:
- $ref: '#/components/schemas/Payload'
+ $ref: '#/components/schemas/Request'
required:
- - payloadType
- decisionType
- - payload
+ - requests
PolicyExecutionResponse:
type: object
@@ -127,7 +115,7 @@ components:
example: "550e8400-e29b-41d4-a716-446655440000"
decision:
type: string
- description: "The decision outcome. Currently supported values: 'permit','deny'"
+ description: "The decision outcome. Currently supported values: 'allow','deny'"
example: "deny"
message:
type: string
@@ -139,16 +127,16 @@ components:
- message
responses:
- NotFound:
- description: "The specified resource was not found"
+ BadRequest:
+ description: "Bad request"
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
example:
- status: 404
- message: "Resource Not Found"
- details: "The requested resource is not found"
+ status: 400
+ message: "Bad Request"
+ details: "The provided request is not valid"
Unauthorized:
description: "Unauthorized request"
content:
@@ -169,16 +157,6 @@ components:
status: 403
message: "Request Forbidden"
details: "This request is forbidden"
- BadRequest:
- description: "Bad request"
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 400
- message: "Bad Request"
- details: "The provided request is not valid"
InternalServerError:
description: "Internal server error"
diff --git a/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json
new file mode 100644
index 0000000000..2ec9daf949
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0",
+ "$ref": "#/definitions/NcmpCreate",
+ "definitions": {
+ "NcmpCreate": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ },
+ "cmChangeRequest": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "targetIdentifier",
+ "cmChangeRequest"
+ ]
+ }
+ }
+}
diff --git a/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json
new file mode 100644
index 0000000000..5df0325e39
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-delete-schema:1.0.0",
+ "$ref": "#/definitions/NcmpDelete",
+ "definitions": {
+ "NcmpDelete": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "targetIdentifier"
+ ]
+ }
+ }
+}
diff --git a/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json
new file mode 100644
index 0000000000..e26c244c94
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-patch-schema:1.0.0",
+ "$ref": "#/definitions/NcmpPatch",
+ "definitions": {
+ "NcmpPatch": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ },
+ "cmChangeRequest": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "targetIdentifier",
+ "cmChangeRequest"
+ ]
+ }
+ }
+}
diff --git a/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json
new file mode 100644
index 0000000000..0a497e38c5
--- /dev/null
+++ b/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-update-schema:1.0.0",
+ "$ref": "#/definitions/NcmpUpdate",
+ "definitions": {
+ "NcmpUpdate": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cmHandleId": {
+ "type": "string"
+ },
+ "resourceIdentifier": {
+ "type": "string"
+ },
+ "targetIdentifier": {
+ "type": "string"
+ },
+ "cmChangeRequest": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "targetIdentifier",
+ "cmChangeRequest"
+ ]
+ }
+ }
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
index 869b72d8ce..b274324d71 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/DataServiceIntegrationSpec.groovy
@@ -224,7 +224,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'a new (multiple-data-tree:invoice) datanodes'
def json = '{"bookstore-address":[{"bookstore-name":"Easons","address":"Bangalore,India","postal-code":"560043"}]}'
when: 'the new list elements are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore-address[@bookstore-name="Easons"]', INCLUDE_ALL_DESCENDANTS)
and: 'there is one extra datanode'
@@ -239,7 +239,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two new (categories) data nodes'
def json = '{"categories": [ {"code":"new1"}, {"code":"new2" } ] }'
when: 'the new list elements are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -256,7 +256,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two (categories) data nodes, one new and one existing'
def json = '{"categories": [ {"code":"1"}, {"code":"new1"} ] }'
when: 'attempt to save the list element'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'an exception that (one cps paths is) already defined is thrown '
def exceptionThrown = thrown(AlreadyDefinedException)
exceptionThrown.alreadyDefinedObjectNames == ['/bookstore/categories[@code=\'1\']' ] as Set
@@ -270,7 +270,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'a new (categories) data nodes'
def json = '{"categories": [ {"code":"new1"} ] }'
and: 'the new list element is saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
when: 'the new element is deleted'
objectUnderTest.deleteListOrListElement(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', now)
then: 'the original number of data nodes is restored'
@@ -281,7 +281,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'two new (categories) data nodes in a single batch'
def json = '{"categories": [ {"code":"new1"}, {"code":"new2"} ] }'
when: 'the batches of new list element(s) are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'they can be retrieved by their xpaths'
assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new1"]', DIRECT_CHILDREN_ONLY).size() == 1
assert objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="new2"]', DIRECT_CHILDREN_ONLY).size() == 1
@@ -298,7 +298,7 @@ class DataServiceIntegrationSpec extends FunctionalSpecBase {
given: 'one existing and one new (categories) data nodes in a single batch'
def json = '{"categories": [ {"code":"new1"}, {"code":"1"} ] }'
when: 'the batches of new list element(s) are saved'
- objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now)
+ objectUnderTest.saveListElements(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', json, now, ContentType.JSON)
then: 'an already defined (batch) exception is thrown for the existing path'
def exceptionThrown = thrown(AlreadyDefinedException)
assert exceptionThrown.alreadyDefinedObjectNames == ['/bookstore/categories[@code=\'1\']' ] as Set
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
index 0ca200211a..f6ae27d129 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/base/NcmpPerfTestBase.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023-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.
@@ -22,6 +23,7 @@ package org.onap.cps.integration.performance.base
import org.onap.cps.integration.ResourceMeter
import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.utils.ContentType
import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME
import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR
@@ -79,7 +81,7 @@ class NcmpPerfTestBase extends PerfTestBase {
def batchSize = 100
for (def i = 0; i < TOTAL_CM_HANDLES; i += batchSize) {
def data = '{ "cm-handles": [' + (1..batchSize).collect { innerNodeJsonTemplate.replace('CMHANDLE_ID_HERE', (it + i).toString()) }.join(',') + ']}'
- cpsDataService.saveListElements(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '/dmi-registry', data, now)
+ cpsDataService.saveListElements(NCMP_PERFORMANCE_TEST_DATASPACE, REGISTRY_ANCHOR, '/dmi-registry', data, now, ContentType.JSON)
}
}
@@ -91,7 +93,7 @@ class NcmpPerfTestBase extends PerfTestBase {
innerNodeJsonTemplate.replace('CM_HANDLE_ID_HERE', (it + i).toString())
.replace('ALTERNATE_ID_AS_PATH', (it + i).toString())
}.join(',') + ']}'
- cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry', data, now)
+ cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, '/dmi-registry', data, now, ContentType.JSON)
}
}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
index 0195611740..9f6c78d5f5 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/performance/cps/WritePerfTest.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2023-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,8 @@
package org.onap.cps.integration.performance.cps
+import org.onap.cps.utils.ContentType
+
import java.time.OffsetDateTime
import org.onap.cps.integration.performance.base.CpsPerfTestBase
@@ -87,7 +90,7 @@ class WritePerfTest extends CpsPerfTestBase {
']}'
when: 'device nodes are added'
resourceMeter.start()
- cpsDataService.saveListElements(CPS_PERFORMANCE_TEST_DATASPACE, WRITE_TEST_ANCHOR, '/openroadm-devices', jsonListData, OffsetDateTime.now())
+ cpsDataService.saveListElements(CPS_PERFORMANCE_TEST_DATASPACE, WRITE_TEST_ANCHOR, '/openroadm-devices', jsonListData, OffsetDateTime.now(), ContentType.JSON)
resourceMeter.stop()
then: 'the operation takes less than #expectedDuration and memory used is within limit'
recordAndAssertResourceUsage("Saving list of ${totalNodes} devices",
diff --git a/policy-executor-stub/pom.xml b/policy-executor-stub/pom.xml
index f076a2c55c..afdc1c7d3b 100644
--- a/policy-executor-stub/pom.xml
+++ b/policy-executor-stub/pom.xml
@@ -22,6 +22,11 @@
</properties>
<dependencies>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <scope>provided</scope>
+ </dependency>
<!-- S P R I N G D E P E N D E N C I E S -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -163,6 +168,20 @@
</execution>
</executions>
</plugin>
+
+ <plugin>
+ <groupId>org.jsonschema2pojo</groupId>
+ <artifactId>jsonschema2pojo-maven-plugin</artifactId>
+ <configuration>
+ <useJakartaValidation>true</useJakartaValidation>
+ <sourceDirectory>${project.parent.basedir}/../docs/schemas/policy-executor</sourceDirectory>
+ <targetPackage>org.onap.cps.policyexecutor.stub.model</targetPackage>
+ <generateBuilders>true</generateBuilders>
+ <serializable>true</serializable>
+ <includeJsr303Annotations>true</includeJsr303Annotations>
+ </configuration>
+ </plugin>
+
</plugins>
</build>
diff --git a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
index a5ec6dcac9..5b3a9931a0 100644
--- a/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
+++ b/policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java
@@ -20,12 +20,17 @@
package org.onap.cps.policyexecutor.stub.controller;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import lombok.RequiredArgsConstructor;
import org.onap.cps.policyexecutor.stub.api.PolicyExecutorApi;
+import org.onap.cps.policyexecutor.stub.model.NcmpDelete;
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionRequest;
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionResponse;
+import org.onap.cps.policyexecutor.stub.model.Request;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
@@ -34,9 +39,13 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("${rest.api.policy-executor-base-path}")
+@RequiredArgsConstructor
public class PolicyExecutorStubController implements PolicyExecutorApi {
- private final Pattern errorCodePattern = Pattern.compile("(\\d{3})");
+ private final ObjectMapper objectMapper;
+
+ private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("(\\d{3})");
+
private int decisionCounter = 0;
@Override
@@ -44,31 +53,55 @@ public class PolicyExecutorStubController implements PolicyExecutorApi {
final String action,
final PolicyExecutionRequest policyExecutionRequest,
final String authorization) {
- if (policyExecutionRequest.getPayload().isEmpty()) {
+ if (policyExecutionRequest.getRequests().isEmpty()) {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+ final Request firstRequest = policyExecutionRequest.getRequests().iterator().next();
+ if ("ncmp-delete-schema:1.0.0".equals(firstRequest.getSchema())) {
+ return handleNcmpDeleteSchema(firstRequest);
+ }
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ private ResponseEntity<PolicyExecutionResponse> handleNcmpDeleteSchema(final Request request) {
+ final NcmpDelete ncmpDelete;
+ try {
+ ncmpDelete = objectMapper.readValue((String) request.getData(), NcmpDelete.class);
+ } catch (final JsonProcessingException e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
- final String firstTargetFdn = policyExecutionRequest.getPayload().iterator().next().getTargetFdn();
+ final String targetIdentifier = ncmpDelete.getTargetIdentifier();
+ if (targetIdentifier == null) {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
- final Matcher matcher = errorCodePattern.matcher(firstTargetFdn);
+ final Matcher matcher = ERROR_CODE_PATTERN.matcher(targetIdentifier);
if (matcher.find()) {
final int errorCode = Integer.parseInt(matcher.group(1));
return new ResponseEntity<>(HttpStatusCode.valueOf(errorCode));
}
+ return createPolicyExecutionResponse(targetIdentifier);
+ }
+
+ private ResponseEntity<PolicyExecutionResponse> createPolicyExecutionResponse(final String targetIdentifier) {
final String decisionId = String.valueOf(++decisionCounter);
final String decision;
final String message;
- if (firstTargetFdn.toLowerCase(Locale.getDefault()).contains("cps-is-great")) {
- decision = "permit";
+ if (targetIdentifier.toLowerCase(Locale.getDefault()).contains("cps-is-great")) {
+ decision = "allow";
message = "All good";
} else {
decision = "deny";
- message = "Only FDNs containing 'cps-is-great' are permitted";
+ message = "Only FDNs containing 'cps-is-great' are allowed";
}
+
final PolicyExecutionResponse policyExecutionResponse =
new PolicyExecutionResponse(decisionId, decision, message);
+
return ResponseEntity.ok(policyExecutionResponse);
}
+
}
diff --git a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
index 871db81ac8..efb12ac619 100644
--- a/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
+++ b/policy-executor-stub/src/test/groovy/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubControllerSpec.groovy
@@ -21,9 +21,10 @@
package org.onap.cps.policyexecutor.stub.controller
import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.policyexecutor.stub.model.Payload
+import org.onap.cps.policyexecutor.stub.model.NcmpDelete
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionRequest
import org.onap.cps.policyexecutor.stub.model.PolicyExecutionResponse
+import org.onap.cps.policyexecutor.stub.model.Request
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.HttpStatus
@@ -44,11 +45,9 @@ class PolicyExecutorStubControllerSpec extends Specification {
def url = '/policy-executor/api/v1/some-action'
- def 'Execute Policy Actions.'() {
- given: 'a policy execution request with target fdn: #targetFdn'
- def payload = new Payload(targetFdn, 'some change request')
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def 'Execute policy action.'() {
+ given: 'a policy execution request with target: #targetIdentifier'
+ def requestBody = createRequestBody(targetIdentifier)
when: 'request is posted'
def response = mockMvc.perform(post(url)
.header('Authorization','some string')
@@ -61,19 +60,17 @@ class PolicyExecutorStubControllerSpec extends Specification {
def responseBody = response.contentAsString
def policyExecutionResponse = objectMapper.readValue(responseBody, PolicyExecutionResponse.class)
assert policyExecutionResponse.decisionId == expectedDecsisonId
- assert policyExecutionResponse.decision == expectedDecsison
+ assert policyExecutionResponse.decision == expectedDecision
assert policyExecutionResponse.message == expectedMessage
where: 'the following targets are used'
- targetFdn || expectedDecsisonId | expectedDecsison | expectedMessage
- 'some fdn' || '1' | 'deny' | "Only FDNs containing 'cps-is-great' are permitted"
- 'fdn with cps-is-great' || '2' | 'permit' | "All good"
+ targetIdentifier || expectedDecsisonId | expectedDecision | expectedMessage
+ 'some fdn' || '1' | 'deny' | "Only FDNs containing 'cps-is-great' are allowed"
+ 'fdn with cps-is-great' || '2' | 'allow' | 'All good'
}
- def 'Execute Policy Action with a HTTP Error Code.'() {
+ def 'Execute policy action with a HTTP error code.'() {
given: 'a policy execution request with a target fdn with a 3-digit error code'
- def payload = new Payload('fdn with error code 418', 'some change request')
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def requestBody = createRequestBody('target with error code 418')
when: 'request is posted'
def response = mockMvc.perform(post(url)
.header('Authorization','some string')
@@ -84,11 +81,9 @@ class PolicyExecutorStubControllerSpec extends Specification {
assert response.status == 418
}
- def 'Execute Policy Action without Authorization Header.'() {
+ def 'Execute policy action without authorization header.'() {
given: 'a valid policy execution request'
- def payload = new Payload('some fdn', 'some change request')
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [payload])
- def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
+ def requestBody = createRequestBody('some target')
when: 'request is posted without authorization header'
def response = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
@@ -98,9 +93,9 @@ class PolicyExecutorStubControllerSpec extends Specification {
assert response.status == HttpStatus.OK.value()
}
- def 'Execute Policy Action with Empty Payload.'() {
- given: 'a policy execution request with empty payload list'
- def policyExecutionRequest = new PolicyExecutionRequest('some payload type','some decision type', [])
+ def 'Execute policy action with no requests.'() {
+ given: 'a policy execution request'
+ def policyExecutionRequest = new PolicyExecutionRequest('some decision type', [])
def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
when: 'request is posted'
def response = mockMvc.perform(post(url)
@@ -112,26 +107,49 @@ class PolicyExecutorStubControllerSpec extends Specification {
assert response.status == HttpStatus.BAD_REQUEST.value()
}
- def 'Execute Policy Action without other required attributes.'() {
- given: 'a policy execution request with payloadType=#payloadType, decisionType=decisionType, targetFdn=#targetFdn, changeRequest=#changeRequest'
- def payload = new Payload(targetFdn, changeRequest)
- def policyExecutionRequest = new PolicyExecutionRequest(payloadType, decisionType, [payload])
+ def 'Execute policy action with invalid json for request data.'() {
+ given: 'a policy execution request'
+ def request = new Request('ncmp-delete-schema:1.0.0', 'invalid json')
+ def policyExecutionRequest = new PolicyExecutionRequest('some decision type', [request])
def requestBody = objectMapper.writeValueAsString(policyExecutionRequest)
when: 'request is posted'
def response = mockMvc.perform(post(url)
+ .header('Authorization','some string')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBody))
+ .andReturn().response
+ then: 'response status is Bad Request'
+ assert response.status == HttpStatus.BAD_REQUEST.value()
+ }
+
+ def 'Execute policy action with missing or invalid attributes.'() {
+ given: 'a policy execution request with decisionType=#decisionType, schema=#schema, targetIdentifier=#targetIdentifier'
+ def requestBody = createRequestBody(decisionType, schema, targetIdentifier)
+ when: 'request is posted'
+ def response = mockMvc.perform(post(url)
.header('Authorization','something')
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andReturn().response
then: 'response status as expected'
assert response.status == expectedStatus.value()
- where: 'following parameters are populated or not'
- payloadType | decisionType | targetFdn | changeRequest || expectedStatus
- 'something' | 'something' | 'something' | 'something' || HttpStatus.OK
- null | 'something' | 'something' | 'something' || HttpStatus.BAD_REQUEST
- 'something' | null | 'something' | 'something' || HttpStatus.BAD_REQUEST
- 'something' | 'something' | null | 'something' || HttpStatus.BAD_REQUEST
- 'something' | 'something' | 'something' | null || HttpStatus.BAD_REQUEST
+ where: 'following parameters are used'
+ decisionType | schema | targetIdentifier || expectedStatus
+ 'something' | 'ncmp-delete-schema:1.0.0' | 'something' || HttpStatus.OK
+ null | 'ncmp-delete-schema:1.0.0' | 'something' || HttpStatus.BAD_REQUEST
+ 'something' | 'other schema' | 'something' || HttpStatus.BAD_REQUEST
+ 'something' | 'ncmp-delete-schema:1.0.0' | null || HttpStatus.BAD_REQUEST
+ }
+
+ def createRequestBody(decisionType, schema, targetIdentifier) {
+ def ncmpDelete = new NcmpDelete(targetIdentifier: targetIdentifier)
+ def request = new Request(schema, objectMapper.writeValueAsString(ncmpDelete))
+ def policyExecutionRequest = new PolicyExecutionRequest(decisionType, [request])
+ return objectMapper.writeValueAsString(policyExecutionRequest)
+ }
+
+ def createRequestBody(targetIdentifier) {
+ return createRequestBody('some decision type', 'ncmp-delete-schema:1.0.0', targetIdentifier)
}
}
diff --git a/postman-collections/CPS Environment.postman_environment.json b/postman-collections/CPS Environment.postman_environment.json
index 84dcb642d7..7ee9c6779e 100644
--- a/postman-collections/CPS Environment.postman_environment.json
+++ b/postman-collections/CPS Environment.postman_environment.json
@@ -1,5 +1,5 @@
{
- "id": "e8e90dd2-20be-49be-813f-07db44c6a4c2",
+ "id": "fd705c16-c17e-434d-ad2e-ba040a7ed062",
"name": "CPS Environment",
"values": [
{
@@ -16,30 +16,18 @@
},
{
"key": "DMI_HOST",
- "value": "localhost",
+ "value": "ncmp-dmi-plugin-demo-and-csit-stub",
"type": "default",
"enabled": true
},
{
"key": "DMI_PORT",
- "value": "8784",
- "type": "default",
- "enabled": true
- },
- {
- "key": "SDNC_HOST",
- "value": "localhost",
- "type": "default",
- "enabled": true
- },
- {
- "key": "SDNC_PORT",
- "value": "8282",
+ "value": "8092",
"type": "default",
"enabled": true
}
],
"_postman_variable_scope": "environment",
- "_postman_exported_at": "2024-02-23T13:00:33.537Z",
- "_postman_exported_using": "Postman/10.23.4"
-} \ No newline at end of file
+ "_postman_exported_at": "2024-07-30T15:54:00.831Z",
+ "_postman_exported_using": "Postman/11.5.1"
+}
diff --git a/postman-collections/DMI Stub.postman_collection.json b/postman-collections/DMI Stub.postman_collection.json
deleted file mode 100644
index fe7250f7db..0000000000
--- a/postman-collections/DMI Stub.postman_collection.json
+++ /dev/null
@@ -1,161 +0,0 @@
-{
- "info": {
- "_postman_id": "4baf7902-0f1e-49a9-9c6a-f68f412240af",
- "name": "DMI Stub",
- "description": "A collection of the DMI Stub endpoints.",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
- "_exporter_id": "17907116"
- },
- "item": [
- {
- "name": "Execute a data operation for group of cm handle ids by supplied operation details",
- "request": {
- "method": "POST",
- "header": [],
- "body": {
- "mode": "raw",
- "raw": "{ \"operations\":\n [\n {\n \"resourceIdentifier\": \"some resource identifier\",\n \"datastore\": \"ncmp-datastore:passthrough-operational\",\n \"options\": \"some option\",\n \"operationId\": \"12\",\n \"cmHandles\": [\n {\n \"id\": \"cmHandle123\",\n \"cmHandleProperties\": {\n \"myProp\": \"some value\",\n \"otherProp\": \"other value\"\n }\n },\n {\n \"id\": \"cmHandle123\",\n \"cmHandleProperties\": {\n \"myProp\": \"some value\",\n \"otherProp\": \"other value\"\n }\n }\n ],\n \"operation\": \"read\"\n }\n ]\n}",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
- "url": {
- "raw": "http://{{DMI_HOST}}:{{DMI_PORT}}/dmi/v1/data?topic=ncmp-async-m2m&requestId=4753fc1f-7de2-449a-b306-a6204b5370b33",
- "protocol": "http",
- "host": [
- "{{DMI_HOST}}"
- ],
- "port": "{{DMI_PORT}}",
- "path": [
- "dmi",
- "v1",
- "data"
- ],
- "query": [
- {
- "key": "topic",
- "value": "ncmp-async-m2m"
- },
- {
- "key": "requestId",
- "value": "4753fc1f-7de2-449a-b306-a6204b5370b33"
- }
- ]
- }
- },
- "response": []
- },
- {
- "name": "Retrieve module resources for one or more modules",
- "request": {
- "method": "POST",
- "header": [],
- "body": {
- "mode": "raw",
- "raw": "{}",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
- "url": {
- "raw": "http://{{DMI_HOST}}:{{DMI_PORT}}/dmi/v1/ch/cm-bookStore/moduleResources",
- "protocol": "http",
- "host": [
- "{{DMI_HOST}}"
- ],
- "port": "{{DMI_PORT}}",
- "path": [
- "dmi",
- "v1",
- "ch",
- "cm-bookStore",
- "moduleResources"
- ]
- }
- },
- "response": []
- },
- {
- "name": "Get all modules for given cm handle",
- "protocolProfileBehavior": {
- "disabledSystemHeaders": {
- "accept": true
- }
- },
- "request": {
- "method": "POST",
- "header": [
- {
- "key": "Accept",
- "value": "application/json",
- "type": "text"
- }
- ],
- "body": {
- "mode": "raw",
- "raw": "{}",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
- "url": {
- "raw": "http://{{DMI_HOST}}:{{DMI_PORT}}/dmi/v1/ch/cm-bookStore/modules",
- "protocol": "http",
- "host": [
- "{{DMI_HOST}}"
- ],
- "port": "{{DMI_PORT}}",
- "path": [
- "dmi",
- "v1",
- "ch",
- "cm-bookStore",
- "modules"
- ]
- }
- },
- "response": []
- }
- ],
- "auth": {
- "type": "basic",
- "basic": [
- {
- "key": "password",
- "value": "cpsr0cks!",
- "type": "string"
- },
- {
- "key": "username",
- "value": "cpsuser",
- "type": "string"
- }
- ]
- },
- "event": [
- {
- "listen": "prerequest",
- "script": {
- "type": "text/javascript",
- "exec": [
- ""
- ]
- }
- },
- {
- "listen": "test",
- "script": {
- "type": "text/javascript",
- "exec": [
- ""
- ]
- }
- }
- ]
-} \ No newline at end of file