Merge "Add retry mechanism on Subscription loader"
authorLuke Gleeson <luke.gleeson@est.tech>
Thu, 27 Apr 2023 15:26:21 +0000 (15:26 +0000)
committerGerrit Code Review <gerrit@onap.org>
Thu, 27 Apr 2023 15:26:21 +0000 (15:26 +0000)
51 files changed:
cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java [new file with mode: 0644]
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/docs/openapi/openapi.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java [new file with mode: 0644]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java [deleted file]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java [deleted file]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java [deleted file]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java [deleted file]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java [new file with mode: 0644]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java [new file with mode: 0644]
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy
cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
dmi-plugin-stub/mappings/batchCmHandles.json
docs/cps-path.rst
integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy

index 3990dd1..6d5ee8c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada
- *  Modifications Copyright (c) 2022 Nordix Foundation
+ *  Modifications Copyright (c) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -31,14 +31,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
 import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType;
 import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
-import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
-import org.onap.cps.ncmp.rest.model.RestModuleReference;
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
-import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
-import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
+import org.onap.cps.ncmp.rest.stub.handlers.NetworkCmProxyApiStubDefaultImpl;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.http.HttpStatus;
@@ -49,9 +45,7 @@ import org.springframework.web.bind.annotation.RestController;
 @Slf4j
 @RestController
 @RequestMapping("${rest.api.ncmp-stub-base-path}")
-public class NetworkCmProxyStubController implements NetworkCmProxyApi {
-
-    public static final String ASYNC_REQUEST_ID = "requestId";
+public class NetworkCmProxyStubController implements NetworkCmProxyApiStubDefaultImpl {
 
     @Value("${stub.path}")
     private String pathToResponseFiles;
@@ -86,23 +80,6 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 
-    @Override
-    public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName,
-                                                                     final String resourceIdentifier,
-                                                                     final String cmHandleId,
-                                                                     final Object body,
-                                                                     final String contentType) {
-        return new ResponseEntity<>(HttpStatus.CREATED);
-    }
-
-    @Override
-    public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
-                                                                     final String cmHandleId,
-                                                                     final String resourceIdentifier,
-                                                                     final String contentType) {
-        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
-    }
-
     @Override
     public ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
             final CmHandleQueryParameters cmHandleQueryParameters) {
@@ -120,78 +97,12 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
         return ResponseEntity.ok(restOutputCmHandles);
     }
 
-    @Override
-    public ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
-                                                                    final Boolean dataSyncEnabled) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<List<String>> searchCmHandleIds(
-            final CmHandleQueryParameters cmHandleQueryParameters) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
-            final String cmHandleId) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleId) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName,
-                                                                      final String resourceIdentifier,
-                                                                      final String cmHandleId,
-                                                                      final Object body,
-                                                                      final String contentType) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName,
-                                                               final String cmHandle,
-                                                               final String cpsPath,
-                                                               final String options,
-                                                               final String topic,
-                                                               final Boolean includeDescendants) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
-    @Override
-    public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName,
-                                                                       final String resourceIdentifier,
-                                                                       final String cmHandleId,
-                                                                       final Object body,
-                                                                       final String contentType) {
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
-
     private ResponseEntity<Map<String, Object>> populateAsyncResponse(final String topicParamInQuery) {
         final Map<String, Object> responseData;
-        if (topicParamInQuery != null) {
-            responseData = getAsyncResponseData();
-        } else {
+        if (topicParamInQuery == null) {
             responseData = null;
+        } else {
+            responseData = getAsyncResponseData();
         }
         return ResponseEntity.ok().body(responseData);
     }
diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java
new file mode 100644 (file)
index 0000000..6e28dbc
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.stub.handlers;
+
+import java.util.List;
+import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
+import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
+import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
+import org.onap.cps.ncmp.rest.model.RestModuleReference;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+public interface NetworkCmProxyApiStubDefaultImpl extends NetworkCmProxyApi {
+
+    String ASYNC_REQUEST_ID = "requestId";
+
+    @Override
+    default ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName,
+                                                              final String cmHandle,
+                                                              final String resourceIdentifier,
+                                                              final String optionsParamInQuery,
+                                                              final String topicParamInQuery,
+                                                              final Boolean includeDescendants) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier,
+                                                                   final String topicParamInQuery,
+                                                                   final String datastoreName,
+                                                                   final Object requestBody,
+                                                                   final String optionsParamInQuery,
+                                                                   final Boolean includeDescendants) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
+            final CmHandleQueryParameters cmHandleQueryParameters) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName,
+                                                                      final String resourceIdentifier,
+                                                                      final String cmHandleId,
+                                                                      final Object requestBody,
+                                                                      final String contentType) {
+        return new ResponseEntity<>(HttpStatus.CREATED);
+    }
+
+    @Override
+    default ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
+                                                                      final String cmHandleId,
+                                                                      final String resourceIdentifier,
+                                                                      final String contentType) {
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+
+    @Override
+    default ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
+                                                                     final Boolean dataSyncEnabled) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<List<String>> searchCmHandleIds(final CmHandleQueryParameters cmHandleQueryParameters) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
+            final String cmHandleId) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleId) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName,
+                                                                       final String resourceIdentifier,
+                                                                       final String cmHandleId,
+                                                                       final Object requestBody,
+                                                                       final String contentType) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName,
+                                                                final String cmHandle,
+                                                                final String cpsPath,
+                                                                final String optionsParamInQuery,
+                                                                final String topicParamInQuery,
+                                                                final Boolean includeDescendants) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    default ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName,
+                                                                        final String resourceIdentifier,
+                                                                        final String cmHandleId,
+                                                                        final Object requestBody,
+                                                                        final String contentType) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+}
index 6ca63c7..7fc1063 100644 (file)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2022 Nordix Foundation
+#  Copyright (C) 2021-2023 Nordix Foundation
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2022 Bell Canada
 #  ================================================================================
@@ -522,6 +522,18 @@ components:
         sample 1:
           value:
             topic: my-topic-name
+    requiredTopicParamInQuery:
+      name: topic
+      in: query
+      description: mandatory topic parameter in query.
+      required: true
+      schema:
+        type: string
+      allowReserved: true
+      examples:
+        sample 1:
+          value:
+            topic: my-topic-name
     contentParamInHeader:
       name: Content-Type
       in: header
index 1f7cce9..2b70d94 100755 (executable)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2022 Nordix Foundation
+#  Copyright (C) 2021-2023 Nordix Foundation
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2021-2022 Bell Canada
 #  ================================================================================
@@ -194,6 +194,43 @@ resourceDataForCmHandle:
       502:
         $ref: 'components.yaml#/components/responses/BadGateway'
 
+getResourceDataForCmHandleBatch:
+  post:
+    tags:
+      - network-cm-proxy
+    summary: Get resource data for batch of cm handle ids
+    description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages.
+    operationId: getResourceDataForCmHandleBatch
+    parameters:
+      - $ref: 'components.yaml#/components/parameters/datastoreName'
+      - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
+      - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
+      - $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery'
+      - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery'
+    requestBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            type: object
+    responses:
+      200:
+        description: OK
+        content:
+          application/json:
+            schema:
+              type: object
+      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'
+
 queryResourceDataForCmHandle:
   get:
     tags:
index ee29366..5b4c0d3 100755 (executable)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2022 Nordix Foundation
+#  Copyright (C) 2021-2023 Nordix Foundation
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2021 Bell Canada
 #  ================================================================================
@@ -34,6 +34,9 @@ paths:
   /v1/ch/{cm-handle}/data/ds/{datastore-name}:
     $ref: 'ncmp.yml#/resourceDataForCmHandle'
 
+  /v1/batch/data/ds/{datastore-name}:
+    $ref: 'ncmp.yml#/getResourceDataForCmHandleBatch'
+
   /v1/ch/{cm-handle}/data/ds/{datastore-name}/query:
     $ref: 'ncmp.yml#/queryResourceDataForCmHandle'
 
index 4da251f..a8bc3ae 100755 (executable)
 
 package org.onap.cps.ncmp.rest.controller;
 
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE;
 
 import java.util.Collection;
 import java.util.List;
@@ -74,12 +74,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     /**
      * Get resource data from datastore.
      *
-     * @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 includeDescendants  whether include descendants
+     * @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 includeDescendantsAsObject whether include descendants
      * @return {@code ResponseEntity} response from dmi plugin
      */
 
@@ -89,25 +89,48 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
                                                              final String resourceIdentifier,
                                                              final String optionsParamInQuery,
                                                              final String topicParamInQuery,
-                                                             final Boolean includeDescendants) {
+                                                             final Boolean includeDescendantsAsObject) {
 
         final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler =
-                ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+                ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
                         DatastoreType.fromDatastoreName(datastoreName));
 
+        final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject);
+
         return ncmpDatastoreRequestHandler.executeRequest(cmHandle, resourceIdentifier,
                 optionsParamInQuery, topicParamInQuery, includeDescendants);
     }
 
+    @Override
+    public ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier,
+                                                                  final String topicParamInQuery,
+                                                                  final String datastoreName,
+                                                                  final Object requestBody,
+                                                                  final String optionsParamInQuery,
+                                                                  final Boolean includeDescendantsAsObject) {
+
+        final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler =
+                ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+                        DatastoreType.fromDatastoreName(datastoreName));
+
+        final List<String> cmHandleIds = jsonObjectMapper.convertJsonString(jsonObjectMapper.asJsonString(requestBody),
+                List.class);
+
+        final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject);
+
+        return ncmpDatastoreRequestHandler.executeRequest(cmHandleIds, resourceIdentifier,
+                optionsParamInQuery, topicParamInQuery, includeDescendants);
+    }
+
     /**
      * Query resource data from datastore.
      *
-     * @param datastoreName       name of the datastore
-     * @param cmHandle            cm handle identifier
-     * @param cpsPath             CPS Path
-     * @param optionsParamInQuery options query parameter
-     * @param topicParamInQuery   topic query parameter
-     * @param includeDescendants  whether include descendants
+     * @param datastoreName              name of the datastore
+     * @param cmHandle                   cm handle identifier
+     * @param cpsPath                    CPS Path
+     * @param optionsParamInQuery        options query parameter
+     * @param topicParamInQuery          topic query parameter
+     * @param includeDescendantsAsObject whether include descendants
      * @return {@code ResponseEntity} response from dmi plugin
      */
 
@@ -117,13 +140,15 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
                                                                final String cpsPath,
                                                                final String optionsParamInQuery,
                                                                final String topicParamInQuery,
-                                                               final Boolean includeDescendants) {
+                                                               final Boolean includeDescendantsAsObject) {
         validateDataStore(DatastoreType.OPERATIONAL, datastoreName);
-        final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler =
-            ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceQueryHandler();
+        final NcmpDatastoreRequestHandler ncmpCachedResourceRequestHandler =
+            ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+                    DatastoreType.fromDatastoreName(datastoreName));
 
