Merge "Adding config for RTD"
authorToine Siebelink <toine.siebelink@est.tech>
Thu, 1 Sep 2022 16:38:12 +0000 (16:38 +0000)
committerGerrit Code Review <gerrit@onap.org>
Thu, 1 Sep 2022 16:38:12 +0000 (16:38 +0000)
20 files changed:
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
cps-ncmp-rest/docs/openapi/openapi-inventory.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/lcm/LcmEventsCmHandleStateHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/event/lcm/LcmEventsCmHandleStateHandlerImpl.java
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/CmHandleQueries.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateUtils.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/ModelledDmiServiceLeaves.java [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/event/lcm/LcmEventsCmHandleStateHandlerImplSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandleSpec.groovy [moved from cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/YangModelCmHandleSpec.groovy with 71% similarity]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy

index c1e0587..427f083 100644 (file)
@@ -168,7 +168,6 @@ components:
               }
             }
 
-
     CmHandleQueryParameters:
       type: object
       title: Cm Handle query parameters for executing cm handle search
@@ -462,6 +461,14 @@ components:
       schema:
         type: string
         default: /
+    dmiPluginIdentifierInQuery:
+      name: dmi-plugin-identifier
+      in: query
+      description: dmi-plugin-identifier
+      required: true
+      schema:
+        type: string
+        example: my-dmi-plugin
     resourceIdentifierInQuery:
       name: resourceIdentifier
       in: query
index 0a408c2..0c3dffd 100755 (executable)
@@ -97,3 +97,28 @@ updateDmiRegistration:
                   "errorText": "cm-handle has an invalid character(s) in id"
                 }
               ]
+
+getAllCmHandleIdsForRegisteredDmi:
+  get:
+    description: Get all cm handle IDs for a registered DMI plugin
+    tags:
+      - network-cm-proxy-inventory
+    summary: Get all cm handle IDs for a registered DMI plugin (DMI plugin, DMI data plugin, DMI model plugin)
+    operationId: getAllCmHandleIdsForRegisteredDmi
+    parameters:
+      - $ref: 'components.yaml#/components/parameters/dmiPluginIdentifierInQuery'
+    responses:
+      200:
+        description: OK
+        content:
+          application/json:
+            schema:
+              type: array
+              items:
+                type: string
+      401:
+        $ref: 'components.yaml#/components/responses/Unauthorized'
+      403:
+        $ref: 'components.yaml#/components/responses/Forbidden'
+      500:
+        $ref: 'components.yaml#/components/responses/InternalServerError'
\ No newline at end of file
index ee09d05..08270bc 100755 (executable)
@@ -1,5 +1,6 @@
 #  ============LICENSE_START=======================================================
 #  Copyright (C) 2021 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.
@@ -26,3 +27,6 @@ servers:
 paths:
   /v1/ch:
     $ref: 'ncmp-inventory.yml#/updateDmiRegistration'
+
+  /v1/ch/cmHandles:
+    $ref: 'ncmp-inventory.yml#/getAllCmHandleIdsForRegisteredDmi'
\ No newline at end of file
index 105a6a5..0c428e4 100755 (executable)
@@ -22,6 +22,7 @@
 package org.onap.cps.ncmp.rest.controller;
 
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 import javax.validation.Valid;
 import lombok.RequiredArgsConstructor;
@@ -46,6 +47,19 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
     private final NetworkCmProxyDataService networkCmProxyDataService;
     private final NcmpRestInputMapper ncmpRestInputMapper;
 
+    /**
+     * Get all cm-handle IDs under a registered DMI plugin.
+     *
+     * @param dmiPluginIdentifier DMI plugin identifier
+     * @return list of cm handle IDs
+     */
+    @Override
+    public ResponseEntity<List<String>> getAllCmHandleIdsForRegisteredDmi(final String dmiPluginIdentifier) {
+        final Set<String> cmHandleIds =
+                networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier);
+        return ResponseEntity.ok(List.copyOf(cmHandleIds));
+    }
+
     /**
      * Update DMI Plugin Registration (used for first registration also).
      *
@@ -69,7 +83,6 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
         return dmiPluginRegistrationErrorResponse.getFailedCreatedCmHandles().isEmpty()
             && dmiPluginRegistrationErrorResponse.getFailedUpdatedCmHandles().isEmpty()
             && dmiPluginRegistrationErrorResponse.getFailedRemovedCmHandles().isEmpty();
-
     }
 
     private DmiPluginRegistrationErrorResponse getFailureRegistrationResponse(
@@ -103,5 +116,3 @@ public class NetworkCmProxyInventoryController implements NetworkCmProxyInventor
     }
 
 }
-
-
index 6673b21..b5a089b 100644 (file)
@@ -27,6 +27,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyDataService
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse
 import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse
 import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration
@@ -41,6 +42,7 @@ import org.springframework.http.MediaType
 import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Specification
 
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
 
 @WebMvcTest(NetworkCmProxyInventoryController)
@@ -158,6 +160,23 @@ class NetworkCmProxyInventoryControllerSpec extends Specification {
             'update delete failed' | successResponse('cm-handle-1') | failedResponse('cm-handle-2')  | failedResponse('cm-handle-3')  || []                                  | [failedRestResponse('cm-handle-2')] | [failedRestResponse('cm-handle-3')]
     }
 
+    def 'Get all cm handle IDs by DMI plugin identifier.'() {
+        given: 'an endpoint for returning cm handle IDs for a registered dmi plugin'
+            def getUrl = "$ncmpBasePathV1/ch/cmHandles?dmi-plugin-identifier=some-dmi-plugin-identifier"
+        and: 'a collection of cm handle IDs are returned'
+            1 * mockNetworkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
+                    >> ['cm-handle-id-1','cm-handle-id-2']
+        when: 'the endpoint is invoked'
+            def response = mvc.perform(
+                    get(getUrl)
+                            .contentType(MediaType.APPLICATION_JSON)
+                            .accept(MediaType.APPLICATION_JSON_VALUE)
+            ).andReturn().response
+        then: 'the response matches the result returned by the service layer'
+            assert response.contentAsString.contains('cm-handle-id-1')
+            assert response.contentAsString.contains('cm-handle-id-2')
+    }
+
     def failedRestResponse(cmHandle) {
         return new CmHandlerRegistrationErrorResponse('cmHandle': cmHandle, 'errorCode': '00', 'errorText': 'Failed')
     }
index 3295a6e..45dba21 100644 (file)
@@ -162,4 +162,12 @@ public interface NetworkCmProxyDataService {
      * @param dataSyncEnabled data sync enabled flag
      */
     void setDataSyncEnabled(String cmHandleId, boolean dataSyncEnabled);
