Merge "PoC Contract Stubs NCMP Rest Endpoints"
authorToine Siebelink <toine.siebelink@est.tech>
Thu, 9 Jun 2022 16:51:03 +0000 (16:51 +0000)
committerGerrit Code Review <gerrit@onap.org>
Thu, 9 Jun 2022 16:51:03 +0000 (16:51 +0000)
64 files changed:
cps-application/src/main/java/org/onap/cps/Application.java
cps-application/src/main/resources/application.yml
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/docs/openapi/openapi.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/executor/CpsNcmpTaskExecutor.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.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/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandlerQueryService.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/NcmpEventsPublisher.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleQueryApiParameters.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ConditionApiProperties.java [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/async/CpsAsyncRequestResponseEventIntegrationSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/NcmpEventsPublisherSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/DmiServiceUrlBuilderSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/MessagingSpec.groovy [new file with mode: 0644]
cps-ncmp-service/src/test/resources/expectedNcmpEvent.json [new file with mode: 0644]
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java
cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
cps-ri/src/test/resources/data/cps-path-query.sql
cps-ri/src/test/resources/data/fragment.sql
cps-service/src/main/java/org/onap/cps/api/CpsAdminService.java
cps-service/src/main/java/org/onap/cps/api/CpsDataService.java
cps-service/src/main/java/org/onap/cps/api/impl/CpsAdminServiceImpl.java
cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
cps-service/src/main/java/org/onap/cps/spi/model/CmHandleQueryParameters.java
cps-service/src/main/java/org/onap/cps/spi/model/ConditionProperties.java [new file with mode: 0644]
cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java
cps-service/src/main/java/org/onap/cps/spi/model/DataNodeBuilder.java
cps-service/src/main/java/org/onap/cps/spi/model/DataNodeIdentifier.java [new file with mode: 0644]
cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java [new file with mode: 0644]
cps-service/src/main/java/org/onap/cps/utils/DataMapUtils.java
cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAdminServiceImplSpec.groovy
cps-service/src/test/groovy/org/onap/cps/spi/model/DataNodeBuilderSpec.groovy
cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy [new file with mode: 0644]
cps-service/src/test/groovy/org/onap/cps/utils/DataMapUtilsSpec.groovy
csit/plans/cps/testplan.txt
csit/tests/cm-handle-query/cm-handle-query.robot [moved from csit/tests/public-properties-query/public-properties-query.robot with 61% similarity]
csit/tests/cps-data/cps-data.robot
docker-compose/docker-compose.yml

index ba707e9..79d5950 100644 (file)
@@ -22,7 +22,9 @@ package org.onap.cps;
 \r
 import org.springframework.boot.SpringApplication;\r
 import org.springframework.boot.autoconfigure.SpringBootApplication;\r
+import org.springframework.retry.annotation.EnableRetry;\r
 \r
+@EnableRetry\r
 @SpringBootApplication\r
 public class Application {\r
 \r
index af886a1..3f005c9 100644 (file)
@@ -86,7 +86,8 @@ app:
     ncmp:\r
         async-m2m:\r
             topic: ${NCMP_ASYNC_M2M_TOPIC:ncmp-async-m2m}\r
-\r
+        events:\r
+            topic: ${NCMP_EVENTS_TOPIC:ncmp-events}\r
 notification:\r
     data-updated:\r
         enabled: false\r
index 5fe47e4..248b1da 100644 (file)
@@ -133,52 +133,7 @@ components:
             type: string
             example: my-property
 
-    Conditions:
-      type: object
-      properties:
-        conditions:
-          $ref: '#/components/schemas/ConditionsData'
-    ConditionsData:
-      type: array
-      items:
-        type: object
-        $ref: '#/components/schemas/ConditionProperties'
-    ConditionProperties:
-      properties:
-        name:
-          type: string
-          example: hasAllModules
-        conditionParameters:
-          $ref: '#/components/schemas/ModuleNamesAsJsonArray'
-    ModuleNamesAsJsonArray:
-      type: array
-      items:
-        type: object
-        $ref: '#/components/schemas/ModuleNameAsJsonObject'
-        example: [my-module-1, my-module-2, my-module-3]
-    ModuleNameAsJsonObject:
-        properties:
-          moduleName:
-            type: string
-            example: my-module
-
     #Response Schemas
-    CmHandles:
-      type: object
-      properties:
-        cmHandles:
-          $ref: '#/components/schemas/CmHandleProperties'
-    CmHandleProperties:
-      type: array
-      items:
-        type: object
-        $ref: '#/components/schemas/CmHandleProperty'
-    CmHandleProperty:
-      properties:
-        cmHandleId:
-          type: string
-          example: my-cm-handle-id
-
     RestModuleReference:
       type: object
       title: Module reference details
@@ -190,15 +145,59 @@ components:
           type: string
           example: my-module-revision
 
-    CmHandleQueryRestParameters:
+    CmHandleQueryParameters:
       type: object
       title: Cm Handle query parameters for executing cm handle search
       properties:
-        publicCmHandleProperties:
-          type: object
-          additionalProperties:
-            type: string
-            example: Book Type
+        cmHandleQueryParameters:
+          type: array
+          items:
+            type: object
+            $ref: '#/components/schemas/ConditionProperties'
+        conditions:
+          deprecated: true
+          type: array
+          items:
+            type: object
+            $ref: '#/components/schemas/OldConditionProperties'
+          description: not necessary, it is just for backward compatibility
+      example:
+        cmHandleQueryParameters:
+          - conditionName: hasAllModules
+            conditionParameters:
+              - { "moduleName": "my-module-1" }
+              - { "moduleName": "my-module-2" }
+              - { "moduleName": "my-module-3" }
+          - conditionName: hasAllProperties
+            conditionParameters:
+              - { "Color": "yellow" }
+              - { "Shape": "circle" }
+              - { "Size": "small" }
+    ConditionProperties:
+      properties:
+        conditionName:
+          type: string
+        conditionParameters:
+          type: array
+          items:
+            type: object
+            additionalProperties:
+              type: string
+    OldConditionProperties:
+      deprecated: true
+      properties:
+        name:
+          type: string
+        conditionParameters:
+          type: array
+          items:
+            type: object
+            $ref: '#/components/schemas/ModuleNameAsJsonObject'
+    ModuleNameAsJsonObject:
+      properties:
+        moduleName:
+          type: string
+          example: my-module
 
     RestOutputCmHandle:
       type: object
index 318e6e6..3259032 100755 (executable)
@@ -246,26 +246,28 @@ fetchModuleReferencesByCmHandle:
       500:
         $ref: 'components.yaml#/components/responses/InternalServerError'
 
-executeCmHandleSearch:
+searchCmHandles:
   post:
-    description: Execute cm handle searches using 'hasAllModules' condition to get all cm handles for the given module names
+    description: Execute cm handle query search, to be included in the result a cm-handle must fulfill ALL the conditions listed here, if one of the given module names does not exists, return with an empty collection.
     tags:
       - network-cm-proxy
     summary: Execute cm handle search using the available conditions
-    operationId: executeCmHandleSearch
+    operationId: searchCmHandles
     requestBody:
       required: true
       content:
         application/json:
           schema:
-            $ref: 'components.yaml#/components/schemas/Conditions'
+            $ref: 'components.yaml#/components/schemas/CmHandleQueryParameters'
     responses:
       200:
         description: OK
         content:
           application/json:
             schema:
-              $ref: 'components.yaml#/components/schemas/CmHandles'
+              type: array
+              items:
+                $ref: 'components.yaml#/components/schemas/RestOutputCmHandle'
       400:
         $ref: 'components.yaml#/components/responses/BadRequest'
       401:
@@ -317,19 +319,19 @@ getCmHandlePropertiesById:
       500:
         $ref: 'components.yaml#/components/responses/InternalServerError'
 
-queryCmHandles:
+searchCmHandleIds:
   post:
-    description: Execute cm handle query search
+    description: Execute cm handle query search, to be included in the result a cm-handle must fulfill ALL the conditions listed here, if one of the given module names does not exists, return with an empty collection.
     tags:
       - network-cm-proxy
     summary: Execute cm handle query upon a given set of query parameters
-    operationId: queryCmHandles
+    operationId: searchCmHandleIds
     requestBody:
       required: true
       content:
         application/json:
           schema:
-            $ref: 'components.yaml#/components/schemas/CmHandleQueryRestParameters'
+            $ref: 'components.yaml#/components/schemas/CmHandleQueryParameters'
     responses:
       200:
         description: OK
index b408291..81ebf05 100755 (executable)
@@ -36,7 +36,7 @@ paths:
     $ref: 'ncmp.yml#/fetchModuleReferencesByCmHandle'
 
   /v1/ch/searches:
-    $ref: 'ncmp.yml#/executeCmHandleSearch'
+    $ref: 'ncmp.yml#/searchCmHandles'
 
   /v1/ch/{cm-handle}:
     $ref: 'ncmp.yml#/retrieveCmHandleDetailsById'
@@ -44,5 +44,5 @@ paths:
   /v1/ch/{cm-handle}/properties:
     $ref: 'ncmp.yml#/getCmHandlePropertiesById'
 
-  /v1/data/ch/searches:
-    $ref: 'ncmp.yml#/queryCmHandles'
+  /v1/ch/id-searches:
+    $ref: 'ncmp.yml#/searchCmHandleIds'
index 11517bc..ccb1e9b 100755 (executable)
@@ -28,9 +28,6 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH;
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -48,18 +45,12 @@ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
 import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper;
-import org.onap.cps.ncmp.rest.model.CmHandleProperties;
-import org.onap.cps.ncmp.rest.model.CmHandleProperty;
 import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
-import org.onap.cps.ncmp.rest.model.CmHandleQueryRestParameters;
-import org.onap.cps.ncmp.rest.model.CmHandles;
-import org.onap.cps.ncmp.rest.model.ConditionProperties;
-import org.onap.cps.ncmp.rest.model.Conditions;
-import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject;
-import org.onap.cps.ncmp.rest.model.ModuleNamesAsJsonArray;
+import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
 import org.onap.cps.ncmp.rest.model.RestModuleReference;
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
 import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
+import org.onap.cps.ncmp.rest.util.DeprecationHelper;
 import org.onap.cps.utils.CpsValidator;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.beans.factory.annotation.Value;
@@ -79,6 +70,8 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     private static final String NO_TOPIC = null;
     private final NetworkCmProxyDataService networkCmProxyDataService;
     private final JsonObjectMapper jsonObjectMapper;
+
+    private final DeprecationHelper deprecationHelper;
     private final NcmpRestInputMapper ncmpRestInputMapper;
     private final RestOutputCmHandleStateMapper restOutputCmHandleStateMapper;
     private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
@@ -212,30 +205,35 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     }
 
     /**
-     * Execute cm handle search.
+     * Query and return cm handles that match the given query parameters.
      *
-     * @param conditions the conditions
-     * @return cm handles returned from search.
+     * @param cmHandleQueryParameters the cm handle query parameters
+     * @return collection of cm handles
      */
     @Override
-    public ResponseEntity<CmHandles> executeCmHandleSearch(final Conditions conditions) {
-        final List<ConditionProperties> conditionProperties =
-            conditions.getConditions().stream().collect(Collectors.toList());
-        final CmHandles cmHandles = new CmHandles();
-        cmHandles.setCmHandles(toCmHandleProperties(processConditions(conditionProperties)));
-        return ResponseEntity.ok(cmHandles);
+    public ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
+            final CmHandleQueryParameters cmHandleQueryParameters) {
+        final CmHandleQueryApiParameters cmHandleQueryApiParameters =
+                deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters);
+        final Set<NcmpServiceCmHandle> cmHandles = networkCmProxyDataService
+                .executeCmHandleSearch(cmHandleQueryApiParameters);
+        final List<RestOutputCmHandle> outputCmHandles =
+                cmHandles.stream().map(this::toRestOutputCmHandle).collect(Collectors.toList());
+        return ResponseEntity.ok(outputCmHandles);
     }
 
     /**
-     * Query and return cm handles that match the given query parameters.
+     * Query and return cm handle ids that match the given query parameters.
      *
-     * @param cmHandleQueryRestParameters the cm handle query parameters
+     * @param cmHandleQueryParameters the cm handle query parameters
      * @return collection of cm handle ids
      */
-    public ResponseEntity<List<String>> queryCmHandles(
-        final CmHandleQueryRestParameters cmHandleQueryRestParameters) {
-        final Set<String> cmHandleIds = networkCmProxyDataService.queryCmHandles(
-            jsonObjectMapper.convertToValueType(cmHandleQueryRestParameters, CmHandleQueryApiParameters.class));
+    @Override
+    public ResponseEntity<List<String>> searchCmHandleIds(
+        final CmHandleQueryParameters cmHandleQueryParameters) {
+        final CmHandleQueryApiParameters cmHandleQueryApiParameters =
+                jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class);
+        final Set<String> cmHandleIds = networkCmProxyDataService.executeCmHandleIdSearch(cmHandleQueryApiParameters);
         return ResponseEntity.ok(List.copyOf(cmHandleIds));
     }
 
@@ -281,41 +279,6 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         return new ResponseEntity<>(restModuleReferences, HttpStatus.OK);
     }
 
