Implement merging all ncmp datastore endpoints into one

- Merging all endpoints
under /v1/ch/{cm-handle}/data/ds/ncmp-datastore:*
to /v1/ch/{cm-handle}/data/ds/{ncmp-datastore-name}

- Implementing missing tests from parent
- Introducing abstract class to keep the common code and just pass in
  the supplier to be executed in sync or async manner
- Removed the existing get endpoints for passthrough-running,
  passthrough-operational and operational and merged them into a common
  get endpoint

Issue-ID: CPS-1178
Issue-ID: CPS-1001
Change-Id: I6956c81d5acfa8fb11217bcc16cb795b62070fa3
Signed-off-by: bmiklos <miklos.baranyak@est.tech>
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 427f083..7ca09ce 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -86,7 +86,7 @@
           type: array
           items:
             type: string
-          example: [my-cm-handle1, my-cm-handle2, my-cm-handle3]
+          example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ]
     DmiPluginRegistrationErrorResponse:
       type: object
       properties:
@@ -124,14 +124,14 @@
           type: string
           example: my-cm-handle
         cmHandleProperties:
-            $ref: '#/components/schemas/RestCmHandleProperties'
+          $ref: '#/components/schemas/RestCmHandleProperties'
         publicCmHandleProperties:
-            $ref: '#/components/schemas/RestCmHandleProperties'
+          $ref: '#/components/schemas/RestCmHandleProperties'
     RestCmHandleProperties:
-        type: object
-        additionalProperties:
-            type: string
-            example: my-property
+      type: object
+      additionalProperties:
+        type: string
+        example: my-property
 
     #Response Schemas
     RestModuleReference:
@@ -288,21 +288,21 @@
 
   examples:
     dataSampleRequest:
-        summary: Sample request
-        description: Sample request body
-        value:
-          test:bookstore:
-            bookstore-name: Chapters
-            categories:
-              - code: '01'
-                name: SciFi
-                books:
+      summary: Sample request
+      description: Sample request body
+      value:
+        test:bookstore:
+          bookstore-name: Chapters
+          categories:
+            - code: '01'
+              name: SciFi
+              books:
                 - authors:
                     - Iain M. Banks
                     - Ursula K. Le Guin
-              - code: '02'
-                name: kids
-                books:
+            - code: '02'
+              name: kids
+              books:
                 - authors:
                     - Philip Pullman
 
@@ -351,22 +351,22 @@
                             - Philip Pullman
 
     dataSampleResponse:
-        summary: Sample response
-        description: Sample response for selecting 'sample 1'.
-        value:
-          bookstore:
-            categories:
-              - code: '01'
-                books:
-                  - authors:
-                      - Iain M. Banks
-                      - Ursula K. Le Guin
-                name: SciFi
-              - code: '02'
-                books:
-                  - authors:
-                      - Philip Pullman
-                name: kids
+      summary: Sample response
+      description: Sample response for selecting 'sample 1'.
+      value:
+        bookstore:
+          categories:
+            - code: '01'
+              books:
+                - authors:
+                    - Iain M. Banks
+                    - Ursula K. Le Guin
+              name: SciFi
+            - code: '02'
+              books:
+                - authors:
+                    - Philip Pullman
+              name: kids
 
     allCmHandleQueryParameters:
       value:
@@ -448,7 +448,7 @@
     includeDescendantsOptionInQuery:
       name: include-descendants
       in: query
-      description: include-descendants
+      description: Determines if descendants are included in response
       required: false
       schema:
         type: boolean
@@ -526,6 +526,14 @@
         type: string
         default: application/json
         example: application/yang-data+json
+    datastoreName:
+      name: ncmp-datastore-name
+      in: path
+      description: The type of the requested data
+      required: true
+      schema:
+        type: string
+        example: ncmp-datastore:operational
 
   responses:
     NotFound:
@@ -555,9 +563,9 @@
           schema:
             $ref: '#/components/schemas/ErrorMessage'
           example:
-           status: 403
-           message: Forbidden error message
-           details: Forbidden error details
+            status: 403
+            message: Forbidden error message
+            details: Forbidden error details
     BadRequest:
       description: Bad Request
       content:
@@ -565,9 +573,9 @@
           schema:
             $ref: '#/components/schemas/ErrorMessage'
           example:
-           status: 400 BAD_REQUEST
-           message: Bad request error message
-           details: Bad request error details
+            status: 400 BAD_REQUEST
+            message: Bad request error message
+            details: Bad request error details
     Conflict:
       description: Conflict
       content:
@@ -575,9 +583,9 @@
           schema:
             $ref: '#/components/schemas/ErrorMessage'
           example:
-           status: 409 CONFLICT
-           message: Conflict error message
-           details: Conflict error details
+            status: 409 CONFLICT
+            message: Conflict error message
+            details: Conflict error details
     NotImplemented:
       description: The given path has not been implemented
       content:
@@ -585,9 +593,9 @@
           schema:
             $ref: '#/components/schemas/ErrorMessage'
           example:
-           status: 501
-           message: Not implemented error message
-           details: Not implemented error details
+            status: 501
+            message: Not implemented error message
+            details: Not implemented error details
     Ok:
       description: OK
       content:
@@ -596,10 +604,10 @@
             type: object
     Created:
       description: Created
-      content: {}
+      content: { }
     NoContent:
       description: No Content
-      content: {}
+      content: { }
     InternalServerError:
       description: Internal Server Error
       content:
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index 4266fc4..5e22f77 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -17,18 +17,21 @@
 #
 #  SPDX-License-Identifier: Apache-2.0
 #  ============LICENSE_END=========================================================
-getResourceDataForPassthroughOperational:
+
+getResourceDataForCmHandle:
   get:
     tags:
       - network-cm-proxy
-    summary: Get resource data from pass-through operational for cm handle
-    description: Get resource data from pass-through operational for given cm handle
-    operationId: getResourceDataOperationalForCmHandle
+    summary: Get resource data for cm handle
+    description: Get resource data for given cm handle
+    operationId: getResourceDataForCmHandle
     parameters:
+      - $ref: 'components.yaml#/components/parameters/datastoreName'
       - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
       - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
       - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
       - $ref: 'components.yaml#/components/parameters/topicParamInQuery'
+      - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery'
     responses:
       200:
         description: OK