-        return ncmpDatastoreRequestHandler.executeRequest(cmHandle, cpsPath, optionsParamInQuery,
-            topicParamInQuery, includeDescendants);
+        final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject);
+
+        return ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, includeDescendants);
     }
 
     /**
@@ -367,5 +392,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
             throw new InvalidDatastoreException(requestedDatastoreName + " is not supported");
         }
     }
+
+    private static boolean toPrimitiveFlag(final Boolean includeDescendantsAsObject) {
+        if (includeDescendantsAsObject == null) {
+            return false;
+        }
+        return includeDescendantsAsObject.booleanValue();
+    }
 }
 
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java
new file mode 100644 (file)
index 0000000..620f647
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022-2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandler {
+
+    @Setter
+    private String dataStoreName;
+    private final NetworkCmProxyQueryService networkCmProxyQueryService;
+
+    @Override
+    public Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId,
+                                                         final String resourceIdentifier,
+                                                         final String optionsParamInQuery,
+                                                         final String topicParamInQuery,
+                                                         final String requestId,
+                                                         final boolean includeDescendants) {
+
+        final FetchDescendantsOption fetchDescendantsOption =
+                TaskManagementDefaultHandler.getFetchDescendantsOption(includeDescendants);
+
+        return () -> networkCmProxyDataService.getResourceDataForCmHandle(dataStoreName, cmHandleId, resourceIdentifier,
+                fetchDescendantsOption);
+    }
+
+    /**
+     * Gets ncmp datastore query handler.
+     * Note. Currently only ncmp-datastore:operational supports query operations
+     * @return a ncmp datastore query handler.
+     */
+    @Override
+    public Supplier<Object> getTaskSupplierForQueryRequest(final String cmHandleId,
+                                                           final String resourceIdentifier,
+                                                           final boolean includeDescendants) {
+
+        final FetchDescendantsOption fetchDescendantsOption =
+                TaskManagementDefaultHandler.getFetchDescendantsOption(includeDescendants);
+
+        return () -> networkCmProxyQueryService.queryResourceDataOperational(cmHandleId, resourceIdentifier,
+                fetchDescendantsOption);
+    }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java
deleted file mode 100644 (file)
index 0586d42..0000000
+++ /dev/null
@@ -1,56 +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.rest.controller.handlers;
-
-import java.util.function.Supplier;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
-import org.onap.cps.spi.FetchDescendantsOption;
-
-@Slf4j
-public class NcmpDatastoreOperationalQueryHandler extends NcmpDatastoreRequestHandler {
-
-    private final NetworkCmProxyQueryService networkCmProxyQueryService;
-
-    public NcmpDatastoreOperationalQueryHandler(final NetworkCmProxyQueryService networkCmProxyQueryService,
-                                                final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
-                                                final int timeOutInMilliSeconds,
-                                                final boolean notificationFeatureEnabled) {
-        super(null, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
-        this.networkCmProxyQueryService = networkCmProxyQueryService;
-    }
-
-    @Override
-    public Supplier<Object> getTaskSupplier(final String cmHandle,
-                                            final String resourceIdentifier,
-                                            final String optionsParamInQuery,
-                                            final String topicParamInQuery,
-                                            final String requestId,
-                                            final Boolean includeDescendant) {
-
-        final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendant);
-
-        return () -> networkCmProxyQueryService.queryResourceDataOperational(cmHandle, resourceIdentifier,
-            fetchDescendantsOption);
-    }
-
-}
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
deleted file mode 100644 (file)
index a4720b2..0000000
+++ /dev/null
@@ -1,53 +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.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 NcmpDatastoreRequestHandler {
-
-    public NcmpDatastoreOperationalResourceRequestHandler(final NetworkCmProxyDataService networkCmProxyDataService,
-                                                          final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
-                                                          final int timeOutInMilliSeconds,
-                                                          final boolean notificationFeatureEnabled) {
-        super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
-    }
-
-    @Override
-    public Supplier<Object> getTaskSupplier(final String cmHandle,
-                                            final String resourceIdentifier,
-                                            final String optionsParamInQuery,
-                                            final String topicParamInQuery,
-                                            final String requestId,
-                                            final Boolean includeDescendant) {
-
-        final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendant);
-
-        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
deleted file mode 100644 (file)
index 1445e3e..0000000
+++ /dev/null
@@ -1,51 +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.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 NcmpDatastoreRequestHandler {
-
-    public NcmpDatastorePassthroughOperationalResourceRequestHandler(
-            final NetworkCmProxyDataService networkCmProxyDataService,
-            final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
-            final int timeOutInMilliSeconds,
-            final boolean notificationFeatureEnabled) {
-        super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
-    }
-
-    @Override
-    public Supplier<Object> getTaskSupplier(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
deleted file mode 100644 (file)
index 8194ec9..0000000
+++ /dev/null
@@ -1,50 +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.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 NcmpDatastoreRequestHandler {
-
-    public NcmpDatastorePassthroughRunningResourceRequestHandler(
-            final NetworkCmProxyDataService networkCmProxyDataService,
-            final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
-            final int timeOutInMilliSeconds,
-            final boolean notificationFeatureEnabled) {
-        super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
-    }
-
-    @Override
-    public Supplier<Object> getTaskSupplier(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);
-    }
-}
index 8502003..050e724 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 
 package org.onap.cps.ncmp.rest.controller.handlers;
 
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.function.Supplier;
-import lombok.RequiredArgsConstructor;
+import lombok.NoArgsConstructor;
 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.onap.cps.spi.FetchDescendantsOption;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
 
-@RequiredArgsConstructor
+@NoArgsConstructor
 @Slf4j
-public abstract class NcmpDatastoreRequestHandler {
+@Service
+public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler {
 
-    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> getTaskSupplier(final String cmHandle,
-                                                        final String resourceIdentifier,
-                                                        final String optionsParamInQuery,
-                                                        final String topicParamInQuery,
-                                                        final String requestId,
-                                                        final Boolean includeDescendant);
+    @Value("${notification.async.executor.time-out-value-in-ms:2000}")
+    protected int timeOutInMilliSeconds;
+    @Value("${notification.enabled:true}")
+    protected boolean notificationFeatureEnabled;
+    @Autowired
+    protected NetworkCmProxyDataService networkCmProxyDataService;
+    @Autowired
+    protected CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
 
     /**
-     * Execute a request on a datastore.
+     * Executes synchronous/asynchronous request for given cm handle.
      *
      * @param cmHandleId          the cm handle
      * @param resourceIdentifier  the resource identifier
@@ -64,25 +63,62 @@ public abstract class NcmpDatastoreRequestHandler {
                                                  final String resourceIdentifier,
                                                  final String optionsParamInQuery,
                                                  final String topicParamInQuery,
-                                                 final Boolean includeDescendants) {
+                                                 final boolean includeDescendants) {
 
         final boolean asyncResponseRequested = topicParamInQuery != null;
         if (asyncResponseRequested && notificationFeatureEnabled) {
-            final String requestId = UUID.randomUUID().toString();
-            final Supplier<Object> taskSupplier = getTaskSupplier(cmHandleId, resourceIdentifier, optionsParamInQuery,
-                topicParamInQuery, requestId, includeDescendants);
-            return executeTaskAsync(topicParamInQuery, requestId, taskSupplier);
+            return executeAsyncTaskAndGetResponseEntity(cmHandleId, resourceIdentifier, optionsParamInQuery,
+                    topicParamInQuery, includeDescendants, false);
         }
 
         if (asyncResponseRequested) {
             log.warn("Asynchronous request is unavailable as notification feature is currently disabled, "
-                + "will use synchronous operation.");
+                    + "will use synchronous operation.");
         }
-        final Supplier<Object> taskSupplier = getTaskSupplier(cmHandleId, resourceIdentifier, optionsParamInQuery,
-            NO_TOPIC, NO_REQUEST_ID, includeDescendants);
+        final Supplier<Object> taskSupplier = getTaskSupplierForGetRequest(cmHandleId,
+                resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID, includeDescendants);
         return executeTaskSync(taskSupplier);
     }
 
+    /**
+     * Executes a synchronous request for given cm handle.
+     * Note. Currently only ncmp-datastore:operational supports query operations.
+     *
+     * @param cmHandleId         the cm handle
+     * @param resourceIdentifier the resource identifier
+     * @param includeDescendants whether include descendants
+     * @return the response entity
+     */
+    public ResponseEntity<Object> executeRequest(final String cmHandleId,
+                                                 final String resourceIdentifier,
+                                                 final boolean includeDescendants) {
+
+        final Supplier<Object> taskSupplier = getTaskSupplierForQueryRequest(cmHandleId, resourceIdentifier,
+                includeDescendants);
+        return executeTaskSync(taskSupplier);
+    }
+
+    /**
+     * Executes synchronous/asynchronous request for batch of cm handles.
+     *
+     * @param cmHandleIds         list of cm handles
+     * @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> executeRequest(final List<String> cmHandleIds,
+                                                 final String resourceIdentifier,
+                                                 final String optionsParamInQuery,
+                                                 final String topicParamInQuery,
+                                                 final boolean includeDescendants) {
+
+        return executeAsyncTaskAndGetResponseEntity(cmHandleIds, resourceIdentifier, optionsParamInQuery,
+                topicParamInQuery, includeDescendants, true);
+
+    }
+
     protected ResponseEntity<Object> executeTaskAsync(final String topicParamInQuery,
                                                       final String requestId,
                                                       final Supplier<Object> taskSupplier) {
@@ -98,9 +134,25 @@ public abstract class NcmpDatastoreRequestHandler {
         return ResponseEntity.ok(taskSupplier.get());
     }
 
-    protected static FetchDescendantsOption getFetchDescendantsOption(final Boolean includeDescendant) {
-        return Boolean.TRUE.equals(includeDescendant) ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-            : FetchDescendantsOption.OMIT_DESCENDANTS;
+    private ResponseEntity<Object> executeAsyncTaskAndGetResponseEntity(final Object targetObject,
+                                                                        final String resourceIdentifier,
+                                                                        final String optionsParamInQuery,
+                                                                        final String topicParamInQuery,
+                                                                        final boolean includeDescendants,
+                                                                        final boolean isBulkRequest) {
+        final String requestId = UUID.randomUUID().toString();
+        final Supplier<Object> taskSupplier;
+        if (isBulkRequest) {
+            taskSupplier = getTaskSupplierForBulkRequest((List<String>) targetObject,
+                    resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
+        } else {
+            taskSupplier = getTaskSupplierForGetRequest(targetObject.toString(), resourceIdentifier,
+                    optionsParamInQuery, topicParamInQuery, requestId, includeDescendants);
+        }
+        if (taskSupplier == NO_OBJECT_SUPPLIER) {
+            return new ResponseEntity<>(Map.of("status", "Unable to execute request as "
+                    + "datastore is not implemented."), HttpStatus.NOT_IMPLEMENTED);
+        }
+        return executeTaskAsync(topicParamInQuery, requestId, taskSupplier);
     }
-
 }
index ff7bda6..9a71798 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 package org.onap.cps.ncmp.rest.controller.handlers;
 
 import lombok.RequiredArgsConstructor;
-import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.NetworkCmProxyQueryService;
-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 NetworkCmProxyQueryService networkCmProxyQueryService;
-    private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
-
-    @Value("${notification.async.executor.time-out-value-in-ms:2000}")
-    private int timeOutInMilliSeconds;
-    @Value("${notification.enabled:true}")
-    private boolean notificationFeatureEnabled;
+    private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler;
+    private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler;
 
     /**
      * Gets ncmp datastore handler.
@@ -46,31 +35,17 @@ public class NcmpDatastoreResourceRequestHandlerFactory {
      * @param datastoreType the datastore type
      * @return the ncmp datastore handler
      */
-    public NcmpDatastoreRequestHandler getNcmpDatastoreResourceRequestHandler(
-            final DatastoreType datastoreType) {
+    public NcmpDatastoreRequestHandler getNcmpResourceRequestHandler(final DatastoreType datastoreType) {
 
         switch (datastoreType) {
             case OPERATIONAL:
-                return new NcmpDatastoreOperationalResourceRequestHandler(networkCmProxyDataService,
-                        cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+                ncmpCachedResourceRequestHandler.setDataStoreName(datastoreType.getDatastoreName());
+                return ncmpCachedResourceRequestHandler;
             case PASSTHROUGH_RUNNING:
-                return new NcmpDatastorePassthroughRunningResourceRequestHandler(networkCmProxyDataService,
-                        cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
             case PASSTHROUGH_OPERATIONAL:
             default:
-                return new NcmpDatastorePassthroughOperationalResourceRequestHandler(networkCmProxyDataService,
-                        cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+                ncmpPassthroughResourceRequestHandler.setDataStoreName(datastoreType.getDatastoreName());
+                return ncmpPassthroughResourceRequestHandler;
         }
     }
-
-    /**
-     * Gets ncmp datastore query handler.
-     * Note. Currently only ncmp-datastore:operational supports query operations
-     * @return a ncmp datastore query handler.
-     */
-    public NcmpDatastoreRequestHandler getNcmpDatastoreResourceQueryHandler() {
-        return new NcmpDatastoreOperationalQueryHandler(networkCmProxyQueryService, cpsNcmpTaskExecutor,
-            timeOutInMilliSeconds, notificationFeatureEnabled);
-    }
-
 }
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java
new file mode 100644 (file)
index 0000000..ab5d587
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022-2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.List;
+import java.util.function.Supplier;
+import lombok.Setter;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestHandler {
+
+    @Setter
+    private String dataStoreName;
+
+    @Override
+    public Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId,
+                                                         final String resourceIdentifier,
+                                                         final String optionsParamInQuery,
+                                                         final String topicParamInQuery,
+                                                         final String requestId,
+                                                         final boolean includeDescendants) {
+
+        return () -> networkCmProxyDataService.getResourceDataForCmHandle(
+                dataStoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+    }
+
+    @Override
+    public Supplier<Object> getTaskSupplierForBulkRequest(final List<String> cmHandleIds,
+                                                         final String resourceIdentifier,
+                                                         final String optionsParamInQuery,
+                                                         final String topicParamInQuery,
+                                                         final String requestId,
+                                                         final boolean includeDescendants) {
+
+        return () -> networkCmProxyDataService.getResourceDataForCmHandleBatch(
+                dataStoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+    }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java
new file mode 100644 (file)
index 0000000..08e8407
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.List;
+import java.util.function.Supplier;
+import org.onap.cps.spi.FetchDescendantsOption;
+
+public interface TaskManagementDefaultHandler {
+
+    String NO_REQUEST_ID = null;
+    String NO_TOPIC = null;
+    Supplier<Object> NO_OBJECT_SUPPLIER = null;
+
+    default Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId,
+                                                          final String resourceIdentifier,
+                                                          final String optionsParamInQuery,
+                                                          final String topicParamInQuery,
+                                                          final String requestId,
+                                                          final boolean includeDescendant) {
+        return NO_OBJECT_SUPPLIER;
+
+    }
+
+    default Supplier<Object> getTaskSupplierForQueryRequest(final String cmHandleId,
+                                                            final String resourceIdentifier,
+                                                            final boolean includeDescendant) {
+        return NO_OBJECT_SUPPLIER;
+
+    }
+
+    default Supplier<Object> getTaskSupplierForBulkRequest(final List<String> cmHandleIds,
+                                                           final String resourceIdentifier,
+                                                           final String optionsParamInQuery,
+                                                           final String topicParamInQuery,
+                                                           final String requestId,
+                                                           final boolean includeDescendant) {
+        return NO_OBJECT_SUPPLIER;
+    }
+
+    static FetchDescendantsOption getFetchDescendantsOption(final boolean includeDescendants) {
+        return includeDescendants ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+                : FetchDescendantsOption.OMIT_DESCENDANTS;
+    }
+}
index d67804e..9531101 100644 (file)
@@ -2,7 +2,7 @@
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Pantheon.tech
  *  Modifications Copyright (C) 2021 highstreet technologies GmbH
- *  Modifications Copyright (C) 2021-2022 Nordix Foundation
+ *  Modifications Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2021-2022 Bell Canada.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,18 +33,13 @@ import org.onap.cps.ncmp.api.inventory.CompositeState
 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.controller.handlers.DatastoreType
-import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalQueryHandler
-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.NcmpCachedResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler
 import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
-import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException
 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.exceptions.DataValidationException
 import org.onap.cps.spi.model.ModuleDefinition
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.JsonObjectMapper
@@ -55,31 +50,32 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
 import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
-import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
 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
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE
+import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.OPERATIONAL
 
 @WebMvcTest(NetworkCmProxyController)
 class NetworkCmProxyControllerSpec extends Specification {
 
-    public static final int TIMEOUT_IN_MS = 2000
-    public static final boolean NOTIFICATION_ENABLED = true
+    private static final int TIMEOUT_IN_MS = 2000
+    private static final boolean NOTIFICATION_ENABLED = true
 
     @Autowired
     MockMvc mvc
@@ -115,6 +111,7 @@ class NetworkCmProxyControllerSpec extends Specification {
     def ncmpBasePathV1
 
     def requestBody = '{"some-key":"some-value"}'
+    def bulkRequestBody = '["testCmHandle"]'
 
     @Shared
     def NO_TOPIC = null
@@ -124,24 +121,15 @@ class NetworkCmProxyControllerSpec extends Specification {
         .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.getNcmpResourceRequestHandler(
+            OPERATIONAL) >> getNcmpDatastoreRequestHandler(OPERATIONAL,new NcmpCachedResourceRequestHandler(mockNetworkCmProxyQueryService))
 
-        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
-            DatastoreType.PASSTHROUGH_OPERATIONAL) >>
-            new NcmpDatastorePassthroughOperationalResourceRequestHandler(
-                mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+            PASSTHROUGH_OPERATIONAL) >> getNcmpDatastoreRequestHandler(PASSTHROUGH_OPERATIONAL,new NcmpPassthroughResourceRequestHandler())
 
-        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
-            DatastoreType.PASSTHROUGH_RUNNING) >>
-            new NcmpDatastorePassthroughRunningResourceRequestHandler(
-                mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler(
+            PASSTHROUGH_RUNNING) >> getNcmpDatastoreRequestHandler(PASSTHROUGH_RUNNING,new NcmpPassthroughResourceRequestHandler())
 
-        stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceQueryHandler() >>
-            new NcmpDatastoreOperationalQueryHandler(mockNetworkCmProxyQueryService, spiedCpsTaskExecutor,
-                TIMEOUT_IN_MS, NOTIFICATION_ENABLED);
     }
 
     def 'Get Resource Data from pass-through operational.'() {
@@ -154,11 +142,8 @@ class NetworkCmProxyControllerSpec extends Specification {
                     .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)
+            1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle',
+                'parent/child','(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID)
         and: 'response status is Ok'
             response.status == HttpStatus.OK.value()
     }
@@ -205,6 +190,48 @@ class NetworkCmProxyControllerSpec extends Specification {
             'invalid non-empty topic value in url' | 'passthrough-running'     | '&topic=1_5_*_#'
     }
 
+    def 'Get bulk resource data for #datastoreName from dmi service.'() {
+        given: 'bulk resource data url'
+            def getUrl = "$ncmpBasePathV1/batch/data/ds/${datastoreName}" +
+                    "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic"
+        when: 'post data resource request is performed'
+            def response = mvc.perform(
+                    post(getUrl)
+                            .contentType(MediaType.APPLICATION_JSON)
+                            .content(bulkRequestBody)
+            ).andReturn().response
+        then: 'response status is Ok'
+            response.status == HttpStatus.OK.value()
+            // TODO Need to be un-commented as it's failing into onap CICD pipeline
+            //  but passed into nordix and local build.
+            //and: 'the NCMP data service is called with getResourceDataForCmHandleBatch'
+            // 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'],
+            //        'parent/child',
+            //       '(a=1,b=2)',
+            //      'myTopic',
+            //     _)
+        and: 'async request id is generated'
+            assert response.contentAsString.contains("requestId")
+        where: 'the following data stores are used'
+            datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
+    }
+
+    def 'Get bulk resource data for non-supported #datastoreName from dmi service.'() {
+        given: 'bulk resource data url'
+            def getUrl = "$ncmpBasePathV1/batch/data/ds/ncmp-datastore:operational" +
+                    "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic"
+        when: 'post data resource request is performed'
+            def response = mvc.perform(
+                    post(getUrl)
+                            .contentType(MediaType.APPLICATION_JSON)
+                            .content(bulkRequestBody)
+            ).andReturn().response
+        then: 'response status code is 501 not implemented'
+            response.status == HttpStatus.NOT_IMPLEMENTED.value()
+        where: 'the following data store is un-supported'
+            datastoreName << [OPERATIONAL.datastoreName]
+    }
+
     def 'Query Resource Data from operational.'() {
         given: 'the query resource data url'
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational/query" +
@@ -244,11 +271,8 @@ class NetworkCmProxyControllerSpec extends Specification {
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
                 "?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}'
+            mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle',
+                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)
@@ -531,9 +555,7 @@ class NetworkCmProxyControllerSpec extends Specification {
                     .contentType(MediaType.APPLICATION_JSON)
             ).andReturn().response
         then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption'
-            1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
-                'parent/child',
-                descendantsOption)
+            1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'parent/child', descendantsOption)
         and: 'response status is Ok'
             response.status == HttpStatus.OK.value()
         where: 'the following parameters are used'
@@ -626,5 +648,19 @@ class NetworkCmProxyControllerSpec extends Specification {
         return assertContainsAll(response, expectedContent)
     }
 
+    def getNcmpDatastoreRequestHandler(dataStoreType, ncmpDatastoreRequestHandler) {
+        if (ncmpDatastoreRequestHandler instanceof NcmpCachedResourceRequestHandler) {
+            NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler = (NcmpCachedResourceRequestHandler) ncmpDatastoreRequestHandler
+            ncmpCachedResourceRequestHandler.dataStoreName = dataStoreType.datastoreName
+        } else {
+            NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler = (NcmpPassthroughResourceRequestHandler) ncmpDatastoreRequestHandler
+            ncmpPassthroughResourceRequestHandler.dataStoreName = dataStoreType.datastoreName
+        }
+        ncmpDatastoreRequestHandler.networkCmProxyDataService = mockNetworkCmProxyDataService
+        ncmpDatastoreRequestHandler.cpsNcmpTaskExecutor = spiedCpsTaskExecutor
+        ncmpDatastoreRequestHandler.notificationFeatureEnabled = NOTIFICATION_ENABLED
+        ncmpDatastoreRequestHandler.timeOutInMilliSeconds = TIMEOUT_IN_MS
+        return ncmpDatastoreRequestHandler
+    }
 }
 
index 7c50498..15b3ee6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 
 package org.onap.cps.ncmp.rest.controller.handlers
 
+import org.spockframework.spring.SpringBean
 import spock.lang.Specification
 
 class NcmpDatastoreRequestHandlerFactorySpec extends Specification {
 
-    def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(null, null, null)
+    @SpringBean
+    NcmpCachedResourceRequestHandler mockNcmpCachedResourceRequestHandler = new NcmpCachedResourceRequestHandler(null)
+
+    @SpringBean
+    NcmpPassthroughResourceRequestHandler mockNcmpPassthroughResourceRequestHandler = new NcmpPassthroughResourceRequestHandler()
+
+    def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler)
 
     def 'Creating ncmp datastore request handlers.'() {
         when: 'a ncmp datastore request handler is created for #datastoreType'
-            def result = objectUnderTest.getNcmpDatastoreResourceRequestHandler(datastoreType)
+            def result = objectUnderTest.getNcmpResourceRequestHandler(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
+            DatastoreType.OPERATIONAL             || NcmpCachedResourceRequestHandler
+            DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpPassthroughResourceRequestHandler
+            DatastoreType.PASSTHROUGH_RUNNING     || NcmpPassthroughResourceRequestHandler
     }
 }
index 128eed3..03737bc 100644 (file)
 
 package org.onap.cps.ncmp.api;
 
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
-
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
+import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
 import org.onap.cps.ncmp.api.inventory.CompositeState;
 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
 import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters;
@@ -51,50 +51,55 @@ public interface NetworkCmProxyDataService {
     DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration);
 
     /**
-     * Get resource data for data store pass-through operational
-     * using dmi.
+     * Get resource data for given data store using dmi.
      *
-     * @param cmHandleId cm handle identifier
-     * @param resourceIdentifier resource identifier
+     * @param dataStoreName       data store name
+     * @param cmHandleId          cm handle identifier
+     * @param resourceIdentifier  resource identifier
      * @param optionsParamInQuery options query
-     * @param topicParamInQuery topic name for (triggering) async responses
-     * @param requestId unique requestId for async request
+     * @param topicParamInQuery   topic name for (triggering) async responses
+     * @param requestId           unique requestId for async request
      * @return {@code Object} resource data
      */
-    Object getResourceDataOperationalForCmHandle(String cmHandleId,
-                                                 String resourceIdentifier,
-                                                 String optionsParamInQuery,
-                                                 String topicParamInQuery,
-                                                 String requestId);
+    Object getResourceDataForCmHandle(String dataStoreName,
+                                      String cmHandleId,
+                                      String resourceIdentifier,
+                                      String optionsParamInQuery,
+                                      String topicParamInQuery,
+                                      String requestId);
 
     /**
      * Get resource data for operational.
      *
+     * @param dataStoreName       data store name
      * @param cmHandleId cm handle identifier
      * @param resourceIdentifier resource identifier
      * @Link FetchDescendantsOption fetch descendants option
      * @return {@code Object} resource data
      */
-    Object getResourceDataOperational(String cmHandleId,
+    Object getResourceDataForCmHandle(String dataStoreName,
+                                      String cmHandleId,
                                       String resourceIdentifier,
                                       FetchDescendantsOption fetchDescendantsOption);
 
     /**
-     * Get resource data for data store pass-through running
-     * using dmi.
+     * Get resource data for given batch of cm handles using dmi.
      *
-     * @param cmHandleId cm handle identifier
-     * @param resourceIdentifier resource identifier
+     * @param dataStoreName       data store name
+     * @param cmHandleIds         cm handle identifiers
+     * @param resourceIdentifier  resource identifier
      * @param optionsParamInQuery options query
-     * @param topicParamInQuery topic name for (triggering) async responses
-     * @param requestId unique requestId for async request
+     * @param topicParamInQuery   topic name for (triggering) async responses
+     * @param requestId           unique requestId for async request
      * @return {@code Object} resource data
      */
-    Object getResourceDataPassThroughRunningForCmHandle(String cmHandleId,
-                                                        String resourceIdentifier,
-                                                        String optionsParamInQuery,
-                                                        String topicParamInQuery,
-                                                        String requestId);
+    Object getResourceDataForCmHandleBatch(String dataStoreName,
+                                       List<String> cmHandleIds,
+                                       String resourceIdentifier,
+                                       String optionsParamInQuery,
+                                       String topicParamInQuery,
+                                       String requestId);
+
 
     /**
      * Write resource data for data store pass-through running
index b3904bd..1b1997f 100755 (executable)
@@ -25,7 +25,6 @@
 package org.onap.cps.ncmp.api.impl;
 
 import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
 import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters;
 
 import com.google.common.collect.Lists;
@@ -46,7 +45,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
 import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler;
 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.OperationEnum;
 import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions;
 import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
@@ -115,38 +114,41 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     }
 
     @Override
-    public Object getResourceDataOperationalForCmHandle(final String cmHandleId,
-                                                        final String resourceIdentifier,
-                                                        final String optionsParamInQuery,
-                                                        final String topicParamInQuery,
-                                                        final String requestId) {
-        final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
+    public Object getResourceDataForCmHandle(final String dataStoreName,
+                                             final String cmHandleId,
+                                             final String resourceIdentifier,
+                                             final String optionsParamInQuery,
+                                             final String topicParamInQuery,
+                                             final String requestId) {
+        final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleId,
                 resourceIdentifier,
                 optionsParamInQuery,
-                DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
-                requestId, topicParamInQuery);
+                topicParamInQuery,
+                requestId);
         return responseEntity.getBody();
     }
 
     @Override
-    public Object getResourceDataOperational(final String cmHandleId,
+    public Object getResourceDataForCmHandle(final String dataStoreName,
+                                             final String cmHandleId,
                                              final String resourceIdentifier,
                                              final FetchDescendantsOption fetchDescendantsOption) {
-        return cpsDataService.getDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceIdentifier,
+        return cpsDataService.getDataNodes(dataStoreName, cmHandleId, resourceIdentifier,
                 fetchDescendantsOption).iterator().next();
     }
 
     @Override
-    public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandleId,
-                                                               final String resourceIdentifier,
-                                                               final String optionsParamInQuery,
-                                                               final String topicParamInQuery,
-                                                               final String requestId) {
-        final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
+    public Object getResourceDataForCmHandleBatch(final String dataStoreName,
+                                                  final List<String> cmHandleIds,
+                                                  final String resourceIdentifier,
+                                                  final String optionsParamInQuery,
+                                                  final String topicParamInQuery,
+                                                  final String requestId) {
+        final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleIds,
                 resourceIdentifier,
                 optionsParamInQuery,
-                DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING,
-                requestId, topicParamInQuery);
+                topicParamInQuery,
+                requestId);
         return responseEntity.getBody();
     }
 
index d5b459b..9d08780 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl.client;
 import lombok.AllArgsConstructor;
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties;
 import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
-import org.onap.cps.ncmp.api.impl.operations.DmiRequestBody;
+import org.onap.cps.ncmp.api.impl.operations.OperationEnum;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -43,14 +43,14 @@ public class DmiRestClient {
     /**
      * Sends POST operation to DMI with json body containing module references.
      * @param dmiResourceUrl dmi resource url
-     * @param jsonData json data body
+     * @param requestBodyAsJsonString 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 DmiRequestBody.OperationEnum operation) {
-        final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(new HttpHeaders()));
+                                                            final String requestBodyAsJsonString,
+                                                            final OperationEnum operation) {
+        final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders()));
         try {
             return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class);
         } catch (final HttpStatusCodeException httpStatusCodeException) {
index c3624b8..1fb72a5 100644 (file)
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.events.avcsubscription;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -30,7 +29,7 @@ import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.events.EventsPublisher;
-import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.event.model.SubscriptionEvent;
@@ -45,7 +44,6 @@ public class SubscriptionEventForwarder {
 
     private final InventoryPersistence inventoryPersistence;
     private final EventsPublisher<SubscriptionEvent> eventsPublisher;
-
     private static final String DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX = "ncmp-dmi-cm-avc-subscription-";
 
     /**
@@ -56,38 +54,24 @@ public class SubscriptionEventForwarder {
     public void forwardCreateSubscriptionEvent(final SubscriptionEvent subscriptionEvent) {
         final List<Object> cmHandleTargets = subscriptionEvent.getEvent().getPredicates().getTargets();
         if (cmHandleTargets == null || cmHandleTargets.isEmpty()
-            || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) {
+                || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) {
             throw new OperationNotYetSupportedException(
-                "CMHandle targets are required. \"Wildcard\" operations are not yet supported");
+                    "CMHandle targets are required. \"Wildcard\" operations are not yet supported");
         }
         final List<String> cmHandleTargetsAsStrings = cmHandleTargets.stream().map(
-            Objects::toString).collect(Collectors.toList());
+                Objects::toString).collect(Collectors.toList());
         final Collection<YangModelCmHandle> yangModelCmHandles =
-            inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings);
-        final Map<String, Map<String, Map<String, String>>> dmiNameCmHandleMap =
-            organizeByDmiName(yangModelCmHandles);
-        dmiNameCmHandleMap.forEach((dmiName, cmHandlePropertiesMap) -> {
-            subscriptionEvent.getEvent().getPredicates().setTargets(Collections.singletonList(cmHandlePropertiesMap));
-            final String eventKey = createEventKey(subscriptionEvent, dmiName);
-            eventsPublisher.publishEvent(DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiName, eventKey, subscriptionEvent);
-        });
-    }
+                inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings);
 
-    private Map<String, Map<String, Map<String, String>>> organizeByDmiName(
-        final Collection<YangModelCmHandle> yangModelCmHandles) {
-        final Map<String, Map<String, Map<String, String>>> dmiNameCmHandlePropertiesMap = new HashMap<>();
-        yangModelCmHandles.forEach(cmHandle -> {
-            final String dmiName = cmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
-            if (!dmiNameCmHandlePropertiesMap.containsKey(dmiName)) {
-                final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>();
-                cmHandleDmiPropertiesMap.put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle));
-                dmiNameCmHandlePropertiesMap.put(cmHandle.getDmiDataServiceName(), cmHandleDmiPropertiesMap);
-            } else {
-                dmiNameCmHandlePropertiesMap.get(cmHandle.getDmiDataServiceName())
-                    .put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle));
-            }
+        final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName
+                = DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
+        dmiPropertiesPerCmHandleIdPerServiceName.forEach((dmiServiceName, cmHandlePropertiesMap) -> {
+            subscriptionEvent.getEvent().getPredicates().setTargets(Collections
+                    .singletonList(cmHandlePropertiesMap));
+            final String eventKey = createEventKey(subscriptionEvent, dmiServiceName);
+            eventsPublisher.publishEvent(DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiServiceName, eventKey,
+                    subscriptionEvent);
         });
-        return dmiNameCmHandlePropertiesMap;
     }
 
     private String createEventKey(final SubscriptionEvent subscriptionEvent, final String dmiName) {
@@ -98,9 +82,4 @@ public class SubscriptionEventForwarder {
             + dmiName;
     }
 
-    public Map<String, String> dmiPropertiesAsMap(final YangModelCmHandle yangModelCmHandle) {
-        return yangModelCmHandle.getDmiProperties().stream().collect(
-            Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue));
-    }
-
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java
new file mode 100644 (file)
index 0000000..192062f
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.executor;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class TaskExecutor {
+
+    /**
+     * Execute task asynchronously.
+     *
+     * @param taskSupplier    functional method is get() task need to executed asynchronously
+     * @param timeOutInMillis the timeout value in milliseconds
+     */
+    public static CompletableFuture<Object> executeTask(final Supplier<Object> taskSupplier,
+                                                        final long timeOutInMillis) {
+        return CompletableFuture.supplyAsync(taskSupplier::get)
+                .orTimeout(timeOutInMillis, MILLISECONDS);
+    }
+}
+
+
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java
new file mode 100644 (file)
index 0000000..24edc73
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import lombok.Getter;
+
+@Getter
+public enum DataStoreEnum {
+    PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"),
+    PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running");
+    private final String value;
+
+    DataStoreEnum(final String value) {
+        this.value = value;
+    }
+}
index 83faa00..d648352 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
 
 package org.onap.cps.ncmp.api.impl.operations;
 
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ;
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING;
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ;
 
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 import lombok.extern.slf4j.Slf4j;
 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.executor.TaskExecutor;
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer;
 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.CmHandleState;
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
 import org.onap.cps.spi.exceptions.CpsException;
 import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 
@@ -44,15 +49,14 @@ import org.springframework.stereotype.Component;
 @Slf4j
 public class DmiDataOperations extends DmiOperations {
 
-    /**
-     * Constructor for {@code DmiOperations}. This method also manipulates url properties.
-     *
-     * @param dmiRestClient {@code DmiRestClient}
-     */
+    private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L;
+    private static final String NO_CM_HANDLE_ID = "";
+
     public DmiDataOperations(final InventoryPersistence inventoryPersistence,
                              final JsonObjectMapper jsonObjectMapper,
                              final NcmpConfiguration.DmiProperties dmiProperties,
-                             final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
+                             final DmiRestClient dmiRestClient,
+                             final DmiServiceUrlBuilder dmiServiceUrlBuilder) {
         super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder);
     }
 
@@ -60,48 +64,78 @@ public class DmiDataOperations extends DmiOperations {
      * This method fetches the resource data from operational data store for given cm handle
      * identifier on given resource using dmi client.
      *
-     * @param cmHandleId    network resource identifier
-     * @param resourceId  resource identifier
+     * @param dataStoreName       name of data store
+     * @param cmHandleId          network resource identifier
+     * @param resourceId          resource identifier
      * @param optionsParamInQuery options query
-     * @param dataStore           data store enum
-     * @param requestId           requestId for async responses
      * @param topicParamInQuery   topic name for (triggering) async responses
+     * @param requestId           requestId for async responses
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId,
+    public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+                                                         final String cmHandleId,
                                                          final String resourceId,
                                                          final String optionsParamInQuery,
-                                                         final DataStoreEnum dataStore,
-                                                         final String requestId,
-                                                         final String topicParamInQuery) {
+                                                         final String topicParamInQuery,
+                                                         final String requestId) {
         final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
-        final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
-        final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, resourceId, optionsParamInQuery, dataStore,
-                topicParamInQuery, yangModelCmHandle);
         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
-        isCmHandleStateReady(yangModelCmHandle, cmHandleState);
-        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
+        validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
+        final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
+                yangModelCmHandle);
+        final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery,
+                topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
+        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ);
+    }
+
+    /**
+     * This method fetches the resource data by data store for given list of cm handles using dmi client.
+     *
+     * @param dataStoreName           data store name
+     * @param cmHandleIds         list of cm handles
+     * @param resourceId          resource identifier
+     * @param optionsParamInQuery options query
+     * @param topicParamInQuery   topic name for (triggering) async responses
+     * @param requestId           requestId for async responses
+     * @return {@code ResponseEntity} response entity
+     */
+    public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+                                                         final List<String> cmHandleIds,
+                                                         final String resourceId,
+                                                         final String optionsParamInQuery,
+                                                         final String topicParamInQuery,
+                                                         final String requestId) {
+        final Collection<YangModelCmHandle> yangModelCmHandles
+                = inventoryPersistence.getYangModelCmHandles(cmHandleIds);
+        final Map<String, Map<String, Map<String, String>>> dmiServiceNameCmHandlePropertiesMap =
+                DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
+
+        buildBulkResourceDataRequestAndSend(dataStoreName, resourceId, optionsParamInQuery,
+                topicParamInQuery, requestId, dmiServiceNameCmHandlePropertiesMap);
+        return new ResponseEntity<>(HttpStatus.ACCEPTED);
     }
 
     /**
      * This method fetches all the resource data from operational data store for given cm handle
      * identifier using dmi client.
      *
+     * @param dataStoreName  data store name
      * @param cmHandleId network resource identifier
-     * @param dataStore  data store enum
      * @param requestId  requestId for async responses
      * @return {@code ResponseEntity} response entity
      */
-    public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId,
-                                                         final DataStoreEnum dataStore,
+    public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName,
+                                                         final String cmHandleId,
                                                          final String requestId) {
         final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
-        final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
-        final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, "/", null, dataStore,
-                null, yangModelCmHandle);
+        final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null,
+                yangModelCmHandle);
+        final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/", null,
+                null, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
-        isCmHandleStateReady(yangModelCmHandle, cmHandleState);
-        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ);
+        validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
+        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
+                READ);
     }
 
     /**
@@ -121,12 +155,14 @@ public class DmiDataOperations extends DmiOperations {
                                                                              final String requestData,
                                                                              final String dataType) {
         final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
-        final String jsonBody = getDmiRequestBody(operation, null, requestData, dataType, yangModelCmHandle);
-        final String dmiUrl = getDmiRequestUrl(cmHandleId, resourceId, null, PASSTHROUGH_RUNNING,
-                null, yangModelCmHandle);
+        final String jsonRequestBody = getDmiRequestBody(operation, null, requestData, dataType,
+                yangModelCmHandle);
+        final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getValue(), cmHandleId, resourceId,
+                null, null,
+                yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA));
         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
-        isCmHandleStateReady(yangModelCmHandle, cmHandleState);
-        return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation);
+        validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
+        return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operation);
     }
 
     private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
@@ -145,24 +181,80 @@ public class DmiDataOperations extends DmiOperations {
         return jsonObjectMapper.asJsonString(dmiRequestBody);
     }
 
-    private String getDmiRequestUrl(final String cmHandleId,
-                                      final String resourceId,
-                                      final String optionsParamInQuery,
-                                      final DataStoreEnum dataStore,
-                                      final String topicParamInQuery,
-                                      final YangModelCmHandle yangModelCmHandle) {
+    private String getDmiBulkRequestBody(final OperationEnum operation,
+                                         final String requestId,
+                                         final String requestData) {
+        final DmiRequestBody dmiBulkRequestBody = DmiRequestBody.builder()
+                .operation(operation)
+                .requestId(requestId)
+                .data(requestData)
+                .build();
+        return jsonObjectMapper.asJsonString(dmiBulkRequestBody);
+    }
+
+    private String getDmiRequestUrl(final String dataStoreName,
+                                    final String cmHandleId,
+                                    final String resourceId,
+                                    final String optionsParamInQuery,
+                                    final String topicParamInQuery,
+                                    final String dmiServiceName) {
         return dmiServiceUrlBuilder.getDmiDatastoreUrl(
                 dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
-                        topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(
-                        yangModelCmHandle, cmHandleId, dataStore));
+                        topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
+                        cmHandleId));
     }
 
-    private void isCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) {
+    private String getDmiServiceBulkRequestUrl(final String dataStoreName,
+                                               final String resourceId,
+                                               final String optionsParamInQuery,
+                                               final String topicParamInQuery,
+                                               final String dmiServiceName) {
+        return dmiServiceUrlBuilder.getBulkRequestUrl(
+                dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
+                        topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName,
+                        NO_CM_HANDLE_ID));
+    }
+
+    private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle,
+                                              final CmHandleState cmHandleState) {
         if (cmHandleState != CmHandleState.READY) {
             throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. "
-                + "cm handle state is "
-                + yangModelCmHandle.getCompositeState().getCmHandleState());
+                    + "cm handle state is "
+                    + yangModelCmHandle.getCompositeState().getCmHandleState());
         }
     }
 
+    private void buildBulkResourceDataRequestAndSend(final String dataStoreName,
+                                                     final String resourceId,
+                                                     final String optionsParamInQuery,
+                                                     final String topicParamInQuery,
+                                                     final String requestId,
+                                                     final Map<String, Map<String, Map<String, String>>>
+                                                             dmiServiceNameCmHandlePropertiesMap) {
+        dmiServiceNameCmHandlePropertiesMap.entrySet().parallelStream().forEach(
+                dmiServiceNameCmHandlePropertiesEntry -> {
+                    final String dmiBulkResourceDataUrl = getDmiServiceBulkRequestUrl(dataStoreName, resourceId,
+                            optionsParamInQuery, topicParamInQuery, dmiServiceNameCmHandlePropertiesEntry.getKey());
+                    final String jsonRequestBodyAsJsonString =
+                            jsonObjectMapper.asJsonString(dmiServiceNameCmHandlePropertiesEntry.getValue());
+                    final String jsonRequestBody
+                            = getDmiBulkRequestBody(READ, requestId, jsonRequestBodyAsJsonString);
+                    sendDmiResourceDataRequestToDmiService(dmiBulkResourceDataUrl, jsonRequestBody);
+                });
+    }
+
+    private void sendDmiResourceDataRequestToDmiService(final String dmiBulkResourceDataUrl,
+                                                        final String dmiResourceDataRequestAsJsonString) {
+        TaskExecutor.executeTask(() ->
+                                dmiRestClient.postOperationWithJsonData(dmiBulkResourceDataUrl,
+                                        dmiResourceDataRequestAsJsonString, READ),
+                        DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
+                .whenCompleteAsync(this::handleTaskCompletion);
+    }
+
+    private void handleTaskCompletion(final Object response, final Throwable throwable) {
+        // TODO Need to publish an error response to client given topic.
+        //  Code should be implemented into https://jira.onap.org/browse/CPS-1558 (
+        //  NCMP : Handle non responding DMI-Plugin)
+    }
 }
index d8d0304..392e9c1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -98,17 +98,18 @@ public class DmiModelOperations extends DmiOperations {
      * Get resources from DMI for modules.
      *
      * @param dmiServiceName dmi service name
-     * @param jsonData module names and revisions as JSON
+     * @param jsonRequestBody module names and revisions as JSON
      * @param cmHandle cmHandle
      * @param resourceName name of the resource(s)
      * @return {@code ResponseEntity} response entity
      */
     private ResponseEntity<Object> getResourceFromDmiWithJsonData(final String dmiServiceName,
-                                                                  final String jsonData,
+                                                                  final String jsonRequestBody,
                                                                   final String cmHandle,
                                                                   final String resourceName) {
         final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName);