-    private Collection<String> processConditions(final List<ConditionProperties> conditionProperties) {
-        for (final ConditionProperties conditionProperty : conditionProperties) {
-            if (conditionProperty.getName().equals("hasAllModules")) {
-                return executeCmHandleSearchesForModuleNames(conditionProperty);
-            } else {
-                log.warn("Unrecognized condition name {}.", conditionProperty.getName());
-            }
-        }
-        log.warn("No valid conditions found {}.", conditionProperties);
-        return Collections.emptyList();
-    }
-
-    private Collection<String> executeCmHandleSearchesForModuleNames(final ConditionProperties conditionProperties) {
-        return networkCmProxyDataService
-            .executeCmHandleHasAllModulesSearch(getModuleNames(conditionProperties.getConditionParameters()));
-    }
-
-    private Collection<String> getModuleNames(final ModuleNamesAsJsonArray moduleNamesAsJsonArray) {
-        final Collection<String> moduleNames = new ArrayList<>(moduleNamesAsJsonArray.size());
-        for (final ModuleNameAsJsonObject moduleNameAsJsonObject : moduleNamesAsJsonArray) {
-            moduleNames.add(moduleNameAsJsonObject.getModuleName());
-        }
-        return moduleNames;
-    }
-
-    private CmHandleProperties toCmHandleProperties(final Collection<String> cmHandleIdentifiers) {
-        final CmHandleProperties cmHandleProperties = new CmHandleProperties();
-        for (final String cmHandleIdentifier : cmHandleIdentifiers) {
-            final CmHandleProperty cmHandleProperty = new CmHandleProperty();
-            cmHandleProperty.setCmHandleId(cmHandleIdentifier);
-            cmHandleProperties.add(cmHandleProperty);
-        }
-        return cmHandleProperties;
-    }
-
     private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) {
         final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle();
         final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
index 93aa285..5adbb25 100644 (file)
@@ -40,11 +40,7 @@ public class CpsNcmpTaskExecutor {
     public void executeTask(final Supplier<Object> taskSupplier, final int timeOutInMillis) {
         CompletableFuture.supplyAsync(taskSupplier::get)
             .orTimeout(timeOutInMillis, MILLISECONDS)
-            .whenCompleteAsync(
-                (responseAsJson, throwable) -> {
-                    handleTaskCompletion(throwable);
-                }
-            );
+            .whenCompleteAsync((responseAsJson, throwable) -> handleTaskCompletion(throwable));
     }
 
     private void handleTaskCompletion(final Throwable throwable) {
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/DeprecationHelper.java
new file mode 100644 (file)
index 0000000..fc992da
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
+import org.onap.cps.ncmp.api.models.ConditionApiProperties;
+import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class DeprecationHelper {
+
+    private final JsonObjectMapper jsonObjectMapper;
+
+    /**
+     * Convert the old condition properties to the new schema.
+     * !!! remove it after the old condition removed !!!
+     * it only works for module names
+     *
+     * @param cmHandleQueryParameters the original input parameter
+     */
+    @Deprecated //this method wil be removed in Release 12 (No Name know yet)
+    public CmHandleQueryApiParameters mapOldConditionProperties(
+                                           final CmHandleQueryParameters cmHandleQueryParameters) {
+        final CmHandleQueryApiParameters cmHandleQueryApiParameters =
+                jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class);
+        if (cmHandleQueryParameters.getConditions() != null
+                && cmHandleQueryApiParameters.getCmHandleQueryParameters().isEmpty()) {
+            cmHandleQueryApiParameters.setCmHandleQueryParameters(new ArrayList<>());
+            cmHandleQueryParameters.getConditions().parallelStream().forEach(
+                oldConditionProperty -> {
+                    if (oldConditionProperty.getConditionParameters() != null
+                            && oldConditionProperty.getName() != null) {
+                        final ConditionApiProperties conditionApiProperties = new ConditionApiProperties();
+                        conditionApiProperties.setConditionName(oldConditionProperty.getName());
+                        conditionApiProperties.setConditionParameters(new ArrayList<>());
+                        oldConditionProperty.getConditionParameters().parallelStream().forEach(
+                            oldConditionParameter ->
+                                conditionApiProperties.getConditionParameters().add(Collections
+                                    .singletonMap("moduleName", oldConditionParameter.getModuleName()))
+                        );
+                        cmHandleQueryApiParameters.getCmHandleQueryParameters().add(conditionApiProperties);
+                    }
+                }
+            );
+        }
+
+        return cmHandleQueryApiParameters;
+    }
+}
index 60ea736..036928f 100644 (file)
@@ -29,6 +29,7 @@ import org.onap.cps.ncmp.api.inventory.CompositeState
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
+import org.onap.cps.ncmp.rest.util.DeprecationHelper
 import spock.lang.Shared
 
 import java.time.OffsetDateTime
@@ -86,6 +87,9 @@ class NetworkCmProxyControllerSpec extends Specification {
     @SpringBean
     CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy()
 
+    @SpringBean
+    DeprecationHelper stubbedDeprecationHelper = Stub()
+
     @Value('${rest.api.ncmp-base-path}/v1')
     def ncmpBasePathV1
 
@@ -239,8 +243,14 @@ class NetworkCmProxyControllerSpec extends Specification {
         given: 'an endpoint and json data'
             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 handle ids'
-            mockNetworkCmProxyDataService.executeCmHandleHasAllModulesSearch(['module1', 'module2']) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
+        and: 'the service method is invoked with module names and returns two cm handles'
+            def cmHandle1 = new NcmpServiceCmHandle()
+            cmHandle1.cmHandleId = 'some-cmhandle-id1'
+            cmHandle1.publicProperties = [color:'yellow']
+            def cmHandle2 = new NcmpServiceCmHandle()
+            cmHandle2.cmHandleId = 'some-cmhandle-id2'
+            cmHandle2.publicProperties = [color:'green']
+            mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2]
         when: 'the searches api is invoked'
             def response = mvc.perform(post(searchesEndpoint)
                     .contentType(MediaType.APPLICATION_JSON)
@@ -248,7 +258,7 @@ class NetworkCmProxyControllerSpec extends Specification {
         then: 'response status returns OK'
             response.status == HttpStatus.OK.value()
         and: 'the expected response content is returned'
-            response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}'
+            response.contentAsString == '[{"cmHandle":"some-cmhandle-id1","publicCmHandleProperties":[{"color":"yellow"}],"state":null},{"cmHandle":"some-cmhandle-id2","publicCmHandleProperties":[{"color":"green"}],"state":null}]'
     }
 
     def 'Get Cm Handle details by Cm Handle id.'() {
@@ -290,31 +300,38 @@ class NetworkCmProxyControllerSpec extends Specification {
         given: 'an endpoint and json data'
             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 cmHandel1 = new NcmpServiceCmHandle()
+            cmHandel1.cmHandleId = 'some-cmhandle-id1'
+            cmHandel1.publicProperties = [color:'yellow']
+            def cmHandel2 = new NcmpServiceCmHandle()
+            cmHandel2.cmHandleId = 'some-cmhandle-id2'
+            cmHandel2.publicProperties = [color:'green']
+            mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2]
         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'
-            response.contentAsString == '{"cmHandles":[]}'
+            response.contentAsString == '[{"cmHandle":"some-cmhandle-id1","publicCmHandleProperties":[{"color":"yellow"}],"state":null},{"cmHandle":"some-cmhandle-id2","publicCmHandleProperties":[{"color":"green"}],"state":null}]'
     }
 
     def 'Query for cm handles matching query parameters'() {
         given: 'an endpoint and json data'
-            def searchesEndpoint = "$ncmpBasePathV1/data/ch/searches"
-            String jsonString = '{"publicCmHandleProperties": {"name": "Contact", "value": "newemailforstore@bookstore.com"}}'
+            def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches"
         and: 'the service method is invoked with module names and returns cm handle ids'
-            1 * mockNetworkCmProxyDataService.queryCmHandles(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
+            1 * mockNetworkCmProxyDataService.executeCmHandleIdSearch(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
         when: 'the searches api is invoked'
             def response = mvc.perform(post(searchesEndpoint)
                 .contentType(MediaType.APPLICATION_JSON)
-                .content(jsonString)).andReturn().response
+                .content('{}')).andReturn().response
         then: 'cm handle ids are returned'
             response.contentAsString == '["some-cmhandle-id1","some-cmhandle-id2"]'
     }
 
     def 'Query for cm handles with invalid request payload'() {
         when: 'the searches api is invoked'
-            def searchesEndpoint = "$ncmpBasePathV1/data/ch/searches"
+            def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches"
             def invalidInputData = '{invalidJson}'
             def response = mvc.perform(post(searchesEndpoint)
                     .contentType(MediaType.APPLICATION_JSON)
index 45ed3d3..1563c75 100644 (file)
@@ -31,6 +31,7 @@ import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
 import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper
 import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
 import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
+import org.onap.cps.ncmp.rest.util.DeprecationHelper
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.exceptions.DataValidationException
@@ -47,7 +48,6 @@ import spock.lang.Specification
 
 import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP
 import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY
-import static org.springframework.http.HttpStatus.BAD_GATEWAY
 import static org.springframework.http.HttpStatus.BAD_REQUEST
 import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
 import static org.springframework.http.HttpStatus.NOT_FOUND
@@ -75,6 +75,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
     @SpringBean
     CpsNcmpTaskExecutor stubbedCpsTaskExecutor = Stub()
 
+    @SpringBean
+    DeprecationHelper stubbedDeprecationHelper = Stub()
+
     @Value('${rest.api.ncmp-base-path}')
     def basePathNcmp
 
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/DeprecationHelperSpec.groovy
new file mode 100644 (file)
index 0000000..8c212d3
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.util
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
+import org.onap.cps.ncmp.api.models.ConditionApiProperties
+import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters
+import org.onap.cps.ncmp.rest.model.ConditionProperties
+import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject
+import org.onap.cps.ncmp.rest.model.OldConditionProperties
+import org.onap.cps.utils.JsonObjectMapper
+import spock.lang.Specification
+
+class DeprecationHelperSpec extends Specification {
+
+    DeprecationHelper deprecationHelper = new DeprecationHelper(new JsonObjectMapper(new ObjectMapper()))
+
+    def 'Map deprecated condition properties - #scenario.'() {
+        given: 'a deprecated condition properties'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            cmHandleQueryParameters.conditions = oldConditionPropertiesArray
+            cmHandleQueryParameters.cmHandleQueryParameters = cmHandleQueryParametersArray
+        when: 'converted into the new format'
+            def result = deprecationHelper.mapOldConditionProperties(cmHandleQueryParameters)
+        then: 'result is the expected'
+            assert result == new CmHandleQueryApiParameters(cmHandleQueryParameters: expectedCmHandleQueryApiParametersArray)
+        where:
+            scenario                           | oldConditionPropertiesArray                                                                                                   | cmHandleQueryParametersArray                                                                           || expectedCmHandleQueryApiParametersArray
+            'mapping old query'                | [new OldConditionProperties(name: 'hasAllModule', conditionParameters: [new ModuleNameAsJsonObject(moduleName: 'module-1')])] | []                                                                                                     || [new ConditionApiProperties(conditionName: 'hasAllModule', conditionParameters: [[moduleName:'module-1']])]
+            'old condition is null'            | null                                                                                                                          | []                                                                                                     || []
+            'old condition parameters is null' | [new OldConditionProperties(name: 'hasAllModule', conditionParameters: null)]                                                 | []                                                                                                     || []
+            'old condition name is null'       | [new OldConditionProperties(name: null, conditionParameters: [new ModuleNameAsJsonObject(moduleName: 'module-1')])]           | []                                                                                                     || []
+            'new query parameters are filled'  | [new OldConditionProperties(name: 'hasAllModule', conditionParameters: [new ModuleNameAsJsonObject(moduleName: 'module-1')])] | [new ConditionProperties(conditionName: 'hasAllModule', conditionParameters: [[moduleName:'module-2']])] || [new ConditionApiProperties(conditionName: 'hasAllModule', conditionParameters: [[moduleName:'module-2']])]
+    }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandlerQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandlerQueryService.java
new file mode 100644 (file)
index 0000000..f8d51fe
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api;
+
+import java.util.Collection;
+import org.onap.cps.spi.model.CmHandleQueryParameters;
+import org.onap.cps.spi.model.DataNode;
+
+public interface NetworkCmProxyCmHandlerQueryService {
+    /**
+     * Query and return cm handles that match the given query parameters.
+     *
+     * @param cmHandleQueryParameters the cm handle query parameters
+     * @return collection of cm handles
+     */
+    Collection<DataNode> queryCmHandles(CmHandleQueryParameters cmHandleQueryParameters);
+}
index 7527ae5..ce850cc 100644 (file)
@@ -105,15 +105,6 @@ public interface NetworkCmProxyDataService {
      */
     Collection<ModuleReference> getYangResourcesModuleReferences(String cmHandleId);
 
-    /**
-     * Query cm handle identifiers for the given collection of module names.
-     *
-     * @param moduleNames module names.
-     * @return a collection of cm handle identifiers. The schema set for each cm handle must include all the
-     *         given module names
-     */
-    Collection<String> executeCmHandleHasAllModulesSearch(Collection<String> moduleNames);
-
     /**
      * Query cm handle details by cm handle's name.
      *
@@ -134,7 +125,15 @@ public interface NetworkCmProxyDataService {
      * Query and return cm handles that match the given query parameters.
      *
      * @param cmHandleQueryApiParameters the cm handle query parameters
+     * @return collection of cm handles
+     */
+    Set<NcmpServiceCmHandle> executeCmHandleSearch(CmHandleQueryApiParameters cmHandleQueryApiParameters);
+
+    /**
+     * Query and return cm handle ids that match the given query parameters.
+     *
+     * @param cmHandleQueryApiParameters the cm handle query parameters
      * @return collection of cm handle ids
      */
-    Set<String> queryCmHandles(CmHandleQueryApiParameters cmHandleQueryApiParameters);
+    Set<String> executeCmHandleIdSearch(CmHandleQueryApiParameters cmHandleQueryApiParameters);
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceImpl.java
new file mode 100644 (file)
index 0000000..ef6e953
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl;
+
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DATASPACE_NAME;
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NCMP_DMI_REGISTRY_ANCHOR;
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
+import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateModuleNameConditionProperties;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService;
+import org.onap.cps.spi.CpsAdminPersistenceService;
+import org.onap.cps.spi.CpsDataPersistenceService;
+import org.onap.cps.spi.model.Anchor;
+import org.onap.cps.spi.model.CmHandleQueryParameters;
+import org.onap.cps.spi.model.ConditionProperties;
+import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.spi.model.DataNodeIdentifier;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class NetworkCmProxyCmHandlerQueryServiceImpl implements NetworkCmProxyCmHandlerQueryService {
+
+    private static final String PROPERTY_QUERY_NAME = "hasAllProperties";
+    private static final String MODULE_QUERY_NAME = "hasAllModules";
+    private final CpsDataPersistenceService cpsDataPersistenceService;
+    private final CpsAdminPersistenceService cpsAdminPersistenceService;
+    private final JsonObjectMapper jsonObjectMapper;
+
+    /**
+     * Query and return cm handles that match the given query parameters.
+     *
+     * @param cmHandleQueryParameters the cm handle query parameters
+     * @return collection of cm handles
+     */
+    @Override
+    public Collection<DataNode> queryCmHandles(final CmHandleQueryParameters cmHandleQueryParameters) {
+
+        if (cmHandleQueryParameters.getCmHandleQueryParameters().isEmpty()) {
+            return getAllCmHandles();
+        }
+
+        final Collection<DataNodeIdentifier> amalgamatedQueryResultIdentifiers = new ArrayList<>();
+        final Map<DataNodeIdentifier, DataNode> amalgamatedQueryResults = new HashMap<>();
+
+        final boolean firstQuery = moduleNameQuery(cmHandleQueryParameters,
+                amalgamatedQueryResultIdentifiers, amalgamatedQueryResults);
+
+        publicPropertyQuery(cmHandleQueryParameters, amalgamatedQueryResultIdentifiers,
+                amalgamatedQueryResults, firstQuery);
+
+        final Collection<DataNode> filteredDataNodes = new ArrayList<>();
+        amalgamatedQueryResultIdentifiers.forEach(amalgamatedQueryResultIdentifier ->
+            filteredDataNodes.add(amalgamatedQueryResults.get(amalgamatedQueryResultIdentifier))
+        );
+
+        return filteredDataNodes;
+    }
+
+    private void publicPropertyQuery(final CmHandleQueryParameters cmHandleQueryParameters,
+                                     final Collection<DataNodeIdentifier> amalgamatedQueryResultIdentifiers,
+                                     final Map<DataNodeIdentifier, DataNode> amalgamatedQueryResults,
+                                     boolean firstQuery) {
+        for (final Map.Entry<String, String> entry :
+                getPublicPropertyPairs(cmHandleQueryParameters.getCmHandleQueryParameters()).entrySet()) {
+            final String cmHandlePath = "//public-properties[@name='" + entry.getKey() + "' " + "and @value='"
+                    + entry.getValue() + "']" + "/ancestor::cm-handles";
+
+            final Collection<DataNode> dataNodes = getDataNodes(cmHandlePath);
+
+            if (firstQuery) {
+                firstQuery = false;
+                dataNodes.forEach(dataNode -> {
+                    final DataNodeIdentifier dataNodeIdentifier =
+                            jsonObjectMapper.convertToValueType(dataNode, DataNodeIdentifier.class);
+                    amalgamatedQueryResultIdentifiers.add(dataNodeIdentifier);
+                    amalgamatedQueryResults.put(dataNodeIdentifier, dataNode);
+                });
+            } else {
+                final Collection<DataNodeIdentifier> singleConditionQueryDataNodeIdentifiers = new ArrayList<>();
+                dataNodes.forEach(dataNode -> {
+                    final DataNodeIdentifier dataNodeIdentifier =
+                            jsonObjectMapper.convertToValueType(dataNode, DataNodeIdentifier.class);
+                    singleConditionQueryDataNodeIdentifiers.add(dataNodeIdentifier);
+                    amalgamatedQueryResults.put(dataNodeIdentifier, dataNode);
+                });
+                amalgamatedQueryResultIdentifiers.retainAll(singleConditionQueryDataNodeIdentifiers);
+            }
+
+            if (amalgamatedQueryResultIdentifiers.isEmpty()) {
+                break;
+            }
+        }
+    }
+
+    private boolean moduleNameQuery(final CmHandleQueryParameters cmHandleQueryParameters,
+                                    final Collection<DataNodeIdentifier> amalgamatedQueryResultIdentifiers,
+                                    final Map<DataNodeIdentifier, DataNode> amalgamatedQueryResults) {
+        boolean firstQuery = true;
+        if (!getModuleNames(cmHandleQueryParameters.getCmHandleQueryParameters()).isEmpty()) {
+            final Collection<Anchor> anchors = cpsAdminPersistenceService.queryAnchors("NFP-Operational",
+                    getModuleNames(cmHandleQueryParameters.getCmHandleQueryParameters()));
+            anchors.forEach(anchor -> {
+                final List<DataNode> dataNodes = getDataNodes("//cm-handles[@id='" + anchor.getName() + "']");
+                dataNodes.parallelStream().forEach(dataNode -> {
+                    final DataNodeIdentifier dataNodeIdentifier =
+                            jsonObjectMapper.convertToValueType(dataNode, DataNodeIdentifier.class);
+                    amalgamatedQueryResultIdentifiers.add(dataNodeIdentifier);
+                    amalgamatedQueryResults.put(dataNodeIdentifier, dataNode);
+                });
+            });
+            firstQuery = false;
+        }
+        return firstQuery;
+    }
+
+    private List<Map<String, String>> getConditions(final List<ConditionProperties> conditionProperties,
+                                                    final String name) {
+        for (final ConditionProperties conditionProperty : conditionProperties) {
+            if (conditionProperty.getConditionName().equals(name)) {
+                return conditionProperty.getConditionParameters();
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    private List<String> getModuleNames(final List<ConditionProperties> conditionProperties) {
+        final List<String> result = new ArrayList<>();
+        getConditions(conditionProperties, MODULE_QUERY_NAME).parallelStream().forEach(
+                conditionProperty -> {
+                    validateModuleNameConditionProperties(conditionProperty);
+                    result.add(conditionProperty.get("moduleName"));
+                }
+        );
+        return result;
+    }
+
+    private Map<String, String> getPublicPropertyPairs(final List<ConditionProperties> conditionProperties) {
+        final Map<String, String> result = new HashMap<>();
+        getConditions(conditionProperties, PROPERTY_QUERY_NAME).forEach(result::putAll);
+        return result;
+    }
+
+    private Collection<DataNode> getAllCmHandles() {
+        return getDataNodes("//public-properties/ancestor::cm-handles");
+    }
+
+    private List<DataNode> getDataNodes(final String cmHandlePath) {
+        return cpsDataPersistenceService.queryDataNodes(
+                NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cmHandlePath, INCLUDE_ALL_DESCENDANTS);
+    }
+}
index 717cae5..d1f72a5 100755 (executable)
@@ -30,12 +30,11 @@ import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPER
 import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
+import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateCmHandleQueryParameters;
 
-import com.google.common.base.Strings;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -45,10 +44,13 @@ import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsModuleService;
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService;
 import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
 import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.CmHandleState;
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence;
 import org.onap.cps.ncmp.api.inventory.sync.ModuleSyncService;
 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
@@ -61,6 +63,7 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
 import org.onap.cps.spi.exceptions.DataValidationException;
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
+import org.onap.cps.spi.model.CmHandleQueryParameters;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.CpsValidator;
 import org.onap.cps.utils.JsonObjectMapper;
@@ -88,21 +91,23 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private final ModuleSyncService moduleSyncService;
 
+    private final NetworkCmProxyCmHandlerQueryService networkCmProxyCmHandlerQueryService;
+
     @Override
     public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(
-        final DmiPluginRegistration dmiPluginRegistration) {
+            final DmiPluginRegistration dmiPluginRegistration) {
         dmiPluginRegistration.validateDmiPluginRegistration();
         final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse();
         dmiPluginRegistrationResponse.setRemovedCmHandles(
-            parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles()));
+                parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration.getRemovedCmHandles()));
         if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
             dmiPluginRegistrationResponse.setCreatedCmHandles(
-                parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration));
+                    parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration));
         }
         if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
             dmiPluginRegistrationResponse.setUpdatedCmHandles(
-                networkCmProxyDataServicePropertyHandler
-                    .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
+                    networkCmProxyDataServicePropertyHandler
+                            .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles()));
         }
         return dmiPluginRegistrationResponse;
     }
@@ -154,28 +159,35 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     }
 
     /**
-     * Retrieve cm handle identifiers for the given list of module names.
+     * Retrieve cm handles with details for the given query parameters.
      *
-     * @param moduleNames module names.
-     * @return a collection of anchor identifiers
+     * @param cmHandleQueryApiParameters cm handle query parameters
+     * @return cm handles with details
      */
     @Override
-    public Collection<String> executeCmHandleHasAllModulesSearch(final Collection<String> moduleNames) {
-        return cpsAdminService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNames);
-    }
+    public Set<NcmpServiceCmHandle> executeCmHandleSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters) {
 
-    @Override
-    public Set<String> queryCmHandles(final CmHandleQueryApiParameters cmHandleQueryApiParameters) {
+        final CmHandleQueryParameters cmHandleQueryParameters = jsonObjectMapper.convertToValueType(
+                cmHandleQueryApiParameters, CmHandleQueryParameters.class);
 
-        cmHandleQueryApiParameters.getPublicProperties().forEach((key, value) -> {
-            if (Strings.isNullOrEmpty(key)) {
-                throw new DataValidationException("Invalid Query Parameter.",
-                    "Missing property name - please supply a valid name.");
-            }
-        });
+        validateCmHandleQueryParameters(cmHandleQueryParameters);
 
-        return cpsAdminService.queryCmHandles(jsonObjectMapper.convertToValueType(cmHandleQueryApiParameters,
-            org.onap.cps.spi.model.CmHandleQueryParameters.class));
+        return networkCmProxyCmHandlerQueryService.queryCmHandles(cmHandleQueryParameters).stream()
+                .map(dataNode -> YangDataConverter
+                        .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()))
+                .map(YangDataConverter::convertYangModelCmHandleToNcmpServiceCmHandle).collect(Collectors.toSet());
+    }
+
+    /**
+     * Retrieve cm handle ids for the given query parameters.
+     *
+     * @param cmHandleQueryApiParameters cm handle query parameters
+     * @return cm handle ids
+     */
+    @Override
+    public Set<String> executeCmHandleIdSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters) {
+        return executeCmHandleSearch(cmHandleQueryApiParameters).stream().map(NcmpServiceCmHandle::getCmHandleId)
+                .collect(Collectors.toSet());
     }
 
     /**
@@ -187,16 +199,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     @Override
     public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) {
         CpsValidator.validateNameCharacters(cmHandleId);
-        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
-        final YangModelCmHandle yangModelCmHandle =
-            inventoryPersistence.getYangModelCmHandle(cmHandleId);
-        final List<YangModelCmHandle.Property> dmiProperties = yangModelCmHandle.getDmiProperties();
-        final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties();
-        ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId());
-        ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState());
-        setDmiProperties(dmiProperties, ncmpServiceCmHandle);
-        setPublicProperties(publicProperties, ncmpServiceCmHandle);
-        return ncmpServiceCmHandle;
+        return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(
+                inventoryPersistence.getYangModelCmHandle(cmHandleId));
     }
 
     /**
@@ -212,7 +216,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
             inventoryPersistence.getYangModelCmHandle(cmHandleId);
         final List<YangModelCmHandle.Property> yangModelPublicProperties = yangModelCmHandle.getPublicProperties();
         final Map<String, String> cmHandlePublicProperties = new HashMap<>();
-        asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties);
+        YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties);
         return cmHandlePublicProperties;
     }
 
@@ -223,7 +227,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
      * @return cm-handle registration response for create cm-handle requests.
      */
     public List<CmHandleRegistrationResponse> parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
