Fetch Cm Handles with optional private properties 57/140957/10
authorleventecsanyi <levente.csanyi@est.tech>
Tue, 27 May 2025 08:01:57 +0000 (10:01 +0200)
committerleventecsanyi <levente.csanyi@est.tech>
Thu, 5 Jun 2025 12:20:49 +0000 (14:20 +0200)
  - added output mapper
  - implemented endpoint to query cm handles on E-01
  - added unit tests
  - re-generated relevant docs

Issue-ID: CPS-1872
Change-Id: I18df1b151a6e818ee1c13b43a0c8e43ae9c22258
Signed-off-by: leventecsanyi <levente.csanyi@est.tech>
28 files changed:
cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/docs/openapi/openapi-inventory.yml
cps-ncmp-rest/pom.xml
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/NetworkCmProxyInventoryController.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/RestOutputCmHandleMapper.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/NetworkCmProxyInventoryControllerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/RestOutputCmHandleMapperSpec.groovy [new file with mode: 0644]
cps-ncmp-rest/src/test/resources/cm-handle-search-by-dmi-service.json [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/NetworkCmProxyInventoryFacadeImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/CmHandleQueryConditions.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/models/CmHandleQueryConditionsSpec.groovy
cps-rest/pom.xml
docker-compose/cps-base.yml
docs/api/swagger/cps/openapi.yaml
docs/api/swagger/ncmp/openapi-inventory.yaml
docs/api/swagger/ncmp/openapi.yaml
policy-executor-stub/pom.xml

index 1664887..309b4ed 100644 (file)
@@ -43,7 +43,7 @@ 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.model.RestOutputPublicCmHandleProperties;
 import org.onap.cps.ncmp.rest.stub.providers.ResourceProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -159,7 +159,7 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi {
     }
 
     @Override
-    public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
+    public ResponseEntity<RestOutputPublicCmHandleProperties> getCmHandlePublicPropertiesByCmHandleId(
             final String cmHandle) {
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
index e4283d9..835cfc2 100644 (file)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2024 Nordix Foundation
+#  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2022 Bell Canada
 #  ================================================================================
@@ -130,9 +130,15 @@ components:
           type: string
           example: my-cm-handle
         cmHandleProperties:
-          $ref: '#/components/schemas/RestCmHandleProperties'
+          type: object
+          additionalProperties:
+            type: string
+            example: my-property
         publicCmHandleProperties:
-          $ref: '#/components/schemas/RestCmHandleProperties'
+          type: object
+          additionalProperties:
+            type: string
+            example: my-property
         moduleSetTag:
           type: string
           example: "my-module-set-tag"
@@ -146,11 +152,6 @@ components:
         dataProducerIdentifier:
           type: string
           example: "my-data-producer-identifier"
-    RestCmHandleProperties:
-      type: object
-      additionalProperties:
-        type: string
-        example: my-property
     #Module upgrade schema
     UpgradedCmHandles:
       required:
@@ -253,7 +254,17 @@ components:
           type: string
           example: my-cm-handle1
         publicCmHandleProperties:
-          $ref: '#/components/schemas/CmHandlePublicProperties'
+          type: object
+          items:
+            type: object
+            additionalProperties:
+              type: string
+              example: '3gpp Type'
+        privateCmHandleProperties:
+          type: object
+          additionalProperties:
+            type: string
+            example: '3gpp Type'
         state:
           $ref: '#/components/schemas/CmHandleCompositeState'
         trustLevel:
@@ -267,13 +278,6 @@ components:
         dataProducerIdentifier:
           type: string
           example: my-data-producer-identifier
-    CmHandlePublicProperties:
-      type: object
-      items:
-        type: object
-        additionalProperties:
-          type: string
-          example: '3gpp Type'
     CmHandleCompositeState:
       type: object
       properties:
@@ -323,12 +327,16 @@ components:
           type: string
           example: 2022-12-31T20:30:40.000+0000
 
-    RestOutputCmHandlePublicProperties:
+    RestOutputPublicCmHandleProperties:
       type: object
       properties:
         publicCmHandleProperties:
-          $ref: '#/components/schemas/CmHandlePublicProperties'
-
+          type: object
+          items:
+            type: object
+            additionalProperties:
+              type: string
+              example: '3gpp Type'
     RestOutputCmHandleCompositeState:
       type: object
       properties:
index 7b1c8d4..4aa977e 100755 (executable)
@@ -1,6 +1,6 @@
 #  ============LICENSE_START=======================================================
 #  Copyright (C) 2021 Bell Canada
-#  Modifications Copyright (C) 2021-2024 Nordix Foundation
+#  Modifications Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
 #  ================================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -146,5 +146,41 @@ searchCmHandleIds:
                 type: string
       403:
         $ref: 'components.yaml#/components/responses/Forbidden'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
+
+searchCmHandles:
+  post:
+    description: Execute cm handle query search and return a list of cm handle details. Any number of conditions can be applied. To be included in the result a cm-handle must fulfill ALL the conditions. An empty collection will be returned in the case that the cm handle does not match a condition. For more on cm handle query search please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/ncmp-cmhandle-querying.html">cm handle query search Read the Docs</a>.<br/>By supplying a CPS Path it is possible to query on any data related to the cm handle. For more on CPS Path please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html">CPS Path Read the Docs</a>. The cm handle ancestor is automatically returned for this query.
+    tags:
+      - network-cm-proxy-inventory
+    summary: Query Cm Handles for a requested DMI Service
+    operationId: searchCmHandles
+    parameters:
+      - name: includePrivatePropertiesInQuery
+        in: query
+        description: Whether to include private properties in the response.
+        required: false
+        schema:
+          type: boolean
+    requestBody:
+      required: true
+      content:
+        application/json:
+          schema:
+            $ref: 'components.yaml#/components/schemas/CmHandleQueryParameters'
+    responses:
+      200:
+        description: OK
+        content:
+          application/json:
+            schema:
+              type: array
+              items:
+                $ref: 'components.yaml#/components/schemas/RestOutputCmHandle'
+      400:
+        $ref: 'components.yaml#/components/responses/BadRequest'
+      403:
+        $ref: 'components.yaml#/components/responses/Forbidden'
       500:
         $ref: 'components.yaml#/components/responses/InternalServerError'
\ No newline at end of file
index 6dd9113..bd26750 100755 (executable)
@@ -1,5 +1,5 @@
 #  ============LICENSE_START=======================================================
-#  Copyright (C) 2021-2024 OpenInfra Foundation Europe. All rights reserved.
+#  Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
 #  Modifications Copyright (C) 2021 Pantheon.tech
 #  Modifications Copyright (C) 2021-2022 Bell Canada
 #  ================================================================================
@@ -392,7 +392,7 @@ getCmHandlePropertiesById:
         content:
           application/json:
             schema:
-              $ref: 'components.yaml#/components/schemas/RestOutputCmHandlePublicProperties'
+              $ref: 'components.yaml#/components/schemas/RestOutputPublicCmHandleProperties'
       400:
         $ref: 'components.yaml#/components/responses/BadRequest'
       404:
index 7658075..8f507c6 100755 (executable)
@@ -33,3 +33,6 @@ paths:
 
   /v1/ch/searches:
     $ref: 'ncmp-inventory.yml#/searchCmHandleIds'
+
+  /v1/ch/searchCmHandles:
+    $ref: 'ncmp-inventory.yml#/searchCmHandles'
\ No newline at end of file
index 4084f9b..0715b42 100644 (file)
             <plugin>
                 <groupId>org.openapitools</groupId>
                 <artifactId>openapi-generator-maven-plugin</artifactId>
-                <version>6.6.0</version>
+                <version>7.12.0</version>
                 <executions>
                     <execution>
                         <id>ncmp-code-gen</id>
                             <apiPackage>org.onap.cps.ncmp.rest.api</apiPackage>
                             <generatorName>spring</generatorName>
                             <generateSupportingFiles>false</generateSupportingFiles>
+                            <generateAliasAsModel>true</generateAliasAsModel>
                             <configOptions>
                                 <sourceFolder>src/gen/java</sourceFolder>
                                 <dateLibrary>java11</dateLibrary>
index 387f48a..2b0ea31 100644 (file)
@@ -34,6 +34,7 @@ import io.micrometer.core.annotation.Timed;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -48,19 +49,19 @@ import org.onap.cps.ncmp.api.inventory.models.CompositeState;
 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
 import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade;
 import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
-import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
 import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
 import org.onap.cps.ncmp.rest.model.DataOperationRequest;
 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.model.RestOutputPublicCmHandleProperties;
 import org.onap.cps.ncmp.rest.util.CmHandleStateMapper;
 import org.onap.cps.ncmp.rest.util.CountCmHandleSearchExecution;
 import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper;
 import org.onap.cps.ncmp.rest.util.DeprecationHelper;
 import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper;
+import org.onap.cps.ncmp.rest.util.RestOutputCmHandleMapper;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -82,6 +83,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     private final NcmpRestInputMapper ncmpRestInputMapper;
     private final CmHandleStateMapper cmHandleStateMapper;
     private final DataOperationRequestMapper dataOperationRequestMapper;
+    private final RestOutputCmHandleMapper restOutputCmHandleMapper;
 
     /**
      * Get resource data from datastore.
@@ -265,7 +267,8 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
                 deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters);
         final List<RestOutputCmHandle> restOutputCmHandles =
                 networkCmProxyInventoryFacade.executeCmHandleSearch(cmHandleQueryApiParameters)
-                        .map(this::toRestOutputCmHandle).collectList().block();
+                        .map(handle -> restOutputCmHandleMapper
+                                .toRestOutputCmHandle(handle, false)).collectList().block();
         return ResponseEntity.ok(restOutputCmHandles);
     }
 
@@ -299,7 +302,8 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleReference) {
         final NcmpServiceCmHandle ncmpServiceCmHandle
             = networkCmProxyInventoryFacade.getNcmpServiceCmHandle(cmHandleReference);
-        final RestOutputCmHandle restOutputCmHandle = toRestOutputCmHandle(ncmpServiceCmHandle);
+        final RestOutputCmHandle restOutputCmHandle = restOutputCmHandleMapper
+                .toRestOutputCmHandle(ncmpServiceCmHandle, false);
         return ResponseEntity.ok(restOutputCmHandle);
     }
 
@@ -310,14 +314,14 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
      * @return cm handle properties
      */
     @Override
-    public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
+    public ResponseEntity<RestOutputPublicCmHandleProperties> getCmHandlePublicPropertiesByCmHandleId(
             final String cmHandleReference) {
-        final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
+        final List<Map<String, String>> cmHandlePublicProperties = new ArrayList<>(1);
         cmHandlePublicProperties.add(networkCmProxyInventoryFacade.getCmHandlePublicProperties(cmHandleReference));
-        final RestOutputCmHandlePublicProperties restOutputCmHandlePublicProperties =
-                new RestOutputCmHandlePublicProperties();
-        restOutputCmHandlePublicProperties.setPublicCmHandleProperties(cmHandlePublicProperties);
-        return ResponseEntity.ok(restOutputCmHandlePublicProperties);
+        final RestOutputPublicCmHandleProperties restOutputPublicCmHandleProperties =
+                new RestOutputPublicCmHandleProperties();
+        restOutputPublicCmHandleProperties.setPublicCmHandleProperties(cmHandlePublicProperties);
+        return ResponseEntity.ok(restOutputPublicCmHandleProperties);
     }
 
     /**
@@ -396,23 +400,6 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         return new ResponseEntity<>(HttpStatus.OK);
     }
 
-    private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) {
-        final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle();
-        final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
-        restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleId());
-        cmHandlePublicProperties.add(ncmpServiceCmHandle.getPublicProperties());
-        restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties);
-        restOutputCmHandle.setState(cmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(
-                ncmpServiceCmHandle.getCompositeState()));
-        if (ncmpServiceCmHandle.getCurrentTrustLevel() != null) {
-            restOutputCmHandle.setTrustLevel(ncmpServiceCmHandle.getCurrentTrustLevel().toString());
-        }
-        restOutputCmHandle.setModuleSetTag(ncmpServiceCmHandle.getModuleSetTag());
-        restOutputCmHandle.setAlternateId(ncmpServiceCmHandle.getAlternateId());
-        restOutputCmHandle.setDataProducerIdentifier(ncmpServiceCmHandle.getDataProducerIdentifier());
-        return restOutputCmHandle;
-    }
-
     private void validateDataStore(final DatastoreType acceptableDataStoreType, final String requestedDatastoreName) {
         final DatastoreType datastoreType = DatastoreType.fromDatastoreName(requestedDatastoreName);
 
index 5de8c12..2af4441 100644 (file)
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade;
+import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryApiParameters;
 import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters;
 import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse;
 import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse.Status;
@@ -37,8 +38,11 @@ import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
 import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse;
 import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse;
 import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
 import org.onap.cps.ncmp.rest.util.CountCmHandleSearchExecution;
+import org.onap.cps.ncmp.rest.util.DeprecationHelper;
 import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper;
+import org.onap.cps.ncmp.rest.util.RestOutputCmHandleMapper;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -51,6 +55,8 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
 
     private final NetworkCmProxyInventoryFacade networkCmProxyInventoryFacade;
     private final NcmpRestInputMapper ncmpRestInputMapper;
+    private final DeprecationHelper deprecationHelper;
+    private final RestOutputCmHandleMapper restOutputCmHandleMapper;
 
     /**
      * Get all cm handle references under a registered DMI plugin.
@@ -73,6 +79,28 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
         return ResponseEntity.ok(List.copyOf(cmHandleIds));
     }
 
+    /**
+     * Execute cm handle query search and return a list of cm handle details. Any number of conditions can be applied.
+     *
+     * @param cmHandleQueryParameters the cm handle query parameters
+     * @param includePrivateProperties boolean value to determine the inclusion of private properties
+     * @return collection of cm handles
+     */
+    @Override
+    public ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
+            final CmHandleQueryParameters cmHandleQueryParameters,
+            final Boolean includePrivateProperties) {
+        final CmHandleQueryApiParameters cmHandleQueryApiParameters =
+                deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters);
+        final boolean includePrivatePropertiesParameter = Boolean.TRUE.equals(includePrivateProperties);
+        final List<RestOutputCmHandle> restOutputCmHandles =
+                networkCmProxyInventoryFacade.executeCmHandleInventorySearch(cmHandleQueryApiParameters)
+                        .map(handle -> restOutputCmHandleMapper
+                                .toRestOutputCmHandle(handle, includePrivatePropertiesParameter))
+                        .collectList().block();
+        return ResponseEntity.ok(restOutputCmHandles);
+    }
+
     /**
      * Get all cm-handle IDs under a registered DMI plugin.
      *
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/RestOutputCmHandleMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/RestOutputCmHandleMapper.java
new file mode 100644 (file)
index 0000000..095d062
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.util;
+
+import java.util.Collections;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class RestOutputCmHandleMapper {
+
+    private final CmHandleStateMapper cmHandleStateMapper;
+
+    /**
+     * Map NcmpServiceCmHandle to a RestOutputCmHandle object.
+     *
+     * @param ncmpServiceCmHandle            DMI plugin identifier
+     * @param includePrivateProperties       Boolean for cm handle reference type either
+     *                                       cm handle id (False) or alternate id (True)
+     * @return                               list of cm handles
+     */
+    public RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle,
+                                                   final boolean includePrivateProperties) {
+        final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle();
+        restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleId());
+        restOutputCmHandle.setPublicCmHandleProperties(
+                Collections.singletonList(ncmpServiceCmHandle.getPublicProperties()));
+        if (includePrivateProperties) {
+            restOutputCmHandle.setPrivateCmHandleProperties(ncmpServiceCmHandle.getDmiProperties());
+        }
+        restOutputCmHandle.setState(
+                cmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(
+                        ncmpServiceCmHandle.getCompositeState()));
+        if (ncmpServiceCmHandle.getCurrentTrustLevel() != null) {
+            restOutputCmHandle.setTrustLevel(ncmpServiceCmHandle.getCurrentTrustLevel().toString());
+        }
+        restOutputCmHandle.setModuleSetTag(ncmpServiceCmHandle.getModuleSetTag());
+        restOutputCmHandle.setAlternateId(ncmpServiceCmHandle.getAlternateId());
+        restOutputCmHandle.setDataProducerIdentifier(ncmpServiceCmHandle.getDataProducerIdentifier());
+        return restOutputCmHandle;
+    }
+}
\ No newline at end of file
index 8c7f5fb..32fcd53 100644 (file)
@@ -43,12 +43,14 @@ import org.onap.cps.ncmp.api.inventory.models.LockReasonCategory
 import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.ncmp.rest.model.DataOperationDefinition
 import org.onap.cps.ncmp.rest.model.DataOperationRequest
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandle
 import org.onap.cps.ncmp.rest.util.CmHandleStateMapper
 import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper
 import org.onap.cps.ncmp.rest.util.DeprecationHelper
 import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper
 import org.onap.cps.api.model.ModuleDefinition
 import org.onap.cps.api.model.ModuleReference