-        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, DmiRequestBody.OperationEnum.READ);
+        return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody,
+                OperationEnum.READ);
     }
 
     private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences,
index e26ffef..7e9079e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,6 @@
 
 package org.onap.cps.ncmp.api.impl.operations;
 
-import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
@@ -34,17 +33,6 @@ import org.springframework.stereotype.Service;
 @Service
 public class DmiOperations {
 
-    @Getter
-    public enum DataStoreEnum {
-        PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"),
-        PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running");
-        private final String value;
-
-        DataStoreEnum(final String value) {
-            this.value = value;
-        }
-    }
-
     protected final InventoryPersistence inventoryPersistence;
     protected final JsonObjectMapper jsonObjectMapper;
     protected final NcmpConfiguration.DmiProperties dmiProperties;
@@ -52,7 +40,7 @@ public class DmiOperations {
     protected final DmiServiceUrlBuilder dmiServiceUrlBuilder;
 
     String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) {
-        return dmiServiceUrlBuilder.getCmHandleUrl()
+        return dmiServiceUrlBuilder.getResourceDataBasePathUriBuilder()
                 .pathSegment("{resourceName}")
                 .buildAndExpand(dmiServiceName, dmiProperties.getDmiBasePath(), cmHandle, resourceName).toUriString();
     }
index c84e4cb..3aa6366 100644 (file)
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.operations;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonValue;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -34,24 +33,6 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 @Getter
 @Builder
 public class DmiRequestBody {
-    public enum OperationEnum {
-        READ("read"),
-        CREATE("create"),
-        UPDATE("update"),
-        PATCH("patch"),
-        DELETE("delete");
-        private final String value;
-
-        OperationEnum(final String value) {
-            this.value = value;
-        }
-
-        @Override
-        @JsonValue
-        public String toString() {
-            return String.valueOf(value);
-        }
-    }
 
     private OperationEnum operation;
     private String dataType;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java
new file mode 100644 (file)
index 0000000..796cef2
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.operations;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum OperationEnum {
+
+    READ("read"),
+    CREATE("create"),
+    UPDATE("update"),
+    PATCH("patch"),
+    DELETE("delete");
+    private final String value;
+
+    OperationEnum(final String value) {
+        this.value = value;
+    }
+
+    @Override
+    @JsonValue
+    public String toString() {
+        return String.valueOf(value);
+    }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java
new file mode 100644 (file)
index 0000000..26e9486
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class DmiServiceNameOrganizer {
+
+    /**
+     * organizes a map with dmi service name as key for cm handle with its properties.
+     *
+     * @param yangModelCmHandles list of cm handle model
+     */
+    public static Map<String, Map<String, Map<String, String>>> getDmiPropertiesPerCmHandleIdPerServiceName(
+            final Collection<YangModelCmHandle> yangModelCmHandles) {
+        final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName
+                = new HashMap<>();
+        yangModelCmHandles.forEach(yangModelCmHandle -> {
+            final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA);
+            if (!dmiPropertiesPerCmHandleIdPerServiceName.containsKey(dmiServiceName)) {
+                final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>();
+                cmHandleDmiPropertiesMap.put(yangModelCmHandle.getId(),
+                        dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties()));
+                dmiPropertiesPerCmHandleIdPerServiceName.put(dmiServiceName, cmHandleDmiPropertiesMap);
+            } else {
+                dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName)
+                        .put(yangModelCmHandle.getId(), dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties()));
+            }
+        });
+        return dmiPropertiesPerCmHandleIdPerServiceName;
+    }
+
+    private static Map<String, String> dmiPropertiesAsMap(final List<YangModelCmHandle.Property> dmiProperties) {
+        return dmiProperties.stream().collect(
+                Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue));
+    }
+}
index 5f4a654..bba8f48 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 
 package org.onap.cps.ncmp.api.impl.utils;
 