-        final DmiPluginRegistration dmiPluginRegistration) {
+            final DmiPluginRegistration dmiPluginRegistration) {
         List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
         try {
             cmHandleRegistrationResponses = dmiPluginRegistration.getCreatedCmHandles().stream()
@@ -231,52 +235,47 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
                     YangModelCmHandle.toYangModelCmHandle(
                         dmiPluginRegistration.getDmiPlugin(),
                         dmiPluginRegistration.getDmiDataPlugin(),
-                        dmiPluginRegistration.getDmiModelPlugin(), cmHandle)
+                        dmiPluginRegistration.getDmiModelPlugin(),
+                        CmHandleState.ADVISED,
+                        cmHandle)
                 )
-                .map(this::registerAndSyncNewCmHandle)
+                .map(this::registerNewCmHandle)
                 .collect(Collectors.toList());
         } catch (final DataValidationException dataValidationException) {
             cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createFailureResponse(dmiPluginRegistration
-                    .getCreatedCmHandles().stream()
-                    .map(NcmpServiceCmHandle::getCmHandleId).findFirst().orElse(null),
-                RegistrationError.CM_HANDLE_INVALID_ID));
+                            .getCreatedCmHandles().stream()
+                            .map(NcmpServiceCmHandle::getCmHandleId).findFirst().orElse(null),
+                    RegistrationError.CM_HANDLE_INVALID_ID));
         }
         return cmHandleRegistrationResponses;
     }
 
-    protected void syncModulesAndCreateAnchor(final YangModelCmHandle yangModelCmHandle) {
-        final String schemaSetName = moduleSyncService.syncAndCreateSchemaSet(yangModelCmHandle);
-        final String anchorName = yangModelCmHandle.getId();
-        cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
-            anchorName);
-    }
-
     protected List<CmHandleRegistrationResponse> parseAndRemoveCmHandlesInDmiRegistration(
-        final List<String> tobeRemovedCmHandles) {
+            final List<String> tobeRemovedCmHandles) {
         final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses =
-            new ArrayList<>(tobeRemovedCmHandles.size());
+                new ArrayList<>(tobeRemovedCmHandles.size());
         for (final String cmHandle : tobeRemovedCmHandles) {
             try {
                 CpsValidator.validateNameCharacters(cmHandle);
                 deleteSchemaSetWithCascade(cmHandle);
                 cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-                    "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
+                        "/dmi-registry/cm-handles[@id='" + cmHandle + "']", NO_TIMESTAMP);
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandle));
             } catch (final DataNodeNotFoundException dataNodeNotFoundException) {
                 log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}",
-                    cmHandle, dataNodeNotFoundException.getMessage());
+                        cmHandle, dataNodeNotFoundException.getMessage());
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
-                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
+                        .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_DOES_NOT_EXIST));
             } catch (final DataValidationException dataValidationException) {
                 log.error("Unable to de-register cm-handle id: {}, caused by: {}",
-                    cmHandle, dataValidationException.getMessage());
+                        cmHandle, dataValidationException.getMessage());
                 cmHandleRegistrationResponses.add(CmHandleRegistrationResponse
-                    .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_INVALID_ID));
+                        .createFailureResponse(cmHandle, RegistrationError.CM_HANDLE_INVALID_ID));
             } catch (final Exception exception) {
                 log.error("Unable to de-register cm-handle id : {} , caused by : {}",
-                    cmHandle, exception.getMessage());
+                        cmHandle, exception.getMessage());
                 cmHandleRegistrationResponses.add(
-                    CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
+                        CmHandleRegistrationResponse.createFailureResponse(cmHandle, exception));
             }
         }
         return cmHandleRegistrationResponses;
@@ -285,47 +284,24 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
     private void deleteSchemaSetWithCascade(final String schemaSetName) {
         try {
             cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
-                CASCADE_DELETE_ALLOWED);
+                    CASCADE_DELETE_ALLOWED);
         } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
             log.warn("Schema set {} does not exist or already deleted", schemaSetName);
         }
     }
 
-    private void setDmiProperties(final List<YangModelCmHandle.Property> dmiProperties,
-                                  final NcmpServiceCmHandle ncmpServiceCmHandle) {
-        final Map<String, String> dmiPropertiesMap = new LinkedHashMap<>(dmiProperties.size());
-        asPropertiesMap(dmiProperties, dmiPropertiesMap);
-        ncmpServiceCmHandle.setDmiProperties(dmiPropertiesMap);
-    }
-
-    private void setPublicProperties(final List<YangModelCmHandle.Property> publicProperties,
-                                     final NcmpServiceCmHandle ncmpServiceCmHandle) {
-        final Map<String, String> publicPropertiesMap = new LinkedHashMap<>();
-        asPropertiesMap(publicProperties, publicPropertiesMap);
-        ncmpServiceCmHandle.setPublicProperties(publicPropertiesMap);
-    }
-
-    private void asPropertiesMap(final List<YangModelCmHandle.Property> properties,
-                                 final Map<String, String> propertiesMap) {
-        for (final YangModelCmHandle.Property property: properties) {
-            propertiesMap.put(property.getName(), property.getValue());
-        }
-    }
-
-    private CmHandleRegistrationResponse registerAndSyncNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
+    private CmHandleRegistrationResponse registerNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
         try {
             final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
-                jsonObjectMapper.asJsonString(yangModelCmHandle));
+                    jsonObjectMapper.asJsonString(yangModelCmHandle));
             cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
-                cmHandleJsonData, NO_TIMESTAMP);
-            syncModulesAndCreateAnchor(yangModelCmHandle);
+                    cmHandleJsonData, NO_TIMESTAMP);
             return CmHandleRegistrationResponse.createSuccessResponse(yangModelCmHandle.getId());
         } catch (final AlreadyDefinedException alreadyDefinedException) {
             return CmHandleRegistrationResponse.createFailureResponse(
-                yangModelCmHandle.getId(), RegistrationError.CM_HANDLE_ALREADY_EXIST);
+                    yangModelCmHandle.getId(), RegistrationError.CM_HANDLE_ALREADY_EXIST);
         } catch (final Exception exception) {
             return CmHandleRegistrationResponse.createFailureResponse(yangModelCmHandle.getId(), exception);
         }
     }
-
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/NcmpEventsPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/NcmpEventsPublisher.java
new file mode 100644 (file)
index 0000000..52ac468
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.ncmp.cmhandle.lcm.event.NcmpEvent;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.support.SendResult;
+import org.springframework.stereotype.Service;
+import org.springframework.util.concurrent.ListenableFuture;
+import org.springframework.util.concurrent.ListenableFutureCallback;
+
+/**
+ * NcmpEventsPublisher to publish the NcmpEvents on event of CREATE, UPDATE and DELETE.
+ */
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class NcmpEventsPublisher {
+
+    private final KafkaTemplate<String, NcmpEvent> ncmpEventKafkaTemplate;
+
+    /**
+     * NCMP Event publisher.
+     *
+     * @param topicName valid topic name
+     * @param eventKey  message key
+     * @param ncmpEvent message payload
+     */
+    public void publishEvent(final String topicName, final String eventKey, final NcmpEvent ncmpEvent) {
+        final ListenableFuture<SendResult<String, NcmpEvent>> ncmpEventFuture =
+                ncmpEventKafkaTemplate.send(topicName, eventKey, ncmpEvent);
+
+        ncmpEventFuture.addCallback(new ListenableFutureCallback<>() {
+            @Override
+            public void onFailure(final Throwable throwable) {
+                log.error("Unable to publish event to topic : {} due to {}", topicName, throwable.getMessage());
+            }
+
+            @Override
+            public void onSuccess(final SendResult<String, NcmpEvent> result) {
+                log.debug("Successfully published event to topic : {} , NcmpEvent : {}",
+                        result.getRecordMetadata().topic(), result.getProducerRecord().value());
+            }
+        });
+    }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java
new file mode 100644 (file)
index 0000000..82ea00e
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.utils;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder;
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.model.DataNode;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class YangDataConverter {
+
+    /**
+     * This method convert yang model cm handle to ncmp service cm handle.
+     * @param yangModelCmHandle the yang model of the cm handle
+     * @return ncmp service cm handle
+     */
+    public static NcmpServiceCmHandle convertYangModelCmHandleToNcmpServiceCmHandle(
+            final YangModelCmHandle yangModelCmHandle) {
+        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
+        final List<YangModelCmHandle.Property> dmiProperties = yangModelCmHandle.getDmiProperties();
+        final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties();
+        ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId());
+        ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState());
+        setDmiProperties(dmiProperties, ncmpServiceCmHandle);
+        setPublicProperties(publicProperties, ncmpServiceCmHandle);
+        return ncmpServiceCmHandle;
+    }
+
+    /**
+     * This method convert yang model cm handle properties to simple map.
+     * @param properties the yang model cm handle properties
+     * @param propertiesMap the String, String map for the results
+     */
+    public static void asPropertiesMap(final List<YangModelCmHandle.Property> properties,
+                                       final Map<String, String> propertiesMap) {
+        for (final YangModelCmHandle.Property property : properties) {
+            propertiesMap.put(property.getName(), property.getValue());
+        }
+    }
+
+    /**
+     * This method convert cm handle data node to yang model cm handle.
+     * @param cmHandleDataNode the datanode of the cm handle
+     * @param cmHandleId the id of the cm handle
+     * @return yang model cm handle
+     */
+    public static YangModelCmHandle convertCmHandleToYangModel(final DataNode cmHandleDataNode,
+                                                               final String cmHandleId) {
+        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
+        ncmpServiceCmHandle.setCmHandleId(cmHandleId);
+        populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle);
+        return YangModelCmHandle.toYangModelCmHandle(
+                (String) cmHandleDataNode.getLeaves().get("dmi-service-name"),
+                (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
+                (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"),
+                ncmpServiceCmHandle.getCompositeState().getCmHandleState(),
+                ncmpServiceCmHandle
+        );
+    }
+
+    private static void populateCmHandleDetails(final DataNode cmHandleDataNode,
+                                                final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        final Map<String, String> dmiProperties = new LinkedHashMap<>();
+        final Map<String, String> publicProperties = new LinkedHashMap<>();
+        final CompositeStateBuilder compositeStateBuilder = new CompositeStateBuilder();
+        CompositeState compositeState = compositeStateBuilder.build();
+        for (final DataNode childDataNode: cmHandleDataNode.getChildDataNodes()) {
+            if (childDataNode.getXpath().contains("/additional-properties[@name=")) {
+                addProperty(childDataNode, dmiProperties);
+            } else if (childDataNode.getXpath().contains("/public-properties[@name=")) {
+                addProperty(childDataNode, publicProperties);
+            } else if (childDataNode.getXpath().endsWith("/state")) {
+                compositeState = compositeStateBuilder.fromDataNode(childDataNode).build();
+            }
+        }
+        ncmpServiceCmHandle.setDmiProperties(dmiProperties);
+        ncmpServiceCmHandle.setPublicProperties(publicProperties);
+        ncmpServiceCmHandle.setCompositeState(compositeState);
+    }
+
+    private static void addProperty(final DataNode propertyDataNode, final Map<String, String> propertiesAsMap) {
+        propertiesAsMap.put(String.valueOf(propertyDataNode.getLeaves().get("name")),
+                String.valueOf(propertyDataNode.getLeaves().get("value")));
+    }
+
+    private static void setDmiProperties(final List<YangModelCmHandle.Property> dmiProperties,
+                                  final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        final Map<String, String> dmiPropertiesMap = new LinkedHashMap<>(dmiProperties.size());
+        asPropertiesMap(dmiProperties, dmiPropertiesMap);
+        ncmpServiceCmHandle.setDmiProperties(dmiPropertiesMap);
+    }
+
+    private static void setPublicProperties(final List<YangModelCmHandle.Property> publicProperties,
+                                     final NcmpServiceCmHandle ncmpServiceCmHandle) {
+        final Map<String, String> publicPropertiesMap = new LinkedHashMap<>();
+        asPropertiesMap(publicProperties, publicPropertiesMap);
+        ncmpServiceCmHandle.setPublicProperties(publicPropertiesMap);
+    }
+}
index 65e03f1..5b71905 100644 (file)
@@ -34,7 +34,9 @@ import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
+import org.onap.cps.ncmp.api.inventory.CmHandleState;
 import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.utils.CpsValidator;
 
@@ -68,6 +70,8 @@ public class YangModelCmHandle {
     @JsonProperty("public-properties")
     private List<Property> publicProperties;
 
+    private static final CompositeStateBuilder compositeStateBuilder = new CompositeStateBuilder();
+
     /**
      * Create a yangModelCmHandle.
      *
@@ -80,6 +84,7 @@ public class YangModelCmHandle {
     public static YangModelCmHandle toYangModelCmHandle(final String dmiServiceName,
                                                         final String dmiDataServiceName,
                                                         final String dmiModelServiceName,
+                                                        final CmHandleState cmHandleState,
                                                         final NcmpServiceCmHandle ncmpServiceCmHandle) {
         CpsValidator.validateNameCharacters(ncmpServiceCmHandle.getCmHandleId());
         final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle();
@@ -90,7 +95,8 @@ public class YangModelCmHandle {
         yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
         yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
                 ncmpServiceCmHandle.getPublicProperties()));
-        yangModelCmHandle.setCompositeState(ncmpServiceCmHandle.getCompositeState());
+        compositeStateBuilder.withCmHandleState(cmHandleState);
+        yangModelCmHandle.setCompositeState(compositeStateBuilder.build());
         return yangModelCmHandle;
     }
 
index 873a449..2fc2dc5 100644 (file)
 package org.onap.cps.ncmp.api.inventory;
 
 import java.time.OffsetDateTime;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.api.CpsDataService;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
-import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.model.DataNode;
@@ -43,6 +41,8 @@ public class InventoryPersistence {
 
     private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
 
+    private static final String XPATH_TO_CM_HANDLE = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
+
     private final JsonObjectMapper jsonObjectMapper;
 
     private final CpsDataService cpsDataService;
@@ -59,7 +59,7 @@ public class InventoryPersistence {
      */
     public CompositeState getCmHandleState(final String cmHandleId) {
         final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            "/dmi-registry/cm-handles[@id='" + cmHandleId + "']/state",
+                String.format(XPATH_TO_CM_HANDLE, cmHandleId) + "/state",
             FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
         return compositeStateBuilder.fromDataNode(stateAsDataNode).build();
     }
@@ -74,7 +74,7 @@ public class InventoryPersistence {
         final String cmHandleJsonData = String.format("{\"state\":%s}",
             jsonObjectMapper.asJsonString(compositeState));
         cpsDataService.replaceNodeTree(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            "/dmi-registry/cm-handles[@id='" + cmHandleId + "']",
+            String.format(XPATH_TO_CM_HANDLE, cmHandleId),
             cmHandleJsonData, OffsetDateTime.now());
     }
 
@@ -92,55 +92,20 @@ public class InventoryPersistence {
     }
 
     /**
-     * This method retrieves DMI service name and DMI properties for a given cm handle.
+     * This method retrieves DMI service name, DMI properties and the state for a given cm handle.
      * @param cmHandleId the id of the cm handle
      * @return yang model cm handle
      */
     public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
         CpsValidator.validateNameCharacters(cmHandleId);
-        final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId);
-        final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
-        ncmpServiceCmHandle.setCmHandleId(cmHandleId);
-        populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle);
-        return YangModelCmHandle.toYangModelCmHandle(
-            (String) cmHandleDataNode.getLeaves().get("dmi-service-name"),
-            (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"),
-            (String) cmHandleDataNode.getLeaves().get("dmi-model-service-name"),
-            ncmpServiceCmHandle
-        );
+        return YangDataConverter.convertCmHandleToYangModel(getCmHandleDataNode(cmHandleId), cmHandleId);
     }
 
     private DataNode getCmHandleDataNode(final String cmHandle) {
-        final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']";
         return cpsDataService.getDataNode(NCMP_DATASPACE_NAME,
             NCMP_DMI_REGISTRY_ANCHOR,
-            xpathForDmiRegistryToFetchCmHandle,
+            String.format(XPATH_TO_CM_HANDLE, cmHandle),
             FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
     }
 
-    private static void populateCmHandleDetails(final DataNode cmHandleDataNode,
-                                                final NcmpServiceCmHandle ncmpServiceCmHandle) {
-        final Map<String, String> dmiProperties = new LinkedHashMap<>();
-        final Map<String, String> publicProperties = new LinkedHashMap<>();
-        final CompositeStateBuilder compositeStateBuilder = new CompositeStateBuilder();
-        CompositeState compositeState = compositeStateBuilder.build();
-        for (final DataNode childDataNode: cmHandleDataNode.getChildDataNodes()) {
-            if (childDataNode.getXpath().contains("/additional-properties[@name=")) {
-                addProperty(childDataNode, dmiProperties);
-            } else if (childDataNode.getXpath().contains("/public-properties[@name=")) {
-                addProperty(childDataNode, publicProperties);
-            } else if (childDataNode.getXpath().endsWith("/state")) {
-                compositeState = compositeStateBuilder.fromDataNode(childDataNode).build();
-            }
-        }
-        ncmpServiceCmHandle.setDmiProperties(dmiProperties);
-        ncmpServiceCmHandle.setPublicProperties(publicProperties);
-        ncmpServiceCmHandle.setCompositeState(compositeState);
-    }
-
-    private static void addProperty(final DataNode propertyDataNode, final Map<String, String> propertiesAsMap) {
-        propertiesAsMap.put(String.valueOf(propertyDataNode.getLeaves().get("name")),
-            String.valueOf(propertyDataNode.getLeaves().get("value")));
-    }
-
 }
index 1d00f0d..58e2bf3 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Map;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
@@ -42,13 +43,14 @@ public class ModuleSyncService {
     private final DmiModelOperations dmiModelOperations;
     private final CpsModuleService cpsModuleService;
 
+    private final CpsAdminService cpsAdminService;
+
     /**
      * This method registers a cm handle and initiates modules sync.
      *
      * @param yangModelCmHandle the yang model of cm handle.
-     * @return schemaSetName the name of the schema set (same as cm handle name).
      */
-    public String syncAndCreateSchemaSet(final YangModelCmHandle yangModelCmHandle) {
+    public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
 
         final Collection<ModuleReference> moduleReferencesFromCmHandle =
                 dmiModelOperations.getModuleReferences(yangModelCmHandle);
@@ -68,17 +70,17 @@ public class ModuleSyncService {
             newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
                     identifiedNewModuleReferencesFromCmHandle);
         }
-        return createSchemaSet(yangModelCmHandle, existingModuleReferencesFromCmHandle, newModuleNameToContentMap);
+        createSchemaSetAndAnchor(yangModelCmHandle, newModuleNameToContentMap, existingModuleReferencesFromCmHandle);
     }
 
-    private String createSchemaSet(final YangModelCmHandle yangModelCmHandle,
-                                 final Collection<ModuleReference> existingModuleReferencesFromCmHandle,
-                                 final Map<String, String> newModuleNameToContentMap) {
-        final String schemaSetName = yangModelCmHandle.getId();
-        cpsModuleService
-                .createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
+    private void createSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle,
+                                          final Map<String, String> newModuleNameToContentMap,
+                                          final Collection<ModuleReference> existingModuleReferencesFromCmHandle) {
+        final String schemaSetAndAnchorName = yangModelCmHandle.getId();
+        cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName,
                         newModuleNameToContentMap, existingModuleReferencesFromCmHandle);
-        return schemaSetName;
+        cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName,
+            schemaSetAndAnchorName);
     }
 
 }