+import org.onap.cps.ncmp.rest.util.RestOutputCmHandleMapper
 import org.onap.cps.utils.JsonObjectMapper
 import org.slf4j.LoggerFactory
 import org.spockframework.spring.SpringBean
@@ -112,6 +114,9 @@ class NetworkCmProxyControllerSpec extends Specification {
     @SpringBean
     DataOperationRequestMapper dataOperationRequestMapper = Mappers.getMapper(DataOperationRequestMapper)
 
+    @SpringBean
+    RestOutputCmHandleMapper mockRestOutputCmHandleMapper = Mock()
+
     @SpringBean
     DeprecationHelper stubbedDeprecationHelper = Stub()
 
@@ -261,53 +266,44 @@ class NetworkCmProxyControllerSpec extends Specification {
             assert response.status == HttpStatus.OK.value()
     }
 
-    def 'Retrieve cm handles.'() {
-        given: 'an endpoint and json data'
+    def 'Execute cm handle search.'() {
+        given: 'search endpoint and JSON request'
             def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
             String jsonString = TestUtils.getResourceFileContent('cmhandle-search.json')
-        and: 'the service method is invoked with module names and returns two cm handles'
-            def cmHandle1 = new NcmpServiceCmHandle()
-            cmHandle1.cmHandleId = 'ch-1'
-            cmHandle1.publicProperties = [color: 'yellow']
-            cmHandle1.currentTrustLevel = TrustLevel.NONE
-            def cmHandle2 = new NcmpServiceCmHandle()
-            cmHandle2.cmHandleId = 'ch-2'
-            cmHandle2.publicProperties = [color: 'green']
-            cmHandle2.alternateId = 'someAlternateId'
-            cmHandle2.moduleSetTag = 'someModuleSetTag'
-            cmHandle2.dataProducerIdentifier = 'someDataProducerIdentifier'
-            mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> Flux.fromIterable([cmHandle1, cmHandle2])
-        when: 'the searches api is invoked'
+        and: 'the inventory facade returns two cm handles'
+            def ncmpServiceCmHandle1 = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
+            def ncmpServiceCmHandle2 = new NcmpServiceCmHandle(cmHandleId: 'ch-2')
+            mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> Flux.fromIterable([ncmpServiceCmHandle1, ncmpServiceCmHandle2])
+        and: 'mapper converts cm handles without private properties'
+            def restHandle1 = new RestOutputCmHandle(cmHandle: 'rest ch-1')
+            def restHandle2 = new RestOutputCmHandle(cmHandle: 'rest ch-2')
+            mockRestOutputCmHandleMapper.toRestOutputCmHandle(ncmpServiceCmHandle1, false) >> restHandle1
+            mockRestOutputCmHandleMapper.toRestOutputCmHandle(ncmpServiceCmHandle2, false) >> restHandle2
+        when: 'the search endpoint is invoked'
             def response = mvc.perform(post(searchesEndpoint).contentType(MediaType.APPLICATION_JSON).content(jsonString)).andReturn().response
-        then: 'response status returns OK'
+        then: 'response status is OK'
             assert response.status == HttpStatus.OK.value()
-        and: 'the expected response content is returned'
-            assert response.contentAsString == '[{"cmHandle":"ch-1","publicCmHandleProperties":[{"color":"yellow"}],"state":null,"trustLevel":"NONE","moduleSetTag":null,"alternateId":null,"dataProducerIdentifier":null},{"cmHandle":"ch-2","publicCmHandleProperties":[{"color":"green"}],"state":null,"trustLevel":null,"moduleSetTag":"someModuleSetTag","alternateId":"someAlternateId","dataProducerIdentifier":"someDataProducerIdentifier"}]'
+        and: 'the response contains the rest version of both cm handles'
+            assert response.contentAsString.contains('rest ch-1')
+            assert response.contentAsString.contains('rest ch-2')
     }
 
+
     def 'Get complete Cm Handle details by Cm Handle Reference.'() {
         given: 'an endpoint and a cm handle reference'
-            def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle-reference"
-        and: 'an existing ncmp service cm handle'
-            def cmHandleId = 'some-cm-handle'
-            def alternateId = 'some-alternate-id'
-            def dmiProperties = [prop: 'some DMI property']
-            def publicProperties = ["public prop": 'some public property']
-            def compositeState = compositeStateTestObject()
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: alternateId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState, currentTrustLevel: TrustLevel.COMPLETE)
-        and: 'the service method is invoked with the cm handle reference'
-            1 * mockNetworkCmProxyInventoryFacade.getNcmpServiceCmHandle('some-cm-handle-reference') >> ncmpServiceCmHandle
+            def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/cm handle id in request"
+        and: 'existing cm handle from inventory facade'
+            def cmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch-1')
+            mockNetworkCmProxyInventoryFacade.getNcmpServiceCmHandle('cm handle id in request') >> cmHandle
+        and: 'mapper converts cm handle without private properties'
+            def restOutputCmHandle = new RestOutputCmHandle(cmHandle: 'rest version of the cm handle')
+            mockRestOutputCmHandleMapper.toRestOutputCmHandle(cmHandle, false) >> restOutputCmHandle
         when: 'the cm handle details api is invoked'
-            def response = mvc.perform(
-                    get(cmHandleDetailsEndpoint)).andReturn().response
-        then: 'the correct response is returned'
+            def response = mvc.perform(get(cmHandleDetailsEndpoint)).andReturn().response
+        then: 'response status is OK'
             response.status == HttpStatus.OK.value()
-        and: 'the response contains the public properties'
-            assertContainsPublicProperties(response)
-        and: 'the response contains the cm handle state'
-            assertContainsState(response)
-        and: 'the content does not contain dmi properties'
-            assert !response.contentAsString.contains("some DMI property")
+        and: 'the response contains the rest version of the cm handle'
+            assert response.contentAsString.contains('rest version of the cm handle')
     }
 
     def 'Get Cm Handle public properties by Cm Handle Reference.'() {
@@ -341,23 +337,14 @@ class NetworkCmProxyControllerSpec extends Specification {
     }
 
     def 'Call execute cm handle searches with unrecognized condition name.'() {
-        given: 'an endpoint and json data'
+        given: 'the search endpoint and a request with an unrecognized condition name'
             def searchesEndpoint = "$ncmpBasePathV1/ch/searches"
             String jsonString = TestUtils.getResourceFileContent('invalid-cmhandle-search.json')
-        and: 'the service method is invoked with module names and returns two cm handles'
-            def cmHandle1 = new NcmpServiceCmHandle()
-            cmHandle1.cmHandleId = 'ch-1'
-            cmHandle1.publicProperties = [color: 'yellow']
-            cmHandle1.currentTrustLevel = TrustLevel.COMPLETE
-            def cmHandle2 = new NcmpServiceCmHandle()
-            cmHandle2.cmHandleId = 'ch-2'
-            cmHandle2.publicProperties = [color: 'green']
-            cmHandle2.currentTrustLevel = TrustLevel.NONE
-            mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> Flux.fromIterable([cmHandle1, cmHandle2])
         when: 'the searches api is invoked'
-            def response = mvc.perform(post(searchesEndpoint).contentType(MediaType.APPLICATION_JSON).content(jsonString)).andReturn().response
-        then: 'an empty cm handle identifier is returned'
-            assert response.contentAsString == '[{"cmHandle":"ch-1","publicCmHandleProperties":[{"color":"yellow"}],"state":null,"trustLevel":"COMPLETE","moduleSetTag":null,"alternateId":null,"dataProducerIdentifier":null},{"cmHandle":"ch-2","publicCmHandleProperties":[{"color":"green"}],"state":null,"trustLevel":"NONE","moduleSetTag":null,"alternateId":null,"dataProducerIdentifier":null}]'
+            mvc.perform(post(searchesEndpoint).contentType(MediaType.APPLICATION_JSON).content(jsonString))
+        then: 'the request was still accepted and forwarded to the correct services'
+            1 * mockNetworkCmProxyInventoryFacade.executeCmHandleSearch(_) >> Flux.fromIterable([new NcmpServiceCmHandle()])
+            1 * mockRestOutputCmHandleMapper.toRestOutputCmHandle(_, _) >> new RestOutputCmHandle(cmHandle: 'some cm handle')
     }
 
     def 'Query for cm handles matching query parameters'() {
index 9d79922..7f1f4d6 100644 (file)
@@ -23,6 +23,7 @@ package org.onap.cps.ncmp.rest.controller
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.TestUtils
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.impl.NetworkCmProxyInventoryFacadeImpl
 import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters
 import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
@@ -32,7 +33,10 @@ import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters
 import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse
 import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse
 import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandle
+import org.onap.cps.ncmp.rest.util.DeprecationHelper
 import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper
+import org.onap.cps.ncmp.rest.util.RestOutputCmHandleMapper
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
@@ -42,6 +46,7 @@ import org.springframework.context.annotation.Import
 import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
+import reactor.core.publisher.Flux
 import spock.lang.Specification
 
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
@@ -60,6 +65,12 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
     @SpringBean
     NcmpRestInputMapper ncmpRestInputMapper = Mock()
 
+    @SpringBean
+    RestOutputCmHandleMapper mockRestOutputCmHandleMapper = Mock()
+
+    @SpringBean
+    DeprecationHelper deprecationHelper = Mock()
+
     DmiPluginRegistration mockDmiPluginRegistration = Mock()
 
     CmHandleQueryServiceParameters cmHandleQueryServiceParameters = Mock()
@@ -252,11 +263,26 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
             assert response.contentAsString.contains(firstReference)
             assert response.contentAsString.contains(secondReference)
         where:
-            scenario                | outputAlternateId         || firstReference    | secondReference
+            scenario                        | outputAlternateId         || firstReference    | secondReference
             'output returns cm handle ids'  | ''                        ||  'cm-handle-id-1' | 'cm-handle-id-2'
             'output returns alternate ids'  | '&outputAlternateId=true' ||  'alternate-id-1' | 'alternate-id-2'
     }
 
+    def 'Get a cm handle by DMI service name.'() {
+        given: 'an endpoint for returning cm handles by dmi service name'
+            def postUrl = "$ncmpBasePathV1/ch/searchCmHandles?includePrivatePropertiesInQuery=true"
+            String jsonString = TestUtils.getResourceFileContent('cm-handle-search-by-dmi-service.json')
+        and: 'a cm handle is returned'
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(dmiProperties: ['someName': 'my dmi'])
+            mockNetworkCmProxyInventoryFacade.executeCmHandleInventorySearch(_) >> Flux.fromIterable([ncmpServiceCmHandle])
+        and: 'the mapper is requested to convert the object with private properties'
+            mockRestOutputCmHandleMapper.toRestOutputCmHandle(ncmpServiceCmHandle, true) >> new RestOutputCmHandle()
+        when: 'the endpoint is invoked'
+            def response = mvc.perform(post(postUrl).contentType(MediaType.APPLICATION_JSON).content(jsonString)).andReturn().response
+        then: 'a response status is OK'
+            assert response.status == 200
+    }
+
     def expectedUnknownErrorResponse(cmHandle) {
         return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '108', 'errorText': 'Failed')
     }
index 3a9a0bb..7b16df5 100644 (file)
@@ -44,6 +44,7 @@ import org.onap.cps.api.exceptions.AlreadyDefinedException
 import org.onap.cps.api.exceptions.CpsException
 import org.onap.cps.api.exceptions.DataNodeNotFoundException
 import org.onap.cps.api.exceptions.DataValidationException
+import org.onap.cps.ncmp.rest.util.RestOutputCmHandleMapper
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
 import org.springframework.beans.factory.annotation.Autowired
@@ -105,6 +106,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
     @SpringBean
     DataJobControllerForTest stubbedDataJobControllerForTest = Stub()
 
+    @SpringBean
+    RestOutputCmHandleMapper mockRestOutputCmHandleMapper = Mock()
+
     @Value('${rest.api.ncmp-base-path}')
     def basePathNcmp
 
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/RestOutputCmHandleMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/RestOutputCmHandleMapperSpec.groovy
new file mode 100644 (file)
index 0000000..d6eff59
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.util
+
+import org.onap.cps.ncmp.api.inventory.models.CompositeState
+import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
+import org.onap.cps.ncmp.api.inventory.models.TrustLevel
+import org.onap.cps.ncmp.rest.model.CmHandleCompositeState
+import spock.lang.Specification
+
+class RestOutputCmHandleMapperSpec extends Specification {
+
+    CmHandleStateMapper mockCmHandleStateMapper = Mock()
+    RestOutputCmHandleMapper objectUnderTest = new RestOutputCmHandleMapper(mockCmHandleStateMapper)
+
+    def 'Map cm handles to rest output #scenario.'() {
+        given: 'a cm handle with different states'
+            def ncmpServiceCmHandle = createNcmpServiceCmHandle(trustLevel)
+        and: 'the state mapper returns a composite state'
+            mockCmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(ncmpServiceCmHandle.getCompositeState()) >> new CmHandleCompositeState(cmHandleState: 'ADVISED')
+        when: 'the mapper function is called'
+            def result = objectUnderTest.toRestOutputCmHandle(ncmpServiceCmHandle, includePrivateProperties)
+        then: 'result has the expected properties'
+            assert result.privateCmHandleProperties.containsKey('private property key') == includePrivateProperties
+            if (trustLevel != null) {
+                assert result.trustLevel == trustLevel.toString()
+            }
+            assert result.publicCmHandleProperties[0].containsKey('public property key')
+            assert result.alternateId == 'alt-1'
+            assert result.cmHandle == 'ch-1'
+        where:
+            scenario                     | includePrivateProperties || trustLevel
+            'without private properties' | false                    || null
+            'with private properties'    | true                     || TrustLevel.NONE
+            'with trust level'           | false                    || TrustLevel.COMPLETE
+    }
+
+    def createNcmpServiceCmHandle(trustLevel) {
+        return new NcmpServiceCmHandle(cmHandleId: 'ch-1', dmiProperties: ['private property key': 'some value'],
+                currentTrustLevel: trustLevel,
+                publicProperties: ['public property key': 'public property value'],
+                alternateId: 'alt-1', compositeState: new CompositeState(cmHandleState: 'ADVISED'))
+    }
+}
diff --git a/cps-ncmp-rest/src/test/resources/cm-handle-search-by-dmi-service.json b/cps-ncmp-rest/src/test/resources/cm-handle-search-by-dmi-service.json
new file mode 100644 (file)
index 0000000..0048ecc
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "cmHandleQueryParameters": [
+    {
+      "conditionName": "cmHandleWithDmiPlugin",
+      "conditionParameters": [
+        {
+          "dmiPluginName": "my-dmi"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
index 876a5e7..f6d9518 100644 (file)
@@ -143,4 +143,13 @@ public interface NetworkCmProxyInventoryFacade {
      * @return cm handle state
      */
     CompositeState getCmHandleCompositeState(final String cmHandleReference);
+
+    /**
+     * Retrieve cm handles with details for the given query parameters.
+     *
+     * @param cmHandleQueryApiParameters cm handle query parameters
+     * @return cm handle objects as a reactive stream (flux)
+     */
+    Flux<NcmpServiceCmHandle> executeCmHandleInventorySearch(final CmHandleQueryApiParameters
+                                                                     cmHandleQueryApiParameters);
 }
index 7130afd..a6f50d2 100644 (file)
@@ -127,6 +127,15 @@ public class NetworkCmProxyInventoryFacadeImpl implements NetworkCmProxyInventor
         return parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters);
     }
 
+    @Override
+    public Flux<NcmpServiceCmHandle> executeCmHandleInventorySearch(
+            final CmHandleQueryApiParameters cmHandleQueryApiParameters) {
+        final CmHandleQueryServiceParameters cmHandleQueryServiceParameters =
+                jsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class);
+        validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES);
+        return parameterizedCmHandleQueryService.queryInventoryForCmHandles(cmHandleQueryServiceParameters);
+    }
+
     @Override
     public Collection<String> executeCmHandleIdSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters,
                                                       final boolean outputAlternateId) {
index 64d4689..4d02e03 100644 (file)
@@ -71,4 +71,18 @@ public interface ParameterizedCmHandleQueryService {
      */
     Flux<NcmpServiceCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters);
 
+
+    /**
+     * Query and return cm handles that match the given query parameters.
+     * Supported query types:
+     *      cps-path
+     *      public properties
+     *      private (additional) properties
+     *      dmi-names
+     *
+     * @param cmHandleQueryServiceParameters the cm handle query parameters
+     * @return cm handle objects as a reactive stream (flux)
+     */
+    Flux<NcmpServiceCmHandle> queryInventoryForCmHandles(
+                                                final CmHandleQueryServiceParameters cmHandleQueryServiceParameters);
 }