-import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA;
-
 import java.util.HashMap;
 import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.TriConsumer;
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
-import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.spi.utils.CpsValidator;
 import org.springframework.stereotype.Component;
 import org.springframework.util.LinkedMultiValueMap;
@@ -52,13 +48,21 @@ public class DmiServiceUrlBuilder {
      */
     public String getDmiDatastoreUrl(final MultiValueMap<String, String> queryParams,
                                      final Map<String, Object> uriVariables) {
-        final UriComponentsBuilder uriComponentsBuilder = getCmHandleUrl()
-                .pathSegment("data")
-                .pathSegment("ds")
-                .pathSegment("{dataStore}")
-                .queryParams(queryParams)
-                .uriVariables(uriVariables);
-        return uriComponentsBuilder.buildAndExpand().toUriString();
+        return getUriComponentsBuilder(getResourceDataBasePathUriBuilder(), queryParams, uriVariables)
+                .buildAndExpand().toUriString();
+    }
+
+    /**
+     * This method creates the dmi service url for bulk request.
+     *
+     * @param queryParams  query param map as key,value pair
+     * @param uriVariables uri param map as key (placeholder),value pair
+     * @return {@code String} dmi service url as string
+     */
+    public String getBulkRequestUrl(final MultiValueMap<String, String> queryParams,
+                                    final Map<String, Object> uriVariables) {
+        return getUriComponentsBuilder(getBulkResourceDataBasePathUriBuilder(), queryParams, uriVariables)
+                .buildAndExpand().toUriString();
     }
 
     /**
@@ -66,7 +70,7 @@ public class DmiServiceUrlBuilder {
      *
      * @return {@code UriComponentsBuilder} dmi service url builder object
      */
-    public UriComponentsBuilder getCmHandleUrl() {
+    public UriComponentsBuilder getResourceDataBasePathUriBuilder() {
         return UriComponentsBuilder.newInstance()
                 .path("{dmiServiceName}")
                 .pathSegment("{dmiBasePath}")
@@ -75,24 +79,37 @@ public class DmiServiceUrlBuilder {
                 .pathSegment("{cmHandleId}");
     }
 
+    /**
+     * This method creates the dmi service url builder object with path variables for batch of cm handles.
+     *
+     * @return {@code UriComponentsBuilder} dmi service url builder object
+     */
+    public UriComponentsBuilder getBulkResourceDataBasePathUriBuilder() {
+        return UriComponentsBuilder.newInstance()
+                .path("{dmiServiceName}")
+                .pathSegment("{dmiBasePath}")
+                .pathSegment("v1")
+                .pathSegment("batch");
+    }
+
     /**
      * This method populates uri variables.
      *
-     * @param yangModelCmHandle get dmi service name
+     * @param dataStoreName data store name 
+     * @param dmiServiceName dmi service name
      * @param cmHandleId        cm handle id for dmi registration
      * @return {@code String} dmi service url as string
      */
-    public Map<String, Object> populateUriVariables(final YangModelCmHandle yangModelCmHandle,
-                                                    final String cmHandleId,
-                                                    final DmiOperations.DataStoreEnum dataStore) {
+    public Map<String, Object> populateUriVariables(final String dataStoreName,
+                                                    final String dmiServiceName,
+                                                    final String cmHandleId) {
         cpsValidator.validateNameCharacters(cmHandleId);
         final Map<String, Object> uriVariables = new HashMap<>();
         final String dmiBasePath = dmiProperties.getDmiBasePath();
-        uriVariables.put("dmiServiceName",
-                yangModelCmHandle.resolveDmiServiceName(DATA));
+        uriVariables.put("dmiServiceName", dmiServiceName);
         uriVariables.put("dmiBasePath", dmiBasePath);
         uriVariables.put("cmHandleId", cmHandleId);
-        uriVariables.put("dataStore", dataStore.getValue());
+        uriVariables.put("dataStore", dataStoreName);
         return uriVariables;
     }
 
@@ -124,4 +141,15 @@ public class DmiServiceUrlBuilder {
             }
         };
     }
+
+    private UriComponentsBuilder getUriComponentsBuilder(final UriComponentsBuilder uriComponentsBuilder,
+                                                         final MultiValueMap<String, String> queryParams,
+                                                         final Map<String, Object> uriVariables) {
+        return uriComponentsBuilder
+                .pathSegment("data")
+                .pathSegment("ds")
+                .pathSegment("{dataStore}")
+                .queryParams(queryParams)
+                .uriVariables(uriVariables);
+    }
 }
index 537f501..b9cecfb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,8 @@
 
 package org.onap.cps.ncmp.api.inventory.sync;
 
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL;
+
 import com.fasterxml.jackson.databind.JsonNode;
 import java.time.Duration;
 import java.time.OffsetDateTime;
@@ -37,7 +39,6 @@ import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 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.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
 import org.onap.cps.ncmp.api.inventory.CmHandleQueries;
@@ -167,7 +168,8 @@ public class SyncUtils {
      */
     public String getResourceData(final String cmHandleId) {
         final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi(
-                cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
+                PASSTHROUGH_OPERATIONAL.getValue(),
+                cmHandleId,
                 UUID.randomUUID().toString());
         if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) {
             return getFirstResource(resourceDataResponseEntity.getBody());
index 871af84..5b49e04 100644 (file)
@@ -52,10 +52,10 @@ import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import spock.lang.Specification
 
-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.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
 
 class NetworkCmProxyDataServiceImplSpec extends Specification {
 
@@ -94,61 +94,64 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
 
     def 'Write resource data for pass-through running from DMI using POST.'() {
         given: 'cpsDataService returns valid datanode'
-            mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockDataNode()
         when: 'write resource data is called'
             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
                 'testResourceId', CREATE,
                 '{some-json}', 'application/json')
         then: 'DMI called with correct data'
             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
-                CREATE, '{some-json}', 'application/json')
+                    CREATE, '{some-json}', 'application/json')
                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
     }
 
     def 'Get resource data for pass-through operational from DMI.'() {
         given: 'get data node is called'
-            mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockDataNode()
         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)
+            mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.value,'testCmHandle',
+                    'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
+                    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)
+            def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.value, '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 running from DMI.'() {
         given: 'cpsDataService returns valid data node'
-            mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
-                cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+            mockDataNode()
         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)