index 2187ec6..bcc7daa 100644 (file)
@@ -51,7 +51,7 @@ public class ModuleSyncWatchdog {
             final String cmHandleId = advisedCmHandle.getId();
             final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId);
             try {
-                moduleSyncService.syncAndCreateSchemaSet(advisedCmHandle);
+                moduleSyncService.syncAndCreateSchemaSetAndAnchor(advisedCmHandle);
                 compositeState.setCmHandleState(CmHandleState.READY);
             } catch (final Exception e) {
                 compositeState.setCmHandleState(CmHandleState.LOCKED);
index 3f584ed..bf6600d 100644 (file)
 
 package org.onap.cps.ncmp.api.models;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.Collections;
-import java.util.Map;
+import java.util.List;
 import javax.validation.Valid;
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Setter;
 
-@Setter
 @Getter
-@JsonInclude(Include.NON_NULL)
+@Setter
+@EqualsAndHashCode
+@JsonInclude(Include.NON_EMPTY)
+@JsonIgnoreProperties(ignoreUnknown = true)
 public class CmHandleQueryApiParameters {
-
-    @JsonProperty("publicCmHandleProperties")
+    @JsonProperty("cmHandleQueryParameters")
     @Valid
-    private Map<String, String> publicProperties = Collections.emptyMap();
-
+    private List<ConditionApiProperties> cmHandleQueryParameters = Collections.emptyList();
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ConditionApiProperties.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ConditionApiProperties.java
new file mode 100644 (file)
index 0000000..9f6d64e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.models;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.validation.Valid;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class ConditionApiProperties {
+    @JsonProperty("conditionName")
+    private String conditionName = "";
+
+    @JsonProperty("conditionParameters")
+    @Valid
+    private List<Map<String, String>> conditionParameters = Collections.emptyList();
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
new file mode 100644 (file)
index 0000000..a1ad9af
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
+import org.onap.cps.spi.CpsAdminPersistenceService
+import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.model.Anchor
+import org.onap.cps.spi.model.CmHandleQueryParameters
+import org.onap.cps.spi.model.ConditionProperties
+import org.onap.cps.spi.model.DataNode
+import org.onap.cps.utils.JsonObjectMapper
+import spock.lang.Specification
+
+import java.util.stream.Collectors
+
+class NetworkCmProxyCmHandlerQueryServiceSpec extends Specification {
+
+    def cpsDataPersistenceService = Mock(CpsDataPersistenceService)
+    def cpsAdminPersistenceService = Mock(CpsAdminPersistenceService)
+
+    NetworkCmProxyCmHandlerQueryService objectUnderTest = new NetworkCmProxyCmHandlerQueryServiceImpl(
+        cpsDataPersistenceService, cpsAdminPersistenceService, new JsonObjectMapper(new ObjectMapper())
+    )
+
+    def 'Retrieve cm handles with public properties when #scenario.'() {
+        given: 'a condition property'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def conditionProperties = new ConditionProperties()
+            conditionProperties.conditionName = 'hasAllProperties'
+            conditionProperties.conditionParameters = publicProperties
+            cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
+        and: 'mock services'
+            mockResponses()
+        when: 'the service is invoked'
+            def returnedCmHandles = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
+        then: 'the correct expected cm handles are returned'
+            returnedCmHandles.stream().map(d -> d.leaves.get('id').toString()).collect(Collectors.toList()) == expectedCmHandleIds
+        where: 'the following data is used'
+            scenario                                       | publicProperties                                                                                  || expectedCmHandleIds
+            'single matching property'                     | [['Contact' : 'newemailforstore@bookstore.com']]                                                  || ['PNFDemo', 'PNFDemo2', 'PNFDemo4']
+            'public property dont match'                   | [['wont_match' : 'wont_match']]                                                                   || []
+            '2 properties, only one match (and)'           | [['Contact' : 'newemailforstore@bookstore.com'], ['Contact2': 'newemailforstore2@bookstore.com']] || ['PNFDemo4']
+            '2 properties, no match (and)'                 | [['Contact' : 'newemailforstore@bookstore.com'], ['Contact2': '']]                                || []
+    }
+
+    def 'Retrieve cm handles with module names when #scenario.'() {
+        given: 'a condition property'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def conditionProperties = new ConditionProperties()
+            conditionProperties.conditionName = 'hasAllModules'
+            conditionProperties.conditionParameters = moduleNames
+            cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties])
+        and: 'mock services'
+            mockResponses()
+        when: 'the service is invoked'
+            def returnedCmHandles = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
+        then: 'the correct expected cm handles are returned'
+            returnedCmHandles.stream().map(d -> d.leaves.get('id').toString()).collect(Collectors.toList()) == expectedCmHandleIds
+        where: 'the following data is used'
+            scenario                               | moduleNames                                                             || expectedCmHandleIds
+            'single matching module name'          | [['moduleName' : 'MODULE-NAME-001']]                                    || ['PNFDemo2', 'PNFDemo3', 'PNFDemo']
+            'module name dont match'               | [['moduleName' : 'MODULE-NAME-004']]                                    || []
+            '2 module names, only one match (and)' | [['moduleName' : 'MODULE-NAME-002'], ['moduleName': 'MODULE-NAME-003']] || ['PNFDemo4']
+            '2 module names, no match (and)'       | [['moduleName' : 'MODULE-NAME-002'], ['moduleName': 'MODULE-NAME-004']] || []
+    }
+
+    def 'Retrieve cm handles with combined queries when #scenario.'() {
+        given: 'condition properties'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def conditionProperties1 = new ConditionProperties()
+            conditionProperties1.conditionName = 'hasAllProperties'
+            conditionProperties1.conditionParameters = publicProperties
+            def conditionProperties2 = new ConditionProperties()
+            conditionProperties2.conditionName = 'hasAllModules'
+            conditionProperties2.conditionParameters = moduleNames
+            cmHandleQueryParameters.setCmHandleQueryParameters([conditionProperties1,conditionProperties2])
+        and: 'mock services'
+            mockResponses()
+        when: 'the service is invoked'
+            def returnedCmHandles = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
+        then: 'the correct expected cm handles are returned'
+            returnedCmHandles.stream().map(d -> d.leaves.get('id').toString()).collect(Collectors.toList()) == expectedCmHandleIds
+        where: 'the following data is used'
+            scenario                 | moduleNames                          | publicProperties                                   || expectedCmHandleIds
+            'particularly intersect' | [['moduleName' : 'MODULE-NAME-001']] | [['Contact' : 'newemailforstore@bookstore.com']]   || ['PNFDemo2', 'PNFDemo']
+            'empty intersect'        | [['moduleName' : 'MODULE-NAME-004']] | [['Contact' : 'newemailforstore@bookstore.com']]   || []
+            'total intersect'        | [['moduleName' : 'MODULE-NAME-002']] | [['Contact2' : 'newemailforstore2@bookstore.com']] || ['PNFDemo4']
+    }
+
+    def 'Retrieve cm handles when the query is empty.'() {
+        given: 'mock services'
+            mockResponses()
+        when: 'the service is invoked'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def returnedCmHandles = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
+        then: 'the correct expected cm handles are returned'
+            returnedCmHandles.stream().map(d -> d.leaves.get('id').toString()).collect(Collectors.toList()) == ['PNFDemo', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4']
+    }
+
+    void mockResponses() {
+        def pNFDemo = new DataNode(xpath: 'cmHandle/id[\'PNFDemo\']', leaves: ['id':'PNFDemo'])
+        def pNFDemo2 = new DataNode(xpath: 'cmHandle/id[\'PNFDemo2\']', leaves: ['id':'PNFDemo2'])
+        def pNFDemo3 = new DataNode(xpath: 'cmHandle/id[\'PNFDemo3\']', leaves: ['id':'PNFDemo3'])
+        def pNFDemo4 = new DataNode(xpath: 'cmHandle/id[\'PNFDemo4\']', leaves: ['id':'PNFDemo4'])
+
+        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'Contact\' and @value=\'newemailforstore@bookstore.com\']/ancestor::cm-handles', _)
+                >> [pNFDemo, pNFDemo2, pNFDemo4]
+        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'wont_match\' and @value=\'wont_match\']/ancestor::cm-handles', _)
+                >> []
+        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'Contact2\' and @value=\'newemailforstore2@bookstore.com\']/ancestor::cm-handles', _)
+                >> [pNFDemo4]
+        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\'Contact2\' and @value=\'\']/ancestor::cm-handles', _)
+                >> []
+        cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties/ancestor::cm-handles', _)
+                >> [pNFDemo, pNFDemo2, pNFDemo3, pNFDemo4]
+        cpsDataPersistenceService.queryDataNodes(_, _, '//cm-handles[@id=\'PNFDemo\']', _) >> [pNFDemo]
+        cpsDataPersistenceService.queryDataNodes(_, _, '//cm-handles[@id=\'PNFDemo2\']', _) >> [pNFDemo2]
+        cpsDataPersistenceService.queryDataNodes(_, _, '//cm-handles[@id=\'PNFDemo3\']', _) >> [pNFDemo3]
+        cpsDataPersistenceService.queryDataNodes(_, _, '//cm-handles[@id=\'PNFDemo4\']', _) >> [pNFDemo4]
+
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-001']) >> [new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo3'), new Anchor(name: 'PNFDemo')]
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-004']) >> []
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-003', 'MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo4')]
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-002', 'MODULE-NAME-003']) >> [new Anchor(name: 'PNFDemo4')]
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-004', 'MODULE-NAME-002']) >> []
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-002', 'MODULE-NAME-004']) >> []
+        cpsAdminPersistenceService.queryAnchors(_, ['MODULE-NAME-002']) >> [new Anchor(name: 'PNFDemo2'), new Anchor(name: 'PNFDemo4')]
+    }
+}
index f56aea7..e9d02df 100644 (file)
@@ -22,6 +22,7 @@
 package org.onap.cps.ncmp.api.impl
 
 import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
@@ -64,9 +65,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler)
     def mockInventoryPersistence = Mock(InventoryPersistence)
     def mockModuleSyncService = Mock(ModuleSyncService)
+    def stubbedNetworkCmProxyCmHandlerQueryService = Stub(NetworkCmProxyCmHandlerQueryService)
 
     def noTimestamp = null
-    def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
+    def objectUnderTest = getObjectUnderTest()
 
     def 'DMI Registration: Create, Update & Delete operations are processed in the right order'() {
         given: 'a registration with operations of all three types'
@@ -164,20 +166,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             }
         and: 'save list elements is invoked with the expected parameters'
             interaction {
-                def expectedJsonData = """{"cm-handles":[{"id":"cmhandle","dmi-service-name":"my-server","additional-properties":$expectedDmiProperties,"public-properties":$expectedPublicProperties}]}"""
+                def expectedJsonData = """{"cm-handles":[{"id":"cmhandle","dmi-service-name":"my-server","state":{"cm-handle-state":"ADVISED"},"additional-properties":$expectedDmiProperties,"public-properties":$expectedPublicProperties}]}"""
                 1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
                     '/dmi-registry', expectedJsonData, noTimestamp)
             }
-        then: 'model sync is invoked with expected parameters'
-            1 * objectUnderTest.syncModulesAndCreateAnchor(_) >> { YangModelCmHandle yangModelCmHandle ->
-                {
-                    assert yangModelCmHandle.id == 'cmhandle'
-                    assert yangModelCmHandle.dmiServiceName == 'my-server'
-                    assert spiedJsonObjectMapper.asJsonString(yangModelCmHandle.getPublicProperties()) == expectedPublicProperties
-                    assert spiedJsonObjectMapper.asJsonString(yangModelCmHandle.getDmiProperties()) == expectedDmiProperties
-
-                }
-            }
         where:
             scenario                          | dmiProperties            | publicProperties               || expectedDmiProperties                      | expectedPublicProperties
             'with dmi & public properties'    | ['dmi-key': 'dmi-value'] | ['public-key': 'public-value'] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[{"name":"public-key","value":"public-value"}]'
@@ -233,8 +225,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
                 assert it.registrationError == expectedError
                 assert it.errorText == expectedErrorText
             }
-        and: 'model-sync is not invoked'
-            0 * objectUnderTest.syncModulesAndCreateAnchor(_)
         where:
             scenario                                        | cmHandleId             | exception                                               || expectedError           | expectedErrorText
             'cm-handle already exist'                       | 'cmhandle'             | new AlreadyDefinedException('', new RuntimeException()) || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists'
@@ -242,28 +232,6 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'unknown exception while registering cm-handle' | 'cmhandle'             | new RuntimeException('Failed')                          || UNKNOWN_ERROR           | 'Failed'
     }
 
-    def 'Create CM-Handle Error Handling: Model Sync fails'() {
-        given: 'objects under test without disabled model sync'
-            def objectUnderTest = getObjectUnderTest()
-        and: 'a registration without cm-handle properties'
-            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
-            dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')]
-        and: 'cm-handler models sync fails'
-            objectUnderTest.syncModulesAndCreateAnchor(*_) >> { throw new RuntimeException('Model-Sync failed') }
-        when: 'registration is updated'
-            def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
-        then: 'a failure response is received'
-            response.getCreatedCmHandles().size() == 1
-            with(response.getCreatedCmHandles().get(0)) {
-                assert it.status == Status.FAILURE
-                assert it.cmHandle == 'cmhandle'
-                assert it.registrationError == UNKNOWN_ERROR
-                assert it.errorText == 'Model-Sync failed'
-            }
-        and: 'cm-handle is registered'
-            1 * mockCpsDataService.saveListElements(*_)
-    }
-
     def 'Update CM-Handle: Update Operation Response is added to the response'() {
         given: 'a registration to update CmHandles'
             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
@@ -379,14 +347,9 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
             'an unexpected exception'    | 'cmhandle'             | new RuntimeException("Failed")            || UNKNOWN_ERROR            | 'Failed'
     }
 
-    def getObjectUnderTestWithModelSyncDisabled() {
-        def objectUnderTest = getObjectUnderTest()
-        objectUnderTest.syncModulesAndCreateAnchor(*_) >> null
-        return objectUnderTest
-    }
-
     def getObjectUnderTest() {
         return Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations,
-            mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockModuleSyncService))
+            mockCpsModuleService, mockCpsAdminService, mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence,
+            mockModuleSyncService, stubbedNetworkCmProxyCmHandlerQueryService))
     }
 }
index 55a1a8d..6ba2a2c 100644 (file)
 
 package org.onap.cps.ncmp.api.impl
 
-import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
+import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
 import org.onap.cps.ncmp.api.inventory.CmHandleState
 import org.onap.cps.ncmp.api.inventory.CompositeState
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
+import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters
+import org.onap.cps.ncmp.api.models.ConditionApiProperties
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.ncmp.api.inventory.sync.ModuleSyncService
+import org.onap.cps.spi.model.CmHandleQueryParameters
+import org.onap.cps.spi.model.ConditionProperties
 import spock.lang.Shared
 
 import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
@@ -61,6 +65,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def mockInventoryPersistence = Mock(InventoryPersistence)
     def mockModuleSyncService = Mock(ModuleSyncService)
     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
+    def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandlerQueryService)
 
     def NO_TOPIC = null
     def NO_REQUEST_ID = null
@@ -70,7 +75,8 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
 
     def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations,
-        mockCpsModuleService, mockCpsAdminService, nullNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockModuleSyncService)
+        mockCpsModuleService, mockCpsAdminService, nullNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence,
+        mockModuleSyncService, mockCpsCmHandlerQueryService)
 
     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
@@ -160,13 +166,6 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             0 * mockCpsModuleService.getYangResourcesModuleReferences(*_)
     }
 
-    def 'Get cm handle identifiers for the given module names.'() {
-        when: 'execute a cm handle search for the given module names'
-            objectUnderTest.executeCmHandleHasAllModulesSearch(['some-module-name'])
-        then: 'get anchor identifiers is invoked  with the expected parameters'
-            1 * mockCpsAdminService.queryAnchorNames('NFP-Operational', ['some-module-name'])
-    }
-
     def 'Get a cm handle.'() {
         given: 'the system returns a yang modelled cm handle'
             def dmiServiceName = 'some service name'
@@ -241,8 +240,28 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
         then: 'validate params for creating anchor and list elements'
             1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
                 '/dmi-registry', '{"cm-handles":[{"id":"some-cm-handle-id",' +
-                '"additional-properties":[],"public-properties":[]}]}', null)
-            1 * mockCpsAdminService.createAnchor('NFP-Operational', null,
-                'some-cm-handle-id')
+                '"state":{"cm-handle-state":"ADVISED"},'
+                + '"additional-properties":[],"public-properties":[]}]}', null)
+    }
+
+    def 'Execute cm handle id search'() {
+        given: 'valid CmHandleQueryApiParameters input'
+            def cmHandleQueryApiParameters = new CmHandleQueryApiParameters()
+            def conditionApiProperties = new ConditionApiProperties()
+            conditionApiProperties.conditionName = 'hasAllModules'
+            conditionApiProperties.conditionParameters = [[moduleName: 'module-name-1']]
+            cmHandleQueryApiParameters.cmHandleQueryParameters = [conditionApiProperties]
+        and: 'valid CmHandleQueryParameters input'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def conditionProperties = new ConditionProperties()
+            conditionProperties.conditionName = 'hasAllModules'
+            conditionProperties.conditionParameters = [[moduleName: 'module-name-1']]
+            cmHandleQueryParameters.cmHandleQueryParameters = [conditionProperties]
+        and: 'query cm handle method return with a data node list'
+            mockCpsCmHandlerQueryService.queryCmHandles(cmHandleQueryParameters) >> [new DataNode(leaves: [id: 'cm-handle-id-1'])]
+        when: 'execute cm handle search is called'
+            def result = objectUnderTest.executeCmHandleIdSearch(cmHandleQueryApiParameters)
+        then: 'result is the same collection as returned by the CPS Data Service'
+            assert result == ['cm-handle-id-1'] as Set
     }
 }
index aa6bf1a..31f179a 100644 (file)
 
 package org.onap.cps.ncmp.api.impl.async
 
-import org.apache.kafka.clients.consumer.ConsumerConfig
-import org.apache.kafka.clients.producer.ProducerConfig
-import org.apache.kafka.common.serialization.StringSerializer
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.apache.kafka.clients.consumer.KafkaConsumer
 import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.api.utils.MessagingSpec
 import org.onap.cps.ncmp.event.model.DmiAsyncRequestResponseEvent
 import org.onap.cps.ncmp.event.model.NcmpAsyncRequestResponseEvent
-import org.springframework.kafka.core.DefaultKafkaProducerFactory
-import org.springframework.kafka.core.KafkaTemplate
-import org.springframework.kafka.support.serializer.JsonSerializer
-import org.testcontainers.containers.KafkaContainer
-import org.testcontainers.spock.Testcontainers
-import org.testcontainers.utility.DockerImageName
-
-import java.time.Duration
-import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.ncmp.utils.TestUtils
 import org.onap.cps.utils.JsonObjectMapper
-import org.apache.kafka.clients.consumer.KafkaConsumer
-import org.apache.kafka.common.serialization.StringDeserializer
-import org.onap.cps.ncmp.utils.TestUtils;
-import org.springframework.boot.test.context.SpringBootTest
 import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.annotation.DirtiesContext
-import org.springframework.test.context.DynamicPropertyRegistry
-import org.springframework.test.context.DynamicPropertySource
-import spock.lang.Specification
+import org.testcontainers.spock.Testcontainers
+
+import java.time.Duration
 
-@SpringBootTest(classes = [NcmpAsyncRequestResponseEventProducer, NcmpAsyncRequestResponseEventConsumer])
+@SpringBootTest(classes = [NcmpAsyncRequestResponseEventProducer, NcmpAsyncRequestResponseEventConsumer, ObjectMapper, JsonObjectMapper])
 @Testcontainers
 @DirtiesContext
