Merge "Support Alternate-Id for CPS-E05 data batch interface"
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 99072c4..a8da693 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -366,7 +366,7 @@
           type: array
           items:
             type: string
-            description: targeted cm handles, maximum of 50 supported. If this limit is exceeded the request wil be refused.
+            description: targeted cm handle references, maximum of 200 supported. If this limit is exceeded the request wil be refused.
             example: [ "da310eecdb8d44c2acc0ddaae01174b1","c748c58f8e0b438f9fd1f28370b17d47" ]
 
   examples:
@@ -516,7 +516,7 @@
     outputAlternateIdOptionInQuery:
       name: outputAlternateId
       in: query
-      description: Boolean parameter to determine if returned value(s) will be cmHandle Ids or Alternate Ids for a given query
+      description: Boolean parameter to determine if returned value(s) will be cm handle references for a given query
       required: false
       schema:
         type: boolean
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index a3ddc3f..4624bc1 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -193,7 +193,7 @@
   post:
     tags:
       - network-cm-proxy
-    summary: Execute a data operation for group of cm handle ids
+    summary: Execute a data operation for group of cm handle references
     description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages. A maximum of 200 cm handles per operation is supported.
     operationId: executeDataOperationForCmHandles
     parameters:
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DataOperationRequestMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DataOperationRequestMapper.java
index 42622a2..1e73aca 100644
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DataOperationRequestMapper.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DataOperationRequestMapper.java
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2023 Nordix Foundation
+ * Copyright (C) 2023-2024 Nordix Foundation
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@
     DataOperationRequest toDataOperationRequest(
             org.onap.cps.ncmp.rest.model.DataOperationRequest dataOperationRequest);
 
-    @Mapping(source = "targetIds", target = "cmHandleIds")
+    @Mapping(source = "targetIds", target = "cmHandleReferences")
     DataOperationDefinition toDataOperationDefinition(
             org.onap.cps.ncmp.rest.model.DataOperationDefinition dataOperationDefinition);
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationDefinition.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationDefinition.java
index d1ff1a5..79da44a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationDefinition.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationDefinition.java
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation
+ *  Copyright (C) 2023-2024 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -45,5 +45,5 @@
 
     @JsonProperty("targetIds")
     @Valid
-    private List<String> cmHandleIds = new ArrayList<>();
+    private List<String> cmHandleReferences = new ArrayList<>();
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
index 301b819..c4bdc1c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
@@ -139,10 +139,10 @@
                                            final String requestId,
                                            final String authorization)  {
 
-        final Set<String> cmHandlesIds = getDistinctCmHandleIds(dataOperationRequest);
+        final Set<String> cmHandlesReferences = getDistinctCmHandleReferences(dataOperationRequest);
 
         final Collection<YangModelCmHandle> yangModelCmHandles
-            = inventoryPersistence.getYangModelCmHandles(cmHandlesIds);
+            = inventoryPersistence.getYangModelCmHandlesFromCmHandleReferences(cmHandlesReferences);
 
         final Map<String, List<DmiDataOperation>> operationsOutPerDmiServiceName
                 = DmiDataOperationsHelper.processPerDefinitionInDataOperationsRequest(topicParamInQuery,
@@ -246,10 +246,10 @@
         }
     }
 
-    private static Set<String> getDistinctCmHandleIds(final DataOperationRequest dataOperationRequest) {
+    private static Set<String> getDistinctCmHandleReferences(final DataOperationRequest dataOperationRequest) {
         return dataOperationRequest.getDataOperationDefinitions().stream()
                 .flatMap(dataOperationDefinition ->
-                        dataOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet());
+                        dataOperationDefinition.getCmHandleReferences().stream()).collect(Collectors.toSet());
     }
 
     private void asyncSendMultipleRequest(final String requestId, final String topicParamInQuery,
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java
index a21210c..8920839 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java
@@ -91,10 +91,10 @@
                 throw new InvalidDatastoreException(dataOperationDefinition.getDatastore()
                         + " datastore is not supported");
             }