@@ -51,37 +54,6 @@
         $ref: 'components.yaml#/components/responses/BadGateway'
 
 resourceDataForPassthroughRunning:
-  get:
-    tags:
-      - network-cm-proxy
-    summary: Get resource data from pass-through running for cm handle
-    description: Get resource data from pass-through running for given cm handle
-    operationId: getResourceDataRunningForCmHandle
-    parameters:
-      - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
-      - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
-      - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
-      - $ref: 'components.yaml#/components/parameters/topicParamInQuery'
-    responses:
-      200:
-        description: OK
-        content:
-          application/json:
-            schema:
-              type: object
-            examples:
-              dataSampleResponse:
-                $ref: 'components.yaml#/components/examples/dataSampleResponse'
-      400:
-        $ref: 'components.yaml#/components/responses/BadRequest'
-      401:
-        $ref: 'components.yaml#/components/responses/Unauthorized'
-      403:
-        $ref: 'components.yaml#/components/responses/Forbidden'
-      500:
-        $ref: 'components.yaml#/components/responses/InternalServerError'
-      502:
-        $ref: 'components.yaml#/components/responses/BadGateway'
   post:
     tags:
       - network-cm-proxy
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index 8e02066..ed15fcd 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -26,8 +26,8 @@
 servers:
   - url: /ncmp
 paths:
-  /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-operational:
-    $ref: 'ncmp.yml#/getResourceDataForPassthroughOperational'
+  /v1/ch/{cm-handle}/data/ds/{ncmp-datastore-name}:
+    $ref: 'ncmp.yml#/getResourceDataForCmHandle'
 
   /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running:
     $ref: 'ncmp.yml#/resourceDataForPassthroughRunning'
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml
index 6a700c3..b3021d2 100644
--- a/cps-ncmp-rest/pom.xml
+++ b/cps-ncmp-rest/pom.xml
@@ -185,7 +185,8 @@
                             <goal>copy-resources</goal>
                         </goals>
                         <configuration>
-                            <outputDirectory>${project.basedir}/target/classes/static/api-docs/cps-ncmp</outputDirectory>
+                            <outputDirectory>${project.basedir}/target/classes/static/api-docs/cps-ncmp
+                            </outputDirectory>
                             <resources>
                                 <resource>
                                     <directory>${project.basedir}/target/generated-sources/swagger/</directory>
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 d2ed393..9aa8263 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
@@ -29,21 +29,18 @@
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE;
 
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 import java.util.stream.Collectors;
-import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
 import org.onap.cps.ncmp.api.inventory.CompositeState;
 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.controller.handlers.DatastoreType;
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandler;
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory;
 import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper;
 import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
 import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
@@ -53,9 +50,7 @@
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
 import org.onap.cps.ncmp.rest.util.DeprecationHelper;
-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;
@@ -68,94 +63,50 @@
 public class NetworkCmProxyController implements NetworkCmProxyApi {
 
     private static final String NO_BODY = null;
-    private static final String NO_REQUEST_ID = null;
-    private static final String NO_TOPIC = null;
     private final NetworkCmProxyDataService networkCmProxyDataService;
     private final JsonObjectMapper jsonObjectMapper;
-
     private final DeprecationHelper deprecationHelper;
     private final NcmpRestInputMapper ncmpRestInputMapper;
     private final CmHandleStateMapper cmHandleStateMapper;
-    private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
-    @Value("${notification.async.executor.time-out-value-in-ms:2000}")
-    private int timeOutInMilliSeconds;
-    @Value("${notification.enabled:true}")
-    private boolean asyncEnabled;
+    private final NcmpDatastoreResourceRequestHandlerFactory ncmpDatastoreResourceRequestHandlerFactory;
 
     /**
-     * Get resource data from operational datastore.
+     * Get resource data from datastore.
      *
-     * @param cmHandle cm handle identifier
-     * @param resourceIdentifier resource identifier
+     * @param datastoreName       name of the datastore
+     * @param cmHandle            cm handle identifier
+     * @param resourceIdentifier  resource identifier
      * @param optionsParamInQuery options query parameter
-     * @param topicParamInQuery topic query parameter
+     * @param topicParamInQuery   topic query parameter
+     * @param includeDescendants  whether include descendants
      * @return {@code ResponseEntity} response from dmi plugin
      */
+
     @Override
-    public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandle,
-                                                                        final @NotNull @Valid String resourceIdentifier,
-                                                                        final @Valid String optionsParamInQuery,
-                                                                        final @Valid String topicParamInQuery) {
-        if (asyncEnabled && isValidTopic(topicParamInQuery)) {
-            final String requestId = UUID.randomUUID().toString();
-            log.info("Received Async passthrough-operational request with id {}", requestId);
-            cpsNcmpTaskExecutor.executeTask(() ->
-                    networkCmProxyDataService.getResourceDataOperationalForCmHandle(
-                        cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId
-                    ), timeOutInMilliSeconds
-            );
-            return ResponseEntity.ok(Map.of("requestId", requestId));
-        } else {
-            log.warn("Asynchronous messaging is currently disabled for passthrough-operational."
-                + " Will use synchronous operation.");
-        }
+    public ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName,
+                                                             final String cmHandle,
+                                                             final String resourceIdentifier,
+                                                             final String optionsParamInQuery,
+                                                             final String topicParamInQuery,
+                                                             final Boolean includeDescendants) {
 
-        final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(
-            cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
+        final NcmpDatastoreResourceRequestHandler ncmpDatastoreResourceRequestHandler =
+                ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+                        DatastoreType.fromDatastoreName(datastoreName));
 
-        return ResponseEntity.ok(responseObject);
-    }
-
-    /**
-     * Get resource data from pass-through running datastore.
-     *
-     * @param cmHandle cm handle identifier
-     * @param resourceIdentifier resource identifier
-     * @param optionsParamInQuery options query parameter
-     * @param topicParamInQuery topic query parameter
-     * @return {@code ResponseEntity} response from dmi plugin
-     */
-    @Override
-    public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandle,
-                                                                    final @NotNull @Valid String resourceIdentifier,
-                                                                    final @Valid String optionsParamInQuery,
-                                                                    final @Valid String topicParamInQuery) {
-        if (asyncEnabled && isValidTopic(topicParamInQuery)) {
-            final String requestId = UUID.randomUUID().toString();
-            log.info("Received Async passthrough-running request with id {}", requestId);
-            cpsNcmpTaskExecutor.executeTask(() ->
-                networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
-                    cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId
-                ), timeOutInMilliSeconds
-            );
-            return ResponseEntity.ok(Map.of("requestId", requestId));
-        } else {
-            log.warn("Asynchronous messaging is currently disabled for passthrough-running."
-                + " Will use synchronous operation.");
-        }
-
-        final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
-            cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
-
-        return ResponseEntity.ok(responseObject);
+        return ncmpDatastoreResourceRequestHandler.getResourceData(cmHandle, resourceIdentifier,
+                optionsParamInQuery, topicParamInQuery, includeDescendants);
     }
 
     @Override
     public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String resourceIdentifier,