+
+    /**
+     * Get all cm handle IDs by DMI plugin identifier.
+     *
+     * @param dmiPluginIdentifier DMI plugin identifier
+     * @return set of cm handle IDs
+     */
+    Set<String> getAllCmHandleIdsByDmiPluginIdentifier(String dmiPluginIdentifier);
 }
index 0fdecde..5b072f3 100755 (executable)
@@ -30,6 +30,7 @@ import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -44,6 +45,7 @@ import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations;
 import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries;
 import org.onap.cps.ncmp.api.inventory.CmHandleState;
 import org.onap.cps.ncmp.api.inventory.CompositeState;
 import org.onap.cps.ncmp.api.inventory.CompositeStateUtils;
@@ -80,6 +82,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
 
     private final InventoryPersistence inventoryPersistence;
 
+    private final CmHandleQueries cmHandleQueries;
+
     private final NetworkCmProxyCmHandlerQueryService networkCmProxyCmHandlerQueryService;
 
     private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler;
@@ -218,6 +222,23 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
         }
     }
 
+    /**
+     * Get all cm handle IDs by DMI plugin identifier.
+     *
+     * @param dmiPluginIdentifier DMI plugin identifier
+     * @return set of cm handle IDs
+     */
+    @Override
+    public Set<String> getAllCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) {
+        final Set<NcmpServiceCmHandle> ncmpServiceCmHandles =
+                cmHandleQueries.getCmHandlesByDmiPluginIdentifier(dmiPluginIdentifier);
+        final Set<String> cmHandleIds = new HashSet<>(ncmpServiceCmHandles.size());
+        ncmpServiceCmHandles.forEach(cmHandle -> {
+            cmHandleIds.add(cmHandle.getCmHandleId());
+        });
+        return cmHandleIds;
+    }
+
     /**
      * Retrieve cm handle details for a given cm handle.
      *
index c428b12..5ff2afa 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.cps.ncmp.api.impl.event.lcm;
 
+import java.util.Map;
 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;
@@ -38,12 +39,20 @@ public interface LcmEventsCmHandleStateHandler {
      */
     void updateCmHandleState(final YangModelCmHandle yangModelCmHandle, final CmHandleState targetCmHandleState);
 
+    /**
+     * Updates the composite state of cmHandle based on cmHandleState in batch.
+     *
+     * @param cmHandleStatePerCmHandle Map of Yang Model Cm Handle and corresponding cm handle state.
+     */
+    void updateCmHandleStateBatch(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle);
+
     /**
      * Publish LCM Event.
      *
-     * @param targetNcmpServiceCmHandle   target NcmpServiceCmHandle
-     * @param existingNcmpServiceCmHandle existing NcmpServiceCmHandle
+     * @param targetNcmpServiceCmHandle  target NcmpServiceCmHandle
+     * @param currentNcmpServiceCmHandle current NcmpServiceCmHandle
      */
-    void publishLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
-            final NcmpServiceCmHandle existingNcmpServiceCmHandle);
+    void publishLcmEventAsynchronously(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+            final NcmpServiceCmHandle currentNcmpServiceCmHandle);
+
 }
index 7719e1b..eba0389 100644 (file)
@@ -25,7 +25,15 @@ import static org.onap.cps.ncmp.api.inventory.CmHandleState.DELETED;
 import static org.onap.cps.ncmp.api.inventory.CmHandleState.LOCKED;
 import static org.onap.cps.ncmp.api.inventory.CmHandleState.READY;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.RequiredArgsConstructor;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
@@ -48,26 +56,51 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
     private final LcmEventsService lcmEventsService;
 
     @Override