+            mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.value, 'testCmHandle',
+                    'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >>
+                    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)
+            def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.value, 'testCmHandle',
+                    'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID)
         then: 'get resource data returns expected response'
             response == '{dmi-response}'
     }
 
+    def 'Get bulk resource data for #datastoreName from DMI.'() {
+        given: 'cpsDataService returns valid data node'
+            mockDataNode()
+        and: 'DMI returns valid response and data'
+            mockDmiDataOperations.getResourceDataFromDmi(datastoreName, ['testCmHandle'],
+                    'testResourceId', OPTIONS_PARAM,'some topic','requestId') >>
+                    new ResponseEntity<>('{dmi-bulk-response}', HttpStatus.OK)
+        when: 'get batch resource data is called'
+            def response = objectUnderTest.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'],
+                    'testResourceId',
+                    OPTIONS_PARAM,
+                    'some topic',
+                    'requestId')
+        then: 'get bulk resource data returns expected response'
+            response == '{dmi-bulk-response}'
+        where: 'the following data stores are used'
+            datastoreName << [PASSTHROUGH_RUNNING.value, PASSTHROUGH_OPERATIONAL.value]
+    }
+
     def 'Getting Yang Resources.'() {
         when: 'yang resources is called'
             objectUnderTest.getYangResourcesModuleReferences('some-cm-handle')
@@ -242,7 +245,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 '{some-json}', 'application/json')
         then: 'DMI called with correct data'
             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
