Merge "Add module name to cps core output"
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 7ad22e9..af886a1 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -70,6 +70,22 @@
         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 @@
             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 e468926..9b864b0 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 0000000..2d49a4c
--- /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 0000000..528c063
--- /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 0000000..3fd15bd
--- /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 0000000..84fc12e
--- /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 32d25e3..5fe47e4 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -209,6 +209,8 @@
           example: my-cm-handle1
         publicCmHandleProperties:
           $ref: '#/components/schemas/CmHandlePublicProperties'
+        state:
+          $ref: '#/components/schemas/RestOutputCmHandleState'
     CmHandlePublicProperties:
       type: array
       items:
@@ -216,6 +218,50 @@
         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 ca7e258..11517bc 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.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.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 @@
     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 @@
                                                                         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();
-
-        final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle,
-                resourceIdentifier,
-                optionsParamInQuery,
-                asyncResponseData == null ? NO_TOPIC : topicParamInQuery,
-                asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString());
-
-        if (asyncResponseData == null) {
-            return ResponseEntity.ok(responseObject);
+        if (isValidTopic(topicParamInQuery)) {
+            final String requestId = UUID.randomUUID().toString();
+            cpsNcmpTaskExecutor.executeTask(() ->
+                networkCmProxyDataService.getResourceDataOperationalForCmHandle(
+                    cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery,
+                        requestId
+                ), timeOutInMilliSeconds
+            );
+            return acknowledgeAsyncRequest(requestId);
         }
-        return ResponseEntity.ok(asyncResponse);
+
+        final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(
+            cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
+
+        return ResponseEntity.ok(responseObject);
     }
 
     /**
@@ -123,19 +131,21 @@
                                                                     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();
-
-        final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle,
-                resourceIdentifier,
-                optionsParamInQuery,
-                asyncResponseData == null ? NO_TOPIC : topicParamInQuery,
-                asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString());
-
-        if (asyncResponseData == null) {
-            return ResponseEntity.ok(responseObject);
+        if (isValidTopic(topicParamInQuery)) {
+            final String resourceDataRequestId = UUID.randomUUID().toString();
+            cpsNcmpTaskExecutor.executeTask(() ->
+                networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
+                    cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery,
+                        resourceDataRequestId
+                ), timeOutInMilliSeconds
+            );
+            return acknowledgeAsyncRequest(resourceDataRequestId);
         }
-        return ResponseEntity.ok(asyncResponse);
+
+        final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
+            cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
+
+        return ResponseEntity.ok(responseObject);
     }
 
     @Override
@@ -312,21 +322,12 @@
         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 @@
         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 0000000..3e8929d
--- /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 0000000..93aa285
--- /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 0000000..5f4b311
--- /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 ba49321..60ea736 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 @@
     @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 @@
     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 @@
                     "?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 @@
             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 @@
             ':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 751fdcd..45ed3d3 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.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 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 @@
     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 @@
 
     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 0000000..22c9fe6
--- /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 f2ca8c7..0241696 100644
--- a/cps-ncmp-rest/src/test/resources/application.yml
+++ b/cps-ncmp-rest/src/test/resources/application.yml
@@ -21,3 +21,8 @@
     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 573c76e..99a024e 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 0e748c7..717cae5 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.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 @@
 
     private final NetworkCmProxyDataServicePropertyHandler networkCmProxyDataServicePropertyHandler;
 
-    private final YangModelCmHandleRetriever yangModelCmHandleRetriever;
+    private final InventoryPersistence inventoryPersistence;
 
     private final ModuleSyncService moduleSyncService;
 
@@ -93,7 +92,7 @@
     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 @@
                                                         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 @@
                                                                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 @@
         });
 
         return cpsAdminService.queryCmHandles(jsonObjectMapper.convertToValueType(cmHandleQueryApiParameters,
-                org.onap.cps.spi.model.CmHandleQueryParameters.class));
+            org.onap.cps.spi.model.CmHandleQueryParameters.class));
     }
 
     /**
@@ -185,10 +189,11 @@
         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 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 @@
         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 @@
         }
     }
 
-    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 @@
         }
     }
 
-
     private CmHandleRegistrationResponse registerAndSyncNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
         try {
             final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
@@ -335,14 +328,4 @@
         }
     }
 
-    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 0000000..4e5c57b
--- /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 0000000..5d8ac7f
--- /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 0000000..8ab6db9
--- /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 f1bb95f..d457f26 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 @@
 
 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 @@
     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 f145379..d409a80 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.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 @@
      *
      * @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 @@
                                                          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 @@
                 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 @@
                                                                              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 @@
             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 b033af8..d8d0304 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.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 @@
      *
      * @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 @@
                                                                   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 745007b..e26ffef 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 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 @@
         }
     }
 
-    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/operations/YangModelCmHandleRetriever.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java
deleted file mode 100644
index 5063e82..0000000
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
- *  ================================================================================
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- *  ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.api.impl.operations;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import lombok.AllArgsConstructor;
-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.FetchDescendantsOption;
-import org.onap.cps.spi.model.DataNode;
-import org.onap.cps.utils.CpsValidator;
-import org.springframework.stereotype.Component;
-
-/**
- * Retrieves YangModelCmHandles & properties.
- */
-@Component
-@AllArgsConstructor
-public class YangModelCmHandleRetriever {
-
-    private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
-    private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
-
-    private CpsDataService cpsDataService;
-
-    /**
-     * This method retrieves DMI service name and DMI properties for a given cm handle.
-     * @param cmHandleId the id of the cm handle
-     * @return yang model cm handle
-     */
-    public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
-        CpsValidator.validateNameCharacters(cmHandleId);
-        final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId);
-        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
-        ncmpServiceCmHandle.setCmHandleId(cmHandleId);
-        populateCmHandleProperties(cmHandleDataNode, ncmpServiceCmHandle);
-        return YangModelCmHandle.toYangModelCmHandle(
-            (String) cmHandleDataNode.getLeaves().get("dmi-service-name"),
-            (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
-            (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"),
-            ncmpServiceCmHandle
-        );
-    }
-
-    private DataNode getCmHandleDataNode(final String cmHandle) {
-        final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']";
-        return cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
-            NCMP_DMI_REGISTRY_ANCHOR,
-            xpathForDmiRegistryToFetchCmHandle,
-            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
-    }
-
-    private static void populateCmHandleProperties(final DataNode cmHandleDataNode,
-                                                   final NcmpServiceCmHandle ncmpServiceCmHandle) {
-        final Map<String, String> dmiProperties = new LinkedHashMap<>();
-        final Map<String, String> publicProperties = new LinkedHashMap<>();
-        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);
-            }
-        }
-        ncmpServiceCmHandle.setDmiProperties(dmiProperties);
-        ncmpServiceCmHandle.setPublicProperties(publicProperties);
-    }
-
-    private static void addProperty(final DataNode propertyDataNode, final Map<String, String> propertiesAsMap) {
-        propertiesAsMap.put(String.valueOf(propertyDataNode.getLeaves().get("name")),
-            String.valueOf(propertyDataNode.getLeaves().get("value")));
-    }
-
-}
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 d4c64ea..65e03f1 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 @@
         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 24fab32..0c16adc 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 9ac49a6..eeaa4cd 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 @@
 
 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 @@
 public class CompositeState {
 
     @JsonProperty("cm-handle-state")
-    private CmHandleState cmhandleState;
+    private CmHandleState cmHandleState;
 
     @JsonProperty("lock-reason")
     private LockReason lockReason;
@@ -52,13 +54,24 @@
     @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 @@
 
         @JsonProperty("operational")
         private Operational operationalDataStore;
-
-        @JsonProperty("running")
-        private Running runningDataStore;
     }
 
     @Data