-        final String cmHandle,
-        final Object requestBody, final String contentType) {
-        final Object responseObject = networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
-            resourceIdentifier, PATCH, jsonObjectMapper.asJsonString(requestBody), contentType);
+                                                                      final String cmHandle,
+                                                                      final Object requestBody,
+                                                                      final String contentType) {
+        final Object responseObject = networkCmProxyDataService
+                .writeResourceDataPassThroughRunningForCmHandle(
+                        cmHandle, resourceIdentifier, PATCH,
+                        jsonObjectMapper.asJsonString(requestBody), contentType);
         return ResponseEntity.ok(responseObject);
     }
 
@@ -163,14 +114,16 @@
      * Create resource data in datastore pass-through running for given cm-handle.
      *
      * @param resourceIdentifier resource identifier
-     * @param cmHandle cm handle identifier
-     * @param requestBody the request body
-     * @param contentType content type of body
+     * @param cmHandle           cm handle identifier
+     * @param requestBody        the request body
+     * @param contentType        content type of body
      * @return {@code ResponseEntity} response from dmi plugin
      */
     @Override
     public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String resourceIdentifier,
-        final String cmHandle, final Object requestBody, final String contentType) {
+                                                                     final String cmHandle,
+                                                                     final Object requestBody,
+                                                                     final String contentType) {
         networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
                 resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType);
         return new ResponseEntity<>(HttpStatus.CREATED);
@@ -180,9 +133,9 @@
      * Update resource data in datastore pass-through running for given cm-handle.
      *
      * @param resourceIdentifier resource identifier
-     * @param cmHandle cm handle identifier
-     * @param requestBody the request body
-     * @param contentType content type of the body
+     * @param cmHandle           cm handle identifier
+     * @param requestBody        the request body
+     * @param contentType        content type of the body
      * @return response entity
      */
     @Override
@@ -197,11 +150,11 @@
 
 
     /**
-     *  Delete resource data in datastore pass-through running for a given cm-handle.
+     * Delete resource data in datastore pass-through running for a given cm-handle.
      *
      * @param resourceIdentifier resource identifier
-     * @param cmHandle cm handle identifier
-     * @param contentType content type of the body
+     * @param cmHandle           cm handle identifier
+     * @param contentType        content type of the body
      * @return response entity no content if request is successful
      */
     @Override
@@ -209,7 +162,7 @@
                                                                      final String resourceIdentifier,
                                                                      final String contentType) {
         networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
-            resourceIdentifier, DELETE, NO_BODY, contentType);
+                resourceIdentifier, DELETE, NO_BODY, contentType);
         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
     }
 
@@ -240,7 +193,7 @@
      */
     @Override
     public ResponseEntity<List<String>> searchCmHandleIds(
-        final CmHandleQueryParameters cmHandleQueryParameters) {
+            final CmHandleQueryParameters cmHandleQueryParameters) {
         final CmHandleQueryApiParameters cmHandleQueryApiParameters =
                 jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class);
         final Set<String> cmHandleIds = networkCmProxyDataService.executeCmHandleIdSearch(cmHandleQueryApiParameters);
@@ -249,6 +202,7 @@
 
     /**
      * Search for Cm Handle and Properties by Name.
+     *
      * @param cmHandleId cm-handle identifier
      * @return cm handle and its properties
      */
@@ -261,33 +215,35 @@
 
     /**
      * Get Cm Handle Properties by Cm Handle Id.
+     *
      * @param cmHandleId cm-handle identifier
      * @return cm handle properties
      */
     @Override
     public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
-        final String cmHandleId) {
+            final String cmHandleId) {
         final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
         cmHandlePublicProperties.add(networkCmProxyDataService.getCmHandlePublicProperties(cmHandleId));
         final RestOutputCmHandlePublicProperties restOutputCmHandlePublicProperties =
-            new RestOutputCmHandlePublicProperties();
+                new RestOutputCmHandlePublicProperties();
         restOutputCmHandlePublicProperties.setPublicCmHandleProperties(cmHandlePublicProperties);
         return ResponseEntity.ok(restOutputCmHandlePublicProperties);
     }
 
     /**
      * Get Cm Handle State by Cm Handle Id.
+     *
      * @param cmHandleId cm-handle identifier
      * @return cm handle state
      */
     @Override
     public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(
-        final String cmHandleId) {
+            final String cmHandleId) {
         final CompositeState cmHandleState = networkCmProxyDataService.getCmHandleCompositeState(cmHandleId);
         final RestOutputCmHandleCompositeState restOutputCmHandleCompositeState =
-            new RestOutputCmHandleCompositeState();
+                new RestOutputCmHandleCompositeState();
         restOutputCmHandleCompositeState.setState(
-            cmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(cmHandleState));
+                cmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(cmHandleState));
         return ResponseEntity.ok(restOutputCmHandleCompositeState);
     }
 
@@ -314,22 +270,22 @@
      */
     public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandle) {
         final List<RestModuleReference> restModuleReferences =
-            networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream()
-            .map(ncmpRestInputMapper::toRestModuleReference)
-                .collect(Collectors.toList());
+                networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream()
+                        .map(ncmpRestInputMapper::toRestModuleReference)
+                        .collect(Collectors.toList());
         return new ResponseEntity<>(restModuleReferences, HttpStatus.OK);
     }
 
     /**
      * Set the data sync enabled flag, along with the data sync state for the specified cm handle.
      *
-     * @param cmHandleId cm handle id
+     * @param cmHandleId          cm handle id
      * @param dataSyncEnabledFlag data sync enabled flag
      * @return response entity ok if request is successful
      */
     @Override
     public ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