-                UPDATE, '{some-json}', 'application/json')
+                    UPDATE, '{some-json}', 'application/json')
                 >> { new ResponseEntity<>(HttpStatus.OK) }
     }
 
@@ -365,4 +368,9 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
                 .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED)
                 .lastSyncTime('some-timestamp').build()).build()
     }
+
+    def mockDataNode() {
+        mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry',
+                cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
+    }
 }
index 90839f8..b38ca10 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -34,10 +34,9 @@ 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
-
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
 
 @SpringBootTest
 @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient])
index 03825c2..89b3a2f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,14 +30,14 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.http.ResponseEntity
 import org.springframework.test.context.ContextConfiguration
+import org.springframework.http.HttpStatus
 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
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
+import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE
 
 @SpringBootTest
 @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiDataOperations])
@@ -50,6 +50,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
     def NO_REQUEST_ID = null
     @Shared
     def OPTIONS_PARAM = '(a=1,b=2)'
+    @Shared
+    def expectedBulkRequestAsJson = '{"operation": "read","data": {"fe1c1f1a070c4ce5bbfda7198592a0d3": {"neType": "RadioNode"},"b8e42eed0d9541ed8d8839e8eb86b3e0": {"neType": "RadioNode"}},"requestId": "bbb67474-f705-410a-93d1-b2844e7f45fd"}'
 
     @SpringBean
     JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
@@ -66,8 +68,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, READ) >> responseFromDmi
             dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
         when: 'get resource data is invoked'
-            def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, resourceIdentifier,
-                    options, dataStore, NO_REQUEST_ID, NO_TOPIC)
+            def result = objectUnderTest.getResourceDataFromDmi(dataStore.value, cmHandleId, resourceIdentifier,
+                    options, NO_TOPIC, NO_REQUEST_ID)
         then: 'the result is the response from the DMI service'
             assert result == responseFromDmi
         where: 'the following parameters are used'
@@ -80,6 +82,25 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             'datastore running with properties'    | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING     | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running'     | '&options=(a=1,b=2)'
     }
 
+    def 'call get bulk resource data for #dataStore from DMI service with topic #scenario.'() {
+        given: 'collection of yang model cm Handles'
+            mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty])
+        and: 'a positive response from DMI service when it is called with the expected parameters'
+            def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED)
+            def expectedDmiBulkResourceDataUrl = "ncmp/v1/batch/data/ds/${dataStore}?resourceIdentifier=parent/child%26options=(a=1,b=2)&topic=my-topic-name&options=(fields=schemas/schema)"
+            mockDmiRestClient.postOperationWithJsonData(expectedDmiBulkResourceDataUrl, expectedBulkRequestAsJson, READ) >> responseFromDmi
+            dmiServiceUrlBuilder.getBulkRequestUrl(_, _) >> expectedDmiBulkResourceDataUrl
+        when: 'get resource data for bulk cm handle is invoked'
+            def result = objectUnderTest.getResourceDataFromDmi( dataStore.value, [cmHandleId], resourceIdentifier,
+                    OPTIONS_PARAM,  'some-topic','requestId')
+        then: 'the result is the response from the DMI service'
+            assert result == responseFromDmi
+        where: 'the following parameters are used'
+            scenario                | dataStore
+            'datastore operational' | PASSTHROUGH_OPERATIONAL
+            'datastore running'     | PASSTHROUGH_RUNNING
+    }
+
     def 'call get all resource data.'() {
         given: 'the system returns a cm handle with a sample property'
             mockYangModelCmHandleRetrieval([yangModelCmHandleProperty])
@@ -89,7 +110,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}', READ) >> responseFromDmi
             dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl
         when: 'get resource data is invoked'
-            def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, PASSTHROUGH_OPERATIONAL, NO_REQUEST_ID)
+            def result = objectUnderTest.getResourceDataFromDmi( PASSTHROUGH_OPERATIONAL.value, cmHandleId, NO_REQUEST_ID)
         then: 'the result is the response from the DMI service'
             assert result == responseFromDmi
     }
index ed8f086..ed74ad3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2022 Nordix Foundation
+ *  Copyright (C) 2021-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +24,8 @@ package org.onap.cps.ncmp.api.impl.operations
 import com.fasterxml.jackson.core.JsonProcessingException
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
+import org.onap.cps.ncmp.api.impl.executor.TaskExecutor
+import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
@@ -34,7 +36,7 @@ import org.springframework.http.ResponseEntity
 import org.springframework.test.context.ContextConfiguration
 import spock.lang.Shared
 
-import static  org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
+import static  org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ
 
 @SpringBootTest
 @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations])
@@ -49,6 +51,12 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
     @SpringBean
     JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
 
+    @SpringBean
+    TaskExecutor stubbedTaskExecutor = Stub()
+
+    @SpringBean
+    DmiServiceNameOrganizer stubbedDmiServiceNameOrganizer = Stub()
+
     def 'Retrieving module references.'() {
         given: 'a cm handle'
             mockYangModelCmHandleRetrieval([])
@@ -89,7 +97,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         and: 'a positive response from DMI service when it is called with tha expected parameters'
             def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK)
             mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules",
-                '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> 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 +116,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
                                                       [moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK)
             def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
             mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
-                '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ) >> 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 +148,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         and: 'a positive response from DMI service when it is called with the expected parameters'
             def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
             mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources",
-                '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> 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'
index c4d0020..1b2c50a 100644 (file)
@@ -58,12 +58,21 @@ abstract class DmiOperationsBaseSpec extends Specification {
     def static resourceIdentifier = 'parent/child'
 
     def mockYangModelCmHandleRetrieval(dmiProperties) {
+        populateYangModelCmHandle(dmiProperties)
+        mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
+    }
+
+    def mockYangModelCmHandleCollectionRetrieval(dmiProperties) {
+        populateYangModelCmHandle(dmiProperties)
+        mockInventoryPersistence.getYangModelCmHandles(_) >> [yangModelCmHandle]
+    }
+
+    def populateYangModelCmHandle(dmiProperties) {
         yangModelCmHandle.dmiDataServiceName = dmiServiceName
         yangModelCmHandle.dmiServiceName = dmiServiceName
         yangModelCmHandle.dmiProperties = dmiProperties
         yangModelCmHandle.id = cmHandleId
         yangModelCmHandle.compositeState = new CompositeState()
         yangModelCmHandle.compositeState.cmHandleState = CmHandleState.READY
-        mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle
     }
 }
index 0156988..6ca3105 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 
 package org.onap.cps.ncmp.api.impl.utils
 
+import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService
 import org.onap.cps.spi.utils.CpsValidator
 
-import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING
 
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration
@@ -45,8 +46,8 @@ class DmiServiceUrlBuilderSpec extends Specification {
     def 'Create the dmi service url with #scenario.'() {
         given: 'uri variables'
             dmiProperties.dmiBasePath = 'dmi'
-            def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
-                    "cmHandle", PASSTHROUGH_RUNNING)
+            def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.value, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA),
+                    "cmHandle")
         and: 'query params'
                             def uriQueries = objectUnderTest.populateQueryParams(resourceId,
                     'optionsParamInQuery', topic)
@@ -65,8 +66,8 @@ class DmiServiceUrlBuilderSpec extends Specification {
     def 'Populate dmi data store url #scenario.'() {
         given: 'uri variables are created'
             dmiProperties.dmiBasePath = dmiBasePath
-            def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
-                    "cmHandle", PASSTHROUGH_RUNNING)
+            def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.value, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA),
+                    "cmHandle")
         and: 'null query params'
             def uriQueries = objectUnderTest.populateQueryParams(null,
                     null, null)
index f4176d6..8164dcf 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
 
 package org.onap.cps.ncmp.api.inventory.sync
 