-    public void updateCmHandleState(final YangModelCmHandle yangModelCmHandle,
+    public void updateCmHandleState(final YangModelCmHandle updatedYangModelCmHandle,
             final CmHandleState targetCmHandleState) {
 
-        final CompositeState compositeState = yangModelCmHandle.getCompositeState();
+        final CompositeState compositeState = updatedYangModelCmHandle.getCompositeState();
 
-        if (compositeState != null && compositeState.getCmHandleState() == targetCmHandleState) {
-            log.debug("CmHandle with id : {} already in state : {}", yangModelCmHandle.getId(), targetCmHandleState);
+        if (isCompositeStateSame(compositeState, targetCmHandleState)) {
+            log.debug("CmHandle with id : {} already in state : {}", updatedYangModelCmHandle.getId(),
+                    targetCmHandleState);
         } else {
-            final NcmpServiceCmHandle existingNcmpServiceCmHandle =
-                    new NcmpServiceCmHandle(toNcmpServiceCmHandle(yangModelCmHandle));
-            updateToSpecifiedCmHandleState(yangModelCmHandle, targetCmHandleState);
-            final NcmpServiceCmHandle targetNcmpServiceCmHandle = toNcmpServiceCmHandle(yangModelCmHandle);
-            publishLcmEvent(targetNcmpServiceCmHandle, existingNcmpServiceCmHandle);
+            final YangModelCmHandle currentYangModelCmHandle = YangModelCmHandle.deepCopyOf(updatedYangModelCmHandle);
+            updateToSpecifiedCmHandleState(updatedYangModelCmHandle, targetCmHandleState);
+            persistCmHandle(updatedYangModelCmHandle, currentYangModelCmHandle);
+            publishLcmEventAsynchronously(toNcmpServiceCmHandle(updatedYangModelCmHandle),
+                    toNcmpServiceCmHandle(currentYangModelCmHandle));
         }
+    }
 
+    @Override
+    public void updateCmHandleStateBatch(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) {
+        final Collection<CmHandleTransitionPair> cmHandleTransitionPairs =
+                prepareCmHandleTransitionBatch(cmHandleStatePerCmHandle);
+        persistCmHandleBatch(cmHandleTransitionPairs);
+        publishLcmEventBatchAsynchronously(cmHandleTransitionPairs);
     }
 
     @Async("notificationExecutor")
     @Override
-    public void publishLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+    public void publishLcmEventAsynchronously(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
+            final NcmpServiceCmHandle currentNcmpServiceCmHandle) {
+        publishLcmEvent(targetNcmpServiceCmHandle, currentNcmpServiceCmHandle);
+    }
+
+    /**
+     * Publish LcmEvent in batches and in asynchronous manner.
+     *
+     * @param cmHandleTransitionPairs Pair of existing and modified cm handle represented as YangModelCmHandle
+     */
+    @Async("notificationExecutor")
+    public void publishLcmEventBatchAsynchronously(final Collection<CmHandleTransitionPair> cmHandleTransitionPairs) {
+        cmHandleTransitionPairs.forEach(cmHandleTransitionPair -> publishLcmEvent(
+                toNcmpServiceCmHandle(cmHandleTransitionPair.getTargetYangModelCmHandle()),
+                toNcmpServiceCmHandle(cmHandleTransitionPair.getCurrentYangModelCmHandle())));
+    }
+
+    private void publishLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle,
             final NcmpServiceCmHandle existingNcmpServiceCmHandle) {
         final String cmHandleId = targetNcmpServiceCmHandle.getCmHandleId();
         final LcmEvent lcmEvent =
@@ -75,48 +108,119 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
         lcmEventsService.publishLcmEvent(cmHandleId, lcmEvent);
     }
 
+    private Collection<CmHandleTransitionPair> prepareCmHandleTransitionBatch(
+            final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) {
+        final List<CmHandleTransitionPair> cmHandleTransitionPairs = new ArrayList<>(cmHandleStatePerCmHandle.size());
+        cmHandleStatePerCmHandle.forEach((yangModelCmHandle, targetCmHandleState) -> {
+
+            final CompositeState compositeState = yangModelCmHandle.getCompositeState();
+
+            if (isCompositeStateSame(compositeState, targetCmHandleState)) {
+                log.debug("CmHandle with id : {} already in state : {}", yangModelCmHandle.getId(),
+                        targetCmHandleState);
+            } else {
+                final CmHandleTransitionPair cmHandleTransitionPair = new CmHandleTransitionPair();
+                cmHandleTransitionPair.setCurrentYangModelCmHandle(YangModelCmHandle.deepCopyOf(yangModelCmHandle));
+                updateToSpecifiedCmHandleState(yangModelCmHandle, targetCmHandleState);
+                cmHandleTransitionPair.setTargetYangModelCmHandle(yangModelCmHandle);
+                cmHandleTransitionPairs.add(cmHandleTransitionPair);
+            }
+        });
+
+        return cmHandleTransitionPairs;
+    }
+
+
+    private void persistCmHandle(final YangModelCmHandle targetYangModelCmHandle,
+            final YangModelCmHandle currentYangModelCmHandle) {
+        if (isNew(currentYangModelCmHandle.getCompositeState(), targetYangModelCmHandle.getCompositeState())) {
+            log.debug("Registering a new cm handle {}", targetYangModelCmHandle.getId());
+            inventoryPersistence.saveCmHandle(targetYangModelCmHandle);
+        } else if (isDeleted(targetYangModelCmHandle.getCompositeState())) {
+            log.info("CmHandle with Id : {} is DELETED", targetYangModelCmHandle.getId());
+        } else {
+            inventoryPersistence.saveCmHandleState(targetYangModelCmHandle.getId(),
+                    targetYangModelCmHandle.getCompositeState());
+        }
+    }
+
+    private void persistCmHandleBatch(final Collection<CmHandleTransitionPair> cmHandleTransitionPairs) {
+
+        final List<YangModelCmHandle> newCmHandles = new ArrayList<>();
+        final Map<String, CompositeState> compositeStatePerCmHandleId = new LinkedHashMap<>();
+
+        cmHandleTransitionPairs.forEach(cmHandleTransitionPair -> {
+            if (isNew(cmHandleTransitionPair.getCurrentYangModelCmHandle().getCompositeState(),
+                    cmHandleTransitionPair.getTargetYangModelCmHandle().getCompositeState())) {
+                newCmHandles.add(cmHandleTransitionPair.getTargetYangModelCmHandle());
+            } else if (!isDeleted(cmHandleTransitionPair.getTargetYangModelCmHandle().getCompositeState())) {
+                compositeStatePerCmHandleId.put(cmHandleTransitionPair.getTargetYangModelCmHandle().getId(),
+                        cmHandleTransitionPair.getTargetYangModelCmHandle().getCompositeState());
+            }
+        });
+
+        inventoryPersistence.saveCmHandleBatch(newCmHandles);
+        inventoryPersistence.saveCmHandleStateBatch(compositeStatePerCmHandleId);
+
+    }
+
+
     private void updateToSpecifiedCmHandleState(final YangModelCmHandle yangModelCmHandle,
             final CmHandleState targetCmHandleState) {
 
         if (READY == targetCmHandleState) {
-            CompositeStateUtils.setCompositeStateToReadyWithInitialDataStoreSyncState()
-                    .accept(yangModelCmHandle.getCompositeState());
-            inventoryPersistence.saveCmHandleState(yangModelCmHandle.getId(), yangModelCmHandle.getCompositeState());
+            setInitialStates(yangModelCmHandle);
         } else if (ADVISED == targetCmHandleState) {
             if (yangModelCmHandle.getCompositeState() == null) {
                 registerNewCmHandle(yangModelCmHandle);
             } else if (yangModelCmHandle.getCompositeState().getCmHandleState() == LOCKED) {
                 retryCmHandle(yangModelCmHandle);
             }
-        } else if (DELETED == targetCmHandleState) {
-            setCmHandleState(yangModelCmHandle, targetCmHandleState);
         } else {
-            updateAndSaveCmHandleState(yangModelCmHandle, targetCmHandleState);
+            setCmHandleState(yangModelCmHandle, targetCmHandleState);
         }
     }
 
+    private void setInitialStates(final YangModelCmHandle yangModelCmHandle) {
+        CompositeStateUtils.setInitialDataStoreSyncState().accept(yangModelCmHandle.getCompositeState());
+        CompositeStateUtils.setCompositeState(READY).accept(yangModelCmHandle.getCompositeState());
+    }
+
     private void retryCmHandle(final YangModelCmHandle yangModelCmHandle) {
         CompositeStateUtils.setCompositeStateForRetry().accept(yangModelCmHandle.getCompositeState());
-        inventoryPersistence.saveCmHandleState(yangModelCmHandle.getId(), yangModelCmHandle.getCompositeState());
     }
 
     private void registerNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
         yangModelCmHandle.setCompositeState(new CompositeState());
         setCmHandleState(yangModelCmHandle, ADVISED);
-        inventoryPersistence.saveCmHandle(yangModelCmHandle);
-    }
-
-    private void updateAndSaveCmHandleState(final YangModelCmHandle yangModelCmHandle,
-            final CmHandleState targetCmHandleState) {
-        setCmHandleState(yangModelCmHandle, targetCmHandleState);
-        inventoryPersistence.saveCmHandleState(yangModelCmHandle.getId(), yangModelCmHandle.getCompositeState());
     }
 
     private void setCmHandleState(final YangModelCmHandle yangModelCmHandle, final CmHandleState targetCmHandleState) {
         CompositeStateUtils.setCompositeState(targetCmHandleState).accept(yangModelCmHandle.getCompositeState());
     }
 