-class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends Specification {
-
-    static kafkaTestContainer = new KafkaContainer(
-        DockerImageName.parse('confluentinc/cp-kafka:6.2.1')
-    )
-
-    static {
-        Runtime.getRuntime().addShutdownHook(new Thread(kafkaTestContainer::stop))
-    }
-
-    def setupSpec() {
-        kafkaTestContainer.start()
-    }
-
-    def producerConfigProperties = [
-        (ProducerConfig.BOOTSTRAP_SERVERS_CONFIG)      : kafkaTestContainer.getBootstrapServers().split(',')[0],
-        (ProducerConfig.RETRIES_CONFIG)                : 0,
-        (ProducerConfig.BATCH_SIZE_CONFIG)             : 16384,
-        (ProducerConfig.LINGER_MS_CONFIG)              : 1,
-        (ProducerConfig.BUFFER_MEMORY_CONFIG)          : 33554432,
-        (ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG)   : StringSerializer,
-        (ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG) : JsonSerializer
-    ]
-
-    def consumerConfigProperties = [
-        (ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)       : kafkaTestContainer.getBootstrapServers().split(',')[0],
-        (ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG)  : StringDeserializer,
-        (ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG): StringDeserializer,
-        (ConsumerConfig.AUTO_OFFSET_RESET_CONFIG)       : 'earliest',
-        (ConsumerConfig.GROUP_ID_CONFIG)                : 'test'
-    ]
-
-    def kafkaTemplate = new KafkaTemplate<>(new DefaultKafkaProducerFactory<Integer, String>(producerConfigProperties))
+class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends MessagingSpec {
 
     @SpringBean
     NcmpAsyncRequestResponseEventProducer cpsAsyncRequestResponseEventProducerService =
@@ -96,9 +54,10 @@ class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends Specification
             new NcmpAsyncRequestResponseEventConsumer(cpsAsyncRequestResponseEventProducerService,
                     ncmpAsyncRequestResponseEventMapper)
 
-    def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+    @Autowired
+    JsonObjectMapper jsonObjectMapper
 
-    def kafkaConsumer = new KafkaConsumer<>(getConsumerConfigProperties())
+    def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('test'))
 
     def 'Consume and forward valid message'() {
         given: 'consumer has a subscription'
@@ -118,9 +77,4 @@ class NcmpAsyncRequestResponseEventProducerIntegrationSpec extends Specification
                     NcmpAsyncRequestResponseEvent).getForwardedEvent().getEventId())
     }
 
-    @DynamicPropertySource
-    static void registerKafkaProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
-        dynamicPropertyRegistry.add('spring.kafka.bootstrap-servers', kafkaTestContainer::getBootstrapServers)
-    }
-
 }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/NcmpEventsPublisherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/NcmpEventsPublisherSpec.groovy
new file mode 100644 (file)
index 0000000..774a465
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.impl.event
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.apache.kafka.clients.consumer.KafkaConsumer
+import org.onap.cps.ncmp.api.utils.MessagingSpec
+import org.onap.cps.ncmp.utils.TestUtils
+import org.onap.cps.utils.JsonObjectMapper
+import org.onap.ncmp.cmhandle.lcm.event.Event
+import org.onap.ncmp.cmhandle.lcm.event.NcmpEvent
+import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.annotation.DirtiesContext
+import org.testcontainers.spock.Testcontainers
+
+import java.time.Duration
+
+@SpringBootTest(classes = [NcmpEventsPublisher, ObjectMapper, JsonObjectMapper])
+@Testcontainers
+@DirtiesContext
+class NcmpEventsPublisherSpec extends MessagingSpec {
+
+    def kafkaConsumer = new KafkaConsumer<>(consumerConfigProperties('ncmp-group'))
+
+    def testTopic = 'ncmp-events-test'
+
+    @SpringBean
+    NcmpEventsPublisher ncmpEventsPublisher = new NcmpEventsPublisher(kafkaTemplate)
+
+    @Autowired
+    JsonObjectMapper jsonObjectMapper
+
+
+    def 'Produce and Consume Ncmp Event'() {
+        given: 'event key and event data'
+            def eventKey = 'ncmp'
+            def eventData = new NcmpEvent(eventId: 'test-uuid',
+                eventCorrelationId: 'cmhandle-as-correlationid',
+                eventSchema: URI.create('org.onap.ncmp.cmhandle.lcm.event:v1'),
+                eventSource: URI.create('org.onap.ncmp'),
+                eventTime: '2022-12-31T20:30:40.000+0000',
+                eventType: 'org.onap.ncmp.cmhandle.lcm.event',
+                event: new Event(cmHandleId: 'cmhandle-test', cmhandleState: 'READY', operation: 'CREATE', cmhandleProperties: [['publicProperty1': 'value1'], ['publicProperty2': 'value2']]))
+        and: 'we have an expected NcmpEvent'
+            def expectedJsonString = TestUtils.getResourceFileContent('expectedNcmpEvent.json')
+            def expectedNcmpEvent = jsonObjectMapper.convertJsonString(expectedJsonString, NcmpEvent.class)
+        and: 'consumer has a subscription'
+            kafkaConsumer.subscribe([testTopic] as List<String>)
+        when: 'an event is published'
+            ncmpEventsPublisher.publishEvent(testTopic, eventKey, eventData)
+        and: 'topic is polled'
+            def records = kafkaConsumer.poll(Duration.ofMillis(1500))
+        then: 'no exception is thrown'
+            noExceptionThrown()
+        and: 'poll returns one record'
+            assert records.size() == 1
+        and: 'record key matches the expected event key'
+            def record = records.iterator().next()
+            assert eventKey == record.key
+        and: 'record matches the expected event'
+            assert expectedNcmpEvent == jsonObjectMapper.convertJsonString(record.value, NcmpEvent.class)
+
+    }
+}
index b638eec..a2ebcb5 100644 (file)
@@ -88,7 +88,7 @@ class InventoryPersistenceSpec extends Specification {
         where: 'the following parameters are used'
             scenario                    | childDataNodes                                || expectedDmiProperties                               || expectedPublicProperties                              || expectedCompositeState
             'no properties'             | []                                            || []                                                  || []                                                    || null
-            'DMI and public properties' | childDataNodesForCmHandleWithAllProperties    || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null
+            'DMI and public properties' | childDataNodesForCmHandleWithAllProperties    || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")]   || null
             'just DMI properties'       | childDataNodesForCmHandleWithDMIProperties    || [new YangModelCmHandle.Property("name1", "value1")] || []                                                    || null
             'just public properties'    | childDataNodesForCmHandleWithPublicProperties || []                                                  || [new YangModelCmHandle.Property("name2", "value2")]   || null
             'with state details'        | childDataNodesForCmHandleWithState            || []                                                  || []                                                    || CmHandleState.ADVISED
@@ -105,7 +105,7 @@ class InventoryPersistenceSpec extends Specification {
 
     def "Handling missing service names as null CPS-1043."() {
         given: 'the cps data service returns a data node from the DMI registry with empty child and leaf attributes'
-            def dataNode = new DataNode(childDataNodes:[], leaves: [:])
+            def dataNode = new DataNode(childDataNodes:[], leaves: ["cm-handle-state":"ADVISED"])
             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', xpath, INCLUDE_ALL_DESCENDANTS) >> dataNode
         when: 'retrieving the yang modelled cm handle'
             def result = objectUnderTest.getYangModelCmHandle(cmHandleId)
index 37fdbee..8050a57 100644 (file)
 
 package org.onap.cps.ncmp.api.inventory.sync
 
+import org.onap.cps.api.CpsAdminService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleState
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.spi.model.ModuleReference
 import spock.lang.Specification
@@ -32,8 +34,9 @@ class ModuleSyncServiceSpec extends Specification {
 
     def mockCpsModuleService = Mock(CpsModuleService)
     def mockDmiModelOperations = Mock(DmiModelOperations)
+    def mockCpsAdminService = Mock(CpsAdminService)
 
-    def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService)
+    def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService, mockCpsAdminService)
 
     def expectedDataspaceName = 'NFP-Operational'
 
@@ -42,7 +45,7 @@ class ModuleSyncServiceSpec extends Specification {
             def ncmpServiceCmHandle = new NcmpServiceCmHandle()
             def dmiServiceName = 'some service name'
             ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1'
-            def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '' , '', ncmpServiceCmHandle)
+            def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '' , '', CmHandleState.ADVISED, ncmpServiceCmHandle)
         and: 'DMI operations returns some module references'
             def moduleReferences =  [ new ModuleReference(moduleName:'module1',revision:'1'),
                                                             new ModuleReference(moduleName:'module2',revision:'2') ]
@@ -50,17 +53,19 @@ class ModuleSyncServiceSpec extends Specification {
         and: 'CPS-Core returns list of existing module resources'
             mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps)
         and: 'DMI-Plugin returns resource(s) for "new" module(s)'
-            mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> yangResourceToContentMap
+            mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, [new ModuleReference('module1', '1')]) >> newModuleNameContentToMap
         when: 'module sync is triggered'
             mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences)
-            def result = objectUnderTest.syncAndCreateSchemaSet(yangModelCmHandle)
-        then: 'the resulting schema set name is the same as the cm handle id'
-            assert result == 'cmHandleId-1'
+            objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
+        then: 'create schema set from module is invoked with correct parameters'
+            1 * mockCpsModuleService.createSchemaSetFromModules('NFP-Operational', 'cmHandleId-1', newModuleNameContentToMap, existingModuleReferencesInCps)
+        and: 'anchor is created with the correct parameters'
+            1 * mockCpsAdminService.createAnchor('NFP-Operational', 'cmHandleId-1', 'cmHandleId-1')
         where: 'the following parameters are used'
-            scenario             | existingModuleResourcesInCps           | identifiedNewModuleReferences | yangResourceToContentMap
-            'one new module'     | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']]           | [module1: 'some yang source']
-            'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']]           | [module1: 'some yang source']
-            'no new module'      | [['module1' : '1'], ['module2' : '2']] | []                            | [:]
+            scenario             | existingModuleResourcesInCps           | identifiedNewModuleReferences | newModuleNameContentToMap       | existingModuleReferencesInCps
+            'one new module'     | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']]           | [module1: 'some yang source']   | [new ModuleReference(moduleName:'module2',revision:'2')]
+            'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']]           | [module1: 'some yang source']   | [new ModuleReference(moduleName:'module2',revision:'2')]
+            'no new module'      | [['module1' : '1'], ['module2' : '2']] | []                            | [:]                             | [new ModuleReference(moduleName:'module1',revision:'1'), new ModuleReference(moduleName:'module2',revision:'2')]
     }
 
     def toModuleReference(moduleReferenceAsMap) {
index bcfe47f..97bea09 100644 (file)
@@ -52,7 +52,7 @@ class ModuleSyncSpec extends Specification {
         then: 'the inventory persistence cm handle returns a composite state for the first cm handle'
             1 * mockInventoryPersistence.getCmHandleState('some-cm-handle') >> compositeState1
         and: 'module sync service syncs the first cm handle and creates a schema set'
-            1 * mockModuleSyncService.syncAndCreateSchemaSet(yangModelCmHandle1)
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle1)
         and: 'the composite state cm handle state is now READY'
             assert compositeState1.getCmHandleState() == CmHandleState.READY
         and: 'the first cm handle state is updated'
@@ -60,7 +60,7 @@ class ModuleSyncSpec extends Specification {
         then: 'the inventory persistence cm handle returns a composite state for the second cm handle'
             mockInventoryPersistence.getCmHandleState('some-cm-handle-2') >> compositeState2
         and: 'module sync service syncs the second cm handle and creates a schema set'
-            1 * mockModuleSyncService.syncAndCreateSchemaSet(yangModelCmHandle2)
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle2)
         and: 'the composite state cm handle state is now READY'
             assert compositeState2.getCmHandleState() == CmHandleState.READY
         and: 'the second cm handle state is updated'
@@ -78,7 +78,7 @@ class ModuleSyncSpec extends Specification {
         then: 'the inventory persistence cm handle returns a composite state for the cm handle'
             1 * mockInventoryPersistence.getCmHandleState('some-cm-handle') >> compositeState
         and: 'module sync service attempts to sync the cm handle and throws an exception'
-            1 * mockModuleSyncService.syncAndCreateSchemaSet(*_) >> { throw new Exception('some exception') }
+            1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') }
         and: 'the composite state cm handle state is now LOCKED'
             assert compositeState.getCmHandleState() == CmHandleState.LOCKED
         and: 'update lock reason, details and attempts is invoked'
index 7bbc3d7..cdfcf59 100644 (file)
@@ -21,6 +21,8 @@
 package org.onap.cps.ncmp.api.models
 
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.ncmp.cmhandle.lcm.event.Event
 import spock.lang.Specification
 
 import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA
@@ -35,7 +37,7 @@ class YangModelCmHandleSpec extends Specification {
             ncmpServiceCmHandle.dmiProperties = [myDmiProperty:'value1']
             ncmpServiceCmHandle.publicProperties = [myPublicProperty:'value2']
         when: 'it is converted to a yang model cm handle'
-            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('','','', ncmpServiceCmHandle)
+            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle('','','', CmHandleState.ADVISED, ncmpServiceCmHandle)
         then: 'the result has the right size'
             assert objectUnderTest.dmiProperties.size() == 1
         and: 'the DMI property in the result has the correct name and value'
@@ -48,7 +50,7 @@ class YangModelCmHandleSpec extends Specification {
 
     def 'Resolve DMI service name: #scenario and #requiredService service require.'() {
         given: 'a yang model cm handle'
-            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'))
+            def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, CmHandleState.ADVISED, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'))
         expect:
             assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService
         where:
index 964826b..b3ea3b8 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.onap.cps.ncmp.api.utils
 
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+
 import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
 
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
@@ -33,7 +35,7 @@ class DmiServiceUrlBuilderSpec extends Specification {
 
     @Shared
     YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName',
-            'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'))
+            'dmiDataServiceName', 'dmiModuleServiceName', CmHandleState.ADVISED , new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'))
 
     NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties()
 
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/MessagingSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/utils/MessagingSpec.groovy
new file mode 100644 (file)
index 0000000..097834a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2022 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an 'AS IS' BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.utils
+
+import org.apache.kafka.common.serialization.StringDeserializer
+import org.apache.kafka.common.serialization.StringSerializer
+import org.springframework.kafka.core.DefaultKafkaProducerFactory
+import org.springframework.kafka.core.KafkaTemplate
+import org.springframework.kafka.support.serializer.JsonSerializer
+import org.springframework.test.context.DynamicPropertyRegistry
+import org.springframework.test.context.DynamicPropertySource
+import org.testcontainers.containers.KafkaContainer
+import org.testcontainers.utility.DockerImageName
+import spock.lang.Specification
+
+class MessagingSpec extends Specification {
+
+    static {
+        Runtime.getRuntime().addShutdownHook(new Thread(kafkaTestContainer::stop))
+    }
+
+    def setupSpec() {
+        kafkaTestContainer.start()
+    }
+
+    static kafkaTestContainer = new KafkaContainer(DockerImageName.parse('confluentinc/cp-kafka:6.2.1'))
+
+    def producerConfigProperties() {
+        return [('bootstrap.servers'): kafkaTestContainer.getBootstrapServers().split(',')[0],
+                ('retries')          : 0,
+                ('batch-size')       : 16384,
+                ('linger.ms')        : 1,
+                ('buffer.memory')    : 33554432,
+                ('key.serializer')   : StringSerializer,
+                ('value.serializer') : JsonSerializer]
+    }
+
+    def consumerConfigProperties(consumerGroupId) {
+        return [('bootstrap.servers') : kafkaTestContainer.getBootstrapServers().split(',')[0],
+                ('key.deserializer')  : StringDeserializer,
+                ('value.deserializer'): StringDeserializer,
+                ('auto.offset.reset') : 'earliest',
+                ('group.id')          : consumerGroupId
+        ]
+    }
+
+    def kafkaTemplate = new KafkaTemplate<>(new DefaultKafkaProducerFactory<Integer, String>(producerConfigProperties()))
+
+    @DynamicPropertySource
+    static void registerKafkaProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
+        dynamicPropertyRegistry.add('spring.kafka.bootstrap-servers', kafkaTestContainer::getBootstrapServers)
+    }
+}
diff --git a/cps-ncmp-service/src/test/resources/expectedNcmpEvent.json b/cps-ncmp-service/src/test/resources/expectedNcmpEvent.json
new file mode 100644 (file)
index 0000000..903bc3a
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "eventId": "test-uuid",
+  "eventCorrelationId": "cmhandle-as-correlationid",
+  "eventTime": "2022-12-31T20:30:40.000+0000",
+  "eventSource": "org.onap.ncmp",
+  "eventType": "org.onap.ncmp.cmhandle.lcm.event",
+  "eventSchema": "org.onap.ncmp.cmhandle.lcm.event:v1",
+  "event": {
+    "cmHandleId": "cmhandle-test",
+    "operation": "CREATE",
+    "cmhandle-state": "READY",
+    "cmhandle-properties": [
+      {
+        "publicProperty1": "value1"
+      },
+      {
+        "publicProperty2": "value2"
+      }
+    ]
+  }
+}
\ No newline at end of file
index 2e7bb7e..20a39f9 100755 (executable)
 package org.onap.cps.spi.impl;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 import javax.transaction.Transactional;
-import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.entities.AnchorEntity;
 import org.onap.cps.spi.entities.DataspaceEntity;
@@ -35,26 +36,25 @@ import org.onap.cps.spi.entities.SchemaSetEntity;
 import org.onap.cps.spi.entities.YangResourceModuleReference;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.DataspaceInUseException;
+import org.onap.cps.spi.exceptions.DataspaceNotFoundException;
 import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException;
 import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.CmHandleQueryParameters;
 import org.onap.cps.spi.repository.AnchorRepository;
 import org.onap.cps.spi.repository.DataspaceRepository;
-import org.onap.cps.spi.repository.ModuleReferenceRepository;
 import org.onap.cps.spi.repository.SchemaSetRepository;
 import org.onap.cps.spi.repository.YangResourceRepository;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.stereotype.Component;
 
+@Slf4j
 @Component
-@AllArgsConstructor
+@RequiredArgsConstructor
 public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceService {
 
     private final DataspaceRepository dataspaceRepository;
     private final AnchorRepository anchorRepository;
     private final SchemaSetRepository schemaSetRepository;
     private final YangResourceRepository yangResourceRepository;
-    private final ModuleReferenceRepository moduleReferenceRepository;
 
     @Override
     public void createDataspace(final String dataspaceName) {
@@ -117,7 +117,13 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
 
     @Override
     public Collection<Anchor> queryAnchors(final String dataspaceName, final Collection<String> inputModuleNames) {
-        validateDataspaceAndModuleNames(dataspaceName, inputModuleNames);
+        try {
+            validateDataspaceAndModuleNames(dataspaceName, inputModuleNames);
+        } catch (DataspaceNotFoundException | ModuleNamesNotFoundException  e) {
+            log.info("Module search encountered unknown dataspace or modulename, treating this as nothing found");
+            return Collections.emptySet();
+        }
+
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final Collection<AnchorEntity> anchorEntities = anchorRepository
             .getAnchorsByDataspaceIdAndModuleNames(dataspaceEntity.getId(), inputModuleNames, inputModuleNames.size());
@@ -136,11 +142,6 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
         anchorRepository.delete(anchorEntity);
     }
 
-    @Override
-    public Set<String> queryCmHandles(final CmHandleQueryParameters cmHandleQueryParameters) {
-        return moduleReferenceRepository.queryCmHandles(cmHandleQueryParameters);
-    }
-
     private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) {
         final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
index ded234b..117cb43 100644 (file)
@@ -48,6 +48,8 @@ import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.entities.AnchorEntity;
 import org.onap.cps.spi.entities.DataspaceEntity;
 import org.onap.cps.spi.entities.FragmentEntity;
+import org.onap.cps.spi.entities.SchemaSetEntity;
+import org.onap.cps.spi.entities.YangResourceEntity;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.ConcurrencyException;
 import org.onap.cps.spi.exceptions.CpsAdminException;
@@ -60,6 +62,8 @@ import org.onap.cps.spi.repository.DataspaceRepository;
 import org.onap.cps.spi.repository.FragmentRepository;
 import org.onap.cps.spi.utils.SessionManager;
 import org.onap.cps.utils.JsonObjectMapper;
+import org.onap.cps.yang.YangTextSchemaSourceSetBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.stereotype.Service;
 
@@ -85,33 +89,33 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     @Override
     @Transactional
     public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath,
-        final DataNode newChildDataNode) {
-        addChildDataNodes(dataspaceName, anchorName, parentNodeXpath,  Collections.singleton(newChildDataNode));
+                                 final DataNode newChildDataNode) {
+        addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, Collections.singleton(newChildDataNode));
     }
 
     @Override
     @Transactional
     public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
-        final Collection<DataNode> newListElements) {
+                                final Collection<DataNode> newListElements) {
         addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements);
     }
 
     private void addChildDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
