Performance Improvement: Enhance state handler 37/130537/7
authormpriyank <priyank.maheshwari@est.tech>
Tue, 30 Aug 2022 22:23:53 +0000 (23:23 +0100)
committermpriyank <priyank.maheshwari@est.tech>
Thu, 1 Sep 2022 13:14:49 +0000 (14:14 +0100)
- Introduced batch handling capability to state handler
- Refactored methods in CompositeStateUtils
- Renamed saveCmHandleStates to saveCmHandleStateBatch
- Decoupled processing of events in bulk
- Test scenarios for the new functionality

Issue-ID: CPS-1231
Issue-ID: CPS-1126
Change-Id: Ifacdeb7bbed14712ecf4f5e2a4d9b324bae278d8
Signed-off-by: mpriyank <priyank.maheshwari@est.tech>
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/CompositeStateUtils.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
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/InventoryPersistenceSpec.groovy

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 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());
     }
 
     /**
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 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'