+    private boolean isNew(final CompositeState existingCompositeState, final CompositeState targetCompositeState) {
+        return (existingCompositeState == null && targetCompositeState.getCmHandleState() == ADVISED);
+    }
+
+    private boolean isDeleted(final CompositeState targetCompositeState) {
+        return targetCompositeState.getCmHandleState() == DELETED;
+    }
+
+    private boolean isCompositeStateSame(final CompositeState compositeState, final CmHandleState targetCmHandleState) {
+        return (compositeState != null && compositeState.getCmHandleState() == targetCmHandleState);
+    }
+
     private NcmpServiceCmHandle toNcmpServiceCmHandle(final YangModelCmHandle yangModelCmHandle) {
         return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(yangModelCmHandle);
     }
+
+    @Getter
+    @Setter
+    @NoArgsConstructor
+    static class CmHandleTransitionPair {
+
+        private YangModelCmHandle currentYangModelCmHandle;
+        private YangModelCmHandle targetYangModelCmHandle;
+    }
 }
index 65e03f1..45e2754 100644 (file)
@@ -68,6 +68,26 @@ public class YangModelCmHandle {
     @JsonProperty("public-properties")
     private List<Property> publicProperties;
 
+    /**
+     * Creates a deep copy of Yang Model Cm Handle.
+     *
+     * @param original Yang Model Cm Handle
+     * @return instance of yangModelCmHandle
+     */
+    public static YangModelCmHandle deepCopyOf(final YangModelCmHandle original) {
+        final YangModelCmHandle copy = new YangModelCmHandle();
+        copy.id = original.getId();
+        copy.dmiServiceName = original.getDmiServiceName();
+        copy.dmiDataServiceName = original.getDmiDataServiceName();
+        copy.dmiModelServiceName = original.getDmiModelServiceName();
+        copy.compositeState =
+                original.getCompositeState() == null ? null : new CompositeState(original.getCompositeState());
+        copy.dmiProperties = original.getDmiProperties() == null ? null : new ArrayList<>(original.getDmiProperties());
+        copy.publicProperties =
+                original.getPublicProperties() == null ? null : new ArrayList<>(original.getPublicProperties());
+        return copy;
+    }
+
     /**
      * Create a yangModelCmHandle.
      *
index 9655612..569e91e 100644 (file)
@@ -27,8 +27,10 @@ import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
@@ -57,21 +59,21 @@ public class CmHandleQueries {
      * @return CmHandles which have these public properties
      */
     public Map<String, NcmpServiceCmHandle> queryCmHandlePublicProperties(
-        final Map<String, String> publicPropertyQueryPairs) {
+            final Map<String, String> publicPropertyQueryPairs) {
         if (publicPropertyQueryPairs.isEmpty()) {
             return Collections.emptyMap();
         }
         Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandles = null;
         for (final Map.Entry<String, String> publicPropertyQueryPair : publicPropertyQueryPairs.entrySet()) {
             final String cpsPath = "//public-properties[@name=\"" + publicPropertyQueryPair.getKey()
-                + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]";
+                    + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]";
 
             final Collection<DataNode> dataNodes = queryCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS);
             if (cmHandleIdToNcmpServiceCmHandles == null) {
                 cmHandleIdToNcmpServiceCmHandles = collectDataNodesToNcmpServiceCmHandles(dataNodes);
             } else {
                 final Collection<String> cmHandleIdsToRetain = dataNodes.parallelStream()
-                    .map(dataNode -> dataNode.getLeaves().get("id").toString()).collect(Collectors.toSet());
+                        .map(dataNode -> dataNode.getLeaves().get("id").toString()).collect(Collectors.toSet());
                 cmHandleIdToNcmpServiceCmHandles.keySet().retainAll(cmHandleIdsToRetain);
             }
             if (cmHandleIdToNcmpServiceCmHandles.isEmpty()) {
@@ -89,8 +91,8 @@ public class CmHandleQueries {
      * @return combined Map of CmHandles
      */
     public Map<String, NcmpServiceCmHandle> combineCmHandleQueries(
-        final Map<String, NcmpServiceCmHandle> firstQuery,
-        final Map<String, NcmpServiceCmHandle> secondQuery) {
+            final Map<String, NcmpServiceCmHandle> firstQuery,
+            final Map<String, NcmpServiceCmHandle> secondQuery) {
         if (firstQuery == NO_QUERY_TO_EXECUTE && secondQuery == NO_QUERY_TO_EXECUTE) {
             return NO_QUERY_TO_EXECUTE;
         } else if (firstQuery == NO_QUERY_TO_EXECUTE) {
@@ -150,7 +152,7 @@ public class CmHandleQueries {
     }
 
     private Map<String, NcmpServiceCmHandle> collectDataNodesToNcmpServiceCmHandles(
-        final Collection<DataNode> dataNodes) {
+            final Collection<DataNode> dataNodes) {
         final Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandle = new HashMap<>();
         dataNodes.forEach(dataNode -> {
             final NcmpServiceCmHandle ncmpServiceCmHandle = createNcmpServiceCmHandle(dataNode);
@@ -161,7 +163,36 @@ public class CmHandleQueries {
 
     private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) {
         return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter
-            .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()));
+                .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()));
+    }
+
+    /**
+     * Get all cm handles by DMI plugin identifier.
+     *
+     * @param dmiPluginIdentifier DMI plugin identifier
+     * @return set of cm handles
+     */
+    public Set<NcmpServiceCmHandle> getCmHandlesByDmiPluginIdentifier(final String dmiPluginIdentifier) {
+        final Map<String, DataNode> cmHandleAsDataNodePerCmHandleId = new HashMap<>();
+        for (final ModelledDmiServiceLeaves modelledDmiServiceLeaf : ModelledDmiServiceLeaves.values()) {
+            for (final DataNode cmHandleAsDataNode: getCmHandlesByDmiPluginIdentifierAndDmiProperty(
+                    dmiPluginIdentifier,
+                    modelledDmiServiceLeaf.getLeafName())) {
+                cmHandleAsDataNodePerCmHandleId.put(
+                        cmHandleAsDataNode.getLeaves().get("id").toString(), cmHandleAsDataNode);
+            }
+        }
+        final Set<NcmpServiceCmHandle> ncmpServiceCmHandles = new HashSet<>(cmHandleAsDataNodePerCmHandleId.size());
+        cmHandleAsDataNodePerCmHandleId.values().forEach(
+                cmHandleAsDataNode -> ncmpServiceCmHandles.add(createNcmpServiceCmHandle(cmHandleAsDataNode)));
+        return ncmpServiceCmHandles;
+    }
+
+    private List<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier,
+                                                             final String dmiProperty) {
+        return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                "/dmi-registry/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']",
+                OMIT_DESCENDANTS);
     }
 
     private DataNode getCmHandleState(final String cmHandleId) {
index 6fabc93..cff1000 100644 (file)
@@ -45,15 +45,14 @@ public class CompositeStateUtils {
     }
 
     /**
-     * Sets the cmHandleState to READY and operational datastore sync state based on the global flag.
+     * Set the Operational datastore sync state based on the global flag.
      *
      * @return Updated CompositeState
      */
-    public static Consumer<CompositeState> setCompositeStateToReadyWithInitialDataStoreSyncState() {
+    public static Consumer<CompositeState> setInitialDataStoreSyncState() {
+
         return compositeState -> {
             compositeState.setDataSyncEnabled(false);
-            compositeState.setLastUpdateTimeNow();
-            compositeState.setCmHandleState(CmHandleState.READY);
             final CompositeState.Operational operational =
                     getInitialDataStoreSyncState(compositeState.getDataSyncEnabled());
             final CompositeState.DataStores dataStores =
@@ -66,15 +65,15 @@ public class CompositeStateUtils {
      * Set the data sync enabled flag, along with the data store sync state based on this flag.
      *
      * @param dataSyncEnabled data sync enabled flag
-     * @param compositeState cm handle composite state
+     * @param compositeState  cm handle composite state
      */
     public static void setDataSyncEnabledFlagWithDataSyncState(final boolean dataSyncEnabled,
-                                                               final CompositeState compositeState) {
+            final CompositeState compositeState) {
         compositeState.setDataSyncEnabled(dataSyncEnabled);
         compositeState.setLastUpdateTimeNow();
         final CompositeState.Operational operational = getInitialDataStoreSyncState(dataSyncEnabled);
         final CompositeState.DataStores dataStores =
-            CompositeState.DataStores.builder().operationalDataStore(operational).build();
+                CompositeState.DataStores.builder().operationalDataStore(operational).build();
         compositeState.setDataStores(dataStores);
     }
 
index 7a7ef66..9174dc7 100644 (file)
@@ -103,16 +103,15 @@ public class InventoryPersistence {
     /**
      * Save all cm handles states in batch.
      *
-     * @param cmHandleStates contains cm handle id and updated state
+     * @param cmHandleStatePerCmHandleId contains cm handle id and updated state
      */
-    public void saveCmHandleStates(final Map<String, CompositeState> cmHandleStates) {
+    public void saveCmHandleStateBatch(final Map<String, CompositeState> cmHandleStatePerCmHandleId) {
         final Map<String, String> cmHandlesJsonDataMap = new HashMap<>();
-        cmHandleStates.entrySet().stream().forEach(cmHandleEntry ->
-            cmHandlesJsonDataMap.put(String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleEntry.getKey()),
-                String.format("{\"state\":%s}",
-                    jsonObjectMapper.asJsonString(cmHandleEntry.getValue()))));
+        cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> cmHandlesJsonDataMap.put(
+                String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
+                String.format("{\"state\":%s}", jsonObjectMapper.asJsonString(compositeState))));
         cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
-            cmHandlesJsonDataMap, OffsetDateTime.now());
+                cmHandlesJsonDataMap, OffsetDateTime.now());
     }
 
     /**
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/ModelledDmiServiceLeaves.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/ModelledDmiServiceLeaves.java
new file mode 100644 (file)
index 0000000..0546c38
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  ============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.inventory;
+
+public enum ModelledDmiServiceLeaves {
+    DMI_SERVICE_NAME("dmi-service-name"),
+    DMI_DATA_SERVICE_NAME("dmi-data-service-name"),
+    DMI_MODEL_SERVICE_NAME("dmi-model-service-name");
+
+    private String leafName;
+
+    ModelledDmiServiceLeaves(final String dmiPluginIdentifierKey) {
+        this.leafName = dmiPluginIdentifierKey;
+    }
+
+    public String getLeafName() {
+        return leafName;
+    }
+
+}
index 8d8b3b4..ed985ec 100644 (file)
@@ -29,6 +29,7 @@ import org.onap.cps.ncmp.api.impl.event.lcm.LcmEventsCmHandleStateHandler
 import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries
 import org.onap.cps.ncmp.api.inventory.CmHandleState
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
 import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
@@ -59,6 +60,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
     def mockDmiDataOperations = Mock(DmiDataOperations)
     def mockNetworkCmProxyDataServicePropertyHandler = Mock(NetworkCmProxyDataServicePropertyHandler)
     def mockInventoryPersistence = Mock(InventoryPersistence)
+    def mockCmhandleQueries = Mock(CmHandleQueries)
     def stubbedNetworkCmProxyCmHandlerQueryService = Stub(NetworkCmProxyCmHandlerQueryService)
     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
     def mockCpsDataService = Mock(CpsDataService)
@@ -352,7 +354,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
     def getObjectUnderTest() {
         return Spy(new NetworkCmProxyDataServiceImpl(spiedJsonObjectMapper, mockDmiDataOperations,
-            mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, stubbedNetworkCmProxyCmHandlerQueryService,
-                mockLcmEventsCmHandleStateHandler, mockCpsDataService))
+            mockNetworkCmProxyDataServicePropertyHandler, mockInventoryPersistence, mockCmhandleQueries,
+                stubbedNetworkCmProxyCmHandlerQueryService, mockLcmEventsCmHandleStateHandler, mockCpsDataService))
     }
 }
index a114b61..02cfb15 100644 (file)
@@ -25,6 +25,7 @@ package org.onap.cps.ncmp.api.impl
 import org.onap.cps.ncmp.api.NetworkCmProxyCmHandlerQueryService
 import org.onap.cps.ncmp.api.impl.event.lcm.LcmEventsCmHandleStateHandler
 import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.ncmp.api.inventory.CmHandleQueries
 import org.onap.cps.ncmp.api.inventory.CmHandleState
 import org.onap.cps.ncmp.api.inventory.CompositeState
 import org.onap.cps.ncmp.api.inventory.InventoryPersistence
@@ -63,6 +64,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
     def mockDmiDataOperations = Mock(DmiDataOperations)
     def nullNetworkCmProxyDataServicePropertyHandler = null
     def mockInventoryPersistence = Mock(InventoryPersistence)
+    def mockCmHandleQueries = Mock(CmHandleQueries)
     def mockDmiPluginRegistration = Mock(DmiPluginRegistration)
     def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandlerQueryService)
     def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler)
@@ -79,6 +81,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             mockDmiDataOperations,
             nullNetworkCmProxyDataServicePropertyHandler,
             mockInventoryPersistence,
+            mockCmHandleQueries,
             mockCpsCmHandlerQueryService,
             mockLcmEventsCmHandleStateHandler,
             mockCpsDataService)
@@ -370,6 +373,19 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
             0 * mockInventoryPersistence.saveCmHandleState(_, _)
     }
 
+    def 'Get all cm handle IDs by DMI plugin identifier.' () {
+        given: 'cm handle queries service returns cm handles'
+            1 * mockCmHandleQueries.getCmHandlesByDmiPluginIdentifier('some-dmi-plugin-identifier')
+                    >> [new NcmpServiceCmHandle(cmHandleId: 'cm-handle-1'),
+                        new NcmpServiceCmHandle(cmHandleId: 'cm-handle-2')]
+        when: 'cm handle Ids are requested with dmi plugin identifier'
+            def result = objectUnderTest.getAllCmHandleIdsByDmiPluginIdentifier('some-dmi-plugin-identifier')
+        then: 'the result size is correct'
+            assert result.size() == 2
+        and: 'the result returns the correct details'
+            assert result.containsAll('cm-handle-1','cm-handle-2')
+    }
+
     def dataStores() {
         CompositeState.DataStores.builder()
             .operationalDataStore(CompositeState.Operational.builder()
index 3d2e995..ddede66 100644 (file)
@@ -139,4 +139,73 @@ class LcmEventsCmHandleStateHandlerImplSpec extends Specification {
         and: 'the method to publish Lcm event is called once'
             1 * mockLcmEventsService.publishLcmEvent(cmHandleId, _)
     }
+
+    def 'No state change and no event to be published'() {
+        given: 'Cm Handle batch with same state transition as before'
+            def cmHandleStateMap = setupBatch('NO_CHANGE')
+        when: 'updating a batch of changes'
+            objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap)
+        then: 'batch is empty and nothing to update'
+            1 * mockInventoryPersistence.saveCmHandleBatch(_) >> {
+                args -> {
+                    assert (args[0] as Collection<YangModelCmHandle>).size() == 0
+                }
+            }
+        and: 'no event will be published'
+            0 * mockLcmEventsService.publishLcmEvent(*_)
+    }
+
+    def 'Batch of new cm handles provided'() {
+        given: 'A batch of new cm handles'
+            def cmHandleStateMap = setupBatch('NEW')
+        when: 'updating a batch of changes'
+            objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap)
+        then: 'new cm handles are saved using inventory persistence'
+            1 * mockInventoryPersistence.saveCmHandleBatch(_) >> {
+                args -> {
+                    assert (args[0] as Collection<YangModelCmHandle>).id.containsAll('cmhandle1', 'cmhandle2')
+                }
+            }
+        and: 'event service is called to publish event'
+            2 * mockLcmEventsService.publishLcmEvent(_, _)
+
+    }
+
+    def 'Batch of existing cm handles is updated'() {
+        given: 'A batch of updated cm handles'
+            def cmHandleStateMap = setupBatch('UPDATE')
+        when: 'updating a batch of changes'
+            objectUnderTest.updateCmHandleStateBatch(cmHandleStateMap)
+        then : 'existing cm handles composite state is persisted'
+            1 * mockInventoryPersistence.saveCmHandleStateBatch(_) >> {
+                args -> {
+                    assert (args[0] as Map<String, CompositeState>).keySet().containsAll(['cmhandle1','cmhandle2'])
+                }
+            }
+        and: 'event service is called to publish event'
+            2 * mockLcmEventsService.publishLcmEvent(_, _)
+
+    }
+
+    def setupBatch(type) {
+
+        def yangModelCmHandle1 = new YangModelCmHandle(id: 'cmhandle1', dmiProperties: [], publicProperties: [])
+        def yangModelCmHandle2 = new YangModelCmHandle(id: 'cmhandle2', dmiProperties: [], publicProperties: [])
+
+        if ('NEW' == type) {
+            return [(yangModelCmHandle1): ADVISED, (yangModelCmHandle2): ADVISED]
+        }
+
+        if ('UPDATE' == type) {
+            yangModelCmHandle1.compositeState = new CompositeState(cmHandleState: ADVISED)
+            yangModelCmHandle2.compositeState = new CompositeState(cmHandleState: READY)
+            return [(yangModelCmHandle1): READY, (yangModelCmHandle2): DELETING]
+        }
+
+        if ('NO_CHANGE' == type) {
+            yangModelCmHandle1.compositeState = new CompositeState(cmHandleState: ADVISED)
+            yangModelCmHandle2.compositeState = new CompositeState(cmHandleState: READY)
+            return [(yangModelCmHandle1): ADVISED, (yangModelCmHandle2): READY]
+        }
+    }
 }
\ No newline at end of file
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.ncmp.api.models
+package org.onap.cps.ncmp.api.impl.yangmodels
 
-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.CompositeStateBuilder
 import org.onap.cps.ncmp.api.inventory.LockReasonCategory
 import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 import spock.lang.Specification
 
 import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA
@@ -79,4 +80,31 @@ class YangModelCmHandleSpec extends Specification {
             'only data service registered'  | null               | 'does not matter'  | null                | MODEL           || null
     }
 
+    def 'Yang Model Cm Handle Deep Copy'() {
+        given: 'a yang model cm handle'
+            def currentYangModelCmHandle = new YangModelCmHandle(id: 'cmhandle',
+                publicProperties: [new YangModelCmHandle.Property('publicProperty1', 'value1')],
+                dmiProperties: [new YangModelCmHandle.Property('dmiProperty1', 'value1')],
+                compositeState: new CompositeState(cmHandleState: CmHandleState.ADVISED, dataSyncEnabled: false))
+        when: 'a deep copy is created'
+            def yangModelCmhandleDeepCopy = YangModelCmHandle.deepCopyOf(currentYangModelCmHandle)
+        and: 'we try to mutate current yang model cm handle'
+            currentYangModelCmHandle.id = 'cmhandle-changed'
+            currentYangModelCmHandle.dmiProperties = [new YangModelCmHandle.Property('updatedPublicProperty1', 'value1')]
+            currentYangModelCmHandle.publicProperties = [new YangModelCmHandle.Property('updatedDmiProperty1', 'value1')]
+            currentYangModelCmHandle.compositeState.cmHandleState = CmHandleState.READY
+            currentYangModelCmHandle.compositeState.dataSyncEnabled = true
+        then: 'there is no change in the deep copied object'
+            assert yangModelCmhandleDeepCopy.id == 'cmhandle'
+            assert yangModelCmhandleDeepCopy.dmiProperties == [new YangModelCmHandle.Property('dmiProperty1', 'value1')]
+            assert yangModelCmhandleDeepCopy.publicProperties == [new YangModelCmHandle.Property('publicProperty1', 'value1')]
+            assert yangModelCmhandleDeepCopy.compositeState.cmHandleState == CmHandleState.ADVISED
+            assert yangModelCmhandleDeepCopy.compositeState.dataSyncEnabled == false
+        and: 'equality on reference and hashcode behave as expected'
+            assert currentYangModelCmHandle.hashCode() != yangModelCmhandleDeepCopy.hashCode()
+            assert currentYangModelCmHandle != yangModelCmhandleDeepCopy
+
+    }
+
+
 }
index 21aa164..26b3613 100644 (file)
@@ -135,6 +135,17 @@ class CmHandleQueriesSpec extends Specification {
             assert result.contains(cmHandleDataNode)
     }
 
+    def 'Get all cm handles by dmi plugin identifier'() {
+        given: 'the DataNodes queried for a given cpsPath are returned from the persistence service.'
+            mockResponses()
+        when: 'cm Handles are fetched for a given dmi plugin identifier'
+            def result = objectUnderTest.getCmHandlesByDmiPluginIdentifier('my-dmi-plugin-identifier')
+        then: 'result is the correct size'
+            assert result.size() == 3
+        and: 'result contains the correct cm handles'
+            assert result.cmHandleId.containsAll('PNFDemo', 'PNFDemo2', 'PNFDemo4')
+    }
+
     void mockResponses() {
         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', _) >> []
@@ -142,6 +153,9 @@ class CmHandleQueriesSpec extends Specification {
         cpsDataPersistenceService.queryDataNodes(_, _, '//public-properties[@name=\"Contact2\" and @value=\"\"]/ancestor::cm-handles', _) >> []
         cpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"READY\"]/ancestor::cm-handles', _) >> [pnfDemo, pnfDemo3]
         cpsDataPersistenceService.queryDataNodes(_, _, '//state[@cm-handle-state=\"LOCKED\"]/ancestor::cm-handles', _) >> [pnfDemo2, pnfDemo4]
+        cpsDataPersistenceService.queryDataNodes('NCMP-Admin','ncmp-dmi-registry','/dmi-registry/cm-handles[@dmi-service-name=\'my-dmi-plugin-identifier\']',OMIT_DESCENDANTS) >> [pnfDemo, pnfDemo2]
+        cpsDataPersistenceService.queryDataNodes('NCMP-Admin','ncmp-dmi-registry','/dmi-registry/cm-handles[@dmi-data-service-name=\'my-dmi-plugin-identifier\']',OMIT_DESCENDANTS) >> [pnfDemo,pnfDemo4]
+        cpsDataPersistenceService.queryDataNodes('NCMP-Admin','ncmp-dmi-registry','/dmi-registry/cm-handles[@dmi-model-service-name=\'my-dmi-plugin-identifier\']',OMIT_DESCENDANTS) >> [pnfDemo2,pnfDemo4]
     }
 
     def static createDataNode(dataNodeId) {
index 76f10de..19c8ae8 100644 (file)
@@ -159,7 +159,7 @@ class InventoryPersistenceSpec extends Specification {
             def compositeState2 = new CompositeState(cmHandleState: cmHandleState, lastUpdateTime: formattedDateAndTime)
         when: 'update cm handle state is invoked with the #scenario state'
             def cmHandleStateMap = ['Some-Cm-Handle1' : compositeState1, 'Some-Cm-Handle2' : compositeState2]
-            objectUnderTest.saveCmHandleStates(cmHandleStateMap)
+            objectUnderTest.saveCmHandleStateBatch(cmHandleStateMap)
         then: 'update node leaves is invoked with the correct params'
             1 * mockCpsDataService.updateDataNodesAndDescendants('NCMP-Admin', 'ncmp-dmi-registry', cmHandlesJsonDataMap, _ as OffsetDateTime)
         where: 'the following states are used'