+import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
+
 import com.fasterxml.jackson.databind.JsonNode
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
-import org.onap.cps.ncmp.api.impl.operations.DmiOperations
 import org.onap.cps.ncmp.api.inventory.CmHandleQueries
 import org.onap.cps.ncmp.api.inventory.CmHandleState
 import org.onap.cps.ncmp.api.inventory.CompositeState
@@ -38,7 +39,6 @@ import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import spock.lang.Shared
 import spock.lang.Specification
-
 import java.time.OffsetDateTime
 import java.time.format.DateTimeFormatter
 import java.util.stream.Collectors
@@ -137,7 +137,7 @@ class SyncUtilsSpec extends Specification{
             def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}'
             JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString);
             def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK)
-            mockDmiDataOperations.getResourceDataFromDmi('cm-handle-123', DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, _) >> responseEntity
+            mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.value, 'cm-handle-123', _) >> responseEntity
         when: 'get resource data is called'
             def result = objectUnderTest.getResourceData('cm-handle-123')
         then: 'the returned data is correct'
index 854450c..f44e310 100644 (file)
@@ -24,7 +24,7 @@ package org.onap.cps.cpspath.parser;
 import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -47,7 +47,7 @@ public class CpsPathBuilder extends CpsPathBaseListener {
 
     final CpsPathQuery cpsPathQuery = new CpsPathQuery();
 
-    final Map<String, Object> leavesData = new HashMap<>();
+    final Map<String, Object> leavesData = new LinkedHashMap<>();
 
     final StringBuilder normalizedXpathBuilder = new StringBuilder();
 
index 96fdf88..9552a4d 100644 (file)
@@ -84,6 +84,8 @@ class CpsPathQuerySpec extends Specification {
             'parent & child with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]/child2'                 || "/parent/child[@key1='1' or @key2='abc']/child2"
             'parent & child with multiple AND  operators'                 | '/parent/child[@key1=1 and @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' and @key='xyz']/child2"
             'parent & child with multiple OR  operators'                  | '/parent/child[@key1=1 or @key2="abc" or @key="xyz"]/child2'   || "/parent/child[@key1='1' or @key2='abc' or @key='xyz']/child2"
+            'parent & child with multiple AND/OR combination'             | '/parent/child[@key1=1 and @key2="abc" or @key="xyz"]/child2'  || "/parent/child[@key1='1' and @key2='abc' or @key='xyz']/child2"
+            'parent & child with multiple OR/AND combination'             | '/parent/child[@key1=1 or @key2="abc" and @key="xyz"]/child2'  || "/parent/child[@key1='1' or @key2='abc' and @key='xyz']/child2"
     }
 
     def 'Parse xpath to form the Normalized xpath containing #scenario.'() {
@@ -105,14 +107,17 @@ class CpsPathQuerySpec extends Specification {
         and: 'the right parameters are set'
             result.descendantName == "child"
             result.leavesData.size() == expectedNumberOfLeaves
+        and: 'the given operator(s) returns in the correct order'
             result.booleanOperatorsType == expectedOperators
         where: 'the following data is used'
-            scenario                                                   | cpsPath                                                                          || expectedNumberOfLeaves || expectedOperators
-            'one attribute'                                            | '//child[@common-leaf-name-int=5]'                                               || 1                      || null
-            'more than one attribute has AND operator'                 | '//child[@int-leaf=5 and @leaf-name="leaf value"]'                               || 2                      || ['and']
-            'more than one attribute has OR operator'                  | '//child[@int-leaf=5 or @leaf-name="leaf value"]'                                || 2                      || ['or']
-            'more than one attribute has combinations and/or operator' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2                      || ['and', 'and']
-            'more than one attribute has combinations or/and operator' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]'   || 2                      || ['or', 'or']
+            scenario                                                      | cpsPath                                                                          || expectedNumberOfLeaves || expectedOperators
+            'one attribute'                                               | '//child[@common-leaf-name-int=5]'                                               || 1                      || null
+            'more than one attribute has AND operator'                    | '//child[@int-leaf=5 and @leaf-name="leaf value"]'                               || 2                      || ['and']
+            'more than one attribute has OR operator'                     | '//child[@int-leaf=5 or @leaf-name="leaf value"]'                                || 2                      || ['or']
+            'more than one attribute has combinations and operators'      | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2                      || ['and', 'and']
+            'more than one attribute has combinations or operators'       | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]'   || 2                      || ['or', 'or']
+            'more than one attribute has combinations and/or combination' | '//child[@int-leaf=5 and @leaf-name="leaf value" or @leaf-name="leaf value1" ]'  || 2                      || ['and', 'or']
+            'more than one attribute has combinations or/and combination' | '//child[@int-leaf=5 or @leaf-name="leaf value" and @leaf-name="leaf value1" ]'  || 2                      || ['or', 'and']
     }
 
     def 'Parse #scenario cps path with text function condition'() {
@@ -136,7 +141,7 @@ class CpsPathQuerySpec extends Specification {
             'descendant with leaf value and ancestor' | '//child[@other-leaf=1]/leaf-name[text()="search"]/ancestor::parent' || true                 | true
     }
 
-    def 'Parse #scenario cps path with contains function condition'() {
+    def 'Parse cps path with contains function condition'() {
         when: 'the given cps path is parsed'
             def result = CpsPathQuery.createFrom('//someContainer[contains(@lang,"en")]')
         then: 'the query has the right xpath type'
index 369e528..3d9105f 100644 (file)
@@ -493,21 +493,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         fragmentRepository.save(fragmentEntity);
     }
 
-    @Override
-    public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
-                                             final DataNode dataNode) {
-        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
-        final FragmentEntity fragmentEntity = getFragmentEntity(anchorEntity, dataNode.getXpath());
-        updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode);
-        try {
-            fragmentRepository.save(fragmentEntity);
-        } catch (final StaleStateException staleStateException) {
-            throw new ConcurrencyException("Concurrent Transactions",
-                    String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.",
-                            dataspaceName, anchorName, dataNode.getXpath()));
-        }
-    }
-
     @Override
     public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
                                               final Collection<DataNode> updatedDataNodes) {
index d25832b..8114f10 100755 (executable)
@@ -90,13 +90,6 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>,
     List<FragmentExtract> findByAnchorAndParentXpath(@Param("anchor") AnchorEntity anchorEntity,\r
                                                      @Param("parentXpath") String parentXpath);\r
 \r
-    @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"\r
-            + " CAST(attributes AS TEXT) AS attributes"\r
-            + " FROM FRAGMENT WHERE "\r
-            + "( xpath = :parentXpath OR xpath LIKE CONCAT(:parentXpath,'/%') )",\r
-            nativeQuery = true)\r
-    List<FragmentExtract> findByParentXpath(@Param("parentXpath") String parentXpath);\r
-\r
     @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"\r
         + " CAST(attributes AS TEXT) AS attributes"\r
         + " FROM FRAGMENT WHERE anchor_id = :anchorId"\r
index e60afa7..93d7662 100755 (executable)
@@ -23,7 +23,6 @@
 
 package org.onap.cps.spi.impl
 
-import com.fasterxml.jackson.databind.ObjectMapper
 import com.google.common.collect.ImmutableSet
 import org.onap.cps.spi.CpsDataPersistenceService
 import org.onap.cps.spi.entities.FragmentEntity
@@ -35,7 +34,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.exceptions.DataspaceNotFoundException
 import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
-import org.onap.cps.utils.JsonObjectMapper
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.test.context.jdbc.Sql
 
@@ -49,7 +47,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     @Autowired
     CpsDataPersistenceService objectUnderTest
 
-    static JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
     static DataNodeBuilder dataNodeBuilder = new DataNodeBuilder()
 
     static final String SET_DATA = '/data/fragment.sql'
@@ -353,11 +350,11 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Update data node and descendants by removing descendants.'() {
-        given: 'data node object with leaves updated, no children'
-            def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [])
+    def 'Update data nodes and descendants by removing descendants.'() {
+        given: 'data nodes with leaves updated, no children'
+            def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [])]
         when: 'update data nodes and descendants is performed'
-            objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+            objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
         then: 'leaves have been updated for selected data node'
             def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
             def updatedLeaves = getLeavesMap(updatedFragment)
@@ -370,13 +367,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Update data node and descendants with new descendants'() {
-        given: 'data node object with leaves updated, having child with old content'
-            def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
+    def 'Update data nodes and descendants with new descendants'() {
+        given: 'data nodes with leaves updated, having child with old content'
+            def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
                   buildDataNode('/parent-200/child-201/grand-child', ['leaf-value': 'original'], [])
-            ])
+            ])]
         when: 'update is performed including descendants'
-            objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+            objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
         then: 'leaves have been updated for selected data node'
             def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
             def updatedLeaves = getLeavesMap(updatedFragment)
@@ -390,13 +387,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Update data node and descendants with same descendants but changed leaf value.'() {
-        given: 'data node object with leaves updated, having child with old content'
-            def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
+    def 'Update data nodes and descendants with same descendants but changed leaf value.'() {
+        given: 'data nodes with leaves updated, having child with old content'
+            def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
                     buildDataNode('/parent-200/child-201/grand-child', ['leaf-value': 'new'], [])
-            ])
+            ])]
         when: 'update is performed including descendants'
-            objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+            objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
         then: 'leaves have been updated for selected data node'
             def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
             def updatedLeaves = getLeavesMap(updatedFragment)
@@ -410,13 +407,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Update data node and descendants with different descendants xpath'() {
-        given: 'data node object with leaves updated, having child with old content'
-            def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
+    def 'Update data nodes and descendants with different descendants xpath'() {
+        given: 'data nodes with leaves updated, having child with old content'
+            def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [
                     buildDataNode('/parent-200/child-201/grand-child-new', ['leaf-value': 'new'], [])
-            ])
+            ])]
         when: 'update is performed including descendants'
-            objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode)
+            objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes)
         then: 'leaves have been updated for selected data node'
             def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID)
             def updatedLeaves = getLeavesMap(updatedFragment)
@@ -432,19 +429,17 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
-    def 'Update data node and descendants error scenario: #scenario.'() {
-        given: 'data node object'
-            def submittedDataNode = buildDataNode(xpath, ['leaf-name': 'leaf-value'], [])
+    def 'Update data nodes and descendants error scenario: #scenario.'() {
+        given: 'data nodes collection'
+            def submittedDataNodes = [buildDataNode(xpath, ['leaf-name': 'leaf-value'], [])]
         when: 'attempt to update data node for #scenario'
-            objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, submittedDataNode)
+            objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, submittedDataNodes)
         then: 'a #expectedException is thrown'
             thrown(expectedException)
         where: 'the following data is used'
             scenario                 | dataspaceName  | anchorName                        | xpath                 || expectedException
             'non-existing dataspace' | 'NO DATASPACE' | 'not relevant'                    | '/not relevant'       || DataspaceNotFoundException
             'non-existing anchor'    | DATASPACE_NAME | 'NO ANCHOR'                       | '/not relevant'       || AnchorNotFoundException
-            'non-existing xpath'     | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | '/NON-EXISTING-XPATH' || DataNodeNotFoundException
-            'invalid xpath'          | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH'       || CpsPathException
     }
 
     @Sql([CLEAR_DATA, SET_DATA])
@@ -699,7 +694,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
         return dataNodeBuilder.withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build()
     }
 
-    static Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) {
+    Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) {
         return jsonObjectMapper.convertJsonString(fragmentEntity.attributes, Map<String, Object>.class)
     }
 
index f02aa75..204b934 100644 (file)
@@ -77,24 +77,6 @@ class CpsDataPersistenceServiceSpec extends Specification {
             1 * objectUnderTest.storeDataNodes('dataspace1', 'anchor1', [dataNode])
     }
 