-            if (dataOperationDefinition.getCmHandleIds().size() > MAXIMUM_CM_HANDLES_PER_OPERATION) {
+            if (dataOperationDefinition.getCmHandleReferences().size() > MAXIMUM_CM_HANDLES_PER_OPERATION) {
                 final String errorMessage = String.format(PAYLOAD_TOO_LARGE_TEMPLATE,
                         dataOperationDefinition.getOperationId(),
-                        dataOperationDefinition.getCmHandleIds().size());
+                        dataOperationDefinition.getCmHandleReferences().size());
                 throw new PayloadTooLargeException(errorMessage);
             }
         });
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelper.java
index 3104be5..f1dc9af 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelper.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelper.java
@@ -31,7 +31,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -71,8 +70,9 @@
 
         final Map<String, List<DmiDataOperation>> dmiDataOperationsOutPerDmiServiceName = new HashMap<>();
         final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus,
-                List<String>>> cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
-        final Set<String> nonReadyCmHandleIdsLookup = filterAndGetNonReadyCmHandleIds(yangModelCmHandles);
+                List<String>>> cmHandleReferencesPerResponseCodesPerOperation = new LinkedMultiValueMap<>();
+        final Map<String, String> nonReadyCmHandleReferencesLookup =
+            filterAndGetNonReadyCmHandleReferences(yangModelCmHandles);
 
         final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName =
                 DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles);
@@ -84,17 +84,19 @@
 
         for (final DataOperationDefinition dataOperationDefinitionIn :
                 dataOperationRequestIn.getDataOperationDefinitions()) {
-            final List<String> nonExistingCmHandleIds = new ArrayList<>();
-            final List<String> nonReadyCmHandleIds = new ArrayList<>();
-            for (final String cmHandleId : dataOperationDefinitionIn.getCmHandleIds()) {
-                if (nonReadyCmHandleIdsLookup.contains(cmHandleId)) {
-                    nonReadyCmHandleIds.add(cmHandleId);
+            final List<String> nonExistingCmHandleReferences = new ArrayList<>();
+            final List<String> nonReadyCmHandleReferences = new ArrayList<>();
+            for (final String cmHandleReference : dataOperationDefinitionIn.getCmHandleReferences()) {
+                if (nonReadyCmHandleReferencesLookup.containsKey(cmHandleReference)
+                    || nonReadyCmHandleReferencesLookup.containsValue(cmHandleReference)) {
+                    nonReadyCmHandleReferences.add(cmHandleReference);
                 } else {
+                    final String cmHandleId = getCmHandleId(cmHandleReference, yangModelCmHandles);
                     final String dmiServiceName = dmiServiceNamesPerCmHandleId.get(cmHandleId);
                     final Map<String, String> cmHandleIdProperties
                             = dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).get(cmHandleId);
                     if (cmHandleIdProperties == null) {
-                        nonExistingCmHandleIds.add(cmHandleId);
+                        nonExistingCmHandleReferences.add(cmHandleReference);
                     } else {
                         final DmiDataOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName,
                                 dataOperationDefinitionIn, dmiDataOperationsOutPerDmiServiceName);
@@ -105,14 +107,14 @@
                     }
                 }
             }
-            populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
+            populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleReferencesPerResponseCodesPerOperation,
                     DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
-                    CM_HANDLES_NOT_FOUND, nonExistingCmHandleIds);
-            populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleIdsPerResponseCodesPerOperation,
+                    CM_HANDLES_NOT_FOUND, nonExistingCmHandleReferences);
+            populateCmHandleIdsPerOperationIdPerResponseCode(cmHandleReferencesPerResponseCodesPerOperation,
                     DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn),
-                    CM_HANDLES_NOT_READY, nonReadyCmHandleIds);
+                    CM_HANDLES_NOT_READY, nonReadyCmHandleReferences);
         }
-        publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation);
+        publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleReferencesPerResponseCodesPerOperation);
         return dmiDataOperationsOutPerDmiServiceName;
     }
 
@@ -182,10 +184,26 @@
         return dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1);
     }
 