@@ -101,4 +111,20 @@
         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 0000000..4ab0cec
--- /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/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
new file mode 100644
index 0000000..873a449
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
@@ -0,0 +1,146 @@
+/*
+ *  ============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;
+
+import java.time.OffsetDateTime;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+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;
+
+@RequiredArgsConstructor
+@Component
+public class InventoryPersistence {
+
+    private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
+
+    private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
+
+    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.
+     * @param cmHandleId the id of the cm handle
+     * @return yang model cm handle
+     */
+    public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
+        CpsValidator.validateNameCharacters(cmHandleId);
+        final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId);
+        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
+        ncmpServiceCmHandle.setCmHandleId(cmHandleId);
+        populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle);
+        return YangModelCmHandle.toYangModelCmHandle(
+            (String) cmHandleDataNode.getLeaves().get("dmi-service-name"),
+            (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
+            (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"),
+            ncmpServiceCmHandle
+        );
+    }
+
+    private DataNode getCmHandleDataNode(final String cmHandle) {
+        final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']";
+        return cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
+            NCMP_DMI_REGISTRY_ANCHOR,
+            xpathForDmiRegistryToFetchCmHandle,
+            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+    }
+
+    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) {
+        propertiesAsMap.put(String.valueOf(propertyDataNode.getLeaves().get("name")),
+            String.valueOf(propertyDataNode.getLeaves().get("value")));
+    }
+
+}
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 0000000..596fcb7
--- /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 353db9d..2187ec6 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.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 @@
 @Component
 public class ModuleSyncWatchdog {
 
+    private final InventoryPersistence inventoryPersistence;
+
     private final SyncUtils syncUtils;
 
     private final ModuleSyncService moduleSyncService;
@@ -43,11 +48,21 @@
     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 3bc43c5..a4f29de 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 @@
 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 @@
      * @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 6811b59..963b484 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 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 @@
     @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 5683d57..f56aea7 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.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 @@
     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 @@
 
     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 01f3bfe..55a1a8d 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 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 @@
     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 @@
     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 @@
             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 @@
                 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 @@
         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 @@
             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 @@
         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 @@
             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 @@
         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 @@
                 >> { 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 0000000..aa6bf1a
--- /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 0000000..07e9b49
--- /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 394df1d..90839f8 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 @@
     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 2a19df1..b7ebf29 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.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 @@
         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 @@
             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 @@
             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 574f609..ed8f086 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 @@
 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.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 @@
             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 @@
         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 @@
                                                       [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 @@
         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 dae2bcc..193b94d 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.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 @@
     DmiRestClient mockDmiRestClient = Mock()
 
     @SpringBean
-    YangModelCmHandleRetriever mockCmHandlePropertiesRetriever = Mock()
+    InventoryPersistence mockInventoryPersistence = Mock()
 
     @SpringBean
     ObjectMapper spyObjectMapper = Spy()
@@ -56,6 +57,6 @@
         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/impl/operations/YangModelCmHandleRetrieverSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy
deleted file mode 100644
index 5ecc8b0..0000000
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
- *  ================================================================================
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- *  ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.ncmp.api.impl.operations
-
-import org.onap.cps.api.CpsDataService
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
-import org.onap.cps.spi.exceptions.DataValidationException
-import spock.lang.Shared
-
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-import org.onap.cps.spi.model.DataNode
-import spock.lang.Specification
-
-class YangModelCmHandleRetrieverSpec extends Specification {
-
-    def mockCpsDataService = Mock(CpsDataService)
-
-    def objectUnderTest = new YangModelCmHandleRetriever(mockCpsDataService)
-
-    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"]
-    def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
-
-    @Shared
-    def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
-                                                      new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
-
-    @Shared
-    def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
-
-    @Shared
-    def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
-
-    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)
-            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
-        when: 'retrieving the yang modelled cm handle'
-            def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
-        then: 'the result has the correct id and service names'
-            result.id == cmHandleId
-            result.dmiServiceName == 'common service name'
-            result.dmiDataServiceName == 'data service name'
-            result.dmiModelServiceName == 'model service name'
-        and: 'the expected DMI properties'
-            result.dmiProperties == expectedDmiProperties
-            result.publicProperties == expectedPublicProperties
-        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")]
-    }
-
-    def "Retrieve CmHandle using datanode with invalid CmHandle id."() {
-        when: 'retrieving the yang modelled cm handle with an invalid id'
-            def result = objectUnderTest.getYangModelCmHandle('cm handle id with spaces')
-        then: 'a data validation exception is thrown'
-            thrown(DataValidationException)
-        and: 'the result is not returned'
-            result == null
-    }
-
-    def "Handling missing service names as null CPS-1043."() {
-        given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
-            def dataNode = new DataNode(childDataNodes:[], leaves: [:])
-            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
-        when: 'retrieving the yang modelled cm handle'
-            def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
-        then: 'the service names ae returned as null'
-            result.dmiServiceName == null
-            result.dmiDataServiceName == null
-            result.dmiModelServiceName == null
-    }
-}
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 0000000..d6f4ba6
--- /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 4f4cccf..5387fc6 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.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 @@
     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/inventory/InventoryPersistenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
new file mode 100644
index 0000000..b638eec
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
@@ -0,0 +1,158 @@
+/*
+ *  ============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
+
+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 static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
+
+class InventoryPersistenceSpec extends Specification {
+
+    def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
+
+    def mockCpsDataService = Mock(CpsDataService)
+
+    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"]
+    def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
+
+    @Shared
+    def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
+                                                      new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
+
+    @Shared
+    def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
+
+    @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)
+            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
+        when: 'retrieving the yang modelled cm handle'
+            def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
+        then: 'the result has the correct id and service names'
+            result.id == cmHandleId
+            result.dmiServiceName == 'common service name'
+            result.dmiDataServiceName == 'data service name'
+            result.dmiModelServiceName == 'model service name'
+        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                              || 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."() {
+        when: 'retrieving the yang modelled cm handle with an invalid id'
+            def result = objectUnderTest.getYangModelCmHandle('cm handle id with spaces')
+        then: 'a data validation exception is thrown'
+            thrown(DataValidationException)
+        and: 'the result is not returned'
+            result == null
+    }
+
+    def "Handling missing service names as null CPS-1043."() {
+        given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
+            def dataNode = new DataNode(childDataNodes:[], leaves: [:])
+            mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
+        when: 'retrieving the yang modelled cm handle'
+            def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
+        then: 'the service names ae returned as null'
+            result.dmiServiceName == null
+            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 bfc5c6f..0000000
--- 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 35de99f..bcfe47f 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 c80263e..7d67acc 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 @@
 
 
     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 @@
 
     }
 
-    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 4c8dcac..964826b 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 @@
             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 0000000..bf6c86a
--- /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 a416194..5d246d5 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 b9d6268..1be45d1 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 2f1067a..0772a8c 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 @@
         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 @@
         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 @@
             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 @@
         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 @@
         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 @@
         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 @@
             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 @@
                                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 @@
         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 @@
         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 @@
         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 5124a51..05b9624 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 @@
     // 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 6ef6874..8263c31 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.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 @@
             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 436c3d4..a28b400 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 d633b1e..5954240 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 @@
 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 32d9604..95a8d53 100644
--- a/csit/tests/ncmp-passthrough/ncmp-passthrough.robot
+++ b/csit/tests/ncmp-passthrough/ncmp-passthrough.robot
@@ -36,6 +36,13 @@
 
 *** 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 @@
     ${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 44ebd3b..f2f477f 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 @@
   #    - 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 @@
   #      - 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 @@
       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 d1181d3..b8f18e7 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 23ef44b..12d8a1f 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>