-                                                                final Boolean dataSyncEnabledFlag) {
+                                                                    final Boolean dataSyncEnabledFlag) {
         networkCmProxyDataService.setDataSyncEnabled(cmHandleId, dataSyncEnabledFlag);
         return new ResponseEntity<>(HttpStatus.OK);
     }
@@ -345,15 +301,6 @@
         return restOutputCmHandle;
     }
 
-    private static boolean isValidTopic(final String topicName) {
-        if (topicName == null) {
-            return false;
-        }
-        if (CpsValidator.validateTopicName(topicName)) {
-            return true;
-        }
-        throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic");
-    }
 
 }
 
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java
new file mode 100644
index 0000000..959c85d
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java
@@ -0,0 +1,53 @@
+/*
+ *  ============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.controller.handlers;
+
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+
+@Getter
+public enum DatastoreType {
+
+    OPERATIONAL("ncmp-datastore:operational"),
+    PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"),
+    PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational");
+
+    DatastoreType(final String datastoreName) {
+        this.datastoreName = datastoreName;
+    }
+
+    private final String datastoreName;
+    private static final Map<String, DatastoreType> datastoreNameToDatastoreType = new HashMap<>();
+
+    static {
+        Arrays.stream(DatastoreType.values()).forEach(
+                type -> datastoreNameToDatastoreType.put(type.getDatastoreName(), type));
+    }
+
+    public static DatastoreType fromDatastoreName(final String datastoreName) {
+        return datastoreNameToDatastoreType.get(datastoreName);
+    }
+
+}
+
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java
new file mode 100644
index 0000000..6ed9b8c
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.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.rest.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.onap.cps.spi.FetchDescendantsOption;
+
+@Slf4j
+public class NcmpDatastoreOperationalResourceRequestHandler extends NcmpDatastoreResourceRequestHandler {
+
+    public NcmpDatastoreOperationalResourceRequestHandler(final NetworkCmProxyDataService networkCmProxyDataService,
+                                                          final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
+                                                          final int timeOutInMilliSeconds,
+                                                          final boolean notificationFeatureEnabled) {
+        super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+    }
+
+    @Override
+    public Supplier<Object> getTask(final String cmHandle,
+                                    final String resourceIdentifier,
+                                    final String optionsParamInQuery,
+                                    final String topicParamInQuery,
+                                    final String requestId,
+                                    final Boolean includeDescendant) {
+
+        final FetchDescendantsOption fetchDescendantsOption =
+                Boolean.TRUE.equals(includeDescendant) ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+                        : FetchDescendantsOption.OMIT_DESCENDANTS;
+
+        return () -> networkCmProxyDataService.getResourceDataOperational(cmHandle, resourceIdentifier,
+                fetchDescendantsOption);
+    }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java
new file mode 100644
index 0000000..196e5bd
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java
@@ -0,0 +1,51 @@
+/*
+ *  ============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.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+
+@Slf4j
+public class NcmpDatastorePassthroughOperationalResourceRequestHandler extends NcmpDatastoreResourceRequestHandler {
+
+    public NcmpDatastorePassthroughOperationalResourceRequestHandler(
+            final NetworkCmProxyDataService networkCmProxyDataService,
+            final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
+            final int timeOutInMilliSeconds,
+            final boolean notificationFeatureEnabled) {
+        super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+    }
+
+    @Override
+    public Supplier<Object> getTask(final String cmHandle,
+                                    final String resourceIdentifier,
+                                    final String optionsParamInQuery,
+                                    final String topicParamInQuery,
+                                    final String requestId,
+                                    final Boolean includeDescendant) {
+
+        return () -> networkCmProxyDataService.getResourceDataOperationalForCmHandle(
+                cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+    }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java
new file mode 100644
index 0000000..5bf16b7
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java
@@ -0,0 +1,50 @@
+/*
+ *  ============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.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+
+@Slf4j
+public class NcmpDatastorePassthroughRunningResourceRequestHandler extends NcmpDatastoreResourceRequestHandler {
+
+    public NcmpDatastorePassthroughRunningResourceRequestHandler(
+            final NetworkCmProxyDataService networkCmProxyDataService,
+            final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
+            final int timeOutInMilliSeconds,
+            final boolean notificationFeatureEnabled) {
+        super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+    }
+
+    @Override
+    public Supplier<Object> getTask(final String cmHandle,
+                                    final String resourceIdentifier,
+                                    final String optionsParamInQuery,
+                                    final String topicParamInQuery,
+                                    final String requestId,
+                                    final Boolean includeDescendant) {
+
+        return () -> networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
+                cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+    }
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandler.java
new file mode 100644
index 0000000..a6d313b
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandler.java
@@ -0,0 +1,92 @@
+/*
+ *  ============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.controller.handlers;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Supplier;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.onap.cps.ncmp.rest.util.TopicValidator;
+import org.springframework.http.ResponseEntity;
+
+@RequiredArgsConstructor
+@Slf4j
+public abstract class NcmpDatastoreResourceRequestHandler {
+
+    private static final String NO_REQUEST_ID = null;
+    private static final String NO_TOPIC = null;
+
+    protected final NetworkCmProxyDataService networkCmProxyDataService;
+    protected final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
+    protected final int timeOutInMilliSeconds;
+    protected final boolean notificationFeatureEnabled;
+
+    protected abstract Supplier<Object> getTask(final String cmHandle,
+                                                final String resourceIdentifier,
+                                                final String optionsParamInQuery,
+                                                final String topicParamInQuery,
+                                                final String requestId,
+                                                final Boolean includeDescendant);
+
+
+    /**
+     * Get resource data from datastore.
+     *
+     * @param cmHandleId          the cm handle
+     * @param resourceIdentifier  the resource identifier
+     * @param optionsParamInQuery the options param in query
+     * @param topicParamInQuery   the topic param in query
+     * @param includeDescendants  whether include descendants
+     * @return the response entity
+     */
+    public ResponseEntity<Object> getResourceData(final String cmHandleId,
+                                                  final String resourceIdentifier,
+                                                  final String optionsParamInQuery,
+                                                  final String topicParamInQuery,
+                                                  final Boolean includeDescendants) {
+
+        final String requestId = UUID.randomUUID().toString();
+        final boolean asyncResponseRequested = topicParamInQuery != null;
+
+        if (asyncResponseRequested && notificationFeatureEnabled) {
+            TopicValidator.validateTopicName(topicParamInQuery);
+            log.debug("Received Async request with id {}", requestId);
+            cpsNcmpTaskExecutor.executeTask(
+                    getTask(cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId,
+                            includeDescendants), timeOutInMilliSeconds);
+
+            return ResponseEntity.ok(Map.of("requestId", requestId));
+        }
+
+        if (asyncResponseRequested) {
+            log.warn("Asynchronous messaging is currently disabled, will use synchronous operation.");
+        }
+        
+        final Object responseObject =
+                getTask(cmHandleId, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID,
+                        includeDescendants).get();
+
+        return ResponseEntity.ok(responseObject);
+    }
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
new file mode 100644
index 0000000..35bd578
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
@@ -0,0 +1,64 @@
+/*
+ *  ============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.controller.handlers;
+
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class NcmpDatastoreResourceRequestHandlerFactory {
+
+    private final NetworkCmProxyDataService networkCmProxyDataService;
+    private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
+
+    @Value("${notification.async.executor.time-out-value-in-ms:2000}")
+    private int timeOutInMilliSeconds;
+    @Value("${notification.enabled:true}")
+    private boolean notificationFeatureEnabled;
+
+    /**
+     * Gets ncmp datastore handler.
+     *
+     * @param datastoreType the datastore type
+     * @return the ncmp datastore handler
+     */
+    public NcmpDatastoreResourceRequestHandler getNcmpDatastoreResourceRequestHandler(
+            final DatastoreType datastoreType) {
+
+        switch (datastoreType) {
+            case OPERATIONAL:
+                return new NcmpDatastoreOperationalResourceRequestHandler(networkCmProxyDataService,
+                        cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+            case PASSTHROUGH_RUNNING:
+                return new NcmpDatastorePassthroughRunningResourceRequestHandler(networkCmProxyDataService,
+                        cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+            case PASSTHROUGH_OPERATIONAL:
+                return new NcmpDatastorePassthroughOperationalResourceRequestHandler(networkCmProxyDataService,
+                        cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+            default:
+                return null;
+        }
+    }
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidTopicException.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidTopicException.java
new file mode 100644
index 0000000..6a52d58
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidTopicException.java
@@ -0,0 +1,40 @@
+/*
+ *  ============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 InvalidTopicException extends RuntimeException {
+
+    @Getter
+    final String details;
+
+    /**
+     * Constructor.
+     *
+     * @param message the error message
+     * @param details the error details
+     */
+    public InvalidTopicException(final String message, final String details) {
+        super(message);
+        this.details = details;
+    }
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
index c723733..98d7f6f 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
@@ -25,7 +25,6 @@
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.exception.DmiRequestException;
 import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
-import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
 import org.onap.cps.ncmp.api.impl.exception.NcmpException;
 import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
 import org.onap.cps.ncmp.rest.controller.NetworkCmProxyController;
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/TopicValidator.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/TopicValidator.java
new file mode 100644
index 0000000..313e7bc
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/TopicValidator.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.util;
+
+import java.util.regex.Pattern;
+import org.onap.cps.ncmp.rest.exceptions.InvalidTopicException;
+
+public class TopicValidator {
+
+    private static final Pattern TOPIC_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]([._-](?![._-])|"
+        + "[a-zA-Z0-9]){0,120}[a-zA-Z0-9]$");
+
+    /**
+     * Validate kafka topic name pattern.
+     *
+     * @param topicName name of the topic to be validated
+     *
+     * @throws InvalidTopicException if the topic is not valid
+     */
+    public static void validateTopicName(final String topicName) {
+        if (!TOPIC_NAME_PATTERN.matcher(topicName).matches()) {
+            throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic");
+        }
+    }
+
+}
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 d568a5a..6e461fa 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
@@ -23,39 +23,27 @@
 
 package org.onap.cps.ncmp.rest.controller
 
+import com.fasterxml.jackson.databind.ObjectMapper
 import org.mapstruct.factory.Mappers
+import org.onap.cps.TestUtils
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService
 import org.onap.cps.ncmp.api.inventory.CmHandleState
 import org.onap.cps.ncmp.api.inventory.CompositeState
-import org.onap.cps.ncmp.api.inventory.LockReasonCategory
 import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
+import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughOperationalResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughRunningResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
+import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
 import org.onap.cps.ncmp.rest.util.DeprecationHelper
+import org.onap.cps.spi.FetchDescendantsOption
 import org.onap.cps.spi.model.ModuleDefinition
-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.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
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
-
-import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.TestUtils
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.JsonObjectMapper
-import org.onap.cps.ncmp.api.NetworkCmProxyDataService
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
@@ -63,11 +51,31 @@
 import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
+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.ncmp.api.inventory.CompositeState.DataStores
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
+
 @WebMvcTest(NetworkCmProxyController)
 class NetworkCmProxyControllerSpec extends Specification {
 
+    public static final int TIMEOUT_IN_MS = 2000
+    public static final boolean NOTIFICATION_ENABLED = true
+
     @Autowired
     MockMvc mvc
 
@@ -92,6 +100,9 @@
     @SpringBean
     DeprecationHelper stubbedDeprecationHelper = Stub()
 
+    @SpringBean
+    NcmpDatastoreResourceRequestHandlerFactory stubbedNcmpDatastoreResourceRequestHandlerFactory = Stub()
+
     @Value('${rest.api.ncmp-base-path}/v1')
     def ncmpBasePathV1
 
@@ -104,21 +115,38 @@
     def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
         .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
 
+    void setup() {
+        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+            DatastoreType.OPERATIONAL) >>
+            new NcmpDatastoreOperationalResourceRequestHandler(
+                mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+
+        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+            DatastoreType.PASSTHROUGH_OPERATIONAL) >>
+            new NcmpDatastorePassthroughOperationalResourceRequestHandler(
+                mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+
+        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+            DatastoreType.PASSTHROUGH_RUNNING) >>
+            new NcmpDatastorePassthroughRunningResourceRequestHandler(
+                mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+    }
+
     def 'Get Resource Data from pass-through operational.'() {
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
-                    "?resourceIdentifier=parent/child&options=(a=1,b=2)"
+                "?resourceIdentifier=parent/child&options=(a=1,b=2)"
         when: 'get data resource request is performed'
             def response = mvc.perform(
-                    get(getUrl)
-                            .contentType(MediaType.APPLICATION_JSON)
+                get(getUrl)
+                    .contentType(MediaType.APPLICATION_JSON)
             ).andReturn().response
         then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
             1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
-                    'parent/child',
-                    '(a=1,b=2)',
-                    NO_TOPIC,
-                    NO_REQUEST_ID)
+                'parent/child',
+                '(a=1,b=2)',
+                NO_TOPIC,
+                NO_REQUEST_ID)
         and: 'response status is Ok'
             response.status == HttpStatus.OK.value()
     }
@@ -126,22 +154,22 @@
     def 'Get Resource Data from #datastoreInUrl with #scenario.'() {
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
-                    "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
+                "?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
+                get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
         then: 'task executor is called appropriate number of times'
-            expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, 2000)
+            expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, TIMEOUT_IN_MS)
         and: 'response status is expected'
             response.status == HttpStatus.OK.value()
         where: 'the following parameters are used'