-                                final Collection<DataNode> newChildren) {
+                                   final Collection<DataNode> newChildren) {
         final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
         try {
             for (final DataNode newChildAsDataNode : newChildren) {
                 final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(
-                    parentFragmentEntity.getDataspace(),
-                    parentFragmentEntity.getAnchor(),
-                    newChildAsDataNode);
+                        parentFragmentEntity.getDataspace(),
+                        parentFragmentEntity.getAnchor(),
+                        newChildAsDataNode);
                 newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
                 fragmentRepository.save(newChildAsFragmentEntity);
             }
         } catch (final DataIntegrityViolationException exception) {
             final List<String> conflictXpaths = newChildren.stream()
-                .map(DataNode::getXpath)
-                .collect(Collectors.toList());
+                    .map(DataNode::getXpath)
+                    .collect(Collectors.toList());
             throw AlreadyDefinedException.forDataNodes(conflictXpaths, anchorName, exception);
         }
     }
@@ -121,7 +125,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
         final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
-            dataNode);
+                dataNode);
         try {
             fragmentRepository.save(fragmentEntity);
         } catch (final DataIntegrityViolationException exception) {
@@ -139,13 +143,13 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
      * @return a Fragment built from current DataNode
      */
     private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity,
-        final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) {
+                             final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) {
         final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted);
         final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder();
         for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) {
             final FragmentEntity childFragment =
-                convertToFragmentWithAllDescendants(parentFragment.getDataspace(), parentFragment.getAnchor(),
-                    childDataNode);
+                    convertToFragmentWithAllDescendants(parentFragment.getDataspace(), parentFragment.getAnchor(),
+                            childDataNode);
             childFragmentsImmutableSetBuilder.add(childFragment);
         }
         parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build());
@@ -153,24 +157,24 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity,
-        final AnchorEntity anchorEntity, final DataNode dataNode) {
+                                            final AnchorEntity anchorEntity, final DataNode dataNode) {
         return FragmentEntity.builder()
-            .dataspace(dataspaceEntity)
-            .anchor(anchorEntity)
-            .xpath(dataNode.getXpath())
-            .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves()))
-            .build();
+                .dataspace(dataspaceEntity)
+                .anchor(anchorEntity)
+                .xpath(dataNode.getXpath())
+                .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves()))
+                .build();
     }
 
     @Override
     public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
-        final FetchDescendantsOption fetchDescendantsOption) {
+                                final FetchDescendantsOption fetchDescendantsOption) {
         final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
         return toDataNode(fragmentEntity, fetchDescendantsOption);
     }
 
     private FragmentEntity getFragmentByXpath(final String dataspaceName, final String anchorName,
-        final String xpath) {
+                                              final String xpath) {
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
         if (isRootXpath(xpath)) {
@@ -189,7 +193,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     @Override
     public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath,
-        final FetchDescendantsOption fetchDescendantsOption) {
+                                         final FetchDescendantsOption fetchDescendantsOption) {
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
         final CpsPathQuery cpsPathQuery;
@@ -199,15 +203,15 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             throw new CpsPathException(e.getMessage());
         }
         List<FragmentEntity> fragmentEntities =
-            fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery);
+                fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery);
         if (cpsPathQuery.hasAncestorAxis()) {
             final Set<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
-            fragmentEntities = ancestorXpaths.isEmpty()
-                ? Collections.emptyList() : fragmentRepository.findAllByAnchorAndXpathIn(anchorEntity, ancestorXpaths);
+            fragmentEntities = ancestorXpaths.isEmpty() ? Collections.emptyList()
+                    : fragmentRepository.findAllByAnchorAndXpathIn(anchorEntity, ancestorXpaths);
         }
         return fragmentEntities.stream()
-            .map(fragmentEntity -> toDataNode(fragmentEntity, fetchDescendantsOption))
-            .collect(Collectors.toUnmodifiableList());
+                .map(fragmentEntity -> toDataNode(fragmentEntity, fetchDescendantsOption))
+                .collect(Collectors.toUnmodifiableList());
     }
 
     @Override
@@ -222,16 +226,16 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     @Override
     public void lockAnchor(final String sessionId, final String dataspaceName,
-                            final String anchorName, final Long timeoutInMilliseconds) {
+                           final String anchorName, final Long timeoutInMilliseconds) {
         sessionManager.lockAnchor(sessionId, dataspaceName, anchorName, timeoutInMilliseconds);
     }
 
     private static Set<String> processAncestorXpath(final List<FragmentEntity> fragmentEntities,
-        final CpsPathQuery cpsPathQuery) {
+                                                    final CpsPathQuery cpsPathQuery) {
         final Set<String> ancestorXpath = new HashSet<>();
         final Pattern pattern =
-            Pattern.compile("([\\s\\S]*\\/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
-                + REG_EX_FOR_OPTIONAL_LIST_INDEX + "\\/[\\s\\S]*");
+                Pattern.compile("([\\s\\S]*\\/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
+                        + REG_EX_FOR_OPTIONAL_LIST_INDEX + "\\/[\\s\\S]*");
         for (final FragmentEntity fragmentEntity : fragmentEntities) {
             final Matcher matcher = pattern.matcher(fragmentEntity.getXpath());
             if (matcher.matches()) {
@@ -242,31 +246,42 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private DataNode toDataNode(final FragmentEntity fragmentEntity,
-        final FetchDescendantsOption fetchDescendantsOption) {
+                                final FetchDescendantsOption fetchDescendantsOption) {
         final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption);
         Map<String, Object> leaves = new HashMap<>();
         if (fragmentEntity.getAttributes() != null) {
             leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class);
         }
         return new DataNodeBuilder()
-            .withXpath(fragmentEntity.getXpath())
-            .withLeaves(leaves)
-            .withChildDataNodes(childDataNodes).build();
+                .withModuleNamePrefix(getFirstModuleName(fragmentEntity))
+                .withXpath(fragmentEntity.getXpath())
+                .withLeaves(leaves)
+                .withChildDataNodes(childDataNodes).build();
+    }
+
+    private String getFirstModuleName(final FragmentEntity fragmentEntity) {
+        final SchemaSetEntity schemaSetEntity = fragmentEntity.getAnchor().getSchemaSet();
+        final Map<String, String> yangResourceNameToContent =
+                schemaSetEntity.getYangResources().stream().collect(
+                        Collectors.toMap(YangResourceEntity::getName, YangResourceEntity::getContent));
+        final SchemaContext schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent)
+                .getSchemaContext();
+        return schemaContext.getModules().iterator().next().getName();
     }
 
     private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
-        final FetchDescendantsOption fetchDescendantsOption) {
+                                             final FetchDescendantsOption fetchDescendantsOption) {
         if (fetchDescendantsOption == INCLUDE_ALL_DESCENDANTS) {
             return fragmentEntity.getChildFragments().stream()
-                .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption))
-                .collect(Collectors.toUnmodifiableList());
+                    .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption))
+                    .collect(Collectors.toUnmodifiableList());
         }
         return Collections.emptyList();
     }
 
     @Override
     public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath,
-        final Map<String, Object> leaves) {
+                                 final Map<String, Object> leaves) {
         final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
         fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves));
         fragmentRepository.save(fragmentEntity);
@@ -280,19 +295,19 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             fragmentRepository.save(fragmentEntity);
         } catch (final StaleStateException staleStateException) {
             throw new ConcurrencyException("Concurrent Transactions",
-                String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.",
-                    dataspaceName, anchorName, dataNode.getXpath()),
-                staleStateException);
+                    String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.",
+                            dataspaceName, anchorName, dataNode.getXpath()),
+                    staleStateException);
         }
     }
 
     private void replaceDataNodeTree(final FragmentEntity existingFragmentEntity,
-                                            final DataNode newDataNode) {
+                                     final DataNode newDataNode) {
 
         existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves()));
 
-        final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments()
-            .stream().collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity));
+        final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream()
+                .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity));
 
         final Collection<FragmentEntity> updatedChildFragments = new HashSet<>();
 
@@ -300,7 +315,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             final FragmentEntity childFragment;
             if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) {
                 childFragment = convertToFragmentWithAllDescendants(
-                    existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), newDataNodeChild);
+                        existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), newDataNodeChild);
             } else {
                 childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath());
                 replaceDataNodeTree(childFragment, newDataNodeChild);
@@ -318,14 +333,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         final FragmentEntity parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
         final String listElementXpathPrefix = getListElementXpathPrefix(newListElements);
         final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath =
-            extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix);
+                extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix);
         deleteListElements(parentEntity.getChildFragments(), existingListElementFragmentEntitiesByXPath);
         final Set<FragmentEntity> updatedChildFragmentEntities = new HashSet<>();
         for (final DataNode newListElement : newListElements) {
             final FragmentEntity existingListElementEntity =
-                existingListElementFragmentEntitiesByXPath.get(newListElement.getXpath());
+                    existingListElementFragmentEntitiesByXPath.get(newListElement.getXpath());
             final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, newListElement,
-                existingListElementEntity);
+                    existingListElementEntity);
 
             updatedChildFragmentEntities.add(entityToBeAdded);
         }
@@ -338,8 +353,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     public void deleteDataNodes(final String dataspaceName, final String anchorName) {
         final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
         anchorRepository.findByDataspaceAndName(dataspaceEntity, anchorName)
-            .ifPresent(
-                anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity)));
+                .ifPresent(
+                        anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity)));
     }
 
     @Override
@@ -356,7 +371,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath,
-        final boolean onlySupportListNodeDeletion) {
+                                final boolean onlySupportListNodeDeletion) {
         final String parentNodeXpath;
         FragmentEntity parentFragmentEntity = null;
         boolean targetDeleted = false;
@@ -372,7 +387,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
             final String lastXpathElement = targetXpath.substring(targetXpath.lastIndexOf('/'));
             final boolean isListElement = REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE
-                .matcher(lastXpathElement).find();
+                    .matcher(lastXpathElement).find();
             if (isListElement) {
                 targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath);
             } else {
@@ -385,9 +400,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         }
         if (!targetDeleted) {
             final String additionalInformation = onlySupportListNodeDeletion
-                ? "The target is probably not a List." : "";
+                    ? "The target is probably not a List." : "";
             throw new DataNodeNotFoundException(parentFragmentEntity.getDataspace().getName(),
-                parentFragmentEntity.getAnchor().getName(), targetXpath, additionalInformation);
+                    parentFragmentEntity.getAnchor().getName(), targetXpath, additionalInformation);
         }
     }
 
@@ -398,7 +413,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             return true;
         }
         if (parentFragmentEntity.getChildFragments()
-            .removeIf(fragment -> fragment.getXpath().equals(normalizedTargetXpath))) {
+                .removeIf(fragment -> fragment.getXpath().equals(normalizedTargetXpath))) {
             fragmentRepository.save(parentFragmentEntity);
             return true;
         }
@@ -409,7 +424,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         final String normalizedListXpath = CpsPathUtil.getNormalizedXpath(listXpath);
         final String deleteTargetXpathPrefix = normalizedListXpath + "[";
         if (parentFragmentEntity.getChildFragments()
-            .removeIf(fragment -> fragment.getXpath().startsWith(deleteTargetXpathPrefix))) {
+                .removeIf(fragment -> fragment.getXpath().startsWith(deleteTargetXpathPrefix))) {
             fragmentRepository.save(parentFragmentEntity);
             return true;
         }
@@ -417,26 +432,26 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private static void deleteListElements(
-        final Collection<FragmentEntity> fragmentEntities,
-        final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath) {
+            final Collection<FragmentEntity> fragmentEntities,
+            final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath) {
         fragmentEntities.removeAll(existingListElementFragmentEntitiesByXPath.values());
     }
 
     private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) {
         if (newListElements.isEmpty()) {
             throw new CpsAdminException("Invalid list replacement",
-                "Cannot replace list elements with empty collection");
+                    "Cannot replace list elements with empty collection");
         }
         final String firstChildNodeXpath = newListElements.iterator().next().getXpath();
         return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf('[') + 1);
     }
 
     private FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity,
-                                                            final DataNode newListElement,
-                                                            final FragmentEntity existingListElementEntity) {
+                                                     final DataNode newListElement,
+                                                     final FragmentEntity existingListElementEntity) {
         if (existingListElementEntity == null) {
             return convertToFragmentWithAllDescendants(
-                parentEntity.getDataspace(), parentEntity.getAnchor(), newListElement);
+                    parentEntity.getDataspace(), parentEntity.getAnchor(), newListElement);
         }
         if (newListElement.getChildDataNodes().isEmpty()) {
             copyAttributesFromNewListElement(existingListElementEntity, newListElement);
@@ -457,7 +472,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity,
-                                                         final DataNode newListElement) {
+                                                  final DataNode newListElement) {
         final FragmentEntity replacementFragmentEntity =
                 FragmentEntity.builder().attributes(jsonObjectMapper.asJsonString(
                         newListElement.getLeaves())).build();
@@ -465,10 +480,10 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     }
 
     private static Map<String, FragmentEntity> extractListElementFragmentEntitiesByXPath(
-        final Set<FragmentEntity> childEntities, final String listElementXpathPrefix) {
+            final Set<FragmentEntity> childEntities, final String listElementXpathPrefix) {
         return childEntities.stream()
-            .filter(fragmentEntity -> fragmentEntity.getXpath().startsWith(listElementXpathPrefix))
-            .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity));
+                .filter(fragmentEntity -> fragmentEntity.getXpath().startsWith(listElementXpathPrefix))
+                .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity));
     }
 
     private static boolean isRootXpath(final String xpath) {
index 3719256..cbeb1b7 100755 (executable)
@@ -66,6 +66,7 @@ import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyIn
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.retry.annotation.Backoff;
 import org.springframework.retry.annotation.Retryable;
+import org.springframework.retry.support.RetrySynchronizationManager;
 import org.springframework.stereotype.Component;
 
 @Slf4j
@@ -219,9 +220,12 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ
                         convertToDuplicatedYangResourceException(
                                 dataIntegrityViolationException, newYangResourceEntities);
                 convertedException.ifPresent(
-                    e ->  log.warn(
-                                "Cannot persist duplicated yang resource. "
-                                        + "System will attempt this method up to 5 times.", e));
+                        e -> {
+                            int retryCount = RetrySynchronizationManager.getContext() == null ? 0
+                                    : RetrySynchronizationManager.getContext().getRetryCount();
+                            log.warn("Cannot persist duplicated yang resource. System will attempt this method "
+                                    + "up to 5 times. Current retry count : {}", ++retryCount, e);
+                        });
                 throw convertedException.isPresent() ? convertedException.get() : dataIntegrityViolationException;
             }
         }
index 4bc9dd9..5e4de7f 100644 (file)
@@ -21,8 +21,6 @@
 package org.onap.cps.spi.repository;
 
 import java.util.Collection;
-import java.util.Set;
-import org.onap.cps.spi.model.CmHandleQueryParameters;
 import org.onap.cps.spi.model.ModuleReference;
 
 /**
@@ -32,13 +30,4 @@ public interface ModuleReferenceQuery {
 
     Collection<ModuleReference> identifyNewModuleReferences(
         final Collection<ModuleReference> moduleReferencesToCheck);
-
-    /**
-     * Query and return cm handles that match the given query parameters.
-     *
-     * @param cmHandleQueryParameters the cm handle query parameters
-     * @return collection of cm handle ids
-     */
-    Set<String> queryCmHandles(CmHandleQueryParameters cmHandleQueryParameters);
-
 }
index f85dea3..681bbcd 100644 (file)
@@ -24,19 +24,12 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
-import java.util.stream.Collectors;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.spi.CpsDataPersistenceService;
-import org.onap.cps.spi.FetchDescendantsOption;
-import org.onap.cps.spi.model.CmHandleQueryParameters;
-import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.ModuleReference;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -48,19 +41,17 @@ public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
     @PersistenceContext
     private EntityManager entityManager;
 
-    private final CpsDataPersistenceService cpsDataPersistenceService;
-
     @Override
     @SneakyThrows
     public Collection<ModuleReference> identifyNewModuleReferences(
-        final Collection<ModuleReference> moduleReferencesToCheck) {
+            final Collection<ModuleReference> moduleReferencesToCheck) {
 
         if (moduleReferencesToCheck == null || moduleReferencesToCheck.isEmpty()) {
             return Collections.emptyList();
         }
 
         final String tempTableName = "moduleReferencesToCheckTemp"
-            + UUID.randomUUID().toString().replace("-", "");
+                + UUID.randomUUID().toString().replace("-", "");
 
         createTemporaryTable(tempTableName);
         insertDataIntoTable(tempTableName, moduleReferencesToCheck);
@@ -68,56 +59,6 @@ public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
         return identifyNewModuleReferencesForCmHandle(tempTableName);
     }
 
-    /**
-     * Query and return cm handles that match the given query parameters.
-     *
-     * @param cmHandleQueryParameters the cm handle query parameters
-     * @return collection of cm handle ids
-     */
-    @Override
-    public Set<String> queryCmHandles(final CmHandleQueryParameters cmHandleQueryParameters) {
-
-        if (cmHandleQueryParameters.getPublicProperties().entrySet().isEmpty()) {
-            return getAllCmHandles();
-        }
-
-        final Collection<DataNode> amalgamatedQueryResult = new ArrayList<>();
-        int queryConditionCounter = 0;
-        for (final Map.Entry<String, String> entry : cmHandleQueryParameters.getPublicProperties().entrySet()) {
-            final StringBuilder cmHandlePath = new StringBuilder();
-            cmHandlePath.append("//public-properties[@name='").append(entry.getKey()).append("' ");
-            cmHandlePath.append("and @value='").append(entry.getValue()).append("']");
-            cmHandlePath.append("/ancestor::cm-handles");
-
-            final Collection<DataNode> singleConditionQueryResult =
-                cpsDataPersistenceService.queryDataNodes("NCMP-Admin",
-                "ncmp-dmi-registry", String.valueOf(cmHandlePath), FetchDescendantsOption.OMIT_DESCENDANTS);
-            if (++queryConditionCounter == 1) {
-                amalgamatedQueryResult.addAll(singleConditionQueryResult);
-            } else {
-                amalgamatedQueryResult.retainAll(singleConditionQueryResult);
-            }
-
-            if (amalgamatedQueryResult.isEmpty()) {
-                break;
-            }
-        }
-
-        return extractCmHandleIds(amalgamatedQueryResult);
-    }
-
-    private Set<String> getAllCmHandles() {
-        final Collection<DataNode> cmHandles = cpsDataPersistenceService.queryDataNodes("NCMP-Admin",
-            "ncmp-dmi-registry", "//public-properties/ancestor::cm-handles",
-            FetchDescendantsOption.OMIT_DESCENDANTS);
-        return extractCmHandleIds(cmHandles);
-    }
-
-    private Set<String> extractCmHandleIds(final Collection<DataNode> cmHandles) {
-        return cmHandles.stream().map(cmHandle -> cmHandle.getLeaves().get("id").toString())
-            .collect(Collectors.toSet());
-    }
-
     private void createTemporaryTable(final String tempTableName) {
         final StringBuilder sqlStringBuilder = new StringBuilder("CREATE TEMPORARY TABLE " + tempTableName + "(");
         sqlStringBuilder.append(" id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,");
@@ -149,11 +90,11 @@ public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
 
     private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) {
         final String sql = String.format(
-            "SELECT %1$s.module_name, %1$s.revision"
-                + " FROM %1$s LEFT JOIN yang_resource"
-                + " ON yang_resource.module_name=%1$s.module_name"
-                + " AND yang_resource.revision=%1$s.revision"
-                + " WHERE yang_resource.module_name IS NULL;", tempTableName);
+                "SELECT %1$s.module_name, %1$s.revision"
+                        + " FROM %1$s LEFT JOIN yang_resource"
+                        + " ON yang_resource.module_name=%1$s.module_name"
+                        + " AND yang_resource.revision=%1$s.revision"
+                        + " WHERE yang_resource.module_name IS NULL;", tempTableName);
 
         @SuppressWarnings("unchecked")
         final List<Object[]> resultsAsObjects = entityManager.createNativeQuery(sql).getResultList();