-    def 'Handling of StaleStateException (caused by concurrent updates) during update data node and descendants.'() {
-        given: 'the fragment repository returns a fragment entity'
-            mockFragmentRepository.getByAnchorAndXpath(*_) >> {
-                def fragmentEntity = new FragmentEntity()
-                fragmentEntity.setChildFragments([new FragmentEntity()] as Set<FragmentEntity>)
-                return fragmentEntity
-            }
-        and: 'a data node is concurrently updated by another transaction'
-            mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
-        when: 'attempt to update data node with submitted data nodes'
-            objectUnderTest.updateDataNodeAndDescendants('some-dataspace', 'some-anchor', new DataNodeBuilder().withXpath('/some/xpath').build())
-        then: 'concurrency exception is thrown'
-            def concurrencyException = thrown(ConcurrencyException)
-            assert concurrencyException.getDetails().contains('some-dataspace')
-            assert concurrencyException.getDetails().contains('some-anchor')
-            assert concurrencyException.getDetails().contains('/some/xpath')
-    }
-
     def 'Handling of StaleStateException (caused by concurrent updates) during update data nodes and descendants.'() {
         given: 'the system can update one datanode and has two more datanodes that throw an exception while updating'
             def dataNodes = createDataNodesAndMockRepositoryMethodSupportingThem([
index 214fd69..65d63df 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2022 Bell Canada.
+ *  Modifications Copyright (C) 2021-2023 Nordix Foundation.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the 'License');
  *  you may not use this file except in compliance with the License.
  */
 package org.onap.cps.spi.impl
 
-import com.fasterxml.jackson.databind.ObjectMapper
+
 import org.hibernate.exception.ConstraintViolationException
-import org.onap.cps.spi.CpsAdminPersistenceService
 import org.onap.cps.spi.CpsModulePersistenceService
 import org.onap.cps.spi.entities.DataspaceEntity
 import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
 import org.onap.cps.spi.model.ModuleReference
-import org.onap.cps.spi.repository.AnchorRepository
 import org.onap.cps.spi.repository.DataspaceRepository
-import org.onap.cps.spi.repository.SchemaSetRepository
 import org.onap.cps.spi.repository.YangResourceRepository
-import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.dao.DataIntegrityViolationException
@@ -43,24 +40,12 @@ class CpsModulePersistenceServiceConcurrencySpec extends CpsPersistenceSpecBase
     @Autowired
     CpsModulePersistenceService objectUnderTest
 
-    @Autowired
-    AnchorRepository anchorRepository
-
-    @Autowired
-    SchemaSetRepository schemaSetRepository
-
-    @Autowired
-    CpsAdminPersistenceService cpsAdminPersistenceService
-
     @SpringBean
     YangResourceRepository yangResourceRepositoryMock = Mock()
 
     @SpringBean
     DataspaceRepository dataspaceRepositoryMock = Mock()
 
-    @SpringBean
-    JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
-
     static final String DATASPACE_NAME = 'DATASPACE-001'
     static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW'
     static final String NEW_RESOURCE_NAME = 'some new resource'
index 864b3e3..53f42f5 100644 (file)
@@ -30,31 +30,22 @@ import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.spi.model.ModuleDefinition
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.spi.model.SchemaSet
-import org.onap.cps.spi.repository.AnchorRepository
 import org.onap.cps.spi.repository.SchemaSetRepository
 import org.onap.cps.spi.repository.SchemaSetYangResourceRepositoryImpl
-import org.onap.cps.spi.repository.YangResourceRepository
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.test.context.jdbc.Sql
-import spock.lang.Ignore
 
 class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
 
     @Autowired
     CpsModulePersistenceService objectUnderTest
 
-    @Autowired
-    AnchorRepository anchorRepository
-
     @Autowired
     SchemaSetRepository schemaSetRepository
 
     @Autowired
     CpsAdminPersistenceService cpsAdminPersistenceService
 
-    @Autowired
-    YangResourceRepository yangResourceRepository
-
     final static String SET_DATA = '/data/schemaset.sql'
 
     def static EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1
index 9b722cd..222a828 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation
+ *  Copyright (C) 2022-2023 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@ import org.onap.cps.spi.CpsModulePersistenceService
 import org.onap.cps.spi.entities.SchemaSetEntity
 import org.onap.cps.spi.impl.CpsPersistenceSpecBase
 import org.onap.cps.spi.model.ModuleReference
-import org.onap.cps.spi.repository.DataspaceRepository
 import org.onap.cps.spi.repository.ModuleReferenceRepository
 import org.onap.cps.spi.repository.SchemaSetRepository
 import org.springframework.beans.factory.annotation.Autowired
@@ -52,9 +51,6 @@ class CpsModuleReferenceRepositoryPerfTest extends CpsPersistenceSpecBase {
     @Autowired
     CpsModulePersistenceService objectUnderTest
 
-    @Autowired
-    DataspaceRepository dataspaceRepository
-
     @Autowired
     SchemaSetRepository schemaSetRepository
 
index 5404019..949fbc2 100644 (file)
@@ -137,15 +137,6 @@ public interface CpsDataPersistenceService {
      */
     void updateDataLeaves(String dataspaceName, String anchorName, String xpath, Map<String, Serializable> leaves);
 
-    /**
-     * Replaces an existing data node's content including descendants.
-     *
-     * @param dataspaceName dataspace name
-     * @param anchorName    anchor name
-     * @param dataNode      data node
-     */
-    void updateDataNodeAndDescendants(String dataspaceName, String anchorName, DataNode dataNode);
-
     /**
      * Replaces multiple existing data nodes' content including descendants in a batch operation.
      *
index 2018516..5f290cd 100644 (file)
@@ -1,7 +1,7 @@
 {
   "request": {
     "method": "POST",
-    "urlPattern": "/dmi/v1/ch/batch/data/ds/.*"
+    "urlPattern": "/dmi/v1/batch/data/ds/.*"
   },
   "response": {
     "status": 200,
index f321adf..252310d 100644 (file)
@@ -1,6 +1,7 @@
 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
 .. http://creativecommons.org/licenses/by/4.0
 .. Copyright (C) 2021-2022 Nordix Foundation
+.. Modifications Copyright (C) 2023 TechMahindra Ltd
 
 .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
 .. _path:
@@ -222,7 +223,7 @@ descendant-path
 leaf-conditions
 ---------------
 
-**Syntax**: ``<xpath> '[' @<leaf-name1> '=' <leaf-value1> ( ' and ' @<leaf-name> '=' <leaf-value> )* ']'``
+**Syntax**: ``<xpath> '[' @<leaf-name1> '=' <leaf-value1> ( ' <and|or> ' @<leaf-name> '=' <leaf-value> )* ']'``
   - ``xpath``: Absolute or descendant or xpath to the (list) node which elements will be queried.
   - ``leaf-name``: The name of the leaf which value needs to be compared.
   - ``leaf-value``: The required value of the leaf.
@@ -232,10 +233,13 @@ leaf-conditions
   - ``//categories[@name="Kids"]``
   - ``//categories[@name='Kids']``
   - ``//categories[@code='1']/books/book[@title='Dune' and @price=5]``
+  - ``//categories[@code='1']/books/book[@title='xyz' or @price=15]``
   - ``//categories[@code=1]``
 **Limitations**
   - Only the last list or container can be queried leaf values. Any ancestor list will have to be referenced by its key name-value pair(s).
-  - Multiple attributes can only be combined using ``and``. ``or`` and bracketing is not supported.
+  - When mixing ``and/or`` operators, ``and`` has precedence over ``or`` . So ``and`` operators get evaluated first.
+  - Bracketing is not supported.
+  - Leaf names are not validated so ``or`` operations with invalid leaf names will silently be ignored.
   - Only leaves can be used, leaf-list are not supported.
   - Only string and integer values are supported, boolean and float values are not supported.
   - The key should be supplied with correct data type for it to be queried from DB. In the last example above the attribute code is of type
index 38bb4de..d8f7147 100644 (file)
@@ -63,19 +63,21 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
             assert result.leaves.sort() == expectedLeaves.sort()
             println(expectedLeaves.toArray())
         where: 'the following data is used'
-            scenario                                | cpspath                                                    || expectedResultSize | expectedLeaves
-            'the "OR" condition'                    | '//books[@lang="English" or @price=15]'                    || 6                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
-                                                                                                                                          [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]],
-                                                                                                                                          [lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]],
-                                                                                                                                          [lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]],
-                                                                                                                                          [lang: "English", price: 12, title: "The Colour of Magic", authors: ["Terry Pratchett"], editions: [1983]],
-                                                                                                                                          [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]]]
-            'the "OR" condition with non-json data' | '//books[@title="xyz" or @price=15]'                       || 2                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
-                                                                                                                                          [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
-            'combination of multiple AND'           | '//books[@lang="English" and @price=15 and @edition=1983]' || 0                  | []
-            'combination of multiple OR'            | '//books[ @title="Matilda" or @price=15 or @edition=1983]' || 3                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
-                                                                                                                                          [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]],
-                                                                                                                                          [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
+            scenario                                | cpspath                                                          || expectedResultSize | expectedLeaves
+            'the "OR" condition'                    | '//books[@lang="English" or @price=15]'                          || 6                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
+                                                                                                                                                [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]],
+                                                                                                                                                [lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]],
+                                                                                                                                                [lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]],
+                                                                                                                                                [lang: "English", price: 12, title: "The Colour of Magic", authors: ["Terry Pratchett"], editions: [1983]],
+                                                                                                                                                [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]]]
+            'the "OR" condition with non-json data' | '//books[@title="xyz" or @price=15]'                             || 2                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
+                                                                                                                                                [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
+            'combination of multiple AND'           | '//books[@lang="English" and @price=15 and @edition=1983]'       || 0                  | []
+            'combination of multiple OR'            | '//books[ @title="Matilda" or @price=15 or @edition=1983]'       || 3                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
+                                                                                                                                                [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]],
+                                                                                                                                                [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]]
+            'combination of AND/OR'                 | '//books[@edition=1983 and @price=15 or @title="Good Omens"]'    || 1                  | [[lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]]]
+            'combination of OR/AND'                 | '//books[@title="Annihilation" or @price=39 and @lang="arabic"]' || 1                  | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]]]
     }
 
     def 'Cps Path query for leaf value(s) with #scenario.'() {
@@ -170,17 +172,19 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
             def bookTitles = result.collect { it.getLeaves().get('title') }
             assert bookTitles.sort() == expectedBookTitles.sort()
         where: 'the following data is used'
-            scenario                                                   | cpsPath                                                    || expectedBookTitles
-            'one leaf'                                                 | '//books[@price=14]'                                       || ['The Light Fantastic']
-            'one text'                                                 | '//books/authors[text()="Terry Pratchett"]'                || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic']
-            'more than one leaf'                                       | '//books[@price=12 and @lang="English"]'                   || ['The Colour of Magic']
-            'more than one leaf has "OR" condition'                    | '//books[@lang="English" or @price=15]'                    || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic']
-            'more than one leaf has "OR" condition with non-json data' | '//books[@title="xyz" or @price=13]'                       || ['Good Omens']
-            'more than one leaf has multiple AND'                      | '//books[@lang="English" and @price=13 and @edition=1983]' || []
-            'more than one leaf has multiple OR'                       | '//books[ @title="Matilda" or @price=15 or @edition=2006]' || ['Annihilation', 'Matilda', 'The Gruffalo']
-            'leaves reversed in order'                                 | '//books[@lang="English" and @price=12]'                   || ['The Colour of Magic']
-            'leaf and text'                                            | '//books[@price=14]/authors[text()="Terry Pratchett"]'     || ['The Light Fantastic']
-            'leaf and contains'                                        | '//books[contains(@price,"13")]'                           || ['Good Omens']
+            scenario                                                   | cpsPath                                                                || expectedBookTitles
+            'one leaf'                                                 | '//books[@price=14]'                                                   || ['The Light Fantastic']
+            'one text'                                                 | '//books/authors[text()="Terry Pratchett"]'                            || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic']
+            'more than one leaf'                                       | '//books[@price=12 and @lang="English"]'                               || ['The Colour of Magic']
+            'more than one leaf has "OR" condition'                    | '//books[@lang="English" or @price=15]'                                || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic']
+            'more than one leaf has "OR" condition with non-json data' | '//books[@title="xyz" or @price=13]'                                   || ['Good Omens']
+            'more than one leaf has multiple AND'                      | '//books[@lang="English" and @price=13 and @edition=1983]'             || []
+            'more than one leaf has multiple OR'                       | '//books[ @title="Matilda" or @price=15 or @edition=2006]'             || ['Annihilation', 'Matilda', 'The Gruffalo']
+            'leaves reversed in order'                                 | '//books[@lang="English" and @price=12]'                               || ['The Colour of Magic']
+            'more than one leaf has combination of AND/OR'             | '//books[@edition=1983 and @price=13 or @title="Good Omens"]'          || ['Good Omens']
+            'more than one leaf has OR/AND'                            | '//books[@title="The Light Fantastic" or @price=11 and @edition=1983]' || ['The Light Fantastic']
+            'leaf and text'                                            | '//books[@price=14]/authors[text()="Terry Pratchett"]'                 || ['The Light Fantastic']
+            'leaf and contains'                                        | '//books[contains(@price,"13")]'                                       || ['Good Omens']
     }
 
     def 'Cps Path query using descendant anywhere with #scenario condition(s) for a list element.'() {