index 19b6199..be6ca8a 100644 (file)
@@ -86,6 +86,12 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan
         return getNcmpServiceCmHandles(cmHandleIds);
     }
 
+    @Override
+    public Flux<NcmpServiceCmHandle> queryInventoryForCmHandles(final CmHandleQueryServiceParameters queryParameters) {
+        final Collection<String> cmHandleIds = queryCmHandleIdsForInventory(queryParameters, false);
+        return getNcmpServiceCmHandles(cmHandleIds);
+    }
+
     private Collection<String> queryCmHandlesByDmiPlugin(
             final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, final boolean outputAlternateId) {
         final Map<String, String> dmiPropertyQueryPairs =
index 6be5c8b..d96fdee 100644 (file)
@@ -30,7 +30,8 @@ public enum CmHandleQueryConditions {
     HAS_ALL_PROPERTIES("hasAllProperties"),
     HAS_ALL_MODULES("hasAllModules"),
     WITH_CPS_PATH("cmHandleWithCpsPath"),
-    WITH_TRUST_LEVEL("cmHandleWithTrustLevel");
+    WITH_TRUST_LEVEL("cmHandleWithTrustLevel"),
+    WITH_DMI_SERVICE("cmHandleWithDmiPlugin");
 
     public static final Collection<String> ALL_CONDITION_NAMES = Arrays.stream(CmHandleQueryConditions.values())
         .map(CmHandleQueryConditions::getConditionName).collect(Collectors.toList());
index 29cd92d..bb4a6ff 100644 (file)
@@ -24,6 +24,7 @@
 package org.onap.cps.ncmp.impl.inventory
 
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.api.exceptions.DataValidationException
 import org.onap.cps.api.model.ConditionProperties
 import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
 import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryApiParameters
@@ -263,6 +264,40 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification {
             assert result[1].currentTrustLevel == TrustLevel.COMPLETE
     }
 
+    def 'Execute cm handle reference search with a valid condition name'() {
+        given: 'a valid API parameter with a supported condition'
+            def apiParams = new CmHandleQueryApiParameters(
+                    cmHandleQueryParameters: [
+                            new ConditionApiProperties(
+                                    conditionName: 'hasAllProperties',
+                                    conditionParameters: [[ 'some key': 'some value' ]]
+                            )
+                    ]
+            )
+        and: 'the system returns a cm handle id'
+            mockParameterizedCmHandleQueryService.queryInventoryForCmHandles(_)
+                    >> Flux.fromIterable([new NcmpServiceCmHandle(cmHandleId: 'cm handle from the query service')])
+        when: 'executing the cm handle search'
+            def result = objectUnderTest.executeCmHandleInventorySearch(apiParams).collectList().block()
+        then: 'the result returns the cm handle from the query service'
+            assert result.size() == 1
+            assert result[0].cmHandleId == 'cm handle from the query service'
+    }
+
+    def 'Execute cm handle reference search with an invalid condition name'() {
+        given: 'an API parameter with an unsupported condition name'
+            def apiParams = new CmHandleQueryApiParameters(
+                    cmHandleQueryParameters: [
+                            new ConditionApiProperties(conditionName: 'invalid condition name')
+                    ]
+            )
+        when: 'executing the search'
+            objectUnderTest.executeCmHandleInventorySearch(apiParams).collectList().block()
+        then: 'a data validation exception will be thrown'
+            def exception = thrown(DataValidationException)
+            assert exception.message == 'Invalid Query Parameter.'
+    }
+
     def 'Set Cm Handle Data Sync flag.'() {
         when: 'setting data sync enabled flag'
             objectUnderTest.setDataSyncEnabled('ch-1',true)
index 5d9a05c..82753ef 100644 (file)
@@ -246,6 +246,31 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification {
             'both queries are null'                                      | null                    | null                    || null
     }
 
+    def 'Query CM handle details by DMI service name.'() {
+        given: 'query parameters with the cmHandleWithDmiPlugin condition'
+            def queryParams = new CmHandleQueryServiceParameters(
+                    cmHandleQueryParameters: [
+                            createConditionProperties('cmHandleWithDmiPlugin', [['some-key': 'some-value']])
+                    ]
+            )
+        and: 'the query service returns a matching cm handle id'
+            def expectedCmHandleId = 'cm-handle from query service'
+            partiallyMockedCmHandleQueries.getCmHandleReferencesByDmiPluginIdentifier(_, false) >> [expectedCmHandleId]
+        and: 'the inventory persistence returns the matching cm handle object'
+            mockInventoryPersistence.getYangModelCmHandles([expectedCmHandleId]) >> [
+                    new YangModelCmHandle(
+                            id: expectedCmHandleId,
+                            dmiProperties: [new YangModelCmHandle.Property('name', 'value')],
+                            publicProperties: []
+                    )
+            ]
+        when: 'the query is executed'
+            def result = objectUnderTestWithPartiallyMockedQueries.queryInventoryForCmHandles(queryParams).collectList().block()
+        then: 'the result contains the correct cm handle id'
+            assert result.size() == 1
+            assert result[0].cmHandleId == 'cm-handle from query service'
+    }
+
     def createConditionProperties(String conditionName, List<Map<String, String>> conditionParameters) {
         return new ConditionProperties(conditionName : conditionName, conditionParameters : conditionParameters)
     }
index 26541de..0ca6656 100644 (file)
@@ -26,11 +26,12 @@ import spock.lang.Specification
 class CmHandleQueryConditionsSpec extends Specification {
 
     def 'CmHandle query condition names.'() {
-        expect: '3 conditions with the correct names'
-            assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 4
+        expect: '5 conditions with the correct names'
+            assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 5
             assert CmHandleQueryConditions.ALL_CONDITION_NAMES.containsAll('hasAllProperties',
                                                                            'hasAllModules',
                                                                            'cmHandleWithCpsPath',
+                                                                           'cmHandleWithDmiPlugin',
                                                                             'cmHandleWithTrustLevel')
     }
 
index 8c2e472..2e4100f 100644 (file)
             <plugin>
                 <groupId>org.openapitools</groupId>
                 <artifactId>openapi-generator-maven-plugin</artifactId>
-                <version>6.6.0</version>
+                <version>7.12.0</version>
                 <executions>
                     <execution>
                         <id>code-gen</id>
index 2391fb2..42bfe15 100644 (file)
@@ -56,7 +56,8 @@ services:
   cps-and-ncmp-template:
     image: ${DOCKER_REPO:-nexus3.onap.org:10003}/onap/cps-and-ncmp:${CPS_VERSION:-latest}
     ### DEBUG: Uncomment next line to enable java debugging (ensure 'ports' aligns with 'deploy')
-    ### - ${CPS_CORE_DEBUG_PORT:-5005}:5005-
+    ### ports:
+    ### - ${CPS_CORE_DEBUG_PORT:-5005}:5005
     environment:
       DB_HOST: ${DB_HOST:-dbpostgresql}
       DB_USERNAME: ${DB_USERNAME:-cps}
index 62ac66a..7192ab6 100644 (file)
@@ -1141,7 +1141,6 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSample'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -1239,14 +1238,12 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSample'
-                  value: null
               schema:
                 type: object
             application/xml:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSampleXml'
-                  value: null
               schema:
                 type: object
                 xml:
@@ -1345,14 +1342,12 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSampleForV3'
-                  value: null
               schema:
                 type: object
             application/xml:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSampleXmlForV3'
-                  value: null
               schema:
                 type: object
                 xml:
@@ -1552,14 +1547,12 @@ paths:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSample'
-                value: null
             schema:
               type: string
           application/xml:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSampleXml'
-                value: null
             schema:
               type: object
               xml:
@@ -1680,14 +1673,12 @@ paths:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSample'
-                value: null
             schema:
               type: string
           application/xml:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSampleXml'
-                value: null
             schema:
               type: object
               xml:
@@ -1817,14 +1808,12 @@ paths:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSample'
-                value: null
             schema:
               type: string
           application/xml:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSampleXml'
-                value: null
             schema:
               type: object
               xml:
@@ -2020,14 +2009,12 @@ paths:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSample'
-                value: null
             schema:
               type: string
           application/xml:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSampleXml'
-                value: null
             schema:
               type: object
               xml:
@@ -2145,14 +2132,12 @@ paths:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSample'
-                value: null
             schema:
               type: string
           application/xml:
             examples:
               dataSample:
                 $ref: '#/components/examples/dataSampleXml'
-                value: null
             schema:
               type: object
               xml:
@@ -2256,7 +2241,6 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/deltaReportSample'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -2336,7 +2320,6 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/deltaReportSample'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -2430,7 +2413,6 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSample'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -2526,14 +2508,12 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSample'
-                  value: null
               schema:
                 type: object
             application/xml:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSampleXml'
-                  value: null
               schema:
                 type: object
                 xml:
@@ -2631,7 +2611,6 @@ paths:
               examples:
                 dataSample:
                   $ref: '#/components/examples/dataSampleAcrossAnchors'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -2821,7 +2800,6 @@ paths:
             examples:
               dataSample:
                 $ref: '#/components/examples/NotificationSubscriptionsDataSample'
-                value: null
             schema:
               type: object
         required: true
@@ -3260,6 +3238,10 @@ components:
       description: Unauthorized
   schemas:
     ErrorMessage:
+      example:
+        details: details
+        message: message
+        status: status
       properties:
         status:
           type: string
index c1ca0ae..df47475 100644 (file)
@@ -188,6 +188,74 @@ paths:
       summary: Query for CM Handle IDs
       tags:
       - network-cm-proxy-inventory
+  /v1/ch/searchCmHandles:
+    post:
+      description: Execute cm handle query search and return a list of cm handle details.
+        Any number of conditions can be applied. To be included in the result a cm-handle
+        must fulfill ALL the conditions. An empty collection will be returned in the
+        case that the cm handle does not match a condition. For more on cm handle
+        query search please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/ncmp-cmhandle-querying.html">cm
+        handle query search Read the Docs</a>.<br/>By supplying a CPS Path it is possible
+        to query on any data related to the cm handle. For more on CPS Path please
+        refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html">CPS
+        Path Read the Docs</a>. The cm handle ancestor is automatically returned for
+        this query.
+      operationId: searchCmHandles
+      parameters:
+      - description: Whether to include private properties in the response.
+        in: query
+        name: includePrivatePropertiesInQuery
+        required: false
+        schema:
+          type: boolean
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/CmHandleQueryParameters'
+        required: true
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                items:
+                  $ref: '#/components/schemas/RestOutputCmHandle'
+                type: array
+          description: OK
+        "400":
+          content:
+            application/json:
+              example:
+                status: 400
+                message: Bad request error message
+                details: Bad request error details
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Bad Request
+        "403":
+          content:
+            application/json:
+              example:
+                status: 403
+                message: Forbidden error message
+                details: Forbidden error details
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Forbidden
+        "500":
+          content:
+            application/json:
+              example:
+                status: 500
+                message: Internal Server Error
+                details: Internal Server Error occurred
+              schema:
+                $ref: '#/components/schemas/ErrorMessage'
+          description: Internal Server Error
+      summary: Query Cm Handles for a requested DMI Service
+      tags:
+      - network-cm-proxy-inventory
 components:
   parameters:
     dmiPluginIdentifierInQuery:
@@ -370,11 +438,6 @@ components:
       required:
       - cmHandle
       type: object
-    RestCmHandleProperties:
-      additionalProperties:
-        example: my-property
-        type: string
-      type: object
     UpgradedCmHandles:
       example:
         cmHandles:
@@ -399,6 +462,10 @@ components:
       - cmHandles
       type: object
     ErrorMessage:
+      example:
+        details: details
+        message: message
+        status: status
       properties:
         status:
           type: string
@@ -409,6 +476,35 @@ components:
       title: Error
       type: object
     DmiPluginRegistrationErrorResponse:
+      example:
+        failedCreatedCmHandles:
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        failedUpgradeCmHandles:
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        failedRemovedCmHandles:
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        failedUpdatedCmHandles:
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
+        - cmHandle: my-cm-handle
+          errorText: Unknown error. <error-details>
+          errorCode: "00"
       properties:
         failedCreatedCmHandles:
           items:
@@ -428,6 +524,10 @@ components:
           type: array
       type: object
     CmHandlerRegistrationErrorResponse:
+      example:
+        cmHandle: my-cm-handle
+        errorText: Unknown error. <error-details>
+        errorCode: "00"
       properties:
         cmHandle:
           example: my-cm-handle
@@ -508,3 +608,134 @@ components:
         moduleName:
           example: my-module
           type: string
+    RestOutputCmHandle:
+      example:
+        cmHandle: my-cm-handle1
+        alternateId: "Subnetwork=Europe,ManagedElement=X123"
+        dataProducerIdentifier: my-data-producer-identifier
+        publicCmHandleProperties:
+        - key: 3gpp Type
+        - key: 3gpp Type
+        state:
+          dataSyncEnabled: false
+          dataSyncState:
+            running:
+              lastSyncTime: 2022-12-31T20:30:40.000+0000
+              syncState: NONE_REQUESTED
+            operational:
+              lastSyncTime: 2022-12-31T20:30:40.000+0000
+              syncState: NONE_REQUESTED
+          cmHandleState: ADVISED
+          lockReason:
+            reason: LOCKED_MISBEHAVING
+            details: locked due to failure in module sync
+          lastUpdateTime: 2022-12-31T20:30:40.000+0000
+        trustLevel: COMPLETE
+        moduleSetTag: my-module-set-tag
+        privateCmHandleProperties:
+          key: 3gpp Type
+      properties:
+        cmHandle:
+          example: my-cm-handle1
+          type: string
+        publicCmHandleProperties:
+          items:
+            additionalProperties:
+              example: 3gpp Type
+              type: string
+            type: object
+          type: array
+        privateCmHandleProperties:
+          additionalProperties:
+            example: 3gpp Type
+            type: string
+          type: object
+        state:
+          $ref: '#/components/schemas/CmHandleCompositeState'
+        trustLevel:
+          description: Current trust level of the relevant CM handle ID.
+          example: COMPLETE
+          type: string
+        moduleSetTag:
+          example: my-module-set-tag
+          type: string
+        alternateId:
+          example: "Subnetwork=Europe,ManagedElement=X123"
+          type: string
+        dataProducerIdentifier:
+          example: my-data-producer-identifier
+          type: string
+      title: CM handle Details
+      type: object
+    CmHandleCompositeState:
+      example:
+        dataSyncEnabled: false
+        dataSyncState:
+          running:
+            lastSyncTime: 2022-12-31T20:30:40.000+0000
+            syncState: NONE_REQUESTED
+          operational:
+            lastSyncTime: 2022-12-31T20:30:40.000+0000
+            syncState: NONE_REQUESTED
+        cmHandleState: ADVISED
+        lockReason:
+          reason: LOCKED_MISBEHAVING
+          details: locked due to failure in module sync
+        lastUpdateTime: 2022-12-31T20:30:40.000+0000
+      properties:
+        cmHandleState:
+          example: ADVISED
+          type: string
+        lockReason:
+          $ref: '#/components/schemas/lock-reason'
+        lastUpdateTime:
+          example: 2022-12-31T20:30:40.000+0000
+          type: string
+        dataSyncEnabled:
+          example: false
+          type: boolean
+        dataSyncState:
+          $ref: '#/components/schemas/dataStores'
+      type: object
+    lock-reason:
+      example:
+        reason: LOCKED_MISBEHAVING
+        details: locked due to failure in module sync
+      properties:
+        reason:
+          example: LOCKED_MISBEHAVING
+          type: string
+        details:
+          example: locked due to failure in module sync
+          type: string
+      type: object
+    dataStores:
+      example:
+        running:
+          lastSyncTime: 2022-12-31T20:30:40.000+0000
+          syncState: NONE_REQUESTED
+        operational:
+          lastSyncTime: 2022-12-31T20:30:40.000+0000
+          syncState: NONE_REQUESTED
+      properties:
+        operational:
+          $ref: '#/components/schemas/sync-state'
+        running:
+          $ref: '#/components/schemas/sync-state'
+      type: object
+    sync-state:
+      example:
+        lastSyncTime: 2022-12-31T20:30:40.000+0000
+        syncState: NONE_REQUESTED
+      properties:
+        syncState:
+          example: NONE_REQUESTED
+          type: string
+        lastSyncTime:
+          example: 2022-12-31T20:30:40.000+0000
+          type: string
+      type: object
+    CmHandleTrustLevel:
+      description: Current trust level of the relevant CM handle ID.
+      example: COMPLETE
+      type: string
index aa72e4b..4c62602 100644 (file)
@@ -207,7 +207,6 @@ paths:
               examples:
                 dataSampleResponse:
                   $ref: '#/components/examples/dataSampleResponse'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -310,7 +309,6 @@ paths:
             examples:
               dataSampleRequest:
                 $ref: '#/components/examples/dataSamplePatchRequest'
-                value: null
             schema:
               type: object
         required: true
@@ -429,14 +427,12 @@ paths:
             examples:
               dataSampleRequest:
                 $ref: '#/components/examples/dataSampleRequest'
-                value: null
             schema:
               type: object
           application/yang-data+json:
             examples:
               dataSampleRequest:
                 $ref: '#/components/examples/dataSampleRequest'
-                value: null
             schema:
               type: object
         required: true
@@ -553,14 +549,12 @@ paths:
             examples:
               dataSampleRequest:
                 $ref: '#/components/examples/dataSampleRequest'
-                value: null
             schema:
               type: object
           application/yang-data+json:
             examples:
               dataSampleRequest:
                 $ref: '#/components/examples/dataSampleRequest'
-                value: null
             schema:
               type: object
         required: true
@@ -794,7 +788,6 @@ paths:
               examples:
                 dataSampleResponse:
                   $ref: '#/components/examples/dataSampleResponse'
-                  value: null
               schema:
                 type: object
           description: OK
@@ -980,19 +973,14 @@ paths:
             examples:
               Cm handle properties query:
                 $ref: '#/components/examples/pubPropCmHandleQueryParameters'
-                value: null
               Cm handle modules query:
                 $ref: '#/components/examples/modulesCmHandleQueryParameters'
-                value: null
               All cm handle query parameters:
                 $ref: '#/components/examples/allCmHandleQueryParameters'
-                value: null
               Cm handle with CPS path state query:
                 $ref: '#/components/examples/cpsPathCmHandleStateQueryParameters'
-                value: null
               Cm handle with data sync flag query:
                 $ref: '#/components/examples/cpsPathCmHandleDataSyncQueryParameters'
-                value: null
             schema:
               $ref: '#/components/schemas/CmHandleQueryParameters'
         required: true
@@ -1111,7 +1099,7 @@ paths:
           content:
             application/json:
               schema:
-                $ref: '#/components/schemas/RestOutputCmHandlePublicProperties'
+                $ref: '#/components/schemas/RestOutputPublicCmHandleProperties'
           description: OK
         "400":
           content:
@@ -1174,19 +1162,14 @@ paths:
             examples:
               Cm handle properties query:
                 $ref: '#/components/examples/pubPropCmHandleQueryParameters'
-                value: null
               Cm handle modules query:
                 $ref: '#/components/examples/modulesCmHandleQueryParameters'
-                value: null
               All cm handle query parameters:
                 $ref: '#/components/examples/allCmHandleQueryParameters'
-                value: null
               Cm handle with CPS path state query:
                 $ref: '#/components/examples/cpsPathCmHandleStateQueryParameters'
-                value: null
               Cm handle with data sync flag query:
                 $ref: '#/components/examples/cpsPathCmHandleDataSyncQueryParameters'
-                value: null
             schema:
               $ref: '#/components/schemas/CmHandleQueryParameters'
         required: true
@@ -1737,6 +1720,10 @@ components:
       description: The request is larger than the server is willing or able to process
   schemas:
     ErrorMessage:
+      example:
+        details: details
+        message: message
+        status: status
       properties:
         status:
           type: string
@@ -1747,6 +1734,11 @@ components:
       title: Error
       type: object
     DmiErrorMessage:
+      example:
+        message: Bad Gateway Error Message NCMP
+        dmi-response:
+          body: Bad Request
+          http-code: 400
       properties:
         message:
           example: Bad Gateway Error Message NCMP
@@ -1961,6 +1953,8 @@ components:
           lastUpdateTime: 2022-12-31T20:30:40.000+0000
         trustLevel: COMPLETE
         moduleSetTag: my-module-set-tag
+        privateCmHandleProperties:
+          key: 3gpp Type
       properties:
         cmHandle:
           example: my-cm-handle1
@@ -1972,6 +1966,11 @@ components:
               type: string
             type: object
           type: array
+        privateCmHandleProperties:
+          additionalProperties:
+            example: 3gpp Type
+            type: string
+          type: object
         state:
           $ref: '#/components/schemas/CmHandleCompositeState'
         trustLevel:
@@ -1989,13 +1988,6 @@ components:
           type: string
       title: CM handle Details
       type: object
-    CmHandlePublicProperties:
-      items:
-        additionalProperties:
-          example: 3gpp Type
-          type: string
-        type: object
-      type: array
     CmHandleCompositeState:
       example:
         dataSyncEnabled: false
@@ -2068,7 +2060,7 @@ components:
       description: Current trust level of the relevant CM handle ID.
       example: COMPLETE
       type: string
-    RestOutputCmHandlePublicProperties:
+    RestOutputPublicCmHandleProperties:
       example:
         publicCmHandleProperties:
         - key: 3gpp Type
@@ -2103,6 +2095,9 @@ components:
           $ref: '#/components/schemas/CmHandleCompositeState'
       type: object
     DmiErrorMessage_dmi_response:
+      example:
+        body: Bad Request
+        http-code: 400
       properties:
         http-code:
           example: 400
index 40c684b..af582f7 100644 (file)
             <plugin>
                 <groupId>org.openapitools</groupId>
                 <artifactId>openapi-generator-maven-plugin</artifactId>
-                <version>6.6.0</version>
+                <version>7.12.0</version>
                 <executions>
                     <execution>
                         <id>code-gen</id>