index 2de087f..e037350 100644 (file)
@@ -31,7 +31,6 @@ import org.onap.cps.spi.exceptions.DataspaceNotFoundException
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
 import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException
 import org.onap.cps.spi.model.Anchor
-import org.onap.cps.spi.model.CmHandleQueryParameters
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.test.context.jdbc.Sql
 import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
@@ -45,7 +44,6 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
     ObjectMapper objectMapper
 
     static final String SET_DATA = '/data/anchor.sql'
-    static final String SET_FRAGMENT_DATA = '/data/fragment.sql'
     static final String SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES = '/data/anchors-schemaset-modules.sql'
     static final String DATASPACE_WITH_NO_DATA = 'DATASPACE-002-NO-DATA'
     static final Integer DELETED_ANCHOR_ID = 3002
@@ -176,28 +174,17 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
     @Sql([CLEAR_DATA, SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES])
     def 'Query anchors that have #scenario.'() {
         when: 'all anchor are retrieved for the given dataspace name and module names'
-            def anchors = objectUnderTest.queryAnchors('dataspace-1', inputModuleNames)
+            def anchors = objectUnderTest.queryAnchors(inputDataspaceName, inputModuleNames)
         then: 'the expected anchors are returned'
             anchors.size() == expectedAnchors.size()
             anchors.containsAll(expectedAnchors)
         where: 'the following data is used'
-            scenario                           | inputModuleNames                                    || expectedAnchors
-            'one module'                       | ['module-name-1']                                   || [buildAnchor('anchor-2', 'dataspace-1', 'schema-set-2'), buildAnchor('anchor-1', 'dataspace-1', 'schema-set-1')]
-            'two modules'                      | ['module-name-1', 'module-name-2']                  || [buildAnchor('anchor-2', 'dataspace-1', 'schema-set-2'), buildAnchor('anchor-1', 'dataspace-1', 'schema-set-1')]
-            'no anchors for all three modules' | ['module-name-1', 'module-name-2', 'module-name-3'] || []
-    }
-
-    @Sql([CLEAR_DATA, SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES])
-    def 'Query all anchors for an #scenario.'() {
-        when: 'attempt to query anchors'
-            objectUnderTest.queryAnchors(dataspaceName, moduleNames)
-        then: 'the correct exception is thrown with the relevant details'
-            def thrownException = thrown(expectedException)
-            thrownException.details.contains(expectedMessageDetails)
-        where: 'the following data is used'
-            scenario                          | dataspaceName       | moduleNames                                || expectedException            | expectedMessageDetails  | messageDoesNotContain
-            'unknown dataspace'               | 'db-does-not-exist' | ['does-not-matter']                        || DataspaceNotFoundException   | 'db-does-not-exist'     | 'does-not-matter'
-            'unknown module and known module' | 'dataspace-1'       | ['module-name-1', 'module-does-not-exist'] || ModuleNamesNotFoundException | 'module-does-not-exist' | 'module-name-1'
+            scenario                           | inputDataspaceName  | inputModuleNames                                    || expectedAnchors
+            'one module'                       | 'dataspace-1'       | ['module-name-1']                                   || [buildAnchor('anchor-2', 'dataspace-1', 'schema-set-2'), buildAnchor('anchor-1', 'dataspace-1', 'schema-set-1')]
+            'two modules'                      | 'dataspace-1'       | ['module-name-1', 'module-name-2']                  || [buildAnchor('anchor-2', 'dataspace-1', 'schema-set-2'), buildAnchor('anchor-1', 'dataspace-1', 'schema-set-1')]
+            'no anchors for all three modules' | 'dataspace-1'       | ['module-name-1', 'module-name-2', 'module-name-3'] || []
+            'unknown dataspace'                | 'db-does-not-exist' | ['does-not-matter']                                 || []
+            'unknown module and known module'  | 'dataspace-1'       | ['module-name-1', 'module-does-not-exist']          || []
     }
 
     def buildAnchor(def anchorName, def dataspaceName, def SchemaSetName) {
@@ -225,21 +212,4 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
             'dataspace contains an anchor'  | 'DATASPACE-001' || DataspaceInUseException    | 'contains 2 anchor(s)'
             'dataspace contains schemasets' | 'DATASPACE-003' || DataspaceInUseException    | 'contains 1 schemaset(s)'
     }
-
-    @Sql([CLEAR_DATA, SET_FRAGMENT_DATA])
-    def 'Retrieve cm handle ids when #scenario.'() {
-        when: 'the service is invoked'
-            def cmHandleQueryParameters = new CmHandleQueryParameters()
-            cmHandleQueryParameters.setPublicProperties(publicProperties)
-            def returnedCmHandles = objectUnderTest.queryCmHandles(cmHandleQueryParameters)
-        then: 'the correct expected cm handles are returned'
-            returnedCmHandles == expectedCmHandleIds
-        where: 'the following data is used'
-            scenario                                       | publicProperties                                                                              || expectedCmHandleIds
-            'single matching property'                     | ['Contact' : 'newemailforstore@bookstore.com']                                                || ['PNFDemo2', 'PNFDemo', 'PNFDemo4'] as Set
-            'public property dont match'                   | ['wont_match' : 'wont_match']                                                                 || [] as Set
-            '2 properties, only one match (and)'           | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] as Set
-            '2 properties, no match (and)'                 | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': '']                                || [] as Set
-            'No public properties - return all cm handles' | [ : ]                                                                                         || ['PNFDemo3', 'PNFDemo', 'PNFDemo2', 'PNFDemo4'] as Set
-    }
 }
index a96b6af..bb80199 100644 (file)
@@ -22,7 +22,11 @@ package org.onap.cps.spi.impl
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.hibernate.StaleStateException
 import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.spi.entities.AnchorEntity
+import org.onap.cps.spi.entities.DataspaceEntity
 import org.onap.cps.spi.entities.FragmentEntity
+import org.onap.cps.spi.entities.SchemaSetEntity
+import org.onap.cps.spi.entities.YangResourceEntity
 import org.onap.cps.spi.exceptions.ConcurrencyException
 import org.onap.cps.spi.exceptions.DataValidationException
 import org.onap.cps.spi.model.DataNodeBuilder
@@ -31,6 +35,10 @@ import org.onap.cps.spi.repository.DataspaceRepository
 import org.onap.cps.spi.repository.FragmentRepository
 import org.onap.cps.spi.utils.SessionManager
 import org.onap.cps.utils.JsonObjectMapper
+import org.onap.cps.yang.YangTextSchemaSourceSet
+import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import spock.lang.Shared
 import spock.lang.Specification
 
 class CpsDataPersistenceServiceSpec extends Specification {
@@ -44,6 +52,25 @@ class CpsDataPersistenceServiceSpec extends Specification {
     def objectUnderTest = new CpsDataPersistenceServiceImpl(
             mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper,mockSessionManager)
 
+    @Shared
+    def NEW_RESOURCE_CONTENT = 'module stores {\n' +
+            '    yang-version 1.1;\n' +
+            '    namespace "org:onap:ccsdk:sample";\n' +
+            '\n' +
+            '    prefix book-store;\n' +
+            '\n' +
+            '    revision "2020-09-15" {\n' +
+            '        description\n' +
+            '        "Sample Model";\n' +
+            '    }' +
+            '}'
+
+    @Shared
+    def yangResourceSet = [new YangResourceEntity(moduleName: 'moduleName', content: NEW_RESOURCE_CONTENT,
+            name: 'sampleYangResource'
+    )] as Set
+
+
     def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() {
 
         def parentXpath = '/parent-01'
@@ -51,67 +78,68 @@ class CpsDataPersistenceServiceSpec extends Specification {
         def myAnchorName = 'my-anchor'
 
         given: 'data node object'
-        def submittedDataNode = new DataNodeBuilder()
-                .withXpath(parentXpath)
-                .withLeaves(['leaf-name': 'leaf-value'])
-                .build()
+            def submittedDataNode = new DataNodeBuilder()
+                    .withXpath(parentXpath)
+                    .withLeaves(['leaf-name': 'leaf-value'])
+                    .build()
         and: 'fragment to be updated'
-        mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
-            def fragmentEntity = new FragmentEntity()
-            fragmentEntity.setXpath(parentXpath)
-            fragmentEntity.setChildFragments(Collections.emptySet())
-            return fragmentEntity
-        }
+            mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
+                def fragmentEntity = new FragmentEntity()
+                fragmentEntity.setXpath(parentXpath)
+                fragmentEntity.setChildFragments(Collections.emptySet())
+                return fragmentEntity
+            }
         and: 'data node is concurrently updated by another transaction'
-        mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
+            mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") }
 
         when: 'attempt to update data node'
-        objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
+            objectUnderTest.replaceDataNodeTree(myDataspaceName, myAnchorName, submittedDataNode)
 
         then: 'concurrency exception is thrown'
-        def concurrencyException = thrown(ConcurrencyException)
-        assert concurrencyException.getDetails().contains(myDataspaceName)
-        assert concurrencyException.getDetails().contains(myAnchorName)
-        assert concurrencyException.getDetails().contains(parentXpath)
+            def concurrencyException = thrown(ConcurrencyException)
+            assert concurrencyException.getDetails().contains(myDataspaceName)
+            assert concurrencyException.getDetails().contains(myAnchorName)
+            assert concurrencyException.getDetails().contains(parentXpath)
     }
 
     def 'Retrieving a data node with a property JSON value of #scenario'() {
         given: 'a fragment with a property JSON value of #scenario'
         mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
             new FragmentEntity(childFragments: Collections.emptySet(),
-                    attributes: "{\"some attribute\": ${dataString}}")
+                    attributes: "{\"some attribute\": ${dataString}}",
+                    anchor: new AnchorEntity(schemaSet: new SchemaSetEntity(yangResources: yangResourceSet )))
         }
         when: 'getting the data node represented by this fragment'
-        def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
-                '/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+            def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
+                    '/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
         then: 'the leaf is of the correct value and data type'
-        def attributeValue = dataNode.leaves.get('some attribute')
-        assert attributeValue == expectedValue
-        assert attributeValue.class == expectedDataClass
+            def attributeValue = dataNode.leaves.get('some attribute')
+            assert attributeValue == expectedValue
+            assert attributeValue.class == expectedDataClass
         where: 'the following Data Type is passed'
-        scenario                              | dataString            || expectedValue     | expectedDataClass
-        'just numbers'                        | '15174'               || 15174             | Integer
-        'number with dot'                     | '15174.32'            || 15174.32          | Double
-        'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
-        'number with 0 value before dot'      | '0.32'                || 0.32              | Double
-        'number higher than max int'          | '2147483648'          || 2147483648        | Long
-        'just text'                           | '"Test"'              || 'Test'            | String
-        'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
-        'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
-        'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
-        'number as String'                    | '"12345"'             || '12345'           | String
+            scenario                              | dataString            || expectedValue     | expectedDataClass
+            'just numbers'                        | '15174'               || 15174             | Integer
+            'number with dot'                     | '15174.32'            || 15174.32          | Double
+            'number with 0 value after dot'       | '15174.0'             || 15174.0           | Double
+            'number with 0 value before dot'      | '0.32'                || 0.32              | Double
+            'number higher than max int'          | '2147483648'          || 2147483648        | Long
+            'just text'                           | '"Test"'              || 'Test'            | String
+            'number with exponent'                | '1.2345e5'            || 1.2345e5          | Double
+            'number higher than max int with dot' | '123456789101112.0'   || 123456789101112.0 | Double
+            'text and numbers'                    | '"String = \'1234\'"' || "String = '1234'" | String
+            'number as String'                    | '"12345"'             || '12345'           | String
     }
 
     def 'Retrieving a data node with invalid JSON'() {
         given: 'a fragment with invalid JSON'
-        mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
-            new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json')
-        }
+            mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> {
+                new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json')
+            }
         when: 'getting the data node represented by this fragment'
-        def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
-                '/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+            def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor',
+                    '/parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
         then: 'a data validation exception is thrown'
-        thrown(DataValidationException)
+            thrown(DataValidationException)
     }
 
     def 'start session'() {
index d1a6220..c406203 100644 (file)
@@ -25,6 +25,28 @@ INSERT INTO DATASPACE (ID, NAME) VALUES
 INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
     (2001, 'SCHEMA-SET-001', 1001);
 
+INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
+    (4001, 'TEST','', 'SAMPLECHECKSUM','TESTMODULENAME', 'SAMPLEREVISION');
+
+UPDATE YANG_RESOURCE SET
+content = 'module stores {
+               yang-version 1.1;
+               namespace "org:onap:ccsdk:sample";
+
+               prefix book-store;
+
+               revision "2020-09-15" {
+                   description
+                   "Sample Model";
+               }
+           }
+'
+where ID = 4001;
+
+
+INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
+    (2001, 4001);
+
 INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
     (1003, 'ANCHOR-004', 1001, 2001);
 
index 4106541..fd05900 100755 (executable)
@@ -27,6 +27,27 @@ INSERT INTO DATASPACE (ID, NAME) VALUES
 INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
     (2001, 'SCHEMA-SET-001', 1001);
 
+INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
+    (4001, 'TEST','', 'SAMPLECHECKSUM','TESTMODULENAME', 'SAMPLEREVISION');
+
+UPDATE YANG_RESOURCE SET
+content = 'module stores {
+               yang-version 1.1;
+               namespace "org:onap:ccsdk:sample";
+
+               prefix book-store;
+
+               revision "2020-09-15" {
+                   description
+                   "Sample Model";
+               }
+           }
+'
+where ID = 4001;
+
+INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
+    (2001, 4001);
+
 INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
     (3001, 'ANCHOR-001', 1001, 2001),
     (3003, 'ANCHOR-003', 1001, 2001),
index 2106f15..ab33732 100755 (executable)
 package org.onap.cps.api;
 
 import java.util.Collection;
-import java.util.Set;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.exceptions.CpsException;
 import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.CmHandleQueryParameters;
 
 /**
  * CPS Admin Service.
@@ -102,12 +100,4 @@ public interface CpsAdminService {
      *         given module names
      */
     Collection<String> queryAnchorNames(String dataspaceName, Collection<String> moduleNames);
-
-    /**
-     * Query and return cm handles that match the given query parameters.
-     *
-     * @param cmHandleQueryParameters the cm handle query parameters
-     * @return collection of cm handle ids
-     */
-    Set<String> queryCmHandles(CmHandleQueryParameters cmHandleQueryParameters);
 }
index 93c96ec..cde25a9 100644 (file)
@@ -210,5 +210,4 @@ public interface CpsDataService {
      * @param timeoutInMilliseconds lock attempt timeout in milliseconds
      */
     void lockAnchor(String sessionID, String dataspaceName, String anchorName, Long timeoutInMilliseconds);
-
 }
index 762754f..a67dfe5 100755 (executable)
@@ -24,20 +24,18 @@ package org.onap.cps.api.impl;
 
 import java.time.OffsetDateTime;
 import java.util.Collection;
-import java.util.Set;
 import java.util.stream.Collectors;
-import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
 import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.spi.CpsAdminPersistenceService;
 import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.CmHandleQueryParameters;
 import org.onap.cps.utils.CpsValidator;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 @Component("CpsAdminServiceImpl")
-@AllArgsConstructor(onConstructor = @__(@Lazy))
+@RequiredArgsConstructor(onConstructor = @__(@Lazy))
 public class CpsAdminServiceImpl implements CpsAdminService {
 
     private final CpsAdminPersistenceService cpsAdminPersistenceService;
@@ -93,9 +91,4 @@ public class CpsAdminServiceImpl implements CpsAdminService {
         final Collection<Anchor> anchors = cpsAdminPersistenceService.queryAnchors(dataspaceName, moduleNames);
         return anchors.stream().map(Anchor::getName).collect(Collectors.toList());
     }
-
-    @Override
-    public Set<String> queryCmHandles(final CmHandleQueryParameters cmHandleQueryParameters) {
-        return cpsAdminPersistenceService.queryCmHandles(cmHandleQueryParameters);
-    }
 }
index 25167e8..db2d2b2 100755 (executable)
 package org.onap.cps.spi;
 
 import java.util.Collection;
-import java.util.Set;
 import org.onap.cps.spi.exceptions.AlreadyDefinedException;
 import org.onap.cps.spi.model.Anchor;
-import org.onap.cps.spi.model.CmHandleQueryParameters;
 
 /*
     Service for handling CPS admin data.
@@ -76,7 +74,7 @@ public interface CpsAdminPersistenceService {
 
     /**
      * Query anchor names for the given module names in the provided dataspace.
-     *
+     * If dataspace or one of the given module names does not exists, return with an empty collection.
      *
      * @param dataspaceName dataspace name
      * @param moduleNames a collection of module names
@@ -101,12 +99,4 @@ public interface CpsAdminPersistenceService {
      * @param anchorName anchor name
      */
     void deleteAnchor(String dataspaceName, String anchorName);
-
-    /**
-     * Query and return cm handles that match the given query parameters.
-     *
-     * @param cmHandleQueryParameters the cm handle query parameters
-     * @return collection of cm handle ids
-     */
-    Set<String> queryCmHandles(CmHandleQueryParameters cmHandleQueryParameters);
 }
index fd660e6..b27a297 100644 (file)
@@ -173,5 +173,4 @@ public interface CpsDataPersistenceService {
      * @param timeoutInMilliseconds lock attempt timeout in milliseconds
      */
     void lockAnchor(String sessionID, String dataspaceName, String anchorName, Long timeoutInMilliseconds);
-
 }
index ff4e627..cf364db 100644 (file)
@@ -24,18 +24,18 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.Collections;
-import java.util.Map;
+import java.util.List;
 import javax.validation.Valid;
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Setter;
 
 @Setter
 @Getter