-    private static Set<String> filterAndGetNonReadyCmHandleIds(final Collection<YangModelCmHandle> yangModelCmHandles) {
-        return yangModelCmHandles.stream()
-                .filter(yangModelCmHandle -> yangModelCmHandle.getCompositeState().getCmHandleState()
-                        != CmHandleState.READY).map(YangModelCmHandle::getId).collect(Collectors.toSet());
+    private static Map<String, String> filterAndGetNonReadyCmHandleReferences(
+        final Collection<YangModelCmHandle> yangModelCmHandles) {
+        final Map<String, String> cmHandleReferenceMap = new HashMap<>(yangModelCmHandles.size());
+        for (final YangModelCmHandle yangModelCmHandle: yangModelCmHandles) {
+            if (yangModelCmHandle.getCompositeState().getCmHandleState() != CmHandleState.READY) {
+                cmHandleReferenceMap.put(yangModelCmHandle.getId(), yangModelCmHandle.getAlternateId());
+            }
+        }
+        return cmHandleReferenceMap;
+    }
+
+    private static String getCmHandleId(final String cmHandleReference,
+                                        final Collection<YangModelCmHandle> yangModelCmHandles) {
+        for (final YangModelCmHandle yangModelCmHandle: yangModelCmHandles) {
+            if (cmHandleReference.equals(yangModelCmHandle.getId())
+                || cmHandleReference.equals(yangModelCmHandle.getAlternateId())) {
+                return yangModelCmHandle.getId();
+            }
+        }
+        return cmHandleReference;
     }
 
     private static void populateCmHandleIdsPerOperationIdPerResponseCode(final MultiValueMap<DmiDataOperation,
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
index 850edf7..de8e8e8 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
@@ -72,6 +72,14 @@
     Collection<YangModelCmHandle> getYangModelCmHandles(Collection<String> cmHandleIds);
 
     /**
+     * This method retrieves DMI service name, DMI properties and the state for a given list of cm handle references.
+     *
+     * @param cmHandleReferences a list of the ids of the cm handles
+     * @return collection of yang model cm handles
+     */
+    Collection<YangModelCmHandle> getYangModelCmHandlesFromCmHandleReferences(Collection<String> cmHandleReferences);
+
+    /**
      * Method to return module definitions by cmHandleId.
      *
      * @param cmHandleId cm handle ID
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
index 655d843..d73fae9 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
@@ -22,6 +22,7 @@
 
 package org.onap.cps.ncmp.impl.inventory;
 
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
 
 import com.google.common.collect.Lists;
@@ -33,6 +34,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsAnchorService;
 import org.onap.cps.api.CpsDataService;
@@ -133,6 +135,19 @@
     }
 
     @Override
+    public Collection<YangModelCmHandle> getYangModelCmHandlesFromCmHandleReferences(
+        final Collection<String> cmHandleReferences) {
+
+        final String cpsPathForCmHandlesByReferences = getCpsPathForCmHandlesByReferences(cmHandleReferences);
+
+        final Collection<DataNode> cmHandlesAsDataNodes =
+            cmHandleQueryService.queryNcmpRegistryByCpsPath(
+                cpsPathForCmHandlesByReferences, INCLUDE_ALL_DESCENDANTS);
+
+        return YangDataConverter.toYangModelCmHandles(cmHandlesAsDataNodes);
+    }
+
+    @Override
     public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(final String cmHandleId) {
         return cpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
     }
@@ -190,7 +205,8 @@
             return Collections.emptyList();
         }
         final String cpsPathForCmHandlesByAlternateIds = getCpsPathForCmHandlesByAlternateIds(alternateIds);
-        return cmHandleQueryService.queryNcmpRegistryByCpsPath(cpsPathForCmHandlesByAlternateIds, OMIT_DESCENDANTS);
+        return cmHandleQueryService.queryNcmpRegistryByCpsPath(cpsPathForCmHandlesByAlternateIds,
+            INCLUDE_ALL_DESCENDANTS);
     }
 
     @Override
@@ -234,6 +250,12 @@
                 NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='", "']"));
     }
 
+    private String getCpsPathForCmHandlesByReferences(final Collection<String> cmHandleReferences) {
+        return cmHandleReferences.stream()
+            .flatMap(id -> Stream.of("@id='" + id + "'", "@alternate-id='" + id + "'"))
+            .collect(Collectors.joining(" or ", NCMP_DMI_REGISTRY_PARENT + "/cm-handles[", "]"));
+    }
+
     private static String createStateJsonData(final String state) {
         return "{\"state\":" + state + "}";
     }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
index fd76abb..b046c12 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
@@ -112,7 +112,7 @@
             mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty])
             def dataOperationBatchRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
             def dataOperationRequest = spiedJsonObjectMapper.convertJsonString(dataOperationBatchRequestJsonData, DataOperationRequest.class)
-            dataOperationRequest.dataOperationDefinitions[0].cmHandleIds = [cmHandleId]
+            dataOperationRequest.dataOperationDefinitions[0].cmHandleReferences = [cmHandleId]
         and: 'a positive response from DMI service when it is called with valid request parameters'
             def responseFromDmi = Mono.just(new ResponseEntity<Object>(HttpStatus.ACCEPTED))
             def expectedUrlTemplateWithVariables = new UrlTemplateParameters('myServiceName/dmi/v1/data?requestId={requestId}&topic={topic}', ['requestId': 'requestId', 'topic': 'my-topic-name'])
@@ -129,7 +129,7 @@
             mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty])
             def dataOperationBatchRequestJsonData = TestUtils.getResourceFileContent('dataOperationRequest.json')
             def dataOperationRequest = spiedJsonObjectMapper.convertJsonString(dataOperationBatchRequestJsonData, DataOperationRequest.class)
-            dataOperationRequest.dataOperationDefinitions[0].cmHandleIds = [cmHandleId]
+            dataOperationRequest.dataOperationDefinitions[0].cmHandleReferences = [cmHandleId]
         and: 'the published cloud event will be captured'
             def actualDataOperationCloudEvent = null
             eventsPublisher.publishCloudEvent('my-topic-name', 'my-request-id', _) >> { args -> actualDataOperationCloudEvent = args[2] }
@@ -143,7 +143,7 @@
             assert eventDataValue.statusMessage == UNKNOWN_ERROR.message
         and: 'the event contains the correct operation details'
             assert eventDataValue.operationId == dataOperationRequest.dataOperationDefinitions[0].operationId
-            assert eventDataValue.ids == dataOperationRequest.dataOperationDefinitions[0].cmHandleIds
+            assert eventDataValue.ids == dataOperationRequest.dataOperationDefinitions[0].cmHandleReferences
     }
 
     def 'call get all resource data.'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandlerSpec.groovy
index d5db24c..d5f705f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandlerSpec.groovy
@@ -73,7 +73,7 @@
         and: 'notification feature is turned on/off'
             objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled
         when: 'data operation request is executed'
-            def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleIds: ['ch'])
+            def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleReferences: ['ch'])
             def result = objectUnderTest.executeAsynchronousRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER)
         then: 'the task is executed in an async fashion or not'
             expectedCalls * dmiDataOperations.requestResourceDataFromDmi('someTopic', _, _, NO_AUTH_HEADER)
@@ -120,7 +120,7 @@
         given: 'a data operation definition with too many cm handles'
             def tooMany = objectUnderTest.MAXIMUM_CM_HANDLES_PER_OPERATION + 1
             def cmHandleIds = new String[tooMany]
-            def dataOperationDefinition = new DataOperationDefinition(operationId: 'abc', operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleIds: cmHandleIds)
+            def dataOperationDefinition = new DataOperationDefinition(operationId: 'abc', operation: 'read', datastore: 'ncmp-datastore:passthrough-running', cmHandleReferences: cmHandleIds)
         when: 'data operation request is executed'
             objectUnderTest.executeAsynchronousRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER)
         then: 'a payload too large exception is thrown'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelperSpec.groovy
index 84eafb0..77e2c4f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelperSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelperSpec.groovy
@@ -74,15 +74,16 @@
             assert dmiDataOperationRequestBodyAsJsonNode.get('operationId').asText() == expectedOperationId
             assert dmiDataOperationRequestBodyAsJsonNode.get('datastore').asText() == expectedDatastore
         and: 'the correct cm handles (just for #serviceName)'
-            assert dmiDataOperationRequestBodyAsJsonNode.get('cmHandles').size() == expectedCmHandleIds.size()
-            expectedCmHandleIds.each {
+            assert dmiDataOperationRequestBodyAsJsonNode.get('cmHandles').size() == expectedCmHandleReferences.size()
+            expectedCmHandleReferences.each {
                 dmiDataOperationRequestBodyAsJsonNode.get('cmHandles').toString().contains(it)
             }
         where: 'the following dmi service and operations are checked'
-            serviceName | operationIndex || expectedOperationId | expectedDatastore                        | expectedCmHandleIds
+            serviceName | operationIndex || expectedOperationId | expectedDatastore                        | expectedCmHandleReferences
             'dmi1'      | 0              || 'operational-14'    | 'ncmp-datastore:passthrough-operational' | ['ch6-dmi1']
             'dmi1'      | 1              || 'running-12'        | 'ncmp-datastore:passthrough-running'     | ['ch1-dmi1', 'ch2-dmi1']
             'dmi1'      | 2              || 'operational-15'    | 'ncmp-datastore:passthrough-operational' | ['ch6-dmi1']
+            'dmi1'      | 3              || 'operational-16'    | 'ncmp-datastore:passthrough-operational' | ['alt6-dmi1']
             'dmi2'      | 0              || 'operational-14'    | 'ncmp-datastore:passthrough-operational' | ['ch3-dmi2']
             'dmi2'      | 1              || 'running-12'        | 'ncmp-datastore:passthrough-running'     | ['ch7-dmi2']
             'dmi2'      | 2              || 'operational-15'    | 'ncmp-datastore:passthrough-operational' | ['ch4-dmi2']
@@ -137,14 +138,14 @@
         def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')]
         def readyState = new CompositeStateBuilder().withCmHandleState(READY).withLastUpdatedTimeNow().build()
         def advisedState = new CompositeStateBuilder().withCmHandleState(ADVISED).withLastUpdatedTimeNow().build()
-        return [new YangModelCmHandle(id: 'ch1-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'ch2-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'ch6-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'ch8-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'ch3-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'ch4-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'ch7-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
-                new YangModelCmHandle(id: 'non-ready-cm-handle', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: advisedState)
+        return [new YangModelCmHandle(id: 'ch1-dmi1', 'alternateId': 'alt1-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'ch2-dmi1', 'alternateId': 'alt2-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'ch6-dmi1', 'alternateId': 'alt6-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'ch8-dmi1', 'alternateId': 'alt8-dmi1', dmiServiceName: 'dmi1', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'ch3-dmi2', 'alternateId': 'alt3-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'ch4-dmi2', 'alternateId': 'alt4-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'ch7-dmi2', 'alternateId': 'alt7-dmi2', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: readyState),
+                new YangModelCmHandle(id: 'non-ready-cm-handle', 'alternateId': 'non-ready-alternate', dmiServiceName: 'dmi2', dmiProperties: dmiProperties, compositeState: advisedState)
         ]
     }
 
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy
index 65fda87..d00d3ab 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiOperationsBaseSpec.groovy
@@ -61,7 +61,7 @@
 
     def mockYangModelCmHandleCollectionRetrieval(dmiProperties) {
         populateYangModelCmHandle(dmiProperties, '')
-        mockInventoryPersistence.getYangModelCmHandles(_) >> [yangModelCmHandle]
+        mockInventoryPersistence.getYangModelCmHandlesFromCmHandleReferences(_) >> [yangModelCmHandle]
     }
 
     def populateYangModelCmHandle(dmiProperties, moduleSetTag) {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
index e2261f4..34d9374 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy
@@ -38,7 +38,6 @@
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.ContentType
 import org.onap.cps.utils.JsonObjectMapper
-import org.testcontainers.shaded.com.fasterxml.jackson.databind.introspect.BasicClassIntrospector
 import spock.lang.Shared
 import spock.lang.Specification
 
@@ -75,12 +74,16 @@
             .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
 
     def cmHandleId = 'some-cm-handle'
-    def leaves = ["id":cmHandleId,"dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
+    def alternateId = 'some-alternate-id'
+    def leaves = ["id":cmHandleId, "alternateId":alternateId,"dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
     def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
 
     def cmHandleId2 = 'another-cm-handle'
+    def alternateId2 = 'another-alternate-id'
     def xpath2 = "/dmi-registry/cm-handles[@id='another-cm-handle']"
 
+    def dataNode = new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])
+
     @Shared
     def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
                                                       new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
@@ -135,7 +138,7 @@
             1 * mockCpsValidator.validateNameCharacters(cmHandleId)
     }
 
-    def "Retrieve multiple YangModelCmHandles"() {
+    def "Retrieve multiple YangModelCmHandles using cm handle ids"() {
         given: 'the cps data service returns 2 data nodes from the DMI registry'
             def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2])]
             mockCpsDataService.getDataNodesForMultipleXpaths(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, [xpath, xpath2] , INCLUDE_ALL_DESCENDANTS) >> dataNodes
@@ -146,6 +149,17 @@
             assert results.id.containsAll([cmHandleId, cmHandleId2])
     }
 
+    def "Retrieve multiple YangModelCmHandles using cm handle references"() {
+        given: 'the cps data service returns 2 data nodes from the DMI registry'
+            def dataNodes = [new DataNode(xpath: xpath, leaves: ['id': cmHandleId, 'alternate-id':alternateId]), new DataNode(xpath: xpath2, leaves: ['id': cmHandleId2,'alternate-id':alternateId2])]
+            mockCmHandleQueries.queryNcmpRegistryByCpsPath(_, INCLUDE_ALL_DESCENDANTS) >> dataNodes
+        when: 'retrieving the yang modelled cm handle'
+            def results = objectUnderTest.getYangModelCmHandlesFromCmHandleReferences([cmHandleId, cmHandleId2])
+        then: 'verify both have returned and cmhandleIds are correct'
+            assert results.size() == 2
+            assert results.id.containsAll([cmHandleId, cmHandleId2])
+    }
+
     def 'Get a Cm Handle Composite State'() {
         given: 'a valid cm handle id'
             def cmHandleId = 'Some-Cm-Handle'
@@ -317,15 +331,6 @@
             assert thrownException.getMessage().contains('DataNode not found')
     }
 
-    def 'Get multiple cm handle data nodes by alternate ids'() {
-        given: 'expected xPath to get cmHandle data node'
-            def expectedXPath = "/dmi-registry/cm-handles[@alternate-id='A' or @alternate-id='B']"
-        when: 'getting the cm handle data node'
-            objectUnderTest.getCmHandleDataNodesByAlternateIds(['A', 'B'])
-        then: 'query service is invoked with expected xpath'
-            1 * mockCmHandleQueries.queryNcmpRegistryByCpsPath(expectedXPath, OMIT_DESCENDANTS)
-    }
-
     def 'Get multiple cm handle data nodes by alternate ids, passing empty collection'() {
         when: 'getting the cm handle data node for no alternate ids'
             objectUnderTest.getCmHandleDataNodesByAlternateIds([])
@@ -372,4 +377,13 @@
         then: 'the cps data service method to delete data nodes is invoked once with the same xPaths'
             1 * mockCpsDataService.deleteDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, ['xpath1', 'xpath2'], NO_TIMESTAMP);
     }
+
+    def 'Check if cm handle exists for a given cm handle id'() {
+        given: 'data service returns a datanode with correct cm handle id'
+            mockCpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, INCLUDE_ALL_DESCENDANTS) >> [dataNode]
+        when: 'method is called to check if cm handle exists from cm handle id'
+            def result = objectUnderTest.isExistingCmHandleId('some-cm-handle')
+        then: 'check if cm handle id in datanode is equal to given cm handle id'
+            assert result == true
+    }
 }
diff --git a/cps-ncmp-service/src/test/resources/dataOperationRequest.json b/cps-ncmp-service/src/test/resources/dataOperationRequest.json
index f69b876..3faaf2b 100644
--- a/cps-ncmp-service/src/test/resources/dataOperationRequest.json
+++ b/cps-ncmp-service/src/test/resources/dataOperationRequest.json
@@ -36,6 +36,17 @@
         "ch4-dmi2",
         "ch6-dmi1"
       ]
+    },
+    {
+      "operation": "read",
+      "operationId": "operational-16",
+      "datastore": "ncmp-datastore:passthrough-operational",
+      "options": "some option",
+      "resourceIdentifier": "some resource identifier",
+      "targetIds": [
+        "ch4-dmi2",
+        "alt6-dmi1"
+      ]
     }
   ]
 }
diff --git a/docs/api/swagger/ncmp/openapi-inventory.yaml b/docs/api/swagger/ncmp/openapi-inventory.yaml
index 8552ad5..c72f47a 100644
--- a/docs/api/swagger/ncmp/openapi-inventory.yaml
+++ b/docs/api/swagger/ncmp/openapi-inventory.yaml
@@ -136,8 +136,8 @@
         \ plugin)."
       operationId: searchCmHandleIds
       parameters:
-      - description: Boolean parameter to determine if returned value(s) will be cmHandle
-          Ids or Alternate Ids for a given query
+      - description: Boolean parameter to determine if returned value(s) will be cm
+          handle references for a given query
         in: query
         name: outputAlternateId
         required: false
diff --git a/docs/api/swagger/ncmp/openapi.yaml b/docs/api/swagger/ncmp/openapi.yaml
index aa732c8..024aed6 100644
--- a/docs/api/swagger/ncmp/openapi.yaml
+++ b/docs/api/swagger/ncmp/openapi.yaml
@@ -698,7 +698,7 @@
               schema:
                 $ref: '#/components/schemas/DmiErrorMessage'
           description: Bad Gateway
-      summary: Execute a data operation for group of cm handle ids
+      summary: Execute a data operation for group of cm handle references
       tags:
       - network-cm-proxy
   /v1/ch/{cm-handle}/data/ds/{datastore-name}/query:
@@ -1141,8 +1141,8 @@
         this query.
       operationId: searchCmHandleIds
       parameters:
-      - description: Boolean parameter to determine if returned value(s) will be cmHandle
-          Ids or Alternate Ids for a given query
+      - description: Boolean parameter to determine if returned value(s) will be cm
+          handle references for a given query
         in: query
         name: outputAlternateId
         required: false
@@ -1618,8 +1618,8 @@
         example: 2024-01-22
         type: string
     outputAlternateIdOptionInQuery:
-      description: Boolean parameter to determine if returned value(s) will be cmHandle
-        Ids or Alternate Ids for a given query
+      description: Boolean parameter to determine if returned value(s) will be cm
+        handle references for a given query
       in: query
       name: outputAlternateId
       required: false
@@ -1789,8 +1789,8 @@
           type: string
         targetIds:
           items:
-            description: "targeted cm handles, maximum of 50 supported. If this limit\
-              \ is exceeded the request wil be refused."
+            description: "targeted cm handle references, maximum of 200 supported.\
+              \ If this limit is exceeded the request wil be refused."
             example: "[\"da310eecdb8d44c2acc0ddaae01174b1\",\"c748c58f8e0b438f9fd1f28370b17d47\"\
               ]"
             type: string
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy
index 99e8032..a81058f 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/BearerTokenPassthroughSpec.groovy
@@ -37,11 +37,14 @@
 
     def setup() {
         dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
-        registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG)
+        dmiDispatcher1.moduleNamesPerCmHandleId['ch-2'] = ['M1', 'M3']
+        registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'alt-1')
+        registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'alt-2')
     }
 
     def cleanup() {
         deregisterCmHandle(DMI1_URL, 'ch-1')
+        deregisterCmHandle(DMI1_URL, 'ch-2')
     }
 
     def 'Bearer token is passed from NCMP to DMI in pass-through data operations.'() {
@@ -83,7 +86,7 @@
                 "operationId": "operational-1",
                 "datastore": "ncmp-datastore:passthrough-running",
                 "resourceIdentifier": "my-resource-id",
-                "targetIds": ["ch-1"]
+                "targetIds": ["ch-1","alt-2"]
             }]}"""
         mvc.perform(request(POST, '/ncmp/v1/data')
                     .queryParam('topic', 'my-topic')