-            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
+            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.'() {
@@ -168,17 +196,17 @@
     def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() {
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
-                    "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
+                "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
         and: 'ncmp service returns json object'
             mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
-                    resourceIdentifier,
-                    '(a=1,b=2)',
-                    NO_TOPIC,
-                    NO_REQUEST_ID) >> '{valid-json}'
+                resourceIdentifier,
+                '(a=1,b=2)',
+                NO_TOPIC,
+                NO_REQUEST_ID) >> '{valid-json}'
         when: 'get data resource request is performed'
             def response = mvc.perform(
-                    get(getUrl)
-                            .contentType(MediaType.APPLICATION_JSON)
+                get(getUrl)
+                    .contentType(MediaType.APPLICATION_JSON)
             ).andReturn().response
         then: 'response status is Ok'
             response.status == HttpStatus.OK.value()
@@ -194,7 +222,7 @@
             '? needs to be encoded as %3F' | 'idWith%3F'
     }
 
-    def 'Update resource data from pass-through running.' () {
+    def 'Update resource data from pass-through running.'() {
         given: 'update resource data url'
             def updateUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
                 "?resourceIdentifier=parent/child"
@@ -210,15 +238,15 @@
             response.status == HttpStatus.OK.value()
     }
 
-    def 'Create Resource Data from pass-through running with #scenario.' () {
+    def 'Create Resource Data from pass-through running with #scenario.'() {
         given: 'resource data url'
             def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
-                    "?resourceIdentifier=parent/child"
+                "?resourceIdentifier=parent/child"
             def requestBody = '{"some-key":"some-value"}'
         when: 'create resource request is performed'
             def response = mvc.perform(
-                    post(url)
-                            .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
+                post(url)
+                    .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
             ).andReturn().response
         then: 'ncmp service method to create resource called'
             1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
@@ -227,14 +255,14 @@
             response.status == HttpStatus.CREATED.value()
     }
 
-    def 'Get module references for the given dataspace and cm handle.' () {
+    def 'Get module references for the given dataspace and cm handle.'() {
         given: 'get module references url'
             def getUrl = "$ncmpBasePathV1/ch/some-cmhandle/modules"
         when: 'get module resource request is performed'
-            def response =mvc.perform(get(getUrl)).andReturn().response
+            def response = mvc.perform(get(getUrl)).andReturn().response
         then: 'ncmp service method to get yang resource module references is called'
             mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle')
-                    >> [new ModuleReference(moduleName: 'some-name1',revision: '2021-10-03')]
+                >> [new ModuleReference(moduleName: 'some-name1', revision: '2021-10-03')]
         and: 'response contains an array with the module name and revision'
             response.getContentAsString() == '[{"moduleName":"some-name1","revision":"2021-10-03"}]'
         and: 'response returns an OK http code'
@@ -248,15 +276,15 @@
         and: 'the service method is invoked with module names and returns two cm handles'
             def cmHandle1 = new NcmpServiceCmHandle()
             cmHandle1.cmHandleId = 'some-cmhandle-id1'
-            cmHandle1.publicProperties = [color:'yellow']
+            cmHandle1.publicProperties = [color: 'yellow']
             def cmHandle2 = new NcmpServiceCmHandle()
             cmHandle2.cmHandleId = 'some-cmhandle-id2'
-            cmHandle2.publicProperties = [color:'green']
+            cmHandle2.publicProperties = [color: 'green']
             mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2]
         when: 'the searches api is invoked'
             def response = mvc.perform(post(searchesEndpoint)
-                    .contentType(MediaType.APPLICATION_JSON)
-                    .content(jsonString)).andReturn().response
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(jsonString)).andReturn().response
         then: 'response status returns OK'
             response.status == HttpStatus.OK.value()
         and: 'the expected response content is returned'
@@ -268,14 +296,15 @@
             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 dmiProperties = [prop: 'some DMI property']
+            def publicProperties = ["public prop": 'some public property']
             def compositeState = compositeStateTestObject()
             def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, 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
+            def response = mvc.perform(
+                get(cmHandleDetailsEndpoint)).andReturn().response
         then: 'the correct response is returned'
             response.status == HttpStatus.OK.value()
         and: 'the response contains the public properties'
@@ -286,30 +315,34 @@
             !response.contentAsString.contains("some DMI property")
     }
 
-    def 'Get Cm Handle public properties by Cm Handle id.' () {
+    def 'Get Cm Handle public properties by Cm Handle id.'() {
         given: 'a cm handle properties endpoint'
             def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/properties"
         and: 'some cm handle public properties'
-            def publicProperties =  [ 'public prop':'some public property' ]
+            def publicProperties = ['public prop': 'some public property']
         and: 'the service method is invoked with the cm handle id returning the cm handle public properties'
-            1 * mockNetworkCmProxyDataService.getCmHandlePublicProperties('some-cm-handle') >> publicProperties
+            1 * mockNetworkCmProxyDataService
+                .getCmHandlePublicProperties('some-cm-handle') >> publicProperties
         when: 'the cm handle properties api is invoked'
-            def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response
+            def response = mvc.perform(
+                get(cmHandlePropertiesEndpoint)).andReturn().response
         then: 'the correct response is returned'
             response.status == HttpStatus.OK.value()
         and: 'the response contains the public properties'
             assertContainsPublicProperties(response)
     }
 
-    def 'Get Cm Handle composite state by Cm Handle id.' () {
+    def 'Get Cm Handle composite state by Cm Handle id.'() {
         given: 'a cm handle state endpoint'
             def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/state"
         and: 'some cm handle composite state'
             def compositeState = compositeStateTestObject()
         and: 'the service method is invoked with the cm handle id returning the cm handle composite state'
-            1 * mockNetworkCmProxyDataService.getCmHandleCompositeState('some-cm-handle') >> compositeState
+            1 * mockNetworkCmProxyDataService
+                .getCmHandleCompositeState('some-cm-handle') >> compositeState
         when: 'the cm handle state api is invoked'
-            def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response
+            def response = mvc.perform(
+                get(cmHandlePropertiesEndpoint)).andReturn().response
         then: 'the correct response is returned'
             response.status == HttpStatus.OK.value()
         and: 'the response contains the cm handle state'
@@ -323,13 +356,14 @@
         and: 'the service method is invoked with module names and returns two cm handles'
             def cmHandel1 = new NcmpServiceCmHandle()
             cmHandel1.cmHandleId = 'some-cmhandle-id1'
-            cmHandel1.publicProperties = [color:'yellow']
+            cmHandel1.publicProperties = [color: 'yellow']
             def cmHandel2 = new NcmpServiceCmHandle()
             cmHandel2.cmHandleId = 'some-cmhandle-id2'
-            cmHandel2.publicProperties = [color:'green']
+            cmHandel2.publicProperties = [color: 'green']
             mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2]
         when: 'the searches api is invoked'
-            def response = mvc.perform(post(searchesEndpoint)
+            def response = mvc.perform(
+                post(searchesEndpoint)
                     .contentType(MediaType.APPLICATION_JSON)
                     .content(jsonString)).andReturn().response
         then: 'an empty cm handle identifier is returned'
@@ -342,9 +376,10 @@
         and: 'the service method is invoked with module names and returns cm handle ids'
             1 * mockNetworkCmProxyDataService.executeCmHandleIdSearch(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
         when: 'the searches api is invoked'
-            def response = mvc.perform(post(searchesEndpoint)
-                .contentType(MediaType.APPLICATION_JSON)
-                .content('{}')).andReturn().response
+            def response = mvc.perform(
+                post(searchesEndpoint)
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .content('{}')).andReturn().response
         then: 'cm handle ids are returned'
             response.contentAsString == '["some-cmhandle-id1","some-cmhandle-id2"]'
     }
@@ -353,37 +388,39 @@
         when: 'the searches api is invoked'
             def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches"
             def invalidInputData = '{invalidJson}'
-            def response = mvc.perform(post(searchesEndpoint)
+            def response = mvc.perform(
+                post(searchesEndpoint)
                     .contentType(MediaType.APPLICATION_JSON)
                     .content(invalidInputData)).andReturn().response
         then: 'BAD_REQUEST is returned'
             response.getStatus() == 400
     }
 
-    def 'Patch resource data in pass-through running datastore.' () {
+    def 'Patch resource data in pass-through running datastore.'() {
         given: 'patch resource data url'
             def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
-                    "?resourceIdentifier=parent/child"
+                "?resourceIdentifier=parent/child"
         when: 'patch data resource request is performed'
             def response = mvc.perform(
-                    patch(url)
-                            .contentType(MediaType.APPLICATION_JSON)
-                            .accept(MediaType.APPLICATION_JSON).content(requestBody)
+                patch(url)
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .accept(MediaType.APPLICATION_JSON).content(requestBody)
             ).andReturn().response
         then: 'ncmp service method to update resource is called'
             1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
-                    'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8')
+                'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8')
         and: 'the response status is OK'
             response.status == HttpStatus.OK.value()
     }
 
-    def 'Delete resource data in pass-through running datastore.' () {
+    def 'Delete resource data in pass-through running datastore.'() {
         given: 'delete resource data url'
             def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
-                     "?resourceIdentifier=parent/child"
+                "?resourceIdentifier=parent/child"
         when: 'delete data resource request is performed'
             def response = mvc.perform(
-                delete(url).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response
+                delete(url)
+                    .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response
         then: 'the ncmp service method to delete resource is called (with null as body)'
             1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
                 'parent/child', DELETE, null, 'application/json;charset=UTF-8')
@@ -394,12 +431,12 @@
     def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() {
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
-                    "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name"
+                "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name"
         when: 'get data resource request is performed'
             def response = mvc.perform(
-                    get(getUrl)
-                            .contentType(MediaType.APPLICATION_JSON)
-                            .accept(MediaType.APPLICATION_JSON_VALUE)
+                get(getUrl)
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .accept(MediaType.APPLICATION_JSON_VALUE)
             ).andReturn().response
         then: 'async request id is generated'
             assert response.contentAsString.contains("requestId")
@@ -409,32 +446,68 @@
             ':passthrough-running'     | 'passthrough-running'
     }
 
-    def 'Get module definitions based on cmHandleId.' () {
+    def 'Get module definitions based on cmHandleId.'() {
         when: 'get module definition request is performed'
-            def response = mvc.perform(get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions"))
-                    .andReturn().response
+            def response = mvc.perform(
+                get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions"))
+                .andReturn().response
         then: 'ncmp service method to get module definitions is called'
             mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleId('some-cmhandle')
-                    >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
-                    'module sampleModuleName{ sample module content }')]
+                >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
+                'module sampleModuleName{ sample module content }')]
         and: 'response contains an array with the module name, revision and content'
             response.getContentAsString() == '[{"moduleName":"sampleModuleName","revision":"2021-10-03","content":"module sampleModuleName{ sample module content }"}]'
         and: 'response returns an OK http code'
             response.status == HttpStatus.OK.value()
     }
 
-    def 'Set the data sync enabled based on the cm handle id and the data sync flag is #scenario' () {
+    def 'Set the data sync enabled based on the cm handle id and the data sync flag is #scenario'() {
         when: 'the set data sync enabled request is invoked'
-            def response = mvc.perform(put("$ncmpBasePathV1/ch/some-cm-handle-id/data-sync?dataSyncEnabled=" + dataSyncEnabledFlag))
-                    .andReturn().response
+            def response = mvc.perform(
+                put("$ncmpBasePathV1/ch/some-cm-handle-id/data-sync?dataSyncEnabled=" + dataSyncEnabledFlag))
+                .andReturn().response
         then: 'method to set data sync enabled is called'
             1 * mockNetworkCmProxyDataService.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
         and: 'the response returns an OK http code'
             response.status == HttpStatus.OK.value()
         where: 'the following parameters are used'
-        scenario     |  dataSyncEnabledFlag
-        'enabled'    |  true
-        'disabled'   |  false
+            scenario   | dataSyncEnabledFlag
+            'enabled'  | true
+            'disabled' | false
+    }
+
+    def 'Get Resource Data from operational without descendants.'() {
+        given: 'resource data url'
+            def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" +
+                "?resourceIdentifier=parent/child&include-descendants=false"
+        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 getResourceDataOperational'
+            1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
+                'parent/child',
+                FetchDescendantsOption.OMIT_DESCENDANTS)
+        and: 'response status is Ok'
+            response.status == HttpStatus.OK.value()
+    }
+
+    def 'Get Resource Data from operational including descendants.'() {
+        given: 'resource data url'
+            def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" +
+                "?resourceIdentifier=parent/child&include-descendants=true"
+        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 getResourceDataOperational'
+            1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
+                'parent/child',
+                FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+        and: 'response status is Ok'
+            response.status == HttpStatus.OK.value()
     }
 
     def dataStores() {
@@ -453,7 +526,7 @@
     }
 
     def assertContainsAll(response, assertContent) {
-        assertContent.forEach( string -> { assert(response.contentAsString.contains(string)) })
+        assertContent.forEach(string -> { assert (response.contentAsString.contains(string)) })
         return void
     }
 
@@ -476,8 +549,8 @@
 
     def assertContainsPublicProperties(response) {
         def expectedContent = [
-            '"publicCmHandleProperties":' ,
-            '"public prop"' ,
+            '"publicCmHandleProperties":',
+            '"public prop"',
             '"some public property"'
         ]
         return assertContainsAll(response, expectedContent)
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactorySpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactorySpec.groovy
new file mode 100644
index 0000000..3f7a8a5
--- /dev/null
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactorySpec.groovy
@@ -0,0 +1,20 @@
+package org.onap.cps.ncmp.rest.controller.handlers
+
+import spock.lang.Specification
+
+class NcmpDatastoreResourceRequestHandlerFactorySpec extends Specification {
+
+    def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(null, null)
+
+    def 'Creating ncmp datastore request handlers.'() {
+        when: 'a ncmp datastore request handler is created for #datastoreType'
+            def result = objectUnderTest.getNcmpDatastoreResourceRequestHandler(datastoreType)
+        then: 'the result is of the expected class'
+            result.class == expectedClass
+        where: 'the following type of datastore is used'
+            datastoreType                         || expectedClass
+            DatastoreType.OPERATIONAL             || NcmpDatastoreOperationalResourceRequestHandler
+            DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpDatastorePassthroughOperationalResourceRequestHandler
+            DatastoreType.PASSTHROUGH_RUNNING     || NcmpDatastorePassthroughRunningResourceRequestHandler
+    }
+}
\ No newline at end of file
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 ce908e7..b8b7fe3 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,8 +29,9 @@
 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.CmHandleStateMapper
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
+import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
 import org.onap.cps.ncmp.rest.util.DeprecationHelper
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
@@ -48,9 +49,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_REQUEST
-import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
-import static org.springframework.http.HttpStatus.NOT_FOUND
+import static org.springframework.http.HttpStatus.*
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
 
@@ -78,6 +77,9 @@
     @SpringBean
     DeprecationHelper stubbedDeprecationHelper = Stub()
 
+    @SpringBean
+    NcmpDatastoreResourceRequestHandlerFactory mockedNcmpDatastoreResourceRequestHandlerFactory = Mock()
+
     @Value('${rest.api.ncmp-base-path}')
     def basePathNcmp
 
@@ -125,7 +127,7 @@
 
     def 'Failing DMI Request - passthrough scenario'() {
         given: 'failing DMI request'
-            setupTestException(new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400) , NCMP)
+            setupTestException(new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400), NCMP)
         when: 'the DMI request is executed'
             def response = performTestRequest(NCMP)
         then: 'NCMP service responds with 502 Bad Gateway status'
@@ -150,7 +152,7 @@
         return mvc.perform(post("$dataNodeBaseEndpointNcmpInventory/ch").contentType(MediaType.APPLICATION_JSON).content(jsonData)).andReturn().response
     }
 
-    static void assertTestResponse(response, expectedStatus , expectedErrorMessage , expectedErrorDetails) {
+    static void assertTestResponse(response, expectedStatus, expectedErrorMessage, expectedErrorDetails) {
         assert response.status == expectedStatus.value()
         def content = new JsonSlurper().parseText(response.contentAsString)
         assert content['status'].toString().contains(expectedStatus.toString())
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/TopicValidatorSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/TopicValidatorSpec.groovy
new file mode 100644
index 0000000..e626e15
--- /dev/null
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/TopicValidatorSpec.groovy
@@ -0,0 +1,47 @@
+/*
+ *  ============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.util
+
+import org.onap.cps.ncmp.rest.exceptions.InvalidTopicException
+import spock.lang.Specification
+
+class TopicValidatorSpec extends Specification {
+
+    def 'Valid topic name validation.'() {
+        when: 'a valid topic name is validated'
+            TopicValidator.validateTopicName('my-valid-topic')
+        then: 'no exception is thrown'
+            noExceptionThrown()
+    }
+
+    def 'Validating invalid topic names.'() {
+        when: 'the invalid topic name is validated'
+            TopicValidator.validateTopicName(topicName)
+        then: 'boolean response will be returned for #scenario'
+            thrown(InvalidTopicException)
+        where: 'the following names are used'
+            scenario                  | topicName
+            'empty topic'             | ''
+            'blank topic'             | ' '
+            'invalid non empty topic' | '1_5_*_#'
+    }
+
+}