-@JsonInclude(Include.NON_NULL)
+@EqualsAndHashCode
+@JsonInclude(Include.NON_EMPTY)
 public class CmHandleQueryParameters {
-
-    @JsonProperty("publicCmHandleProperties")
+    @JsonProperty("cmHandleQueryParameters")
     @Valid
-    private Map<String, String> publicProperties = Collections.emptyMap();
-
+    private List<ConditionProperties> cmHandleQueryParameters = Collections.emptyList();
 }
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/ConditionProperties.java b/cps-service/src/main/java/org/onap/cps/spi/model/ConditionProperties.java
new file mode 100644 (file)
index 0000000..4eee7db
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.validation.Valid;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+@EqualsAndHashCode
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class ConditionProperties {
+    @JsonProperty("conditionName")
+    private String conditionName = "";
+
+    @JsonProperty("conditionParameters")
+    @Valid
+    private List<Map<String, String>> conditionParameters = Collections.emptyList();
+}
index 43aa06b..d80306b 100644 (file)
@@ -42,6 +42,7 @@ public class DataNode {
     private String anchorName;
     private ModuleReference moduleReference;
     private String xpath;
+    private String moduleNamePrefix;
     private Map<String, Object> leaves = Collections.emptyMap();
     private Collection<String> xpathsChildren;
     private Collection<DataNode> childDataNodes = Collections.emptySet();
index 4a9957d..f2bde03 100644 (file)
@@ -2,6 +2,7 @@
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Bell Canada. All rights reserved.
  *  Modifications Copyright (C) 2021 Pantheon.tech
+ *  Modifications Copyright (C) 2022 Nordix Foundation.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -45,6 +46,7 @@ public class DataNodeBuilder {
 
     private NormalizedNode<?, ?> normalizedNodeTree;
     private String xpath;
+    private String moduleNamePrefix;
     private String parentNodeXpath = "";
     private Map<String, Object> leaves = Collections.emptyMap();
     private Collection<DataNode> childDataNodes = Collections.emptySet();
@@ -83,6 +85,17 @@ public class DataNodeBuilder {
         return this;
     }
 
+    /**
+     * To use module name for prefix for creating {@link DataNode}.
+     *
+     * @param moduleNamePrefix module name as prefix
+     * @return DataNodeBuilder
+     */
+    public DataNodeBuilder withModuleNamePrefix(final String moduleNamePrefix) {
+        this.moduleNamePrefix = moduleNamePrefix;
+        return this;
+    }
+
     /**
      * To use attributes for creating {@link DataNode}.
      *
@@ -136,6 +149,7 @@ public class DataNodeBuilder {
     private DataNode buildFromAttributes() {
         final var dataNode = new DataNode();
         dataNode.setXpath(xpath);
+        dataNode.setModuleNamePrefix(moduleNamePrefix);
         dataNode.setLeaves(leaves);
         dataNode.setChildDataNodes(childDataNodes);
         return dataNode;
diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeIdentifier.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNodeIdentifier.java
new file mode 100644 (file)
index 0000000..2bd2b77
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+@EqualsAndHashCode
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DataNodeIdentifier {
+    private String dataspace;
+    private String schemaSetName;
+    private String anchorName;
+    private String xpath;
+}
diff --git a/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java b/cps-service/src/main/java/org/onap/cps/utils/CmHandleQueryRestParametersValidator.java
new file mode 100644 (file)
index 0000000..c510a73
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.utils;
+
+import com.google.common.base.Strings;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.onap.cps.spi.exceptions.DataValidationException;
+import org.onap.cps.spi.model.CmHandleQueryParameters;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class CmHandleQueryRestParametersValidator {
+
+    private static final List<String> VALID_PROPERTY_NAMES = Arrays.asList("hasAllProperties", "hasAllModules");
+
+    /**
+     * Validate cm handle query parameters.
+     * @param cmHandleQueryParameters name of data to be validated
+     */
+    public static void validateCmHandleQueryParameters(final CmHandleQueryParameters cmHandleQueryParameters) {
+        cmHandleQueryParameters.getCmHandleQueryParameters().forEach(
+                conditionApiProperty -> {
+                    if (Strings.isNullOrEmpty(conditionApiProperty.getConditionName())) {
+                        throwDataValidationException("Missing 'conditionName' - please supply a valid name.");
+                    }
+                    if (!VALID_PROPERTY_NAMES.contains(conditionApiProperty.getConditionName())) {
+                        throwDataValidationException(
+                                String.format("Wrong 'conditionName': %s - please supply a valid name.",
+                                conditionApiProperty.getConditionName()));
+                    }
+                    if (conditionApiProperty.getConditionParameters().isEmpty()) {
+                        throwDataValidationException(
+                                "Empty 'conditionsParameters' - please supply a valid condition parameter.");
+                    }
+                    conditionApiProperty.getConditionParameters().forEach(
+                            conditionParameter -> {
+                                if (conditionParameter.isEmpty()) {
+                                    throwDataValidationException(
+                                            "Empty 'conditionsParameter' - please supply a valid condition parameter.");
+                                }
+                                if (conditionParameter.size() > 1) {
+                                    throwDataValidationException("Too many name in one 'conditionsParameter' -"
+                                            + " please supply one name in one condition parameter.");
+                                }
+                                conditionParameter.forEach((key, value) -> {
+                                    if (Strings.isNullOrEmpty(key)) {
+                                        throwDataValidationException(
+                                                "Missing 'conditionsParameterName' - please supply a valid name.");
+                                    }
+                                });
+                            }
+                    );
+                }
+        );
+    }
+
+    /**
+     * Validate module name condition properties.
+     * @param conditionProperty name of data to be validated
+     */
+    public static void validateModuleNameConditionProperties(final Map<String, String> conditionProperty) {
+        if (conditionProperty.containsKey("moduleName") && !conditionProperty.get("moduleName").isEmpty()) {
+            return;
+        }
+        throwDataValidationException("Wrong module condition property. - please supply a valid condition property.");
+    }
+
+    private static void throwDataValidationException(final String details) {
+        throw new DataValidationException("Invalid Query Parameter.", details);
+    }
+
+}
index 42719d9..ff5204f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Pantheon.tech
- *  Modifications (C) 2021 Nordix Foundation
+ *  Modifications (C) 2021-2022 Nordix Foundation
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ public class DataMapUtils {
      */
     public static Map<String, Object> toDataMapWithIdentifier(final DataNode dataNode) {
         return ImmutableMap.<String, Object>builder()
-            .put(getNodeIdentifier(dataNode.getXpath()), toDataMap(dataNode))
+            .put(getNodeIdentifierWithPrefix(dataNode.getXpath(), dataNode.getModuleNamePrefix()), toDataMap(dataNode))
             .build();
     }
 
@@ -96,6 +96,13 @@ public class DataMapUtils {
         return toIndex > 0 ? xpath.substring(fromIndex, toIndex) : xpath.substring(fromIndex);
     }
 
+    private static String getNodeIdentifierWithPrefix(final String xpath, final String moduleNamePrefix) {
+        if (moduleNamePrefix != null) {
+            return moduleNamePrefix + ":" + getNodeIdentifier(xpath);
+        }
+        return getNodeIdentifier(xpath);
+    }
+
     private static boolean isContainerNode(final String xpath) {
         return !isListElement(xpath);
     }
index 33868cc..def99e2 100755 (executable)
@@ -177,15 +177,6 @@ class CpsAdminServiceImplSpec extends Specification {
             1 * mockCpsAdminPersistenceService.deleteDataspace('someDataspace')
     }
 
-    def 'Query CM Handles.'() {
-        given: 'a cm handle query'
-            def cmHandleQueryParameters = new CmHandleQueryParameters()
-        when: 'query cm handles is invoked'
-            objectUnderTest.queryCmHandles(cmHandleQueryParameters)
-        then: 'associated persistence service method is invoked with correct parameter'
-            1 * mockCpsAdminPersistenceService.queryCmHandles(cmHandleQueryParameters)
-    }
-
     def 'Delete dataspace with invalid dataspace id.'() {
         when: 'delete dataspace is invoked'
             objectUnderTest.deleteDataspace('some dataspace name')
@@ -194,5 +185,4 @@ class CpsAdminServiceImplSpec extends Specification {
         and: 'associated persistence service method is not invoked'
             0 * mockCpsAdminPersistenceService.deleteDataspace(_)
     }
-
 }
index ce54ead..16d4efc 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.spi.model
 
 import org.onap.cps.TestUtils
 import org.onap.cps.spi.model.DataNodeBuilder
+import org.onap.cps.utils.DataMapUtils
 import org.onap.cps.utils.YangUtils
 import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
 import org.opendaylight.yangtools.yang.common.QName
@@ -172,6 +173,22 @@ class DataNodeBuilderSpec extends Specification {
             'NormalizedNode is an unsupported type' | 'not supported' | Mock(NormalizedNode) | 0            | [ ]
     }
 
+    def 'Use of adding the module name prefix attribute of data node.'() {
+        when: 'data node is built with a prefix'
+            def testDataNode = new DataNodeBuilder()
+                    .withModuleNamePrefix('sampleModuleNamePrefix')
+                    .withXpath(xPath)
+                    .withLeaves(sampleLeaves)
+                    .build()
+        then: 'the result when node request is a #scenario includes the correct prefix'
+            def result = new DataMapUtils().toDataMapWithIdentifier(testDataNode)
+            result.toString() == expectedResult
+        where: 'the following parameters are used'
+            scenario          | xPath                                       | sampleLeaves                   | expectedResult
+            'list attribute'  | '/test-tree/branch[@name=\'Right\']/nest'   | [name: 'Big', birds: ['Owl']]  | '{sampleModuleNamePrefix:nest={name=Big, birds=[Owl]}}'
+            'container xpath' | '/test-tree/branch[@name=\'Left\']'         | [name: 'Left']                 | '{sampleModuleNamePrefix:branch={name=Left}}'
+    }
+
     def static assertLeavesMaps(actualLeavesMap, expectedLeavesMap) {
         expectedLeavesMap.each { key, value ->
             {
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/CmHandleQueryRestParametersValidatorSpec.groovy
new file mode 100644 (file)
index 0000000..645829b
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.utils
+
+import org.onap.cps.spi.exceptions.DataValidationException
+import org.onap.cps.spi.model.CmHandleQueryParameters
+import org.onap.cps.spi.model.ConditionProperties
+import spock.lang.Specification
+
+class CmHandleQueryRestParametersValidatorSpec extends Specification {
+    def 'CM Handle Query validation: empty query.'() {
+        given: 'a cm handle query'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+        when: 'validator is invoked'
+            CmHandleQueryRestParametersValidator.validateCmHandleQueryParameters(cmHandleQueryParameters)
+        then: 'data validation exception is not thrown'
+            noExceptionThrown()
+    }
+
+    def 'CM Handle Query validation: normal query.'() {
+        given: 'a cm handle query'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def condition = new ConditionProperties()
+            condition.conditionName = 'hasAllProperties'
+            condition.conditionParameters = [[key1:'value1'],[key2:'value2']]
+            cmHandleQueryParameters.cmHandleQueryParameters = [condition]
+        when: 'validator is invoked'
+            CmHandleQueryRestParametersValidator.validateCmHandleQueryParameters(cmHandleQueryParameters)
+        then: 'data validation exception is not thrown'
+            noExceptionThrown()
+    }
+
+    def 'CM Handle Query validation: #scenario.'() {
+        given: 'a cm handle query'
+            def cmHandleQueryParameters = new CmHandleQueryParameters()
+            def condition = new ConditionProperties()
+            condition.conditionName = conditionName
+            condition.conditionParameters = conditionParameters
+            cmHandleQueryParameters.cmHandleQueryParameters = [condition]
+        when: 'validator is invoked'
+            CmHandleQueryRestParametersValidator.validateCmHandleQueryParameters(cmHandleQueryParameters)
+        then: 'a data validation exception is thrown'
+            thrown(DataValidationException)
+        where:
+            scenario               | conditionName      | conditionParameters
+            'empty properties'     | 'hasAllProperties' | [[ : ]]
+            'empty conditions'     | 'hasAllProperties' | []
+            'wrong condition name' | 'wrong'            | []
+            'no condition name'    | ''                 | []
+            'too many properties'  | 'hasAllProperties' | [[key1:'value1', key2:'value2']]
+            'wrong properties'     | 'hasAllProperties' | [['':'wrong']]
+    }
+
+    def 'CM Handle Query validation: validate module name condition properties - valid query.'() {
+        given: 'a condition property'
+            def conditionProperty = [moduleName: 'value']
+        when: 'validator is invoked'
+            CmHandleQueryRestParametersValidator.validateModuleNameConditionProperties(conditionProperty)
+        then: 'data validation exception is not thrown'
+            noExceptionThrown()
+    }
+
+    def 'CM Handle Query validation: validate module name condition properties - #scenario.'() {
+        when: 'validator is invoked'
+            CmHandleQueryRestParametersValidator.validateModuleNameConditionProperties(conditionProperty)
+        then: 'a data validation exception is thrown'
+            thrown(DataValidationException)
+        where:
+            scenario        | conditionProperty
+            'invalid value' | [moduleName: '']
+            'invalid name'  | [wrongName: 'value']
+    }
+}
index 90563c0..24e8061 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Pantheon.tech
- *  Modifications Copyright (C) 2020 Nordix Foundation
+ *  Modifications Copyright (C) 2020-2022 Nordix Foundation
  *  Modifications Copyright (C) 2022 Bell Canada.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,13 +29,13 @@ class DataMapUtilsSpec extends Specification {
     def noChildren = []
 
     def dataNode = buildDataNode(
-        "/parent",[parentLeaf:'parentLeafValue', parentLeafList:['parentLeafListEntry1','parentLeafListEntry2']],[
-                buildDataNode('/parent/child-list[@id=1]',[listElementLeaf:'listElement1leafValue'],noChildren),
-                buildDataNode('/parent/child-list[@id=2]',[listElementLeaf:'listElement2leafValue'],noChildren),
-                buildDataNode('/parent/child-object',[childLeaf:'childLeafValue'],
-                        [buildDataNode('/parent/child-object/grand-child-object',[grandChildLeaf:'grandChildLeafValue'],noChildren)]
-                ),
-            ])
+            "/parent",[parentLeaf:'parentLeafValue', parentLeafList:['parentLeafListEntry1','parentLeafListEntry2']],[
+            buildDataNode('/parent/child-list[@id=1]',[listElementLeaf:'listElement1leafValue'],noChildren),
+            buildDataNode('/parent/child-list[@id=2]',[listElementLeaf:'listElement2leafValue'],noChildren),
+            buildDataNode('/parent/child-object',[childLeaf:'childLeafValue'],
+                    [buildDataNode('/parent/child-object/grand-child-object',[grandChildLeaf:'grandChildLeafValue'],noChildren)]
+            ),
+    ])
 
     static def buildDataNode(xpath,  leaves,  children) {
         return new DataNodeBuilder().withXpath(xpath).withLeaves(leaves).withChildDataNodes(children).build()
@@ -81,4 +81,16 @@ class DataMapUtilsSpec extends Specification {
         and: 'leaves for grandchild element is populated under its node identifier'
             parentNode.'child-object'.'grand-child-object'.grandChildLeaf == 'grandChildLeafValue'
     }
+
+    def 'Adding prefix to data node identifier.'() {
+        when: 'a valid xPath is passed to the addPrefixToXpath method'
+            def result = new DataMapUtils().getNodeIdentifierWithPrefix(xPath,'sampleModuleName')
+        then: 'the correct modified node identifier is given'
+            assert result == expectedNodeIdentifier
+        where: 'the following parameters are used'
+            scenario                        | xPath                                       | expectedNodeIdentifier
+            'container xpath'               | '/bookstore'                                | 'sampleModuleName:bookstore'
+            'xpath contains list attribute' | '/bookstore/categories[@code=1]'            | 'sampleModuleName:categories'
+    }
 }
+
index d4615e7..c0cf451 100644 (file)
@@ -21,4 +21,4 @@ cps-admin
 cps-data
 cps-model-sync
 ncmp-passthrough
-public-properties-query
\ No newline at end of file
+cm-handle-query
\ No newline at end of file
@@ -32,20 +32,29 @@ Suite Setup           Create Session      CPS_URL    http://${CPS_CORE_HOST}:${C
 
 ${auth}                                     Basic Y3BzdXNlcjpjcHNyMGNrcyE=
 ${ncmpBasePath}                             /ncmp/v1
-${jsonMatchingQueryParameters}              {"publicCmHandleProperties": {"Contact" : "newemailforstore@bookstore.com", "Contact2" : "storeemail2@bookstore.com"}}
-${jsonMissingPropertyQueryParameters}       {"publicCmHandleProperties": { "" : "doesnt matter"}}
+${jsonModuleAndPropertyQueryParameters}     {"cmHandleQueryParameters": [{"conditionName": "hasAllModules", "conditionParameters": [ {"moduleName": "iana-crypt-hash"} ]}, {"conditionName": "hasAllProperties", "conditionParameters": [ {"Contact": "newemailforstore@bookstore.com"} ]}]}
+${jsonEmptyQueryParameters}                 {}
+${jsonMissingPropertyQueryParameters}       {"cmHandleQueryParameters": [{"conditionName": "hasAllProperties", "conditionParameters": [{"" : "doesnt matter"}]}]}
 
 *** Test Cases ***
-Retrieve CM Handles where query parameters Match
-    ${uri}=              Set Variable       ${ncmpBasePath}/data/ch/searches
+Retrieve CM Handle ids where query parameters Match (module and property query)
+    ${uri}=              Set Variable       ${ncmpBasePath}/ch/id-searches
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
-    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonMatchingQueryParameters}
+    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonModuleAndPropertyQueryParameters}
+    ${responseJson}=     Set Variable       ${response.json()}
+    Should Be Equal As Strings              ${response.status_code}   200
+    Should Contain       ${responseJson}    PNFDemo
+
+Retrieve CM Handle ids where query parameters Match (empty query)
+    ${uri}=              Set Variable       ${ncmpBasePath}/ch/id-searches
+    ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
+    ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonEmptyQueryParameters}
     ${responseJson}=     Set Variable       ${response.json()}
     Should Be Equal As Strings              ${response.status_code}   200
     Should Contain       ${responseJson}    PNFDemo
 
 Throw 400 when Structure of Request is Incorrect
-    ${uri}=              Set Variable       ${ncmpBasePath}/data/ch/searches
+    ${uri}=              Set Variable       ${ncmpBasePath}/ch/id-searches
     ${headers}=          Create Dictionary  Content-Type=application/json   Authorization=${auth}
     ${response}=         POST On Session    CPS_URL   ${uri}   headers=${headers}   data=${jsonMissingPropertyQueryParameters}    expected_status=400
     Should Be Equal As Strings              ${response}   <Response [400]>
index 55667c3..844b5d5 100644 (file)
@@ -1,6 +1,7 @@
 # ============LICENSE_START=======================================================
 # Copyright (c) 2021 Pantheon.tech.
 # Modifications Copyright (C) 2022 Bell Canada.
+# Modifications Copyright (C) 2022 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -46,7 +47,7 @@ Get Data Node by XPath
     ${params}=          Create Dictionary   xpath=/test-tree/branch[@name='Left']/nest
     ${headers}=         Create Dictionary   Authorization=${auth}
     ${response}=        Get On Session      CPS_URL   ${uri}   params=${params}   headers=${headers}   expected_status=200
-    ${responseJson}=    Set Variable        ${response.json()['nest']}
+    ${responseJson}=    Set Variable        ${response.json()['test-tree:nest']}
     Should Be Equal As Strings              ${responseJson['name']}   Small
 
 
index f2f477f..9edea35 100755 (executable)
@@ -112,6 +112,7 @@ services:
       KAFKA_BOOTSTRAP_SERVER: kafka:9092
       notification.data-updated.enabled: 'true'
       NOTIFICATION_DATASPACE_FILTER_PATTERNS: '.*'
+      TIMERS_ADVISED-MODULES-SYNC_SLEEP-TIME-MS: 2000
     restart: unless-stopped
     depends_on:
       - dbpostgresql