summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-application/src/main/resources/application.yml18
-rw-r--r--cps-bom/pom.xml7
-rw-r--r--cps-ncmp-events/pom.xml55
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/dmi-async-request-response-event-schema-v1.json87
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/ncmp-async-request-response-event-schema-v1.json187
-rw-r--r--cps-ncmp-events/src/main/resources/schemas/ncmp-event-schema-v1.json87
-rw-r--r--cps-ncmp-rest/docs/openapi/components.yaml46
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java83
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/CpsTaskExecutionException.java44
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/executor/CpsNcmpTaskExecutor.java60
-rw-r--r--cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java68
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy107
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy16
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy61
-rw-r--r--cps-ncmp-rest/src/test/resources/application.yml5
-rw-r--r--cps-ncmp-service/pom.xml38
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java71
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventConsumer.java55
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapper.java71
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventProducer.java46
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java18
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java13
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java7
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java3
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java1
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleState.java2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java36
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java138
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java (renamed from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java)77
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/LockReasonCategory.java25
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java27
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java56
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java4
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy201
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy126
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapperSpec.groovy67
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy34
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy12
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy5
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy66
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy11
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy (renamed from cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy)88
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/CmHandleStateSpec.groovy46
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy58
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy45
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy6
-rw-r--r--cps-ncmp-service/src/test/resources/dmiAsyncRequestResponseEvent.json30
-rw-r--r--cps-ncmp-service/src/test/resources/expectedStateModel.json12
-rw-r--r--cps-service/pom.xml330
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java31
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy2
-rw-r--r--cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy25
-rw-r--r--cps-service/src/test/resources/application.yml1
-rwxr-xr-xcsit/plans/cps/setup.sh16
-rw-r--r--csit/tests/ncmp-passthrough/ncmp-passthrough.robot9
-rwxr-xr-xdocker-compose/docker-compose.yml63
-rw-r--r--jacoco-report/pom.xml1
-rw-r--r--pom.xml1
60 files changed, 2202 insertions, 719 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 7ad22e977e..af886a1421 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -70,6 +70,22 @@ spring:
producer:
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
client-id: cps-core
+ consumer:
+ group-id: ${NCMP_CONSUMER_GROUP_ID:ncmp-group}
+ key-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
+ value-deserializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
+ properties:
+ spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer
+ spring.deserializer.value.delegate.class: org.springframework.kafka.support.serializer.JsonDeserializer
+ spring.json.value.default.type: org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
+ spring.json.use.type.headers: false
+
+ jackson:
+ default-property-inclusion: NON_NULL
+app:
+ ncmp:
+ async-m2m:
+ topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}
notification:
data-updated:
@@ -85,7 +101,7 @@ notification:
queue-capacity: 500
wait-for-tasks-to-complete-on-shutdown: true
thread-name-prefix: Async-
-
+ time-out-value-in-ms: 2000
springdoc:
swagger-ui:
diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml
index e46892695e..9b864b07fe 100644
--- a/cps-bom/pom.xml
+++ b/cps-bom/pom.xml
@@ -2,7 +2,7 @@
<!--
============LICENSE_START=======================================================
Copyright (C) 2020 Pantheon.tech
- Modifications Copyright (C) 2021 Nordix Foundation
+ Modifications Copyright (C) 2021-2022 Nordix Foundation
Modifications Copyright (C) 2021 Bell Canada.
================================================================================
Licensed under the Apache License, Version 2.0 (the "License");
@@ -101,6 +101,11 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
+ <artifactId>cps-ncmp-events</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
<artifactId>checkstyle</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/cps-ncmp-events/pom.xml b/cps-ncmp-events/pom.xml
new file mode 100644
index 0000000000..2d49a4c011
--- /dev/null
+++ b/cps-ncmp-events/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (c) 2022 Nordix Foundation.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-parent</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ <relativePath>../cps-parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>cps-ncmp-events</artifactId>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jsonschema2pojo</groupId>
+ <artifactId>jsonschema2pojo-maven-plugin</artifactId>
+ <configuration>
+ <sourceDirectory>${basedir}/src/main/resources/schemas</sourceDirectory>
+ <targetPackage>org.onap.cps.ncmp.event.model</targetPackage>
+ <generateBuilders>true</generateBuilders>
+ <serializable>true</serializable>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/cps-ncmp-events/src/main/resources/schemas/dmi-async-request-response-event-schema-v1.json b/cps-ncmp-events/src/main/resources/schemas/dmi-async-request-response-event-schema-v1.json
new file mode 100644
index 0000000000..528c063826
--- /dev/null
+++ b/cps-ncmp-events/src/main/resources/schemas/dmi-async-request-response-event-schema-v1.json
@@ -0,0 +1,87 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:dmi-async-request-response-event-schema:v1",
+ "$ref": "#/definitions/DmiAsyncRequestResponseEvent",
+ "definitions": {
+ "DmiAsyncRequestResponseEvent": {
+ "description": "The payload for NCMP async request response event.",
+ "type": "object",
+ "properties": {
+ "eventId": {
+ "description": "The unique id identifying the event generated by DMI.",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The request id passed by NCMP.",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The time of the event. The expected format is 'yyyy-MM-dd'T'HH:mm:ss.SSSZ'.",
+ "type": "string"
+ },
+ "eventTarget": {
+ "description": "The target of the event.",
+ "type": "string"
+ },
+ "eventType": {
+ "description": "The type of the event.",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The event schema for async request response events.",
+ "type": "string"
+ },
+ "eventSource": {
+ "description": "The source of the event.",
+ "type": "string"
+ },
+ "eventContent": {
+ "$ref": "#/definitions/Event-Content"
+ }
+ },
+ "required": [
+ "eventId",
+ "eventCorrelationId",
+ "eventTime",
+ "eventTarget",
+ "eventType",
+ "eventSchema",
+ "eventSource",
+ "eventContent"
+ ]
+ },
+ "Event-Content": {
+ "description": "The event content.",
+ "type": "object",
+ "properties": {
+ "response-data-schema": {
+ "description": "The schema of response data",
+ "type": "string"
+ },
+ "response-status": {
+ "description": "The status of the response.",
+ "type": "string"
+ },
+ "response-code": {
+ "description": "The code of the response.",
+ "type": "string"
+ },
+ "response-data": {
+ "description": "The data payload",
+ "type": "object",
+ "properties": {
+ "payload": {
+ "type": "object"
+ }
+ }
+ },
+ "required": [
+ "response-data-schema",
+ "response-status",
+ "response-code",
+ "response-data"
+ ]
+ }
+ }
+ }
+}
diff --git a/cps-ncmp-events/src/main/resources/schemas/ncmp-async-request-response-event-schema-v1.json b/cps-ncmp-events/src/main/resources/schemas/ncmp-async-request-response-event-schema-v1.json
new file mode 100644
index 0000000000..3fd15bd5d2
--- /dev/null
+++ b/cps-ncmp-events/src/main/resources/schemas/ncmp-async-request-response-event-schema-v1.json
@@ -0,0 +1,187 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.cps.ncmp.events:ncmp-async-request-response-event-schema:v1",
+ "$ref": "#/definitions/NcmpAsyncRequestResponseEvent",
+ "definitions": {
+ "NcmpAsyncRequestResponseEvent": {
+ "description": "The payload for CPS async request response event.",
+ "type": "object",
+ "properties": {
+ "eventId": {
+ "description": "The unique id identifying the event generated by DMI.",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The request id passed by NCMP.",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The time of the event. The expected format is 'yyyy-MM-dd'T'HH:mm:ss.SSSZ'.",
+ "type": "string"
+ },
+ "eventTarget": {
+ "description": "The target of the event.",
+ "type": "string"
+ },
+ "eventType": {
+ "description": "The type of the event.",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The event schema for async request response events.",
+ "type": "string"
+ },
+ "event": {
+ "$ref": "#/definitions/Event"
+ },
+ "forwardedEvent": {
+ "$ref": "#/definitions/Forwarded-Event"
+ }
+ },
+ "required": [
+ "eventId",
+ "eventCorrelationId",
+ "eventTime",
+ "eventTarget",
+ "eventType",
+ "eventSchema"
+ ]
+ },
+ "Forwarded-Event": {
+ "description": "The event content.",
+ "type": "object",
+ "properties": {
+ "eventId": {
+ "description": "The unique id identifying the event generated by DMI.",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The request id passed by NCMP.",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The time of the event. The expected format is 'yyyy-MM-dd'T'HH:mm:ss.SSSZ'.",
+ "type": "string"
+ },
+ "eventTarget": {
+ "description": "The target of the event.",
+ "type": "string"
+ },
+ "eventType": {
+ "description": "The type of the event.",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The event schema for async request response events.",
+ "type": "string"
+ },
+ "eventSource": {
+ "description": "The source of the event.",
+ "type": "string"
+ },
+ "response-data-schema": {
+ "description": "The received schema of response data",
+ "type": "string"
+ },
+ "response-status": {
+ "description": "The received status of the response.",
+ "type": "string"
+ },
+ "response-code": {
+ "description": "The received code of the response.",
+ "type": "string"
+ },
+ "forwardedEventData": {
+ "description": "The data payload",
+ "type": "object",
+ "properties": {
+ "forwardedEventPayload": {
+ "type": "object"
+ }
+ }
+ },
+ "required": [
+ "eventId",
+ "eventCorrelationId",
+ "eventTime",
+ "eventTarget",
+ "eventType",
+ "eventSchema",
+ "eventSource",
+ "response-data-schema",
+ "response-status",
+ "response-code",
+ "forwardedEventData"
+ ]
+ }
+ },
+ "Event": {
+ "description": "The event content.",
+ "type": "object",
+ "properties": {
+ "eventId": {
+ "description": "The unique id identifying the event generated by DMI",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The request id passed by NCMP.",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The time of the event. The expected format is 'yyyy-MM-dd'T'HH:mm:ss.SSSZ'.",
+ "type": "string"
+ },
+ "eventTarget": {
+ "description": "The target of the event.",
+ "type": "string"
+ },
+ "eventType": {
+ "description": "The type of the event.",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The event schema for async request response events.",
+ "type": "string"
+ },
+ "eventSource": {
+ "description": "The source of the event.",
+ "type": "string"
+ },
+ "response-data-schema": {
+ "description": "The received schema of response data",
+ "type": "string"
+ },
+ "response-status": {
+ "description": "The received status of the response.",
+ "type": "string"
+ },
+ "response-code": {
+ "description": "The received code of the response.",
+ "type": "string"
+ },
+ "response-data": {
+ "description": "The data payload",
+ "type": "object",
+ "properties": {
+ "payload": {
+ "type": "object"
+ }
+ }
+ },
+ "required": [
+ "eventId",
+ "eventCorrelationId",
+ "eventTarget",
+ "eventTime",
+ "eventType",
+ "eventSchema",
+ "eventSource",
+ "response-data-schema",
+ "response-status",
+ "response-code",
+ "event-data"
+ ]
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/cps-ncmp-events/src/main/resources/schemas/ncmp-event-schema-v1.json b/cps-ncmp-events/src/main/resources/schemas/ncmp-event-schema-v1.json
new file mode 100644
index 0000000000..84fc12edbd
--- /dev/null
+++ b/cps-ncmp-events/src/main/resources/schemas/ncmp-event-schema-v1.json
@@ -0,0 +1,87 @@
+{
+
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "urn:cps:org.onap.ncmp.cmhandle.lcm-event:v1",
+
+ "$ref": "#/definitions/NcmpEvent",
+
+ "definitions": {
+
+ "NcmpEvent": {
+ "description": "The payload for NCMP event.",
+ "type": "object",
+ "javaType" : "org.onap.ncmp.cmhandle.lcm.event.NcmpEvent",
+ "properties": {
+ "eventId": {
+ "description": "The unique id identifying the event for the specified source.",
+ "type": "string"
+ },
+ "eventCorrelationId": {
+ "description": "The id identifying the event for the specified source.",
+ "type": "string"
+ },
+ "eventTime": {
+ "description": "The timestamp when the data has been observed.",
+ "type": "string"
+ },
+ "eventSource": {
+ "description": "The source of the event.",
+ "type": "string",
+ "format": "uri"
+ },
+ "eventType": {
+ "description": "The type of the event.",
+ "type": "string"
+ },
+ "eventSchema": {
+ "description": "The schema, including its version, that this event adheres to.",
+ "type": "string",
+ "format": "uri"
+ },
+ "event": {
+ "$ref": "#/definitions/Event"
+ }
+ },
+ "required": [
+ "eventId",
+ "eventTime",
+ "eventSource",
+ "eventType",
+ "eventSchema",
+ "event"
+ ],
+ "additionalProperties": false
+ },
+
+ "Event": {
+ "description": "The Payload of an event.",
+ "type": "object",
+ "properties": {
+ "cmHandleId": {
+ "description": "cmHandle id",
+ "type": "string"
+ },
+ "operation": {
+ "description": "The name of the Operation that triggered this event.",
+ "type": "string",
+ "enum": ["CREATE", "UPDATE", "DELETE"]
+ },
+ "cmhandle-state": {
+ "description": "State of cmHandle.",
+ "type": "string",
+ "enum": ["ADVISED", "READY", "LOCKED"]
+ },
+ "cmhandle-properties": {
+ "description": "cmHandle properties as json object.",
+ "type": "object",
+ "existingJavaType": "java.util.List<java.util.Map<String,String>>",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "operation"
+ ],
+ "additionalProperties": false
+ }
+ }
+}
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 32d25e3951..5fe47e4b0f 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -209,6 +209,8 @@ components:
example: my-cm-handle1
publicCmHandleProperties:
$ref: '#/components/schemas/CmHandlePublicProperties'
+ state:
+ $ref: '#/components/schemas/RestOutputCmHandleState'
CmHandlePublicProperties:
type: array
items:
@@ -216,6 +218,50 @@ components:
additionalProperties:
type: string
example: Book Type
+ RestOutputCmHandleState:
+ type: object
+ properties:
+ cmHandleState:
+ type: string
+ example: ADVISED
+ lockReason:
+ $ref: '#/components/schemas/lock-reason'
+ lastUpdateTime:
+ type: string
+ example: 2022-12-31T20:30:40.000+0000
+ dataSyncEnabled:
+ type: boolean
+ example: false
+ dataSyncState:
+ $ref: '#/components/schemas/dataStores'
+
+ lock-reason:
+ type: object
+ properties:
+ reason:
+ type: string
+ example: LOCKED_OTHER
+ details:
+ type: string
+ example: locked due to module sync
+
+ dataStores:
+ type: object
+ properties:
+ operational:
+ $ref: '#/components/schemas/sync-state'
+ running:
+ $ref: '#/components/schemas/sync-state'
+
+ sync-state:
+ type: object
+ properties:
+ state:
+ type: string
+ example: NONE_REQUESTED
+ lastSyncTime:
+ type: string
+ example: 2022-12-31T20:30:40.000+0000
RestOutputCmHandlePublicProperties:
type: object
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
index ca7e258bc4..11517bcc9e 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
@@ -46,6 +46,8 @@ import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper;
import org.onap.cps.ncmp.rest.model.CmHandleProperties;
import org.onap.cps.ncmp.rest.model.CmHandleProperty;
import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
@@ -60,6 +62,7 @@ import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
import org.onap.cps.utils.CpsValidator;
import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -74,11 +77,14 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
private static final String NO_BODY = null;
private static final String NO_REQUEST_ID = null;
private static final String NO_TOPIC = null;
- public static final String ASYNC_REQUEST_ID = "requestId";
-
private final NetworkCmProxyDataService networkCmProxyDataService;
private final JsonObjectMapper jsonObjectMapper;
private final NcmpRestInputMapper ncmpRestInputMapper;
+ private final RestOutputCmHandleStateMapper restOutputCmHandleStateMapper;
+ private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
+
+ @Value("${notification.async.executor.time-out-value-in-ms:2000}")
+ private int timeOutInMilliSeconds;
/**
* Get resource data from operational datastore.
@@ -94,19 +100,21 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final @NotNull @Valid String resourceIdentifier,
final @Valid String optionsParamInQuery,
final @Valid String topicParamInQuery) {
- final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery);
- final Map<String, Object> asyncResponseData = asyncResponse.getBody();
+ if (isValidTopic(topicParamInQuery)) {
+ final String requestId = UUID.randomUUID().toString();
+ cpsNcmpTaskExecutor.executeTask(() ->
+ networkCmProxyDataService.getResourceDataOperationalForCmHandle(
+ cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery,
+ requestId
+ ), timeOutInMilliSeconds
+ );
+ return acknowledgeAsyncRequest(requestId);
+ }
- final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle,
- resourceIdentifier,
- optionsParamInQuery,
- asyncResponseData == null ? NO_TOPIC : topicParamInQuery,
- asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString());
+ final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(
+ cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
- if (asyncResponseData == null) {
- return ResponseEntity.ok(responseObject);
- }
- return ResponseEntity.ok(asyncResponse);
+ return ResponseEntity.ok(responseObject);
}
/**
@@ -123,19 +131,21 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
final @NotNull @Valid String resourceIdentifier,
final @Valid String optionsParamInQuery,
final @Valid String topicParamInQuery) {
- final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery);
- final Map<String, Object> asyncResponseData = asyncResponse.getBody();
+ if (isValidTopic(topicParamInQuery)) {
+ final String resourceDataRequestId = UUID.randomUUID().toString();
+ cpsNcmpTaskExecutor.executeTask(() ->
+ networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
+ cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery,
+ resourceDataRequestId
+ ), timeOutInMilliSeconds
+ );
+ return acknowledgeAsyncRequest(resourceDataRequestId);
+ }
- final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle,
- resourceIdentifier,
- optionsParamInQuery,
- asyncResponseData == null ? NO_TOPIC : topicParamInQuery,
- asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString());
+ final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
+ cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
- if (asyncResponseData == null) {
- return ResponseEntity.ok(responseObject);
- }
- return ResponseEntity.ok(asyncResponse);
+ return ResponseEntity.ok(responseObject);
}
@Override
@@ -312,21 +322,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleId());
cmHandlePublicProperties.add(ncmpServiceCmHandle.getPublicProperties());
restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties);
+ restOutputCmHandle.setState(restOutputCmHandleStateMapper.toRestOutputCmHandleState(
+ ncmpServiceCmHandle.getCompositeState()));
return restOutputCmHandle;
}
- private ResponseEntity<Map<String, Object>> populateAsyncResponse(final String topicParamInQuery) {
- final boolean processAsynchronously = hasTopicParameter(topicParamInQuery);
- final Map<String, Object> responseData;
- if (processAsynchronously) {
- responseData = getAsyncResponseData();
- } else {
- responseData = null;
- }
- return ResponseEntity.ok().body(responseData);
- }
-
- private static boolean hasTopicParameter(final String topicName) {
+ private static boolean isValidTopic(final String topicName) {
if (topicName == null) {
return false;
}
@@ -336,11 +337,11 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic");
}
- private Map<String, Object> getAsyncResponseData() {
- final Map<String, Object> asyncResponseData = new HashMap<>(1);
- final String resourceDataRequestId = UUID.randomUUID().toString();
- asyncResponseData.put(ASYNC_REQUEST_ID, resourceDataRequestId);
- return asyncResponseData;
+ private ResponseEntity<Object> acknowledgeAsyncRequest(final String requestId) {
+ final Map<String, Object> acknowledgeData = new HashMap<>(1);
+ acknowledgeData.put("requestId", requestId);
+ return ResponseEntity.ok(acknowledgeData);
}
}
+
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/CpsTaskExecutionException.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/CpsTaskExecutionException.java
new file mode 100644
index 0000000000..3e8929d2e3
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/CpsTaskExecutionException.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.exceptions;
+
+import lombok.Getter;
+
+public class CpsTaskExecutionException extends RuntimeException {
+
+ private static final long serialVersionUID = 1481520410918497454L;
+
+ @Getter
+ final String details;
+
+ /**
+ * Constructor.
+ *
+ * @param message the error message
+ * @param details the error details
+ * @param cause the cause of the exception
+ */
+ public CpsTaskExecutionException(final String message, final String details, final Throwable cause) {
+ super(message, cause);
+ this.details = details;
+ }
+
+} \ No newline at end of file
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/executor/CpsNcmpTaskExecutor.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/executor/CpsNcmpTaskExecutor.java
new file mode 100644
index 0000000000..93aa2858ca
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/executor/CpsNcmpTaskExecutor.java
@@ -0,0 +1,60 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.executor;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class CpsNcmpTaskExecutor {
+
+ /**
+ * Execute task asynchronously and publish response to supplied topic.
+ *
+ * @param taskSupplier functional method is get() task need to executed asynchronously
+ * @param timeOutInMillis the time out value in milliseconds
+ */
+ public void executeTask(final Supplier<Object> taskSupplier, final int timeOutInMillis) {
+ CompletableFuture.supplyAsync(taskSupplier::get)
+ .orTimeout(timeOutInMillis, MILLISECONDS)
+ .whenCompleteAsync(
+ (responseAsJson, throwable) -> {
+ handleTaskCompletion(throwable);
+ }
+ );
+ }
+
+ private void handleTaskCompletion(final Throwable throwable) {
+ if (throwable == null) {
+ log.info("Async task completed successfully.");
+ } else {
+ log.error("Async task failed. caused by : {}", throwable.getMessage());
+ }
+ }
+}
+
+
+
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java
new file mode 100644
index 0000000000..5f4b311186
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java
@@ -0,0 +1,68 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.mapper;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.rest.model.DataStores;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandleState;
+import org.onap.cps.ncmp.rest.model.SyncState;
+
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+ nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
+public interface RestOutputCmHandleStateMapper {
+
+ @Mapping(target = "dataSyncState", source = "dataStores", qualifiedByName = "dataStoreToDataSyncState")
+ @Mapping(target = "lockReason.reason", source = "lockReason.lockReasonCategory")
+ RestOutputCmHandleState toRestOutputCmHandleState(CompositeState compositeState);
+
+ /**
+ * Convert from CompositeState datastore to RestOutput Datastores.
+ *
+ * @param compositeStateDataStore Composite State data stores
+ * @return DataStores
+ */
+ @Named("dataStoreToDataSyncState")
+ static DataStores toDataStores(CompositeState.DataStores compositeStateDataStore) {
+
+ if (compositeStateDataStore == null) {
+ return null;
+ }
+
+ final DataStores dataStores = new DataStores();
+
+ if (compositeStateDataStore.getOperationalDataStore() != null) {
+ final SyncState operationalSyncState = new SyncState();
+ operationalSyncState.setState(compositeStateDataStore.getOperationalDataStore().getSyncState());
+ operationalSyncState.setLastSyncTime(compositeStateDataStore.getOperationalDataStore().getLastSyncTime());
+ dataStores.setOperational(operationalSyncState);
+ }
+
+
+ return dataStores;
+
+ }
+
+}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
index ba49321d85..60ea736d72 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -24,10 +24,21 @@
package org.onap.cps.ncmp.rest.controller
import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
import spock.lang.Shared
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Running
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
@@ -69,6 +80,12 @@ class NetworkCmProxyControllerSpec extends Specification {
@SpringBean
NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
+ @SpringBean
+ RestOutputCmHandleStateMapper restOutputCmHandleStateMapper = Mappers.getMapper(RestOutputCmHandleStateMapper)
+
+ @SpringBean
+ CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy()
+
@Value('${rest.api.ncmp-base-path}/v1')
def ncmpBasePathV1
@@ -78,6 +95,9 @@ class NetworkCmProxyControllerSpec extends Specification {
def NO_TOPIC = null
def NO_REQUEST_ID = null
+ def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
+
def 'Get Resource Data from pass-through operational.'() {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
@@ -103,34 +123,40 @@ class NetworkCmProxyControllerSpec extends Specification {
"?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
when: 'get data resource request is performed'
def response = mvc.perform(
- get(getUrl)
- .contentType(MediaType.APPLICATION_JSON)
- ).andReturn().response
- then: 'the NCMP data service is called with operational data for cm handle'
- expectedNumberOfMethodExecutions
- * mockNetworkCmProxyDataService."${expectedMethodName}"('testCmHandle',
- 'parent/child',
- '(a=1,b=2)',
- expectedTopicName,
- _)
- then: 'response status is expected'
- response.status == expectedHttpStatus
+ get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
+ then: 'task executor is called appropriate number of times'
+ expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, 2000)
+ and: 'response status is expected'
+ response.status == HttpStatus.OK.value()
where: 'the following parameters are used'
- scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedMethodName | expectedNumberOfMethodExecutions | expectedHttpStatus
- 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value()
- 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value()
- 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value()
- 'empty topic in url' | 'passthrough-operational' | '&topic=\"\"' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'missing topic in url' | 'passthrough-operational' | '&topic=' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'blank topic value in url' | 'passthrough-operational' | '&topic=\" \"' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value()
- 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value()
- 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value()
- 'empty topic in url' | 'passthrough-running' | '&topic=\"\"' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'missing topic in url' | 'passthrough-running' | '&topic=' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'blank topic value in url' | 'passthrough-running' | '&topic=\" \"' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
- 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value()
+ scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedNumberOfExecutorExecutions
+ 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 1
+ 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 0
+ 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 1
+ 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 1
+ 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 0
+ 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 1
+ }
+
+ def 'Fail to get Resource Data from #datastoreInUrl when #scenario.'() {
+ given: 'resource data url'
+ def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
+ when: 'get data resource request is performed'
+ def response = mvc.perform(
+ get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
+ then: 'abad request is returned'
+ response.status == HttpStatus.BAD_REQUEST.value()
+ where: 'the following parameters are used'
+ scenario | datastoreInUrl | topicQueryParam
+ 'empty topic in url' | 'passthrough-operational' | '&topic=\"\"'
+ 'missing topic in url' | 'passthrough-operational' | '&topic='
+ 'blank topic value in url' | 'passthrough-operational' | '&topic=\" \"'
+ 'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#'
+ 'empty topic in url' | 'passthrough-running' | '&topic=\"\"'
+ 'missing topic in url' | 'passthrough-running' | '&topic='
+ 'blank topic value in url' | 'passthrough-running' | '&topic=\" \"'
+ 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#'
}
def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() {
@@ -225,26 +251,24 @@ class NetworkCmProxyControllerSpec extends Specification {
response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}'
}
- def 'Get Cm Handle details by Cm Handle id.' () {
+ def 'Get Cm Handle details by Cm Handle id.'() {
given: 'an endpoint and a cm handle'
def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle"
and: 'an existing ncmp service cm handle'
- def cmHandleId = 'some-cm-handle'
- def dmiProperties = [ prop:'some DMI property' ]
- def publicProperties = [ "public prop":'some public property' ]
- def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties)
+ def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
+ lastUpdateTime: formattedDateAndTime.toString(),
+ dataStores: dataStores())
+ def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle', compositeState: compositeState)
and: 'the service method is invoked with the cm handle id'
1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle
when: 'the cm handle details api is invoked'
def response = mvc.perform(get(cmHandleDetailsEndpoint)).andReturn().response
then: 'the correct response is returned'
response.status == HttpStatus.OK.value()
- and: 'the response returns public properties and the correct properties'
- response.contentAsString.contains('publicCmHandleProperties')
- response.contentAsString.contains('public prop')
- response.contentAsString.contains('some public property')
- and: 'the content does not contain dmi properties'
- !response.contentAsString.contains("some DMI property")
+ and: 'the response returns the correct state and timestamp'
+ response.contentAsString.contains('some-cm-handle')
+ response.contentAsString.contains('ADVISED')
+ response.contentAsString.contains('2022-12-31T20:30:40.000+0000')
}
def 'Get Cm Handle public properties by Cm Handle id.' () {
@@ -348,5 +372,12 @@ class NetworkCmProxyControllerSpec extends Specification {
':passthrough-running' | 'passthrough-running'
}
+ def dataStores() {
+ DataStores.builder()
+ .operationalDataStore(Operational.builder()
+ .syncState('NONE_REQUESTED')
+ .lastSyncTime(formattedDateAndTime.toString()).build()).build()
+ }
+
}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
index 751fdcd8b9..45ed3d3070 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
@@ -29,6 +29,8 @@ import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper
+import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
import org.onap.cps.spi.exceptions.CpsException
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataValidationException
@@ -45,6 +47,7 @@ import spock.lang.Specification
import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP
import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY
+import static org.springframework.http.HttpStatus.BAD_GATEWAY
import static org.springframework.http.HttpStatus.BAD_REQUEST
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
import static org.springframework.http.HttpStatus.NOT_FOUND
@@ -61,11 +64,17 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
@SpringBean
- JsonObjectMapper jsonObjectMapper = Stub()
+ JsonObjectMapper stubbedJsonObjectMapper = Stub()
@SpringBean
NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
+ @SpringBean
+ RestOutputCmHandleStateMapper restOutputCmHandleStateMapper = Mappers.getMapper(RestOutputCmHandleStateMapper)
+
+ @SpringBean
+ CpsNcmpTaskExecutor stubbedCpsTaskExecutor = Stub()
+
@Value('${rest.api.ncmp-base-path}')
def basePathNcmp
@@ -113,10 +122,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
def 'Failing DMI Request - passthrough scenario'() {
given: 'failing DMI request'
- mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(*_) >> { throw new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400) }
+ setupTestException(new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400) , NCMP)
when: 'the DMI request is executed'
- def response = mvc.perform(get("$dataNodeBaseEndpointNcmp/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=stores:bookstore/categories=100"))
- .andReturn().response
+ def response = performTestRequest(NCMP)
then: 'NCMP service responds with 502 Bad Gateway status'
response.status == HttpStatus.BAD_GATEWAY.value()
and: 'the NCMP response also contains the original DMI response details'
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy
new file mode 100644
index 0000000000..22c9fe6056
--- /dev/null
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.mapper
+
+import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandleState
+import spock.lang.Specification
+
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
+class RestOutputCmHandleStateMapperTest extends Specification {
+
+ def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
+ def objectUnderTest = Mappers.getMapper(RestOutputCmHandleStateMapper)
+
+ def 'Composite State to Rest Output CmHandleState'() {
+ given: 'a composite state model'
+ def compositeState = new CompositeStateBuilder()
+ .withCmHandleState(CmHandleState.ADVISED)
+ .withLastUpdatedTime(formattedDateAndTime.toString())
+ .withLockReason(LockReasonCategory.LOCKED_MISBEHAVING, 'locked other details')
+ .withOperationalDataStores('SYNCHRONIZED', formattedDateAndTime).build()
+ compositeState.setDataSyncEnabled(false)
+ when: 'mapper is called'
+ def result = objectUnderTest.toRestOutputCmHandleState(compositeState)
+ then: 'result is of the correct type'
+ assert result.class == RestOutputCmHandleState.class
+ and: 'mapped result should have correct values'
+ assert !result.dataSyncEnabled
+ assert result.lastUpdateTime == formattedDateAndTime
+ assert result.lockReason.reason == 'LOCKED_MISBEHAVING'
+ assert result.lockReason.details == 'locked other details'
+ assert result.cmHandleState == CmHandleState.ADVISED.name()
+ assert result.dataSyncState.operational.getState() != null
+ }
+
+}
diff --git a/cps-ncmp-rest/src/test/resources/application.yml b/cps-ncmp-rest/src/test/resources/application.yml
index f2ca8c759b..0241696c5b 100644
--- a/cps-ncmp-rest/src/test/resources/application.yml
+++ b/cps-ncmp-rest/src/test/resources/application.yml
@@ -21,3 +21,8 @@ rest:
api:
ncmp-base-path: /ncmp
ncmp-inventory-base-path: /ncmpInventory
+
+notification:
+ async:
+ executor:
+ time-out-value-in-ms: 2000 \ No newline at end of file
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index 573c76e4a8..99a024e65d 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -31,6 +31,9 @@
</parent>
<artifactId>cps-ncmp-service</artifactId>
+ <properties>
+ <minimum-coverage>0.93</minimum-coverage>
+ </properties>
<dependencies>
<dependency>
@@ -38,6 +41,23 @@
<artifactId>cps-service</artifactId>
</dependency>
<dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-ncmp-events</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mapstruct</groupId>
+ <artifactId>mapstruct</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mapstruct</groupId>
+ <artifactId>mapstruct-processor</artifactId>
+ </dependency>
+ <!-- T E S T - D E P E N D E N C I E S -->
+ <dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
@@ -48,6 +68,16 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>kafka</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>spock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
@@ -58,13 +88,5 @@
</exclusion>
</exclusions>
</dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
- </dependency>
</dependencies>
</project>
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 0e748c7fe7..717cae565c 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
@@ -46,11 +46,10 @@ import org.onap.cps.api.CpsAdminService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
-import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.inventory.sync.ModuleSyncService;
import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
@@ -85,7 +84,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
private final NetworkCmProxyDataServicePropertyHandler networkCmProxyDataServicePropertyHandler;
- private final YangModelCmHandleRetriever yangModelCmHandleRetriever;
+ private final InventoryPersistence inventoryPersistence;
private final ModuleSyncService moduleSyncService;
@@ -93,7 +92,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
final DmiPluginRegistration dmiPluginRegistration) {
dmiPluginRegistration.validateDmiPluginRegistration();
- final var dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
+ final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
dmiPluginRegistrationResponse.setRemovedCmHandles(
parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles()));
if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
@@ -114,9 +113,12 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
final String optionsParamInQuery,
final String topicParamInQuery,
final String requestId) {
- CpsValidator.validateNameCharacters(cmHandleId);
- return getResourceDataResponse(cmHandleId, resourceIdentifier,
- DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, optionsParamInQuery, topicParamInQuery, requestId);
+ final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
+ resourceIdentifier,
+ optionsParamInQuery,
+ DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
+ requestId, topicParamInQuery);
+ return responseEntity.getBody();
}
@Override
@@ -125,21 +127,23 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
final String optionsParamInQuery,
final String topicParamInQuery,
final String requestId) {
- CpsValidator.validateNameCharacters(cmHandleId);
- return getResourceDataResponse(cmHandleId, resourceIdentifier,
- DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING, optionsParamInQuery, topicParamInQuery, requestId);
+ final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
+ resourceIdentifier,
+ optionsParamInQuery,
+ DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING,
+ requestId, topicParamInQuery);
+ return responseEntity.getBody();
}
@Override
public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId,
- final String resourceIdentifier,
- final OperationEnum operation,
- final String requestData,
- final String dataType) {
+ final String resourceIdentifier,
+ final OperationEnum operation,
+ final String requestData,
+ final String dataType) {
CpsValidator.validateNameCharacters(cmHandleId);
- return handleResponse(
- dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, operation,
- requestData, dataType), operation);
+ return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, operation,
+ requestData, dataType);
}
@@ -171,7 +175,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
});
return cpsAdminService.queryCmHandles(jsonObjectMapper.convertToValueType(cmHandleQueryApiParameters,
- org.onap.cps.spi.model.CmHandleQueryParameters.class));
+ org.onap.cps.spi.model.CmHandleQueryParameters.class));
}
/**
@@ -185,10 +189,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
CpsValidator.validateNameCharacters(cmHandleId);
final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
final YangModelCmHandle yangModelCmHandle =
- yangModelCmHandleRetriever.getYangModelCmHandle(cmHandleId);
+ inventoryPersistence.getYangModelCmHandle(cmHandleId);
final List<YangModelCmHandle.Property> dmiProperties = yangModelCmHandle.getDmiProperties();
final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties();
ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId());
+ ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState());
setDmiProperties(dmiProperties, ncmpServiceCmHandle);
setPublicProperties(publicProperties, ncmpServiceCmHandle);
return ncmpServiceCmHandle;
@@ -204,7 +209,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) {
CpsValidator.validateNameCharacters(cmHandleId);
final YangModelCmHandle yangModelCmHandle =
- yangModelCmHandleRetriever.getYangModelCmHandle(cmHandleId);
+ inventoryPersistence.getYangModelCmHandle(cmHandleId);
final List<YangModelCmHandle.Property> yangModelPublicProperties = yangModelCmHandle.getPublicProperties();
final Map<String, String> cmHandlePublicProperties = new HashMap<>();
asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties);
@@ -243,7 +248,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
final String schemaSetName = moduleSyncService.syncAndCreateSchemaSet(yangModelCmHandle);
final String anchorName = yangModelCmHandle.getId();
cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
- anchorName);
+ anchorName);
}
protected List<CmHandleRegistrationResponse> parseAndRemoveCmHandlesInDmiRegistration(
@@ -286,17 +291,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
}
- private Object getResourceDataResponse(final String cmHandleId,
- final String resourceIdentifier,
- final DmiOperations.DataStoreEnum dataStore,
- final String optionsParamInQuery,
- final String topicParamInQuery,
- final String requestId) {
- final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(
- cmHandleId, resourceIdentifier, optionsParamInQuery, dataStore, requestId, topicParamInQuery);
- return handleResponse(responseEntity, OperationEnum.READ);
- }
-
private void setDmiProperties(final List<YangModelCmHandle.Property> dmiProperties,
final NcmpServiceCmHandle ncmpServiceCmHandle) {
final Map<String, String> dmiPropertiesMap = new LinkedHashMap<>(dmiProperties.size());
@@ -318,7 +312,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
}
-
private CmHandleRegistrationResponse registerAndSyncNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
try {
final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
@@ -335,14 +328,4 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
}
}
- private static Object handleResponse(final ResponseEntity<?> responseEntity, final OperationEnum operation) {
- if (responseEntity.getStatusCode().is2xxSuccessful()) {
- return responseEntity.getBody();
- } else {
- final String exceptionMessage = "Unable to " + operation.toString() + " resource data.";
- throw new HttpClientRequestException(exceptionMessage, (String) responseEntity.getBody(),
- responseEntity.getStatusCodeValue());
- }
- }
-
-} \ No newline at end of file
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventConsumer.java
new file mode 100644
index 0000000000..4e5c57ba57
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventConsumer.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent;
+import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * Listener for cps-ncmp async request response events.
+ */
+@Component
+@Slf4j
+@RequiredArgsConstructor
+public class NcmpAsyncRequestResponseEventConsumer {
+
+ private final NcmpAsyncRequestResponseEventProducer ncmpAsyncRequestResponseEventProducer;
+ private final NcmpAsyncRequestResponseEventMapper ncmpAsyncRequestResponseEventMapper;
+
+ /**
+ * Consume the specified event.
+ *
+ * @param dmiAsyncRequestResponseEvent the event to be consumed and produced.
+ */
+ @KafkaListener(topics = "${app.ncmp.async-m2m.topic}")
+ public void consumeAndForward(final DmiAsyncRequestResponseEvent dmiAsyncRequestResponseEvent) {
+ log.debug("Consuming event {} ...", dmiAsyncRequestResponseEvent);
+
+ final NcmpAsyncRequestResponseEvent ncmpAsyncRequestResponseEvent =
+ ncmpAsyncRequestResponseEventMapper.toNcmpAsyncEvent(dmiAsyncRequestResponseEvent);
+ ncmpAsyncRequestResponseEventProducer.sendMessage(
+ ncmpAsyncRequestResponseEvent.getEventId(), ncmpAsyncRequestResponseEvent);
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapper.java
new file mode 100644
index 0000000000..5d8ac7f841
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapper.java
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.UUID;
+import org.mapstruct.AfterMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Named;
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent;
+import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent;
+
+/**
+ * Mapper for converting DmiAsyncRequestResponseEvent to NcmpAsyncRequestResponseEvent.
+ */
+@Mapper(componentModel = "spring")
+public interface NcmpAsyncRequestResponseEventMapper {
+
+ @Mapping(source = "eventId", target = "eventId", qualifiedByName = "ncmpAsyncEventId")
+ @Mapping(source = "eventTime", target = "eventTime", qualifiedByName = "currentTime")
+ @Mapping(source = "eventId", target = "forwardedEvent.eventId")
+ @Mapping(source = "eventCorrelationId", target = "forwardedEvent.eventCorrelationId")
+ @Mapping(source = "eventSchema", target = "forwardedEvent.eventSchema")
+ @Mapping(source = "eventSource", target = "forwardedEvent.eventSource")
+ @Mapping(source = "eventTarget", target = "forwardedEvent.eventTarget")
+ @Mapping(source = "eventTime", target = "forwardedEvent.eventTime")
+ @Mapping(source = "eventType", target = "forwardedEvent.eventType")
+ @Mapping(source = "eventContent.responseStatus", target = "forwardedEvent.responseStatus")
+ @Mapping(source = "eventContent.responseCode", target = "forwardedEvent.responseCode")
+ @Mapping(source = "eventContent.responseDataSchema", target = "forwardedEvent.responseDataSchema")
+ NcmpAsyncRequestResponseEvent toNcmpAsyncEvent(DmiAsyncRequestResponseEvent dmiAsyncRequestResponseEvent);
+
+ @Named("ncmpAsyncEventId")
+ static String getNcmpAsyncEventId(String eventId) {
+ return UUID.randomUUID().toString();
+ }
+
+ @Named("currentTime")
+ static String getFormattedCurrentTime(String eventTime) {
+ return ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
+ }
+
+ @AfterMapping
+ default void mapAdditionalProperties(DmiAsyncRequestResponseEvent dmiAsyncRequestResponseEvent,
+ @MappingTarget NcmpAsyncRequestResponseEvent ncmpAsyncRequestResponseEvent) {
+ ncmpAsyncRequestResponseEvent.getForwardedEvent().setAdditionalProperty("response-data",
+ dmiAsyncRequestResponseEvent.getEventContent().getResponseData().getAdditionalProperties());
+ }
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventProducer.java
new file mode 100644
index 0000000000..8ab6db9045
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventProducer.java
@@ -0,0 +1,46 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class NcmpAsyncRequestResponseEventProducer {
+
+ private final KafkaTemplate<String, NcmpAsyncRequestResponseEvent> kafkaTemplate;
+
+
+ /**
+ * Sends message to the configured topic with a message key.
+ *
+ * @param eventId message key
+ * @param ncmpAsyncRequestResponseEvent message payload
+ */
+ public void sendMessage(final String eventId, final NcmpAsyncRequestResponseEvent ncmpAsyncRequestResponseEvent) {
+ kafkaTemplate.send(ncmpAsyncRequestResponseEvent.getEventTarget(), eventId, ncmpAsyncRequestResponseEvent);
+ }
+}
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 f1bb95f34e..d457f2601b 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 Nordix Foundation
+ * Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,11 +23,14 @@ package org.onap.cps.ncmp.api.impl.client;
import lombok.AllArgsConstructor;
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.operations.DmiRequestBody;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
+import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
@Component
@@ -37,17 +40,24 @@ public class DmiRestClient {
private RestTemplate restTemplate;
private DmiProperties dmiProperties;
-
/**
* Sends POST operation to DMI with json body containing module references.
* @param dmiResourceUrl dmi resource url
* @param jsonData json data body
+ * @param operation the type of operation being executed (for error reporting only)
* @return response entity of type String
*/
public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl,
- final String jsonData) {
+ final String jsonData,
+ final DmiRequestBody.OperationEnum operation) {
final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(new HttpHeaders()));
- return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
+ try {
+ return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
+ } catch (final HttpStatusCodeException httpStatusCodeException) {
+ final String exceptionMessage = "Unable to " + operation.toString() + " resource data.";
+ throw new HttpClientRequestException(exceptionMessage, httpStatusCodeException.getResponseBodyAsString(),
+ httpStatusCodeException.getRawStatusCode());
+ }
}
private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders) {
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 f145379406..d409a80e55 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
@@ -29,6 +29,7 @@ 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.utils.DmiServiceUrlBuilder;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.utils.CpsValidator;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
@@ -45,11 +46,11 @@ public class DmiDataOperations extends DmiOperations {
*
* @param dmiRestClient {@code DmiRestClient}
*/
- public DmiDataOperations(final YangModelCmHandleRetriever cmHandlePropertiesRetriever,
+ public DmiDataOperations(final InventoryPersistence inventoryPersistence,
final JsonObjectMapper jsonObjectMapper,
final NcmpConfiguration.DmiProperties dmiProperties,
final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
- super(cmHandlePropertiesRetriever, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
+ super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
}
/**
@@ -72,7 +73,7 @@ public class DmiDataOperations extends DmiOperations {
final String topicParamInQuery) {
CpsValidator.validateNameCharacters(cmHandleId);
final YangModelCmHandle yangModelCmHandle =
- yangModelCmHandleRetriever.getYangModelCmHandle(cmHandleId);
+ inventoryPersistence.getYangModelCmHandle(cmHandleId);
final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
.operation(READ)
.requestId(requestId)
@@ -83,7 +84,7 @@ public class DmiDataOperations extends DmiOperations {
dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(
yangModelCmHandle, cmHandleId, dataStore));
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody);
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
}
/**
@@ -104,7 +105,7 @@ public class DmiDataOperations extends DmiOperations {
final String dataType) {
CpsValidator.validateNameCharacters(cmHandleId);
final YangModelCmHandle yangModelCmHandle =
- yangModelCmHandleRetriever.getYangModelCmHandle(cmHandleId);
+ inventoryPersistence.getYangModelCmHandle(cmHandleId);
final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
.operation(operation)
.data(requestData)
@@ -116,7 +117,7 @@ public class DmiDataOperations extends DmiOperations {
dmiServiceUrlBuilder.getDmiDatastoreUrl(dmiServiceUrlBuilder.populateQueryParams(resourceId,
null, null),
dmiServiceUrlBuilder.populateUriVariables(yangModelCmHandle, cmHandleId, PASSTHROUGH_RUNNING));
- return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody);
+ return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation);
}
}
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 b033af87cd..d8d03041fb 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
@@ -34,6 +34,7 @@ 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.utils.DmiServiceUrlBuilder;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.models.YangResource;
import org.onap.cps.spi.model.ModuleReference;
import org.onap.cps.utils.JsonObjectMapper;
@@ -51,11 +52,11 @@ public class DmiModelOperations extends DmiOperations {
*
* @param dmiRestClient {@code DmiRestClient}
*/
- public DmiModelOperations(final YangModelCmHandleRetriever dmiPropertiesRetriever,
+ public DmiModelOperations(final InventoryPersistence inventoryPersistence,
final JsonObjectMapper jsonObjectMapper,
final NcmpConfiguration.DmiProperties dmiProperties,
final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
- super(dmiPropertiesRetriever, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
+ super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
}
/**
@@ -107,7 +108,7 @@ public class DmiModelOperations extends DmiOperations {
final String cmHandle,
final String resourceName) {
final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
- return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData);
+ return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, DmiRequestBody.OperationEnum.READ);
}
private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences,
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
index 745007bd44..e26ffef870 100644
--- 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
@@ -26,6 +26,7 @@ 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.utils.DmiServiceUrlBuilder;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Service;
@@ -44,7 +45,7 @@ public class DmiOperations {
}
}
- protected final YangModelCmHandleRetriever yangModelCmHandleRetriever;
+ protected final InventoryPersistence inventoryPersistence;
protected final JsonObjectMapper jsonObjectMapper;
protected final NcmpConfiguration.DmiProperties dmiProperties;
protected final DmiRestClient dmiRestClient;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
index d4c64eac90..65e03f1f9d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
@@ -90,6 +90,7 @@ public class YangModelCmHandle {
yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
ncmpServiceCmHandle.getPublicProperties()));
+ yangModelCmHandle.setCompositeState(ncmpServiceCmHandle.getCompositeState());
return yangModelCmHandle;
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleState.java
index 24fab32a5c..0c16adca91 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleState.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleState.java
@@ -21,5 +21,5 @@
package org.onap.cps.ncmp.api.inventory;
public enum CmHandleState {
- ADVISED, READY;
+ ADVISED, READY, LOCKED
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java
index 9ac49a6ceb..eeaa4cd0be 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeState.java
@@ -22,6 +22,8 @@ package org.onap.cps.ncmp.api.inventory;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
@@ -38,7 +40,7 @@ import lombok.Setter;
public class CompositeState {
@JsonProperty("cm-handle-state")
- private CmHandleState cmhandleState;
+ private CmHandleState cmHandleState;
@JsonProperty("lock-reason")
private LockReason lockReason;
@@ -52,13 +54,24 @@ public class CompositeState {
@JsonProperty("datastores")
private DataStores dataStores;
+ /**
+ * Date and Time in the format of yyyy-MM-dd'T'HH:mm:ss.SSSZ
+ */
+ public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+
+ /**
+ * This will specify the latest lock reason for a specific cm handle. If a cm handle is in a state other than LOCKED
+ * it specifies the last lock reason.
+ * This can be used to track retry attempts as part of the lock details.
+ */
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class LockReason {
@JsonProperty("reason")
- private String reason;
+ private LockReasonCategory lockReasonCategory;
@JsonProperty("details")
private String details;
@@ -72,9 +85,6 @@ public class CompositeState {
@JsonProperty("operational")
private Operational operationalDataStore;
-
- @JsonProperty("running")
- private Running runningDataStore;
}
@Data
@@ -101,4 +111,20 @@ public class CompositeState {
private String lastSyncTime;
}
+ /**
+ * The date and time format used for the cm handle sync state.
+ *
+ * @return the date and time in the format of yyyy-MM-dd'T'HH:mm:ss.SSSZ
+ */
+ public static String nowInSyncTimeFormat() {
+ return dateTimeFormatter.format(OffsetDateTime.now());
+ }
+
+ /**
+ * Sets the last updated date and time for the cm handle sync state.
+ */
+ public void setLastUpdateTimeNow() {
+ lastUpdateTime = CompositeState.nowInSyncTimeFormat();
+ }
+
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java
new file mode 100644
index 0000000000..4ab0cecbf5
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java
@@ -0,0 +1,138 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory;
+
+import org.onap.cps.ncmp.api.inventory.CompositeState.DataStores;
+import org.onap.cps.ncmp.api.inventory.CompositeState.LockReason;
+import org.onap.cps.ncmp.api.inventory.CompositeState.Operational;
+import org.onap.cps.spi.model.DataNode;
+
+public class CompositeStateBuilder {
+
+ private CmHandleState cmHandleState;
+ private LockReason lockReason;
+ private DataStores datastores;
+ private String lastUpdatedTime;
+
+ /**
+ * To create the {@link CompositeState}.
+ *
+ * @return {@link DataNode}
+ */
+ public CompositeState build() {
+ final CompositeState compositeState = new CompositeState();
+ compositeState.setCmHandleState(cmHandleState);
+ compositeState.setLockReason(lockReason);
+ compositeState.setDataStores(datastores);
+ compositeState.setLastUpdateTime(lastUpdatedTime);
+ return compositeState;
+ }
+
+ /**
+ * To use attributes for creating {@link CompositeState}.
+ *
+ * @param cmHandleState for the data node
+ * @return CompositeStateBuilder
+ */
+ public CompositeStateBuilder withCmHandleState(final CmHandleState cmHandleState) {
+ this.cmHandleState = cmHandleState;
+ return this;
+ }
+
+ /**
+ * To use attributes for creating {@link CompositeState}.
+ *
+ * @param reason for the locked state
+ * @param details for the locked state
+ * @return CompositeStateBuilder
+ */
+ public CompositeStateBuilder withLockReason(final LockReasonCategory reason, final String details) {
+ this.lockReason = LockReason.builder().lockReasonCategory(reason).details(details).build();
+ return this;
+ }
+
+ /**
+ * To use attributes for creating {@link CompositeState}.
+ *
+ * @param time for the state change
+ * @return CompositeStateBuilder
+ */
+ public CompositeStateBuilder withLastUpdatedTime(final String time) {
+ this.lastUpdatedTime = time;
+ return this;
+ }
+
+ /**
+ * To use attributes for creating {@link CompositeState}.
+ *
+ * @return composite state builder
+ */
+ public CompositeStateBuilder withLastUpdatedTimeNow() {
+ this.lastUpdatedTime = CompositeState.nowInSyncTimeFormat();
+ return this;
+ }
+
+ /**
+ * To use attributes for creating {@link CompositeState}.
+ *
+ * @param syncState for the locked state
+ * @param lastSyncTime for the locked state
+ * @return CompositeStateBuilder
+ */
+ public CompositeStateBuilder withOperationalDataStores(final String syncState, final String lastSyncTime) {
+ this.datastores = DataStores.builder().operationalDataStore(
+ Operational.builder().syncState(syncState).lastSyncTime(lastSyncTime).build()).build();
+ return this;
+ }
+
+ /**
+ * To use dataNode for creating {@link CompositeState}.
+ *
+ * @param dataNode for the dataNode
+ * @return CompositeState
+ */
+ public CompositeStateBuilder fromDataNode(final DataNode dataNode) {
+ this.cmHandleState = CmHandleState.valueOf((String) dataNode.getLeaves()
+ .get("cm-handle-state"));
+ for (final DataNode stateChildNode : dataNode.getChildDataNodes()) {
+ if (stateChildNode.getXpath().endsWith("/lock-reason")) {
+ this.lockReason = new LockReason(LockReasonCategory.valueOf(
+ (String) stateChildNode.getLeaves().get("reason")),
+ (String) stateChildNode.getLeaves().get("details"));
+ }
+ if (stateChildNode.getXpath().endsWith("/datastores")) {
+ for (final DataNode dataStoreNodes : stateChildNode.getChildDataNodes()) {
+ Operational operationalDataStore = null;
+ if (dataStoreNodes.getXpath().contains("/operational")) {
+ operationalDataStore = Operational.builder()
+ .syncState((String) dataStoreNodes.getLeaves().get("sync-state"))
+ .lastSyncTime((String) dataStoreNodes.getLeaves().get("last-sync-time"))
+ .build();
+ }
+ this.datastores = DataStores.builder().operationalDataStore(operationalDataStore).build();
+ }
+ }
+ }
+ return this;
+ }
+
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
index 5063e8298a..873a44913a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,30 +18,78 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.operations;
+package org.onap.cps.ncmp.api.inventory;
+import java.time.OffsetDateTime;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
-import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.utils.CpsValidator;
+import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Component;
-/**
- * Retrieves YangModelCmHandles & properties.
- */
+@RequiredArgsConstructor
@Component
-@AllArgsConstructor
-public class YangModelCmHandleRetriever {
+public class InventoryPersistence {
private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
+
private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
- private CpsDataService cpsDataService;
+ private final JsonObjectMapper jsonObjectMapper;
+
+ private final CpsDataService cpsDataService;
+
+ private final CpsDataPersistenceService cpsDataPersistenceService;
+
+ private static final CompositeStateBuilder compositeStateBuilder = new CompositeStateBuilder();
+
+ /**
+ * Get the Cm Handle Composite State from the data node.
+ *
+ * @param cmHandleId cm handle id
+ * @return the cm handle composite state
+ */
+ public CompositeState getCmHandleState(final String cmHandleId) {
+ final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ "/dmi-registry/cm-handles[@id='" + cmHandleId + "']/state",
+ FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+ return compositeStateBuilder.fromDataNode(stateAsDataNode).build();
+ }
+
+ /**
+ * Save the cm handles state.
+ *
+ * @param cmHandleId cm handle id
+ * @param compositeState composite state
+ */
+ public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) {
+ final String cmHandleJsonData = String.format("{\"state\":%s}",
+ jsonObjectMapper.asJsonString(compositeState));
+ cpsDataService.replaceNodeTree(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ "/dmi-registry/cm-handles[@id='" + cmHandleId + "']",
+ cmHandleJsonData, OffsetDateTime.now());
+ }
+
+ /**
+ * Method which returns cm handles by the cm handles state.
+ *
+ * @param cmHandleState cm handle state
+ * @return a list of cm handles
+ */
+ public List<DataNode> getCmHandlesByState(final CmHandleState cmHandleState) {
+ return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME,
+ NCMP_DMI_REGISTRY_ANCHOR, "//state[@cm-handle-state=\""
+ + cmHandleState + "\"]/ancestor::cm-handles",
+ FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
/**
* This method retrieves DMI service name and DMI properties for a given cm handle.
@@ -53,7 +101,7 @@ public class YangModelCmHandleRetriever {
final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId);
final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
ncmpServiceCmHandle.setCmHandleId(cmHandleId);
- populateCmHandleProperties(cmHandleDataNode, ncmpServiceCmHandle);
+ populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle);
return YangModelCmHandle.toYangModelCmHandle(
(String) cmHandleDataNode.getLeaves().get("dmi-service-name"),
(String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
@@ -70,19 +118,24 @@ public class YangModelCmHandleRetriever {
FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
}
- private static void populateCmHandleProperties(final DataNode cmHandleDataNode,
- final NcmpServiceCmHandle ncmpServiceCmHandle) {
+ private static void populateCmHandleDetails(final DataNode cmHandleDataNode,
+ final NcmpServiceCmHandle ncmpServiceCmHandle) {
final Map<String, String> dmiProperties = new LinkedHashMap<>();
final Map<String, String> publicProperties = new LinkedHashMap<>();
+ final CompositeStateBuilder compositeStateBuilder = new CompositeStateBuilder();
+ CompositeState compositeState = compositeStateBuilder.build();
for (final DataNode childDataNode: cmHandleDataNode.getChildDataNodes()) {
if (childDataNode.getXpath().contains("/additional-properties[@name=")) {
addProperty(childDataNode, dmiProperties);
} else if (childDataNode.getXpath().contains("/public-properties[@name=")) {
addProperty(childDataNode, publicProperties);
+ } else if (childDataNode.getXpath().endsWith("/state")) {
+ compositeState = compositeStateBuilder.fromDataNode(childDataNode).build();
}
}
ncmpServiceCmHandle.setDmiProperties(dmiProperties);
ncmpServiceCmHandle.setPublicProperties(publicProperties);
+ ncmpServiceCmHandle.setCompositeState(compositeState);
}
private static void addProperty(final DataNode propertyDataNode, final Map<String, String> propertiesAsMap) {
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/LockReasonCategory.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/LockReasonCategory.java
new file mode 100644
index 0000000000..596fcb7f99
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/LockReasonCategory.java
@@ -0,0 +1,25 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory;
+
+public enum LockReasonCategory {
+ LOCKED_MISBEHAVING
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
index 353db9d68d..2187ec61ce 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
@@ -1,5 +1,5 @@
/*
- * ============LICENSE_START=======================================================
+ * ============LICENSE_START=======================================================
* Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +24,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.CmHandleState;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -32,6 +35,8 @@ import org.springframework.stereotype.Component;
@Component
public class ModuleSyncWatchdog {
+ private final InventoryPersistence inventoryPersistence;
+
private final SyncUtils syncUtils;
private final ModuleSyncService moduleSyncService;
@@ -43,11 +48,21 @@ public class ModuleSyncWatchdog {
public void executeAdvisedCmHandlePoll() {
YangModelCmHandle advisedCmHandle = syncUtils.getAnAdvisedCmHandle();
while (advisedCmHandle != null) {
- moduleSyncService.syncAndCreateSchemaSet(advisedCmHandle);
- // ToDo Lock Cm Handle if module sync fails
- syncUtils.updateCmHandleState(advisedCmHandle, CmHandleState.READY);
- log.info("{} is now in {} state", advisedCmHandle.getId(),
- advisedCmHandle.getCompositeState().getCmhandleState());
+ final String cmHandleId = advisedCmHandle.getId();
+ final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
+ try {
+ moduleSyncService.syncAndCreateSchemaSet(advisedCmHandle);
+ compositeState.setCmHandleState(CmHandleState.READY);
+ } catch (final Exception e) {
+ compositeState.setCmHandleState(CmHandleState.LOCKED);
+ syncUtils.updateLockReasonDetailsAndAttempts(compositeState,
+ LockReasonCategory.LOCKED_MISBEHAVING,
+ e.getMessage());
+ }
+ compositeState.setLastUpdateTimeNow();
+ inventoryPersistence.saveCmHandleState(cmHandleId, compositeState);
+ log.info("{} is now in {} state", cmHandleId,
+ advisedCmHandle.getCompositeState().getCmHandleState());
advisedCmHandle = syncUtils.getAnAdvisedCmHandle();
}
log.debug("No Cm-Handles currently found in an ADVISED state");
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
index 3bc43c5665..a4f29de3e8 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
@@ -1,5 +1,5 @@
/*
- * ============LICENSE_START=======================================================
+ * ============LICENSE_START=======================================================
* Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,23 +20,18 @@
package org.onap.cps.ncmp.api.inventory.sync;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_PARENT;
-
import java.security.SecureRandom;
-import java.time.OffsetDateTime;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsDataService;
-import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.inventory.CmHandleState;
-import org.onap.cps.spi.CpsDataPersistenceService;
-import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory;
import org.onap.cps.spi.model.DataNode;
-import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Component;
@Slf4j
@@ -45,13 +40,11 @@ import org.springframework.stereotype.Component;
public class SyncUtils {
private static final SecureRandom secureRandom = new SecureRandom();
- private final CpsDataService cpsDataService;
- private final CpsDataPersistenceService cpsDataPersistenceService;
- private final JsonObjectMapper jsonObjectMapper;
+ private final InventoryPersistence inventoryPersistence;
- private final YangModelCmHandleRetriever yangModelCmHandleRetriever;
+ private static final Pattern retryAttemptPattern = Pattern.compile("^Attempt #(\\d+) failed:");
/**
* Query data nodes for cm handles with an "ADVISED" cm handle state, and select a random entry for processing.
@@ -59,30 +52,37 @@ public class SyncUtils {
* @return a random yang model cm handle with an ADVISED state, return null if not found
*/
public YangModelCmHandle getAnAdvisedCmHandle() {
- final List<DataNode> advisedCmHandles = cpsDataPersistenceService.queryDataNodes("NCMP-Admin",
- "ncmp-dmi-registry", "//cm-handles[@state=\"ADVISED\"]",
- FetchDescendantsOption.OMIT_DESCENDANTS);
+ final List<DataNode> advisedCmHandles = inventoryPersistence.getCmHandlesByState(CmHandleState.ADVISED);
if (advisedCmHandles.isEmpty()) {
return null;
}
final int randomElementIndex = secureRandom.nextInt(advisedCmHandles.size());
final String cmHandleId = advisedCmHandles.get(randomElementIndex).getLeaves()
.get("id").toString();
- return yangModelCmHandleRetriever.getYangModelCmHandle(cmHandleId);
+ return inventoryPersistence.getYangModelCmHandle(cmHandleId);
}
+
/**
- * Update the Cm Handle state to "READY".
+ * Update Composite State attempts counter and set new lock reason and details.
*
- * @param yangModelCmHandle yang model cm handle
- * @param cmHandleState cm handle state
+ * @param lockReasonCategory lock reason category
+ * @param errorMessage error message
*/
- public void updateCmHandleState(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) {
- yangModelCmHandle.getCompositeState().setCmhandleState(cmHandleState);
- final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
- jsonObjectMapper.asJsonString(yangModelCmHandle));
- cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- cmHandleJsonData, OffsetDateTime.now());
+ public void updateLockReasonDetailsAndAttempts(final CompositeState compositeState,
+ final LockReasonCategory lockReasonCategory,
+ final String errorMessage) {
+ int attempt = 1;
+ if (compositeState.getLockReason() != null) {
+ final Matcher matcher = retryAttemptPattern.matcher(compositeState.getLockReason().getDetails());
+ if (matcher.find()) {
+ attempt = 1 + Integer.parseInt(matcher.group(1));
+ }
+ }
+ compositeState.setLockReason(CompositeState.LockReason.builder()
+ .details(String.format("Attempt #%d failed: %s", attempt, errorMessage))
+ .lockReasonCategory(lockReasonCategory).build());
}
+
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java
index 6811b59e00..963b484ed7 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java
@@ -27,6 +27,7 @@ import java.util.Map;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.springframework.validation.annotation.Validated;
/**
@@ -47,4 +48,7 @@ public class NcmpServiceCmHandle {
@JsonSetter(nulls = Nulls.AS_EMPTY)
private Map<String, String> publicProperties = Collections.emptyMap();
+ @JsonSetter(nulls = Nulls.AS_EMPTY)
+ private CompositeState compositeState;
+
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
index 5683d57e54..f56aea7f33 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
@@ -28,8 +28,7 @@ import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
-import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
-import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
import org.onap.cps.ncmp.api.models.DmiPluginRegistration
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
@@ -61,10 +60,9 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
def mockCpsModuleService = Mock(CpsModuleService)
def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
def mockCpsAdminService = Mock(CpsAdminService)
- def mockDmiModelOperations = Mock(DmiModelOperations)
def mockDmiDataOperations = Mock(DmiDataOperations)
def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler)
- def mockYangModelCmHandleRetriever = Mock(YangModelCmHandleRetriever)
+ def mockInventoryPersistence = Mock(InventoryPersistence)
def mockModuleSyncService = Mock(ModuleSyncService)
def noTimestamp = null
@@ -389,6 +387,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
def getObjectUnderTest() {
return Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations,
- mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler, mockYangModelCmHandleRetriever, mockModuleSyncService))
+ mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockModuleSyncService))
}
}
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 01f3bfed7b..55a1a8d1fa 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,8 +23,10 @@
package org.onap.cps.ncmp.api.impl
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
-import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeState
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.models.DmiPluginRegistration
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
import org.onap.cps.spi.exceptions.DataValidationException
@@ -34,11 +36,9 @@ import spock.lang.Shared
import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
import org.onap.cps.utils.JsonObjectMapper
-import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
@@ -58,7 +58,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
def mockDmiDataOperations = Mock(DmiDataOperations)
def nullNetworkCmProxyDataServicePropertyHandler = null
- def mockYangModelCmHandleRetriever = Mock(YangModelCmHandleRetriever)
+ def mockInventoryPersistence = Mock(InventoryPersistence)
def mockModuleSyncService = Mock(ModuleSyncService)
def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
@@ -70,17 +70,17 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations,
- mockCpsModuleService, mockCpsAdminService, nullNetworkCmProxyDataServicePropertyHandler, mockYangModelCmHandleRetriever, mockModuleSyncService)
+ mockCpsModuleService, mockCpsAdminService, nullNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockModuleSyncService)
def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
def dataNode = new DataNode(leaves: ['id': 'some-cm-handle', 'dmi-service-name': 'testDmiService'])
- def 'Write resource data for pass-through running from DMI using POST #scenario cm handle properties.'() {
+ def 'Write resource data for pass-through running from DMI using POST.'() {
given: 'cpsDataService returns valid datanode'
mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
- when: 'get resource data is called'
+ when: 'write resource data is called'
objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
'testResourceId', CREATE,
'{some-json}', 'application/json')
@@ -101,102 +101,26 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
0 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi(_, _, _, _, _)
}
- def 'Write resource data for pass-through running from DMI using POST "not found" response (from DMI).'() {
- given: 'cpsDataService returns valid dataNode'
- mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
- and: 'DMI returns a response with 404 status code'
- mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle',
- 'testResourceId', CREATE,
- '{some-json}', 'application/json')
- >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
- when: 'write resource data is called'
- objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'testResourceId', CREATE,
- '{some-json}', 'application/json')
- then: 'exception is thrown'
- def exceptionThrown = thrown(HttpClientRequestException.class)
- and: 'http status (not found) error code: 404'
- exceptionThrown.httpStatus == HttpStatus.NOT_FOUND.value()
- }
-
def 'Get resource data for pass-through operational from DMI.'() {
given: 'get data node is called'
mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
and: 'get resource data from DMI is called'
mockDmiDataOperations.getResourceDataFromDmi(
- 'testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- PASSTHROUGH_OPERATIONAL,
- NO_REQUEST_ID,
- NO_TOPIC) >> new ResponseEntity<>('dmi-response', HttpStatus.OK)
+ 'testCmHandle',
+ 'testResourceId',
+ OPTIONS_PARAM,
+ PASSTHROUGH_OPERATIONAL,
+ NO_REQUEST_ID,
+ NO_TOPIC) >> new ResponseEntity<>('dmi-response', HttpStatus.OK)
when: 'get resource data operational for cm-handle is called'
def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
- then: 'DMI returns a json response'
- response == 'dmi-response'
- }
-
- def 'Get resource data for pass-through operational from DMI with invalid name.'() {\
- when: 'get resource data operational for cm-handle is called'
- objectUnderTest.getResourceDataOperationalForCmHandle('invalid test cm handle',
'testResourceId',
OPTIONS_PARAM,
NO_TOPIC,
NO_REQUEST_ID)
- then: 'A data validation Exception is thrown'
- thrown(DataValidationException)
- }
-
- def 'Get resource data for pass-through operational from DMI with Json Processing Exception.'() {
- given: 'cps data service returns valid data node'
- mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
- and: 'objectMapper not able to parse object'
- spiedJsonObjectMapper.asJsonString(_) >> { throw new JsonProcessingException('testException') }
- and: 'DMI returns NOK response'
- mockDmiDataOperations.getResourceDataFromDmi(*_)
- >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
- when: 'get resource data is called'
- objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
- then: 'exception is thrown with the expected response code and details'
- def exceptionThrown = thrown(HttpClientRequestException.class)
- exceptionThrown.details.contains('NOK-json')
- exceptionThrown.httpStatus == HttpStatus.NOT_FOUND.value()
- }
-
- def 'Get resource data for pass-through operational from DMI return NOK response.'() {
- given: 'cps data service returns valid data node'
- mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
- and: 'DMI returns NOK response'
- mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- PASSTHROUGH_OPERATIONAL,
- NO_REQUEST_ID,
- NO_TOPIC)
- >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
- when: 'get resource data is called'
- objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
- then: 'exception is thrown'
- def exceptionThrown = thrown(HttpClientRequestException.class)
- and: 'details contain the original response'
- exceptionThrown.httpStatus == HttpStatus.NOT_FOUND.value()
- exceptionThrown.details.contains('NOK-json')
+ then: 'DMI returns a json response'
+ response == 'dmi-response'
}
def 'Get resource data for pass-through running from DMI.'() {
@@ -205,55 +129,19 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
and: 'DMI returns valid response and data'
mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- PASSTHROUGH_RUNNING,
- NO_REQUEST_ID,
- NO_TOPIC) >> new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
+ 'testResourceId',
+ OPTIONS_PARAM,
+ PASSTHROUGH_RUNNING,
+ NO_REQUEST_ID,
+ NO_TOPIC) >> new ResponseEntity<>('{dmi-response}', HttpStatus.OK)
when: 'get resource data is called'
def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
- then: 'get resource data returns expected response'
- response == '{dmi-response}'
- }
-
- def 'Get resource data for pass-through running from DMI with invalid name.'() {
- when: 'get resource data operational for cm-handle is called'
- objectUnderTest.getResourceDataPassThroughRunningForCmHandle('invalid test cm handle',
'testResourceId',
OPTIONS_PARAM,
NO_TOPIC,
NO_REQUEST_ID)
- then: 'A data validation Exception is thrown'
- thrown(DataValidationException)
- }
-
- def 'Get resource data for pass-through running from DMI return NOK response.'() {
- given: 'cpsDataService returns valid dataNode'
- mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
- cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
- and: 'DMI returns NOK response'
- mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- PASSTHROUGH_RUNNING,
- NO_REQUEST_ID,
- NO_TOPIC)
- >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
- when: 'get resource data is called'
- objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'testResourceId',
- OPTIONS_PARAM,
- NO_TOPIC,
- NO_REQUEST_ID)
- then: 'exception is thrown'
- def exceptionThrown = thrown(HttpClientRequestException.class)
- and: 'details contain the original response'
- exceptionThrown.details.contains('NOK-json')
- exceptionThrown.httpStatus == HttpStatus.NOT_FOUND.value()
+ then: 'get resource data returns expected response'
+ response == '{dmi-response}'
}
def 'Getting Yang Resources.'() {
@@ -269,7 +157,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
then: 'a data validation exception is thrown'
thrown(DataValidationException)
and: 'CPS module services is not invoked'
- 0 * mockCpsModuleService.getYangResourcesModuleReferences(_, _)
+ 0 * mockCpsModuleService.getYangResourcesModuleReferences(*_)
}
def 'Get cm handle identifiers for the given module names.'() {
@@ -284,15 +172,17 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def dmiServiceName = 'some service name'
def dmiProperties = [new YangModelCmHandle.Property('Book', 'Romance Novel')]
def publicProperties = [new YangModelCmHandle.Property('Public Book', 'Public Romance Novel')]
- def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: dmiServiceName, dmiProperties: dmiProperties, publicProperties: publicProperties)
- 1 * mockYangModelCmHandleRetriever.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
+ def compositeState = new CompositeState(cmHandleState: 'ADVISED')
+ def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', dmiServiceName: dmiServiceName,
+ dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
+ 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
when: 'getting cm handle details for a given cm handle id from ncmp service'
def result = objectUnderTest.getNcmpServiceCmHandle('some-cm-handle')
then: 'the result returns the correct data'
result.cmHandleId == 'some-cm-handle'
result.dmiProperties ==[ Book:'Romance Novel' ]
result.publicProperties == [ "Public Book":'Public Romance Novel' ]
-
+ result.compositeState.cmHandleState == CmHandleState.ADVISED
}
def 'Get a cm handle with an invalid id.'() {
@@ -301,7 +191,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
then: 'an exception is thrown'
thrown(DataValidationException)
and: 'the yang model cm handle retriever is not invoked'
- 0 * mockYangModelCmHandleRetriever.getYangModelCmHandle(_)
+ 0 * mockInventoryPersistence.getYangModelCmHandle(*_)
}
def 'Get cm handle public properties'() {
@@ -310,7 +200,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')]
def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties)
and: 'the system returns this yang modelled cm handle'
- 1 * mockYangModelCmHandleRetriever.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
+ 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle
when: 'getting cm handle public properties for a given cm handle id from ncmp service'
def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle')
then: 'the result returns the correct data'
@@ -323,7 +213,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
then: 'an exception is thrown'
thrown(DataValidationException)
and: 'the yang model cm handle retriever is not invoked'
- 0 * mockYangModelCmHandleRetriever.getYangModelCmHandle(_)
+ 0 * mockInventoryPersistence.getYangModelCmHandle(*_)
}
def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
@@ -340,40 +230,19 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
>> { new ResponseEntity<>(HttpStatus.OK) }
}
- def 'Verify error message from handleResponse is correct for #scenario operation.'() {
- given: 'writeResourceDataPassThroughRunningFromDmi fails to return OK HttpStatus'
- mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi(*_)
- >> new ResponseEntity<>(HttpStatus.NOT_FOUND)
- when: 'get resource data is called'
- objectUnderTest.writeResourceDataPassThroughRunningForCmHandle(
- 'testCmHandle',
- 'testResourceId',
- givenOperation,
- '{some-json}',
- 'application/json')
- then: 'an exception is thrown with the expected error message details with correct operation'
- def exceptionThrown = thrown(HttpClientRequestException.class)
- exceptionThrown.getMessage().contains(expectedResponseMessage)
- where:
- scenario | givenOperation || expectedResponseMessage
- 'CREATE' | CREATE || 'Unable to create resource data.'
- 'READ' | READ || 'Unable to read resource data.'
- 'UPDATE' | UPDATE || 'Unable to update resource data.'
- }
-
def 'Verify modules and create anchor params'() {
given: 'dmi plugin registration return created cm handles'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
- dmiDataPlugin: 'service2')
+ dmiDataPlugin: 'service2')
dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
when: 'parse and create cm handle in dmi registration then sync module'
objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(mockDmiPluginRegistration)
then: 'validate params for creating anchor and list elements'
1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
- '/dmi-registry', '{"cm-handles":[{"id":"some-cm-handle-id",' +
- '"additional-properties":[],"public-properties":[]}]}', null)
+ '/dmi-registry', '{"cm-handles":[{"id":"some-cm-handle-id",' +
+ '"additional-properties":[],"public-properties":[]}]}', null)
1 * mockCpsAdminService.createAnchor('NFP-Operational', null,
- 'some-cm-handle-id')
+ 'some-cm-handle-id')
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy
new file mode 100644
index 0000000000..aa6bf1a783
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy
@@ -0,0 +1,126 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async
+
+import org.apache.kafka.clients.consumer.ConsumerConfig
+import org.apache.kafka.clients.producer.ProducerConfig
+import org.apache.kafka.common.serialization.StringSerializer
+import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
+import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent
+import org.springframework.kafka.core.DefaultKafkaProducerFactory
+import org.springframework.kafka.core.KafkaTemplate
+import org.springframework.kafka.support.serializer.JsonSerializer
+import org.testcontainers.containers.KafkaContainer
+import org.testcontainers.spock.Testcontainers
+import org.testcontainers.utility.DockerImageName
+
+import java.time.Duration
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.utils.JsonObjectMapper
+import org.apache.kafka.clients.consumer.KafkaConsumer
+import org.apache.kafka.common.serialization.StringDeserializer
+import org.onap.cps.ncmp.utils.TestUtils;
+import org.springframework.boot.test.context.SpringBootTest
+import org.spockframework.spring.SpringBean
+import org.springframework.test.annotation.DirtiesContext
+import org.springframework.test.context.DynamicPropertyRegistry
+import org.springframework.test.context.DynamicPropertySource
+import spock.lang.Specification
+
+@SpringBootTest(classes = [NcmpAsyncRequestResponseEventProducer, NcmpAsyncRequestResponseEventConsumer])
+@Testcontainers
+@DirtiesContext
+class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends Specification {
+
+ static kafkaTestContainer = new KafkaContainer(
+ DockerImageName.parse('confluentinc/cp-kafka:6.2.1')
+ )
+
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread(kafkaTestContainer::stop))
+ }
+
+ def setupSpec() {
+ kafkaTestContainer.start()
+ }
+
+ def producerConfigProperties = [
+ (ProducerConfig.BOOTSTRAP_SERVERS_CONFIG) : kafkaTestContainer.getBootstrapServers().split(',')[0],
+ (ProducerConfig.RETRIES_CONFIG) : 0,
+ (ProducerConfig.BATCH_SIZE_CONFIG) : 16384,
+ (ProducerConfig.LINGER_MS_CONFIG) : 1,
+ (ProducerConfig.BUFFER_MEMORY_CONFIG) : 33554432,
+ (ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG) : StringSerializer,
+ (ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG) : JsonSerializer
+ ]
+
+ def consumerConfigProperties = [
+ (ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG) : kafkaTestContainer.getBootstrapServers().split(',')[0],
+ (ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG) : StringDeserializer,
+ (ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG): StringDeserializer,
+ (ConsumerConfig.AUTO_OFFSET_RESET_CONFIG) : 'earliest',
+ (ConsumerConfig.GROUP_ID_CONFIG) : 'test'
+ ]
+
+ def kafkaTemplate = new KafkaTemplate<>(new DefaultKafkaProducerFactory<Integer, String>(producerConfigProperties))
+
+ @SpringBean
+ NcmpAsyncRequestResponseEventProducer cpsAsyncRequestResponseEventProducerService =
+ new NcmpAsyncRequestResponseEventProducer(kafkaTemplate);
+
+ @SpringBean
+ NcmpAsyncRequestResponseEventMapper ncmpAsyncRequestResponseEventMapper =
+ Mappers.getMapper(NcmpAsyncRequestResponseEventMapper.class)
+
+ @SpringBean
+ NcmpAsyncRequestResponseEventConsumer ncmpAsyncRequestResponseEventConsumer =
+ new NcmpAsyncRequestResponseEventConsumer(cpsAsyncRequestResponseEventProducerService,
+ ncmpAsyncRequestResponseEventMapper)
+
+ def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
+ def kafkaConsumer = new KafkaConsumer<>(getConsumerConfigProperties())
+
+ def 'Consume and forward valid message'() {
+ given: 'consumer has a subscription'
+ kafkaConsumer.subscribe(['test-topic'] as List<String>)
+ and: 'an event is sent'
+ def jsonData = TestUtils.getResourceFileContent('dmiAsyncRequestResponseEvent.json')
+ def testEventSent = jsonObjectMapper.convertJsonString(jsonData, DmiAsyncRequestResponseEvent.class)
+ when: 'the event is consumed'
+ ncmpAsyncRequestResponseEventConsumer.consumeAndForward(testEventSent)
+ and: 'the topic is polled'
+ def records = kafkaConsumer.poll(Duration.ofMillis(1500))
+ then: 'poll returns one record'
+ assert records.size() == 1
+ and: 'consumed forwarded event id is the same as sent event id'
+ def record = records.iterator().next()
+ assert testEventSent.eventId.equalsIgnoreCase(jsonObjectMapper.convertJsonString(record.value(),
+ NcmpAsyncRequestResponseEvent).getForwardedEvent().getEventId())
+ }
+
+ @DynamicPropertySource
+ static void registerKafkaProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
+ dynamicPropertyRegistry.add('spring.kafka.bootstrap-servers', kafkaTestContainer::getBootstrapServers)
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapperSpec.groovy
new file mode 100644
index 0000000000..07e9b49ff6
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapperSpec.groovy
@@ -0,0 +1,67 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.async
+
+import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
+import org.onap.cps.ncmp.event.model.EventContent
+import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent
+import org.onap.cps.ncmp.event.model.ResponseData
+import spock.lang.Specification
+
+class NcmpAsyncRequestResponseEventMapperSpec extends Specification {
+
+ def objectUnderTest = Mappers.getMapper(NcmpAsyncRequestResponseEventMapper.class)
+
+ def 'Convert dmi async request response event to ncmp async request response event'() {
+ given: 'a dmi async request response event'
+ def dmiAsyncRequestResponseEvent = new DmiAsyncRequestResponseEvent()
+ .withEventCorrelationId("correlation-id-123").withEventContent(new EventContent()
+ .withResponseData(new ResponseData()))
+ and: 'the event Id and time are empty'
+ dmiAsyncRequestResponseEvent.withEventId('').withEventTime('')
+ when: 'mapper is called'
+ def result = objectUnderTest.toNcmpAsyncEvent(dmiAsyncRequestResponseEvent)
+ then: 'result is of the correct type'
+ assert result.class == NcmpAsyncRequestResponseEvent.class
+ and: 'eventId and eventTime should be overridden by custom method with non-empty values'
+ assert result.eventId != ''
+ assert result.eventTime != ''
+ and: 'target eventCorrelationId of mapped object should be same as source eventCorrelationId'
+ assert result.eventCorrelationId == "correlation-id-123"
+ }
+
+ def 'Dmi async request response event is mapped correctly to forwarded event'() {
+ given: 'a dmi async request response event'
+ def dmiAsyncRequestResponseEvent = new DmiAsyncRequestResponseEvent()
+ .withEventContent(new EventContent().withResponseCode('200')
+ .withResponseData(new ResponseData().withAdditionalProperty('property1', 'value1')
+ .withAdditionalProperty('property2', 'value2')))
+ when: 'mapper is called'
+ def result = objectUnderTest.toNcmpAsyncEvent(dmiAsyncRequestResponseEvent)
+ then: 'result is of the correct type'
+ assert result.class == NcmpAsyncRequestResponseEvent.class
+ and: 'forwarded event content response code is mapped correctly'
+ assert result.forwardedEvent.responseCode == '200'
+ and: 'after mapping additional properties should be stored'
+ result.forwardedEvent.additionalProperties.'response-data' == ['property2': 'value2', 'property1': 'value1']
+ }
+}
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 394df1d076..90839f8ac0 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 Nordix Foundation
+ * Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,17 +22,23 @@
package org.onap.cps.ncmp.api.impl.client
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
+import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
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.springframework.http.HttpHeaders
-import org.springframework.http.HttpMethod
+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 spock.lang.Specification
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
+
+
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient])
class DmiRestClientSpec extends Specification {
@@ -44,14 +50,32 @@ class DmiRestClientSpec extends Specification {
DmiRestClient objectUnderTest
def resourceUrl = 'some url'
+ def mockResponseEntity = Mock(ResponseEntity)
+
def 'DMI POST operation with JSON.'() {
given: 'the rest template returns a valid response entity'
- def mockResponseEntity = Mock(ResponseEntity)
mockRestTemplate.postForEntity(resourceUrl, _ as HttpEntity, Object.class) >> mockResponseEntity
when: 'POST operation is invoked'
- def result = objectUnderTest.postOperationWithJsonData(resourceUrl, 'json-data')
+ def result = objectUnderTest.postOperationWithJsonData(resourceUrl, 'json-data', READ)
then: 'the output of the method is equal to the output from the test template'
result == mockResponseEntity
}
+ 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 }
+ when: 'POST operation is invoked'
+ def result = objectUnderTest.postOperationWithJsonData('some url', 'some json', operation)
+ then: 'a Http Client Exception is thrown'
+ def thrown = thrown(HttpClientRequestException)
+ 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'
+ operation << [CREATE, READ, PATCH]
+ }
+
}
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 2a19df1723..b7ebf2965a 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
@@ -30,12 +30,12 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
-import org.springframework.util.MultiValueMap
import spock.lang.Shared
import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
import org.springframework.http.HttpStatus
@@ -63,7 +63,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
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) >> responseFromDmi
+ mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, READ) >> responseFromDmi
dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
when: 'get resource data is invoked'
def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, resourceIdentifier,
@@ -88,7 +88,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def expectedJson = '{"operation":"' + expectedOperationInUrl + '","dataType":"some data type","data":"requestData","cmHandleProperties":{"prop1":"val1"}}'
def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
- mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson) >> responseFromDmi
+ mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, operation) >> responseFromDmi
when: 'write resource method is invoked'
def result = objectUnderTest.writeResourceDataPassThroughRunningFromDmi(cmHandleId, 'parent/child', operation, 'requestData', 'some data type')
then: 'the result is the response from the DMI service'
@@ -98,4 +98,4 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
CREATE || 'create'
UPDATE || 'update'
}
-} \ No newline at end of file
+}
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 574f609e9e..ed8f08698d 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
@@ -24,7 +24,6 @@ package org.onap.cps.ncmp.api.impl.operations
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.utils.DmiServiceUrlBuilder
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
@@ -33,9 +32,10 @@ import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.test.context.ContextConfiguration
-import org.springframework.web.util.UriComponentsBuilder
import spock.lang.Shared
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
+
@SpringBootTest
@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations])
class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
@@ -56,7 +56,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":{}}')
+ mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"cmHandleProperties":{}}', READ)
>> responseFromDmi
when: 'get module references is called'
def result = objectUnderTest.getModuleReferences(yangModelCmHandle)
@@ -89,7 +89,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
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",
- '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}') >> responseFromDmi
+ '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
when: 'a get module references is called'
def result = objectUnderTest.getModuleReferences(yangModelCmHandle)
then: 'the result is the response from DMI service'
@@ -108,7 +108,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
[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",
- '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}') >> responseFromDmi
+ '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ) >> responseFromDmi
when: 'get new yang resources from DMI service'
def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences)
then: 'the result has the 2 expected yang (re)sources (order is not guaranteed)'
@@ -140,7 +140,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
and: 'a positive response from DMI service when it is called with the expected parameters'
def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
- '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":'+expectedAdditionalPropertiesInRequest+'}') >> responseFromDmi
+ '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi
when: 'get new yang resources from DMI service'
def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, unknownModuleReferences)
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 dae2bcc0ae..193b94d264 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
@@ -25,6 +25,7 @@ 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.inventory.InventoryPersistence
import org.spockframework.spring.SpringBean
import spock.lang.Shared
import spock.lang.Specification
@@ -38,7 +39,7 @@ abstract class DmiOperationsBaseSpec extends Specification {
DmiRestClient mockDmiRestClient = Mock()
@SpringBean
- YangModelCmHandleRetriever mockCmHandlePropertiesRetriever = Mock()
+ InventoryPersistence mockInventoryPersistence = Mock()
@SpringBean
ObjectMapper spyObjectMapper = Spy()
@@ -56,6 +57,6 @@ abstract class DmiOperationsBaseSpec extends Specification {
yangModelCmHandle.dmiServiceName = dmiServiceName
yangModelCmHandle.dmiProperties = dmiProperties
yangModelCmHandle.id = cmHandleId
- mockCmHandlePropertiesRetriever.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
+ mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy
new file mode 100644
index 0000000000..d6f4ba6ede
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy
@@ -0,0 +1,66 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory
+
+import org.onap.cps.spi.model.DataNode
+import org.onap.cps.spi.model.DataNodeBuilder
+import spock.lang.Specification
+
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
+class CompositeStateBuilderSpec extends Specification {
+
+ def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
+
+ def static cmHandleId = 'myHandle1'
+ def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}/state']"
+ def static stateDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/state/lock-reason")
+ .withLeaves(['reason': 'LOCKED_MISBEHAVING', 'details': 'lock details']).build(),
+ new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/state/datastores")
+ .withChildDataNodes(Arrays.asList(new DataNodeBuilder()
+ .withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/state/datastores/operational")
+ .withLeaves(['sync-state': 'UNSYNCHRONIZED']).build())).build()]
+ def static cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: stateDataNodes, leaves: ['cm-handle-state': 'ADVISED'])
+
+ def "Composite State Specification"() {
+ when: 'using composite state builder '
+ def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED)
+ .withLockReason(LockReasonCategory.LOCKED_MISBEHAVING,"").withOperationalDataStores("UNSYNCHRONIZED",
+ formattedDateAndTime.toString()).withLastUpdatedTime(formattedDateAndTime).build()
+ then: 'it matches expected cm handle state and data store sync state'
+ assert compositeState.cmHandleState == CmHandleState.ADVISED
+ assert compositeState.dataStores.operationalDataStore.syncState == 'UNSYNCHRONIZED'
+ }
+
+ def "Build composite state from DataNode "() {
+ given: "a Data Node "
+ new DataNode(leaves: ['cm-handle-state': 'ADVISED'])
+ when: 'build from data node function is invoked'
+ def compositeState = new CompositeStateBuilder().fromDataNode(cmHandleDataNode).build()
+ then: 'it matches expected state model as JSON'
+ assert compositeState.cmHandleState == CmHandleState.ADVISED
+ }
+
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
index 4f4cccfe74..5387fc675f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
@@ -28,21 +28,20 @@ import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import static CompositeState.DataStores
-import static CompositeState.LockReason
import static CompositeState.Operational
-import static CompositeState.Running
import static org.onap.cps.ncmp.utils.TestUtils.getResourceFileContent
import static org.springframework.util.StringUtils.trimAllWhitespace
class CompositeStateSpec extends Specification {
- def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(OffsetDateTime.of(2022, 1, 1, 1, 1, 1, 1, ZoneOffset.MIN))
+ def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
def objectMapper = new ObjectMapper()
def "Composite State Specification"() {
given: "a Composite State"
- def compositeState = new CompositeState(cmhandleState: CmHandleState.ADVISED,
- lockReason: LockReason.builder().reason('lock-reason').details("lock-misbehaving-details").build(),
+ def compositeState = new CompositeState(cmHandleState: CmHandleState.ADVISED,
+ lockReason: CompositeState.LockReason.builder().lockReasonCategory(LockReasonCategory.LOCKED_MISBEHAVING).details("lock misbehaving details").build(),
lastUpdateTime: formattedDateAndTime.toString(),
dataSyncEnabled: false,
dataStores: dataStores())
@@ -56,8 +55,6 @@ class CompositeStateSpec extends Specification {
def dataStores() {
DataStores.builder().operationalDataStore(Operational.builder()
.syncState('NONE_REQUESTED')
- .lastSyncTime(formattedDateAndTime.toString()).build()).runningDataStore(Running.builder()
- .syncState('NONE_REQUESTED')
.lastSyncTime(formattedDateAndTime.toString()).build())
.build()
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
index 5ecc8b0da3..b638eecd41 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
@@ -1,6 +1,6 @@
/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,22 +18,39 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.operations
+package org.onap.cps.ncmp.api.inventory
+import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.api.CpsDataService
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.DataValidationException
+import org.onap.cps.spi.model.DataNode
+import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Shared
+import spock.lang.Specification
+
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-import org.onap.cps.spi.model.DataNode
-import spock.lang.Specification
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-class YangModelCmHandleRetrieverSpec extends Specification {
+class InventoryPersistenceSpec extends Specification {
+
+ def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
def mockCpsDataService = Mock(CpsDataService)
- def objectUnderTest = new YangModelCmHandleRetriever(mockCpsDataService)
+ def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
+
+
+ def objectUnderTest = new InventoryPersistence(spiedJsonObjectMapper, mockCpsDataService, mockCpsDataPersistenceService)
+
+ def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
def cmHandleId = 'some-cm-handle'
def leaves = ["dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
@@ -49,6 +66,9 @@ class YangModelCmHandleRetrieverSpec extends Specification {
@Shared
def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
+ @Shared
+ def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])]
+
def "Retrieve CmHandle using datanode with #scenario."() {
given: 'the cps data service returns a data node from the DMI registry'
def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves)
@@ -63,12 +83,15 @@ class YangModelCmHandleRetrieverSpec extends Specification {
and: 'the expected DMI properties'
result.dmiProperties == expectedDmiProperties
result.publicProperties == expectedPublicProperties
+ and: 'the state details are returned'
+ result.compositeState.cmHandleState == expectedCompositeState
where: 'the following parameters are used'
- scenario | childDataNodes || expectedDmiProperties || expectedPublicProperties
- 'no properties' | [] || [] || []
- 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")]
- 'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || []
- 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")]
+ scenario | childDataNodes || expectedDmiProperties || expectedPublicProperties || expectedCompositeState
+ 'no properties' | [] || [] || [] || null
+ 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null
+ 'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || [] || null
+ 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")] || null
+ 'with state details' | childDataNodesForCmHandleWithState || [] || [] || CmHandleState.ADVISED
}
def "Retrieve CmHandle using datanode with invalid CmHandle id."() {
@@ -91,4 +114,45 @@ class YangModelCmHandleRetrieverSpec extends Specification {
result.dmiDataServiceName == null
result.dmiModelServiceName == null
}
+
+ def 'Get a Cm Handle Composite State'() {
+ given: 'a valid cm handle id'
+ def cmHandleId = 'Some-Cm-Handle'
+ def dataNode = new DataNode(leaves: ['cm-handle-state': 'ADVISED'])
+ and: 'cps data service returns a valid data node'
+ mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
+ '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']/state', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+ when: 'get cm handle state is invoked'
+ def result = objectUnderTest.getCmHandleState(cmHandleId)
+ then: 'result has returned the correct cm handle state'
+ result.cmHandleState == CmHandleState.ADVISED
+ }
+
+ def 'Update Cm Handle with #scenario State'() {
+ given: 'a cm handle and a composite state'
+ def cmHandleId = 'Some-Cm-Handle'
+ def compositeState = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
+ when: 'update cm handle state is invoked with the #scenario state'
+ objectUnderTest.saveCmHandleState(cmHandleId, compositeState)
+ then: 'update node leaves is invoked with the correct params'
+ 1 * mockCpsDataService.replaceNodeTree('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry/cm-handles[@id=\'Some-Cm-Handle\']', expectedJsonData, _ as OffsetDateTime)
+ where: 'the following states are used'
+ scenario | cmHandleState || expectedJsonData
+ 'READY' | CmHandleState.READY || '{"state":{"cm-handle-state":"READY","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
+ 'LOCKED' | CmHandleState.LOCKED || '{"state":{"cm-handle-state":"LOCKED","last-update-time":"2022-12-31T20:30:40.000+0000"}}'
+ }
+
+ def 'Get Cm Handles By State'() {
+ given: 'a cm handle state to query'
+ def cmHandleState = CmHandleState.ADVISED
+ and: 'cps data service returns a list of data nodes'
+ def dataNodes = [new DataNode()]
+ mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+ '//state[@cm-handle-state="ADVISED"]/ancestor::cm-handles', OMIT_DESCENDANTS) >> dataNodes
+ when: 'get cm handles by state is invoked'
+ def result = objectUnderTest.getCmHandlesByState(cmHandleState)
+ then: 'the returned result is a list of data nodes returned by cps data service'
+ assert result == dataNodes
+ }
+
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/CmHandleStateSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/CmHandleStateSpec.groovy
deleted file mode 100644
index bfc5c6fa39..0000000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/CmHandleStateSpec.groovy
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2022 Nordix Foundation
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.api.inventory.sync
-
-import org.onap.cps.ncmp.api.inventory.CmHandleState
-import spock.lang.Specification
-
-class CmHandleStateSpec extends Specification{
-
- def 'Transition to READY state from ADVISED state'() {
- given: 'a cm handle with an ADVISED state'
- def cmHandleState = CmHandleState.ADVISED
- when: 'the state transitions to the READY state'
- cmHandleState = CmHandleState.READY
- then: 'the cm handle state changes to READY'
- assert CmHandleState.READY == cmHandleState
- }
-
- def 'Transition to READY state from READY state'() {
- given: 'a cm handle with a READY state'
- def cmHandleState = CmHandleState.READY
- when: 'the state transitions to READY state'
- cmHandleState = CmHandleState.READY
- then: 'the cm handle state remains as READY'
- assert CmHandleState.READY == cmHandleState
- }
-
-}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
index 35de99fefb..bcfe47fd5d 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
@@ -1,5 +1,5 @@
/*
- * ============LICENSE_START=======================================================
+ * ============LICENSE_START=======================================================
* Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,40 +20,72 @@
package org.onap.cps.ncmp.api.inventory.sync
-
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import spock.lang.Specification
class ModuleSyncSpec extends Specification {
+ def mockInventoryPersistence = Mock(InventoryPersistence)
+
def mockSyncUtils = Mock(SyncUtils)
def mockModuleSyncService = Mock(ModuleSyncService)
def cmHandleState = CmHandleState.ADVISED
- def objectUnderTest = new ModuleSyncWatchdog(mockSyncUtils, mockModuleSyncService)
+ def objectUnderTest = new ModuleSyncWatchdog(mockInventoryPersistence, mockSyncUtils, mockModuleSyncService)
def 'Schedule a Cm-Handle Sync for ADVISED Cm-Handles'() {
given: 'cm handles in an advised state'
- def compositeState = new CompositeState()
- compositeState.cmhandleState = cmHandleState
- def yangModelCmHandle1 = new YangModelCmHandle(compositeState: compositeState)
- def yangModelCmHandle2 = new YangModelCmHandle(compositeState: compositeState)
+ def compositeState1 = new CompositeState(cmHandleState: cmHandleState)
+ def compositeState2 = new CompositeState(cmHandleState: cmHandleState)
+ def yangModelCmHandle1 = new YangModelCmHandle(id: 'some-cm-handle', compositeState: compositeState1)
+ def yangModelCmHandle2 = new YangModelCmHandle(id: 'some-cm-handle-2', compositeState: compositeState2)
and: 'sync utilities return a cm handle twice'
mockSyncUtils.getAnAdvisedCmHandle() >>> [yangModelCmHandle1, yangModelCmHandle2, null]
when: 'module sync poll is executed'
objectUnderTest.executeAdvisedCmHandlePoll()
- then: 'module sync service syncs the first cm handle and creates a schema set'
+ then: 'the inventory persistence cm handle returns a composite state for the first cm handle'
+ 1 * mockInventoryPersistence.getCmHandleState('some-cm-handle') >> compositeState1
+ and: 'module sync service syncs the first cm handle and creates a schema set'
1 * mockModuleSyncService.syncAndCreateSchemaSet(yangModelCmHandle1)
- and: 'the first cm handle is updated to state "READY" from "ADVISED"'
- 1 * mockSyncUtils.updateCmHandleState(yangModelCmHandle1, CmHandleState.READY)
- then: 'module sync service syncs the second cm handle and creates a schema set'
+ and: 'the composite state cm handle state is now READY'
+ assert compositeState1.getCmHandleState() == CmHandleState.READY
+ and: 'the first cm handle state is updated'
+ 1 * mockInventoryPersistence.saveCmHandleState('some-cm-handle', compositeState1)
+ then: 'the inventory persistence cm handle returns a composite state for the second cm handle'
+ mockInventoryPersistence.getCmHandleState('some-cm-handle-2') >> compositeState2
+ and: 'module sync service syncs the second cm handle and creates a schema set'
1 * mockModuleSyncService.syncAndCreateSchemaSet(yangModelCmHandle2)
- then: 'the second cm handle is updated to state "READY" from "ADVISED"'
- 1 * mockSyncUtils.updateCmHandleState(yangModelCmHandle2, CmHandleState.READY)
+ and: 'the composite state cm handle state is now READY'
+ assert compositeState2.getCmHandleState() == CmHandleState.READY
+ and: 'the second cm handle state is updated'
+ 1 * mockInventoryPersistence.saveCmHandleState('some-cm-handle-2', compositeState2)
+ }
+
+ def 'Schedule a Cm-Handle Sync for ADVISED Cm-Handle with failure'() {
+ given: 'cm handles in an advised state'
+ def compositeState = new CompositeState(cmHandleState: cmHandleState)
+ def yangModelCmHandle = new YangModelCmHandle(id: 'some-cm-handle', compositeState: compositeState)
+ and: 'sync utilities return a cm handle'
+ mockSyncUtils.getAnAdvisedCmHandle() >>> [yangModelCmHandle, null]
+ when: 'module sync poll is executed'
+ objectUnderTest.executeAdvisedCmHandlePoll()
+ then: 'the inventory persistence cm handle returns a composite state for the cm handle'
+ 1 * mockInventoryPersistence.getCmHandleState('some-cm-handle') >> compositeState
+ and: 'module sync service attempts to sync the cm handle and throws an exception'
+ 1 * mockModuleSyncService.syncAndCreateSchemaSet(*_) >> { throw new Exception('some exception') }
+ and: 'the composite state cm handle state is now LOCKED'
+ assert compositeState.getCmHandleState() == CmHandleState.LOCKED
+ and: 'update lock reason, details and attempts is invoked'
+ 1 * mockSyncUtils.updateLockReasonDetailsAndAttempts(compositeState, LockReasonCategory.LOCKED_MISBEHAVING ,'some exception')
+ and: 'the cm handle state is updated'
+ 1 * mockInventoryPersistence.saveCmHandleState('some-cm-handle', compositeState)
+
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
index c80263ef05..7d67acccc3 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
@@ -20,29 +20,22 @@
package org.onap.cps.ncmp.api.inventory.sync
-import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.api.CpsDataService
-import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.model.DataNode
-import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Shared
import spock.lang.Specification
-import java.time.OffsetDateTime
-
class SyncUtilsSpec extends Specification{
- def mockCpsDataService = Mock(CpsDataService)
def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService)
- def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
- def mockYangModelCmHandleRetriever = Mock(YangModelCmHandleRetriever)
+ def mockInventoryPersistence = Mock(InventoryPersistence)
- def objectUnderTest = new SyncUtils(mockCpsDataService, mockCpsDataPersistenceService, spiedJsonObjectMapper, mockYangModelCmHandleRetriever)
+ def objectUnderTest = new SyncUtils(mockInventoryPersistence)
@Shared
def dataNode = new DataNode(leaves: ['id': 'cm-handle-123'])
@@ -50,16 +43,14 @@ class SyncUtilsSpec extends Specification{
def 'Get an advised Cm-Handle where ADVISED cm handle #scenario'() {
- given: 'the cps (persistence service) returns a collection of data nodes'
- mockCpsDataPersistenceService.queryDataNodes('NCMP-Admin',
- 'ncmp-dmi-registry', '//cm-handles[@state=\"ADVISED\"]',
- FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNodeCollection
+ given: 'the inventory persistence service returns a collection of data nodes'
+ mockInventoryPersistence.getCmHandlesByState(CmHandleState.ADVISED) >> dataNodeCollection
when: 'get advised cm handle is called'
objectUnderTest.getAnAdvisedCmHandle()
then: 'the returned data node collection is the correct size'
dataNodeCollection.size() == expectedDataNodeSize
and: 'get yang model cm handles is invoked the correct number of times'
- expectedCallsToGetYangModelCmHandle * mockYangModelCmHandleRetriever.getYangModelCmHandle('cm-handle-123')
+ expectedCallsToGetYangModelCmHandle * mockInventoryPersistence.getYangModelCmHandle('cm-handle-123')
where: 'the following scenarios are used'
scenario | dataNodeCollection || expectedCallsToGetYangModelCmHandle | expectedDataNodeSize
'exists' | [ dataNode ] || 1 | 1
@@ -67,16 +58,18 @@ class SyncUtilsSpec extends Specification{
}
- def 'Update cm handle state from Advised to Ready'() {
- given: 'a yang model cm handle and the expected json data'
- def compositeState = new CompositeState()
- compositeState.cmhandleState = CmHandleState.ADVISED
- def yangModelCmHandle = new YangModelCmHandle(id: 'Some-Cm-Handle', compositeState: compositeState )
- def expectedJsonData = '{"cm-handles":[{"id":"Some-Cm-Handle","state":{"cm-handle-state":"READY"}}]}'
- when: 'update cm handle state is called'
- objectUnderTest.updateCmHandleState(yangModelCmHandle, CmHandleState.READY)
- then: 'update data note leaves is invoked with the correct params'
- 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, _ as OffsetDateTime)
+ def 'Update Lock Reason, Details and Attempts where lock reason #scenario'() {
+ given: 'A locked state'
+ def compositeState = new CompositeState(lockReason: lockReason)
+ when: 'update cm handle details and attempts is called'
+ objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, LockReasonCategory.LOCKED_MISBEHAVING, 'new error message')
+ then: 'the composite state lock reason and details are updated'
+ assert compositeState.lockReason.lockReasonCategory == LockReasonCategory.LOCKED_MISBEHAVING
+ assert compositeState.lockReason.details == expectedDetails
+ where:
+ scenario | lockReason || expectedDetails
+ 'does not exist' | null || 'Attempt #1 failed: new error message'
+ 'exists' | CompositeState.LockReason.builder().details("Attempt #2 failed: some error message").build() || 'Attempt #3 failed: new error message'
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy
index 4c8dcace7d..964826be13 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy
@@ -45,14 +45,14 @@ class DmiServiceUrlBuilderSpec extends Specification {
def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
"cmHandle", PASSTHROUGH_RUNNING)
and: 'query params'
- def uriQueries = objectUnderTest.populateQueryParams(resourceId,
- 'optionsParamInQuery', topicParamInQuery)
+ 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 | topicParamInQuery | resourceId || expectedDmiServiceUrl
+ 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'
diff --git a/cps-ncmp-service/src/test/resources/dmiAsyncRequestResponseEvent.json b/cps-ncmp-service/src/test/resources/dmiAsyncRequestResponseEvent.json
new file mode 100644
index 0000000000..bf6c86aaac
--- /dev/null
+++ b/cps-ncmp-service/src/test/resources/dmiAsyncRequestResponseEvent.json
@@ -0,0 +1,30 @@
+{
+ "eventId": "8dbfe0a7-3b28-4109-8fcb-9fbc9c37d56a",
+ "eventCorrelationId": "122ca20b-4f8c-4759-a2b4-f0b9456df204",
+ "eventTime": "2022-05-09T13:34:50.466+0000",
+ "eventSource": "org.onap.ncmp",
+ "eventSchema": "urn:cps:org.onap.cps:async-request-response-event-schema:v1",
+ "eventTarget": "test-topic",
+ "eventContent": {
+ "response-data-schema": "urn:cps:org.onap.cps:async-request-response-event-schema:v1",
+ "response-status": "SUCCESS",
+ "response-code": "200",
+ "response-data": {
+ "ietf-netconf-monitoring:netconf-state": {
+ "schemas": {
+ "schema": [
+ {
+ "identifier": "ietf-tls-server",
+ "version": "2016-11-02",
+ "format": "ietf-netconf-monitoring:yang",
+ "namespace": "urn:ietf:params:xml:ns:yang:ietf-tls-server",
+ "location": [
+ "NETCONF"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+}
diff --git a/cps-ncmp-service/src/test/resources/expectedStateModel.json b/cps-ncmp-service/src/test/resources/expectedStateModel.json
index a416194343..5d246d5dd1 100644
--- a/cps-ncmp-service/src/test/resources/expectedStateModel.json
+++ b/cps-ncmp-service/src/test/resources/expectedStateModel.json
@@ -1,19 +1,15 @@
{
"cm-handle-state" : "ADVISED",
"lock-reason" : {
- "reason" : "lock-reason",
- "details" : "lock-misbehaving-details"
+ "reason" : "LOCKED_MISBEHAVING",
+ "details" : "lock misbehaving details"
},
- "last-update-time" : "2022-01-01T01:01:01.000-1800",
+ "last-update-time" : "2022-12-31T20:30:40.000+0000",
"data-sync-enabled" : false,
"datastores" : {
"operational" : {
"sync-state" : "NONE_REQUESTED",
- "last-sync-time" : "2022-01-01T01:01:01.000-1800"
- },
- "running" : {
- "sync-state" : "NONE_REQUESTED",
- "last-sync-time" : "2022-01-01T01:01:01.000-1800"
+ "last-sync-time" : "2022-12-31T20:30:40.000+0000"
}
}
} \ No newline at end of file
diff --git a/cps-service/pom.xml b/cps-service/pom.xml
index b9d6268746..1be45d1bce 100644
--- a/cps-service/pom.xml
+++ b/cps-service/pom.xml
@@ -1,165 +1,165 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ============LICENSE_START=======================================================
- Copyright (C) 2021-2022 Nordix Foundation
- Modifications Copyright (C) 2021 Bell Canada.
- Modifications Copyright (C) 2021 Pantheon.tech
- ================================================================================
- 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=========================================================
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.onap.cps</groupId>
- <artifactId>cps-parent</artifactId>
- <version>3.1.0-SNAPSHOT</version>
- <relativePath>../cps-parent/pom.xml</relativePath>
- </parent>
-
- <artifactId>cps-service</artifactId>
-
- <properties>
- <minimum-coverage>0.94</minimum-coverage>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.onap.cps</groupId>
- <artifactId>cps-events</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-model-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-model-util</artifactId>
- </dependency>
- <!-- required for processing yang data in json format -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-codec-gson</artifactId>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-cache</artifactId>
- </dependency>
- <dependency>
- <groupId>com.github.ben-manes.caffeine</groupId>
- <artifactId>caffeine</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.kafka</groupId>
- <artifactId>spring-kafka</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-messaging</artifactId>
- </dependency>
- <dependency>
- <!-- For logging -->
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <!-- For dependency injection -->
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
- </dependency>
- <dependency>
- <!-- For parsing JSON object -->
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
- <dependency>
- <groupId>net.logstash.logback</groupId>
- <artifactId>logstash-logback-encoder</artifactId>
- </dependency>
- <dependency>
- <groupId>org.codehaus.janino</groupId>
- <artifactId>janino</artifactId>
- </dependency>
- <!-- T E S T D E P E N D E N C I E S -->
- <dependency>
- <groupId>org.codehaus.groovy</groupId>
- <artifactId>groovy</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.groovy</groupId>
- <artifactId>groovy-json</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.spockframework</groupId>
- <artifactId>spock-core</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.spockframework</groupId>
- <artifactId>spock-spring</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib-nodep</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.testcontainers</groupId>
- <artifactId>kafka</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.kafka</groupId>
- <artifactId>spring-kafka-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjrt</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (C) 2021-2022 Nordix Foundation
+ Modifications Copyright (C) 2021 Bell Canada.
+ Modifications Copyright (C) 2021 Pantheon.tech
+ ================================================================================
+ 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=========================================================
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-parent</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ <relativePath>../cps-parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>cps-service</artifactId>
+
+ <properties>
+ <minimum-coverage>0.94</minimum-coverage>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onap.cps</groupId>
+ <artifactId>cps-events</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ </dependency>
+ <!-- required for processing yang data in json format -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-codec-gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-cache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.ben-manes.caffeine</groupId>
+ <artifactId>caffeine</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.kafka</groupId>
+ <artifactId>spring-kafka</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-messaging</artifactId>
+ </dependency>
+ <dependency>
+ <!-- For logging -->
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <!-- For dependency injection -->
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-validation</artifactId>
+ </dependency>
+ <dependency>
+ <!-- For parsing JSON object -->
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.logstash.logback</groupId>
+ <artifactId>logstash-logback-encoder</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.janino</groupId>
+ <artifactId>janino</artifactId>
+ </dependency>
+ <!-- T E S T D E P E N D E N C I E S -->
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-json</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-spring</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>kafka</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.kafka</groupId>
+ <artifactId>spring-kafka-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
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 2f1067aafe..0772a8c9f6 100755
--- 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
@@ -22,6 +22,10 @@
package org.onap.cps.api.impl;
+import static org.onap.cps.notification.Operation.CREATE;
+import static org.onap.cps.notification.Operation.DELETE;
+import static org.onap.cps.notification.Operation.UPDATE;
+
import java.time.OffsetDateTime;
import java.util.Collection;
import lombok.AllArgsConstructor;
@@ -61,7 +65,7 @@ public class CpsDataServiceImpl implements CpsDataService {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
final DataNode dataNode = buildDataNode(dataspaceName, anchorName, ROOT_NODE_XPATH, jsonData);
cpsDataPersistenceService.storeDataNode(dataspaceName, anchorName, dataNode);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, ROOT_NODE_XPATH, Operation.CREATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, ROOT_NODE_XPATH, CREATE);
}
@Override
@@ -70,7 +74,7 @@ public class CpsDataServiceImpl implements CpsDataService {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData);
cpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, parentNodeXpath, dataNode);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.CREATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, CREATE);
}
@Override
@@ -81,7 +85,7 @@ public class CpsDataServiceImpl implements CpsDataService {
buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonData);
cpsDataPersistenceService.addListElements(dataspaceName, anchorName, parentNodeXpath,
listElementDataNodeCollection);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, UPDATE);
}
@Override
@@ -98,7 +102,7 @@ public class CpsDataServiceImpl implements CpsDataService {
final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData);
cpsDataPersistenceService
.updateDataLeaves(dataspaceName, anchorName, dataNode.getXpath(), dataNode.getLeaves());
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, UPDATE);
}
@Override
@@ -113,7 +117,7 @@ public class CpsDataServiceImpl implements CpsDataService {
for (final DataNode dataNodeUpdate : dataNodeUpdates) {
processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate);
}
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, UPDATE);
}
@Override
@@ -143,7 +147,7 @@ public class CpsDataServiceImpl implements CpsDataService {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
final DataNode dataNode = buildDataNode(dataspaceName, anchorName, parentNodeXpath, jsonData);
cpsDataPersistenceService.replaceDataNodeTree(dataspaceName, anchorName, dataNode);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, UPDATE);
}
@Override
@@ -160,7 +164,7 @@ public class CpsDataServiceImpl implements CpsDataService {
final Collection<DataNode> dataNodes, final OffsetDateTime observedTimestamp) {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
cpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, dataNodes);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, UPDATE);
}
@Override
@@ -168,7 +172,7 @@ public class CpsDataServiceImpl implements CpsDataService {
final OffsetDateTime observedTimestamp) {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
cpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, dataNodeXpath);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, dataNodeXpath, Operation.DELETE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, dataNodeXpath, DELETE);
}
@Override
@@ -177,7 +181,7 @@ public class CpsDataServiceImpl implements CpsDataService {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Anchor anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
cpsDataPersistenceService.deleteDataNodes(dataspaceName, anchorName);
- processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, Operation.DELETE, observedTimestamp);
+ processDataUpdatedEventAsync(anchor, ROOT_NODE_XPATH, DELETE, observedTimestamp);
}
@Override
@@ -185,7 +189,7 @@ public class CpsDataServiceImpl implements CpsDataService {
final OffsetDateTime observedTimestamp) {
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
cpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, listNodeXpath);
- processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, listNodeXpath, Operation.DELETE);
+ processDataUpdatedEventAsync(dataspaceName, anchorName, observedTimestamp, listNodeXpath, DELETE);
}
private DataNode buildDataNode(final String dataspaceName, final String anchorName,
@@ -233,10 +237,13 @@ public class CpsDataServiceImpl implements CpsDataService {
this.processDataUpdatedEventAsync(anchor, xpath, operation, observedTimestamp);
}
- private void processDataUpdatedEventAsync(final Anchor anchor, final String xpath, final Operation operation,
+ private void processDataUpdatedEventAsync(final Anchor anchor,
+ final String xpath,
+ final Operation operation,
final OffsetDateTime observedTimestamp) {
try {
- notificationService.processDataUpdatedEvent(anchor, observedTimestamp, xpath, operation);
+ notificationService.processDataUpdatedEvent(anchor, observedTimestamp, xpath,
+ operation);
} catch (final Exception exception) {
//If async message can't be queued for notification service, the initial request should not failed.
log.error("Failed to send message to notification service", exception);
diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy
index 5124a519a7..05b9624a47 100644
--- a/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/notification/KafkaTestContainerConfig.groovy
@@ -33,7 +33,7 @@ class KafkaTestContainerConfig {
// Not the best performance but it is good enough for test case
private static synchronized KafkaContainer getKafkaContainer() {
if (kafkaContainer == null) {
- kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.1.1"))
+ kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.2.1"))
.withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "false")
kafkaContainer.start()
Runtime.getRuntime().addShutdownHook(new Thread(kafkaContainer::stop))
diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
index 6ef6874b33..8263c31f07 100644
--- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy
@@ -29,7 +29,6 @@ import org.spockframework.spring.SpringSpy
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.test.context.ContextConfiguration
import spock.lang.Shared
import spock.lang.Specification
@@ -107,18 +106,18 @@ class NotificationServiceSpec extends Specification {
1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent)
where:
scenario | xpath | operation || expectedOperationInEvent
- 'Same event is sent when root nodes' | '' | Operation.CREATE || Operation.CREATE
- 'Same event is sent when root nodes' | '' | Operation.UPDATE || Operation.UPDATE
- 'Same event is sent when root nodes' | '' | Operation.DELETE || Operation.DELETE
- 'Same event is sent when root nodes' | '/' | Operation.CREATE || Operation.CREATE
- 'Same event is sent when root nodes' | '/' | Operation.UPDATE || Operation.UPDATE
- 'Same event is sent when root nodes' | '/' | Operation.DELETE || Operation.DELETE
- 'Same event is sent when container nodes' | '/parent' | Operation.CREATE || Operation.CREATE
- 'Same event is sent when container nodes' | '/parent' | Operation.UPDATE || Operation.UPDATE
- 'Same event is sent when container nodes' | '/parent' | Operation.DELETE || Operation.DELETE
- 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.CREATE || Operation.UPDATE
- 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.UPDATE || Operation.UPDATE
- 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.DELETE || Operation.UPDATE
+ 'Same event is sent when root nodes' | '' | Operation.CREATE || Operation.CREATE
+ 'Same event is sent when root nodes' | '' | Operation.UPDATE || Operation.UPDATE
+ 'Same event is sent when root nodes' | '' | Operation.DELETE || Operation.DELETE
+ 'Same event is sent when root nodes' | '/' | Operation.CREATE || Operation.CREATE
+ 'Same event is sent when root nodes' | '/' | Operation.UPDATE || Operation.UPDATE
+ 'Same event is sent when root nodes' | '/' | Operation.DELETE || Operation.DELETE
+ 'Same event is sent when container nodes' | '/parent' | Operation.CREATE || Operation.CREATE
+ 'Same event is sent when container nodes' | '/parent' | Operation.UPDATE || Operation.UPDATE
+ 'Same event is sent when container nodes' | '/parent' | Operation.DELETE || Operation.DELETE
+ 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.CREATE || Operation.UPDATE
+ 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.UPDATE || Operation.UPDATE
+ 'UPDATE event is sent when non root nodes' | '/parent/child' | Operation.DELETE || Operation.UPDATE
}
def 'Error handling in notification service.'() {
diff --git a/cps-service/src/test/resources/application.yml b/cps-service/src/test/resources/application.yml
index 436c3d4c34..a28b400834 100644
--- a/cps-service/src/test/resources/application.yml
+++ b/cps-service/src/test/resources/application.yml
@@ -1,5 +1,6 @@
# ============LICENSE_START=======================================================
# Copyright (c) 2021 Bell Canada.
+# Modification Copyright (C) 2022 Nordix Foundation.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/csit/plans/cps/setup.sh b/csit/plans/cps/setup.sh
index d633b1ee26..59542402f2 100755
--- a/csit/plans/cps/setup.sh
+++ b/csit/plans/cps/setup.sh
@@ -1,6 +1,7 @@
#!/bin/bash
#
# Copyright 2016-2017 Huawei Technologies Co., Ltd.
+# Modifications Copyright (C) 2022 Nordix Foundation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -64,20 +65,7 @@ cd $WORKSPACE/archives/dc-cps
curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` > docker-compose
chmod +x docker-compose
-# start CPS and PostgreSQL containers with docker compose
-./docker-compose up -d
-
-###################### setup onap-dmi-plugin ############################
-
-cd $WORKSPACE/archives
-git clone "https://gerrit.onap.org/r/cps/ncmp-dmi-plugin"
-mkdir -p $WORKSPACE/archives/dc-dmi
-cat $WORKSPACE/archives/ncmp-dmi-plugin/docker-compose/docker-compose.yml
-cp $WORKSPACE/archives/ncmp-dmi-plugin/docker-compose/*.yml $WORKSPACE/archives/dc-dmi
-cd $WORKSPACE/archives/dc-dmi
-# copy docker-compose (downloaded already for cps)
-cp $WORKSPACE/archives/dc-cps/docker-compose .
-chmod +x docker-compose
+# start CPS/NCMP, DMI, and PostgreSQL containers with docker compose
./docker-compose up -d
###################### setup sdnc #######################################
diff --git a/csit/tests/ncmp-passthrough/ncmp-passthrough.robot b/csit/tests/ncmp-passthrough/ncmp-passthrough.robot
index 32d9604e50..95a8d535c7 100644
--- a/csit/tests/ncmp-passthrough/ncmp-passthrough.robot
+++ b/csit/tests/ncmp-passthrough/ncmp-passthrough.robot
@@ -36,6 +36,13 @@ ${netconf} NETCONF
*** Test Cases ***
+Get for Passthrough Operational (CF, RO) with fields & topic
+ ${uri}= Set Variable ${ncmpBasePath}/v1/ch/PNFDemo/data/ds/ncmp-datastore:passthrough-operational?resourceIdentifier=ietf-netconf-monitoring:netconf-state&options=(fields=schemas/schema)&topic=test-topic
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= Get On Session CPS_URL ${uri} headers=${headers} expected_status=200
+ ${responseJson}= Set Variable ${response.json()}
+ Should Be Equal As Strings ${response.status_code} 200
+
Get for Passthrough Operational (CF, RO) with fields
${uri}= Set Variable ${ncmpBasePath}/v1/ch/PNFDemo/data/ds/ncmp-datastore:passthrough-operational?resourceIdentifier=ietf-netconf-monitoring:netconf-state&options=(fields=schemas/schema)
${headers}= Create Dictionary Authorization=${auth}
@@ -136,4 +143,4 @@ Patch will add new category with new book and add a new book to an existing cate
${verifyUri}= Set Variable ${ncmpBasePath}/v1/ch/PNFDemo/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=stores:bookstore/categories=02/books=A%20New%20book%20in%20existing%20category
${verifyResponse}= Get On Session CPS_URL ${verifyUri} headers=${verifyHeaders}
${responseJson}= Set Variable ${verifyResponse.json()}
- Should Be Equal As Strings ${verifyResponse.status_code} 200 \ No newline at end of file
+ Should Be Equal As Strings ${verifyResponse.status_code} 200
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
index 44ebd3bc69..f2f477fe19 100755
--- a/docker-compose/docker-compose.yml
+++ b/docker-compose/docker-compose.yml
@@ -1,7 +1,7 @@
# ============LICENSE_START=======================================================
# Copyright (c) 2020 Pantheon.tech.
# Modifications Copyright (C) 2021 Bell Canada.
-# Modifications Copyright (C) 2021 Nordix Foundation
+# Modifications Copyright (C) 2021-2022 Nordix Foundation.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -64,7 +64,7 @@ services:
# - dbpostgresql
# zookeeper:
- # image: confluentinc/cp-zookeeper:6.1.1
+ # image: confluentinc/cp-zookeeper:6.2.1
# environment:
# ZOOKEEPER_CLIENT_PORT: 2181
# ZOOKEEPER_TICK_TIME: 2000
@@ -72,7 +72,7 @@ services:
# - 22181:2181
#
# kafka:
- # image: confluentinc/cp-kafka:6.1.1
+ # image: confluentinc/cp-kafka:6.2.1
# depends_on:
# - zookeeper
# ports:
@@ -109,9 +109,58 @@ services:
DB_PASSWORD: ${DB_PASSWORD:-cps}
DMI_USERNAME: ${DMI_USERNAME:-cpsuser}
DMI_PASSWORD: ${DMI_PASSWORD:-cpsr0cks!}
- #KAFKA_BOOTSTRAP_SERVER: kafka:9092
- #notification.data-updated.enabled: 'true'
- #NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'
+ KAFKA_BOOTSTRAP_SERVER: kafka:9092
+ notification.data-updated.enabled: 'true'
+ NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'
restart: unless-stopped
depends_on:
- - dbpostgresql \ No newline at end of file
+ - dbpostgresql
+
+ ### if kafka is not required comment out zookeeper and kafka ###
+ zookeeper:
+ image: confluentinc/cp-zookeeper:6.2.1
+ container_name: zookeeper
+ ports:
+ - '2181:2181'
+ environment:
+ ZOOKEEPER_CLIENT_PORT: 2181
+
+ kafka:
+ image: confluentinc/cp-kafka:6.2.1
+ container_name: kafka
+ ports:
+ - "19092:19092"
+ depends_on:
+ - zookeeper
+ environment:
+ KAFKA_BROKER_ID: 1
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,CONNECTIONS_FROM_HOST://localhost:19092
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONNECTIONS_FROM_HOST:PLAINTEXT
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+
+ ### Comment out this section if dmi plugin is not required ###
+ ncmp-dmi-plugin:
+ container_name: ncmp-dmi-plugin
+ image: ${DOCKER_REPO:-nexus3.onap.org:10003}/onap/ncmp-dmi-plugin:${DMI_VERSION:-1.2.0-SNAPSHOT-latest}
+ ports:
+ - ${DMI_PORT:-8783}:8080
+ - ${DMI_MANAGEMENT_PORT:-8787}:8081
+ environment:
+ CPS_USERNAME: ${CPS_CORE_USERNAME:-cpsuser}
+ CPS_PASSWORD: ${CPS_CORE_PASSWORD:-cpsr0cks!}
+ CPS_CORE_HOST: ${CPS_CORE_HOST:-cps-and-ncmp}
+ CPS_CORE_PORT: ${CPS_CORE_PORT:-8080}
+ CPS_CORE_USERNAME: ${CPS_CORE_USERNAME:-cpsuser}
+ CPS_CORE_PASSWORD: ${CPS_CORE_PASSWORD:-cpsr0cks!}
+ SDNC_HOST: ${SDNC_HOST:-sdnc}
+ SDNC_PORT: ${SDNC_PORT:-8181}
+ SDNC_USERNAME: ${SDNC_USERNAME:-admin}
+ SDNC_PASSWORD: ${SDNC_PASSWORD:-Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U}
+ DMI_SERVICE_URL: ${DMI_SERVICE_URL:-http://ncmp-dmi-plugin:8783}
+ DMI_USERNAME: ${DMI_USERNAME:-cpsuser}
+ DMI_PASSWORD: ${DMI_PASSWORD:-cpsr0cks!}
+ KAFKA_BOOTSTRAP_SERVER: kafka:9092
+ notification.data-updated.enabled: 'true'
+ NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'
+ restart: unless-stopped
diff --git a/jacoco-report/pom.xml b/jacoco-report/pom.xml
index d1181d367c..b8f18e734f 100644
--- a/jacoco-report/pom.xml
+++ b/jacoco-report/pom.xml
@@ -69,6 +69,7 @@
<exclude>org/onap/cps/ncmp/rest/model/*</exclude>
<exclude>org/onap/cps/ncmp/rest/controller/*MapperImpl.class</exclude>
<exclude>org/onap/cps/rest/controller/*MapperImpl.class</exclude>
+ <exclude>org/onap/cps/ncmp/api/impl/async/*MapperImpl.class</exclude>
</excludes>
</configuration>
<executions>
diff --git a/pom.xml b/pom.xml
index 23ef44bb5b..12d8a1f44c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,7 @@
<module>cps-events</module>
<module>cps-service</module>
<module>cps-rest</module>
+ <module>cps-ncmp-events</module>
<module>cps-ncmp-service</module>
<module>cps-ncmp-rest</module>
<module>cps-path-parser</module>