LCM Event fix: Include main properties in V2 version 76/143376/3
authorToineSiebelink <toine.siebelink@est.tech>
Tue, 24 Feb 2026 10:08:37 +0000 (10:08 +0000)
committerToineSiebelink <toine.siebelink@est.tech>
Wed, 25 Feb 2026 17:26:10 +0000 (17:26 +0000)
- moduleSetTag now include in old and new properties for V2 if changed
- dataProducerIdentifier now include in old and new properties for V2 if changed
- alternateId now include in old and new properties for V2 if changed (probably not a real use case)
- 'public properties' key-name is actualy "cmHandleProperties" for consistency with V1
- refactored code for consistency and readability
- extended unit test to cover missed scenarios
- add V2 variation of create and update integration test

Issue-ID: CPS-2975
Change-Id: Ia6b6f7368dc8c7824d3270ffc25964016bf50b32
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
cps-charts/values.yaml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/PayloadFactory.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventObjectCreatorSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/lcm/PayloadFactorySpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/inventory/CmHandleCreateSpec.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/inventory/CmHandleUpdateSpec.groovy

index 4b03d5a..78e3c05 100644 (file)
@@ -263,4 +263,4 @@ policyExecutorStub:
       memory: "350Mi"
     limits:
       cpu: "1000m"
-      memory: "2Gi"
\ No newline at end of file
+      memory: "2Gi"
index 3ab8ca1..29a3412 100644 (file)
 
 package org.onap.cps.ncmp.impl.inventory.sync.lcm;
 
-import com.google.common.collect.MapDifference;
-import com.google.common.collect.Maps;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
-import org.onap.cps.ncmp.api.inventory.models.CmHandleState;
 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle;
 import org.onap.cps.ncmp.events.lcm.PayloadV1;
 import org.onap.cps.ncmp.events.lcm.PayloadV2;
@@ -37,6 +34,7 @@ import org.onap.cps.ncmp.events.lcm.Values;
 /**
  * Utility class for examining and identifying changes in CM handle properties.
  */
+@SuppressWarnings("java:S1192")  // Ignore repetition warning for string literals like "alternateId"
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class PayloadFactory {
 
@@ -69,60 +67,41 @@ public class PayloadFactory {
         final Map<String, Object> newProperties = new HashMap<>();
         newProperties.put("dataSyncEnabled", ncmpServiceCmHandle.getCompositeState().getDataSyncEnabled());
         newProperties.put("cmHandleState", ncmpServiceCmHandle.getCompositeState().getCmHandleState().name());
-        newProperties.putAll(ncmpServiceCmHandle.getPublicProperties());
+        newProperties.put("alternateId", ncmpServiceCmHandle.getAlternateId());
+        newProperties.put("moduleSetTag", ncmpServiceCmHandle.getModuleSetTag());
+        newProperties.put("dataProducerIdentifier", ncmpServiceCmHandle.getDataProducerIdentifier());
+        newProperties.put("cmHandleProperties", ncmpServiceCmHandle.getPublicProperties());
         payload.setNewValues(newProperties);
         return payload;
     }
 
     static PayloadV2 identifyChanges(final NcmpServiceCmHandle currentNcmpServiceCmHandle,
                                      final NcmpServiceCmHandle targetNcmpServiceCmHandle) {
-        final Boolean currentDataSync = currentNcmpServiceCmHandle.getCompositeState().getDataSyncEnabled();
-        final Boolean targetDataSync = targetNcmpServiceCmHandle.getCompositeState().getDataSyncEnabled();
-        final boolean dataSyncEnabledChanged = !Objects.equals(currentDataSync, targetDataSync);
-
-        final CmHandleState currentCmHandleState = currentNcmpServiceCmHandle.getCompositeState().getCmHandleState();
-        final CmHandleState targetCmHandleState = targetNcmpServiceCmHandle.getCompositeState().getCmHandleState();
-        final boolean cmHandleStateChanged = !Objects.equals(currentCmHandleState, targetCmHandleState);
-
-        final Map<String, String> currentPublicProperties = currentNcmpServiceCmHandle.getPublicProperties();
-        final Map<String, String> targetPublicProperties = targetNcmpServiceCmHandle.getPublicProperties();
-        final boolean publicPropertiesChanged = !Objects.equals(currentPublicProperties, targetPublicProperties);
-
-        final PayloadV2 payload = new PayloadV2();
-        if (!dataSyncEnabledChanged && !cmHandleStateChanged && !publicPropertiesChanged) {
-            return payload;
-        }
-
         final Map<String, Object> oldProperties = new HashMap<>();
         final Map<String, Object> newProperties = new HashMap<>();
 
-        if (dataSyncEnabledChanged) {
-            oldProperties.put("dataSyncEnabled", currentDataSync);
-            newProperties.put("dataSyncEnabled", targetDataSync);
-        }
-
-        if (cmHandleStateChanged) {
-            oldProperties.put("cmHandleState",
-                currentNcmpServiceCmHandle.getCompositeState().getCmHandleState().name());
-            newProperties.put("cmHandleState",
+        trackChange(oldProperties, newProperties, "dataSyncEnabled",
+                currentNcmpServiceCmHandle.getCompositeState().getDataSyncEnabled(),
+                targetNcmpServiceCmHandle.getCompositeState().getDataSyncEnabled());
+        trackChange(oldProperties, newProperties, "cmHandleState",
+                currentNcmpServiceCmHandle.getCompositeState().getCmHandleState().name(),
                 targetNcmpServiceCmHandle.getCompositeState().getCmHandleState().name());
-        }
-
-        if (publicPropertiesChanged) {
-            final MapDifference<String, String> mapDifference = Maps.difference(
-                targetNcmpServiceCmHandle.getPublicProperties(),
-                currentNcmpServiceCmHandle.getPublicProperties());
+        trackChange(oldProperties, newProperties, "alternateId",
+            currentNcmpServiceCmHandle.getAlternateId(), targetNcmpServiceCmHandle.getAlternateId());
+        trackChange(oldProperties, newProperties, "moduleSetTag",
+            currentNcmpServiceCmHandle.getModuleSetTag(), targetNcmpServiceCmHandle.getModuleSetTag());
+        trackChange(oldProperties, newProperties, "dataProducerIdentifier",
+            currentNcmpServiceCmHandle.getDataProducerIdentifier(),
+            targetNcmpServiceCmHandle.getDataProducerIdentifier());
+        trackChange(oldProperties, newProperties, "cmHandleProperties",
+            currentNcmpServiceCmHandle.getPublicProperties(),
+            targetNcmpServiceCmHandle.getPublicProperties());
 
-            oldProperties.putAll(mapDifference.entriesOnlyOnRight());
-            newProperties.putAll(mapDifference.entriesOnlyOnLeft());
-
-            mapDifference.entriesDiffering().forEach((key, valueDifference) -> {
-                oldProperties.put(key, valueDifference.rightValue());
-                newProperties.put(key, valueDifference.leftValue());
-            });
+        final PayloadV2 payload = new PayloadV2();
+        if (!oldProperties.isEmpty()) {
+            payload.setOldValues(oldProperties);
+            payload.setNewValues(newProperties);
         }
-        payload.setOldValues(oldProperties);
-        payload.setNewValues(newProperties);
         return payload;
     }
 
@@ -146,16 +125,21 @@ public class PayloadFactory {
         if (properties.containsKey("cmHandleState")) {
             values.setCmHandleState(Values.CmHandleState.fromValue((String) properties.get("cmHandleState")));
         }
-        final Map<String, String> publicProperties = new HashMap<>();
-        properties.forEach((key, value) -> {
-            if (!"dataSyncEnabled".equals(key) && !"cmHandleState".equals(key)) {
-                publicProperties.put(key, (String) value);
-            }
-        });
-        if (!publicProperties.isEmpty()) {
-            values.setCmHandleProperties(List.of(publicProperties));
+        if (properties.containsKey("cmHandleProperties")) {
+            values.setCmHandleProperties(List.of((Map<String, String>) properties.get("cmHandleProperties")));
         }
         return values;
     }
 
+    private static void trackChange(final Map<String, Object> oldProperties,
+                                    final Map<String, Object> newProperties,
+                                    final String propertyName,
+                                    final Object oldValue,
+                                    final Object newValue) {
+        if (!Objects.equals(oldValue, newValue)) {
+            oldProperties.put(propertyName, oldValue);
+            newProperties.put(propertyName, newValue);
+        }
+    }
+
 }
index 381b731..0611889 100644 (file)
@@ -32,6 +32,7 @@ import static org.onap.cps.ncmp.api.inventory.models.CmHandleState.READY
 
 class LcmEventObjectCreatorSpec extends Specification {
 
+    def defaultState = new CompositeState(dataSyncEnabled: true, cmHandleState: READY)
     def objectUnderTest = new LcmEventObjectCreator()
     def cmHandleId = 'test-cm-handle'
 
@@ -63,10 +64,8 @@ class LcmEventObjectCreatorSpec extends Specification {
     def 'Map the LcmEvent for all properties NO CHANGE'() {
         given: 'NCMP cm handle details without any changes'
             def publicProperties = ['publicProperty1': 'value3', 'publicProperty2': 'value4']
-            def currentNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: READY),
-                    publicProperties: publicProperties)
-            def targetNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: READY),
-                    publicProperties: publicProperties)
+            def currentNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: defaultState, publicProperties: publicProperties)
+            def targetNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: defaultState, publicProperties: publicProperties)
         when: 'the lcm event is created'
             def result = objectUnderTest.createLcmEventV1(currentNcmpServiceCmHandle, targetNcmpServiceCmHandle)
         then: 'Properties are just the one which are same'
@@ -76,9 +75,8 @@ class LcmEventObjectCreatorSpec extends Specification {
 
     def 'Map the LcmEvent for operation CREATE'() {
         given: 'NCMP cm handle details'
-            def targetNcmpServiceCmhandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: READY),
-                publicProperties: ['publicProperty1': 'value11', 'publicProperty2': 'value22'])
             def currentNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProperty1': 'value1', 'publicProperty2': 'value2'])
+            def targetNcmpServiceCmhandle  = new NcmpServiceCmHandle(cmHandleId: cmHandleId, compositeState: defaultState, publicProperties: ['publicProperty1': 'value11', 'publicProperty2': 'value22'])
         when: 'the lcm event is created'
             def result = objectUnderTest.createLcmEventV1(currentNcmpServiceCmHandle, targetNcmpServiceCmhandle)
         then: 'event header is mapped correctly'
@@ -88,7 +86,7 @@ class LcmEventObjectCreatorSpec extends Specification {
         and: 'event payload is mapped correctly'
             assert result.event.cmHandleId == cmHandleId
             assert result.event.newValues.cmHandleState == Values.CmHandleState.READY
-            assert result.event.newValues.dataSyncEnabled == false
+            assert result.event.newValues.dataSyncEnabled == true
             assert result.event.newValues.cmHandleProperties == [['publicProperty1': 'value11', 'publicProperty2': 'value22']]
         and: 'it should not have any old values'
             assert result.event.oldValues == null
@@ -158,8 +156,8 @@ class LcmEventObjectCreatorSpec extends Specification {
 
     def 'Map the LcmEvent for alternate ID, data producer identifier, and module set tag when they contain #scenario'() {
         given: 'NCMP cm handle details with current and old values for alternate ID, module set tag, and data producer identifier'
-            def currentNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: currentAlternateId, moduleSetTag: currentModuleSetTag, dataProducerIdentifier: currentDataProducerIdentifier, compositeState: new CompositeState(dataSyncEnabled: false))
-            def targetNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: targetAlternateId, moduleSetTag: targetModuleSetTag, dataProducerIdentifier: targetDataProducerIdentifier, compositeState: new CompositeState(dataSyncEnabled: false))
+            def currentNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: currentAlternateId, moduleSetTag: currentModuleSetTag, dataProducerIdentifier: currentDataProducerIdentifier, compositeState: defaultState)
+            def targetNcmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: targetAlternateId, moduleSetTag: targetModuleSetTag, dataProducerIdentifier: targetDataProducerIdentifier, compositeState: defaultState)
         when: 'the lcm event is created'
             def result = objectUnderTest.createLcmEventV1(currentNcmpServiceCmHandle, targetNcmpServiceCmHandle)
         then: 'the alternate ID, module set tag, and data producer identifier are present or are an empty string in the payload'
index d711305..c4ad878 100644 (file)
 
 package org.onap.cps.ncmp.impl.inventory.sync.lcm
 
-import org.onap.cps.ncmp.api.inventory.models.CmHandleState
+import spock.lang.Specification
+
+import static org.onap.cps.ncmp.api.inventory.models.CmHandleState.ADVISED
+import static org.onap.cps.ncmp.api.inventory.models.CmHandleState.DELETED
+import static org.onap.cps.ncmp.api.inventory.models.CmHandleState.READY
+import static org.onap.cps.ncmp.events.lcm.Values.CmHandleState.ADVISED as LCM_ADVISED
+import static org.onap.cps.ncmp.events.lcm.Values.CmHandleState.READY as LCM_READY
+import static org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventType.CREATE
+import static org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventType.DELETE
+import static org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventType.UPDATE
+
 import org.onap.cps.ncmp.api.inventory.models.CompositeState
 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
-import org.onap.cps.ncmp.events.lcm.Values
-import spock.lang.Specification
 
 class PayloadFactorySpec extends Specification {
+
+    def defaultState = new CompositeState(dataSyncEnabled: true, cmHandleState: READY)
+
     def 'Create payload for create operation.'() {
         given: 'a new cm handle'
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch',
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'value1']
-            )
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch1', compositeState: defaultState, publicProperties: [prop1: 'value1'] )
         when: 'payload is created for create'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.CREATE, null, ncmpServiceCmHandle)
+            def result = PayloadFactory.createPayloadV1(CREATE, null, ncmpServiceCmHandle)
         then: 'new values are populated'
-            assert result.cmHandleId == 'ch'
+            assert result.cmHandleId == 'ch1'
             assert result.newValues.dataSyncEnabled == true
-            assert result.newValues.cmHandleState == Values.CmHandleState.READY
-            assert result.newValues.cmHandleProperties == [['prop1': 'value1']]
+            assert result.newValues.cmHandleState == LCM_READY
+        and: 'the public properties are store as a list (fluke in old schema) of maps as cm handle properties (legacy name)'
+            assert result.newValues.cmHandleProperties == [[prop1: 'value1']]
         and: 'old values are not set'
             assert result.oldValues == null
     }
 
     def 'Create payload when no changes detected.'() {
         given: 'current and target cm handles with same properties'
-            def currentCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'value1']
-            )
-            def targetCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch',
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'value1']
-            )
+            def currentCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch1', compositeState: defaultState, publicProperties: [prop1: 'value1'] )
+            def targetCmHandle  = new NcmpServiceCmHandle(cmHandleId: 'ch1', compositeState: defaultState, publicProperties: [prop1: 'value1'])
         when: 'payload is created'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.UPDATE, currentCmHandle, targetCmHandle)
+            def result = PayloadFactory.createPayloadV1(UPDATE, currentCmHandle, targetCmHandle)
         then: 'no updates are detected'
-            assert result.cmHandleId == 'ch'
+            assert result.cmHandleId == 'ch1'
             assert result.oldValues == null
             assert result.newValues == null
     }
 
     def 'Create payload when data sync flag changes.'() {
         given: 'current and target cm handles with different data sync flags'
-            def currentCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: CmHandleState.READY),
-                publicProperties: [:]
-            )
-            def targetCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: [:]
-            )
+            def currentCmHandle = new NcmpServiceCmHandle(compositeState: defaultState)
+            def targetCmHandle  = new NcmpServiceCmHandle(compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: READY))
         when: 'payload is created'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.UPDATE,currentCmHandle, targetCmHandle)
+            def result = PayloadFactory.createPayloadV1(UPDATE,currentCmHandle, targetCmHandle)
         then: 'data sync flag change is detected'
-            assert result.oldValues.dataSyncEnabled == false
-            assert result.newValues.dataSyncEnabled == true
+            assert result.oldValues.dataSyncEnabled == true
+            assert result.newValues.dataSyncEnabled == false
     }
 
     def 'Create payload when cm handle state changes.'() {
         given: 'current and target cm handles with different states'
-            def currentCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.ADVISED),
-                publicProperties: [:]
-            )
-            def targetCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: [:]
-            )
+            def currentCmHandle = new NcmpServiceCmHandle(compositeState: defaultState)
+            def targetCmHandle  = new NcmpServiceCmHandle(compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: ADVISED))
         when: 'payload is created'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.UPDATE,currentCmHandle, targetCmHandle)
+            def result = PayloadFactory.createPayloadV1(UPDATE,currentCmHandle, targetCmHandle)
         then: 'state change is detected'
-            assert result.oldValues.cmHandleState == Values.CmHandleState.ADVISED
-            assert result.newValues.cmHandleState == Values.CmHandleState.READY
+            assert result.oldValues.cmHandleState == LCM_READY
+            assert result.newValues.cmHandleState == LCM_ADVISED
     }
 
     def 'Create payload when public properties change.'() {
         given: 'current and target cm handles with different properties'
-            def currentCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'old value', 'prop2': 'to be deleted', 'prop4': 'unchanged']
-            )
-            def targetCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'new value', 'prop3': 'new', 'prop4': 'unchanged']
-            )
+            def currentCmHandle = new NcmpServiceCmHandle(publicProperties: [prop1: 'old value', prop2: 'to be deleted', prop4: 'unchanged'], compositeState: defaultState)
+            def targetCmHandle  = new NcmpServiceCmHandle(publicProperties: [prop1: 'new value', prop3: 'new', prop4: 'unchanged'], compositeState: defaultState)
         when: 'payload is created'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.UPDATE,currentCmHandle, targetCmHandle)
+            def result = PayloadFactory.createPayloadV1(UPDATE,currentCmHandle, targetCmHandle)
         then: 'property changes are detected'
-            assert result.oldValues.cmHandleProperties[0]['prop1'] == 'old value'
-            assert result.oldValues.cmHandleProperties[0]['prop2'] == 'to be deleted'
-            assert result.newValues.cmHandleProperties[0]['prop1'] == 'new value'
-            assert result.newValues.cmHandleProperties[0]['prop3'] == 'new'
-        and: 'unchanged property is not included in the result'
-            assert !result.oldValues.cmHandleProperties[0].containsKey('prop4')
-            assert !result.newValues.cmHandleProperties[0].containsKey('prop4')
+            assert result.oldValues.cmHandleProperties[0].prop1 == 'old value'
+            assert result.oldValues.cmHandleProperties[0].prop2 == 'to be deleted'
+            assert result.newValues.cmHandleProperties[0].prop1 == 'new value'
+            assert result.newValues.cmHandleProperties[0].prop3 == 'new'
+        and: 'unchanged property is still included in the result'
+            assert result.oldValues.cmHandleProperties[0].prop4 == 'unchanged'
+            assert result.newValues.cmHandleProperties[0].prop4 == 'unchanged'
     }
 
     def 'Create payload when multiple changes occur.'() {
         given: 'current and target cm handles with multiple differences'
-            def currentCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: CmHandleState.ADVISED),
-                publicProperties: ['prop1': 'value1']
-            )
-            def targetCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'newValue1']
-            )
+            def currentCmHandle = new NcmpServiceCmHandle(compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: ADVISED), publicProperties: [prop1: 'value1'])
+            def targetCmHandle  = new NcmpServiceCmHandle(compositeState: defaultState, publicProperties: [prop1: 'newValue1'])
         when: 'payload is created'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.UPDATE,currentCmHandle, targetCmHandle)
+            def result = PayloadFactory.createPayloadV1(UPDATE,currentCmHandle, targetCmHandle)
         then: 'all changes are detected'
             assert result.oldValues.dataSyncEnabled == false
             assert result.newValues.dataSyncEnabled == true
-            assert result.oldValues.cmHandleState == Values.CmHandleState.ADVISED
-            assert result.newValues.cmHandleState == Values.CmHandleState.READY
-            assert result.oldValues.cmHandleProperties[0]['prop1'] == 'value1'
-            assert result.newValues.cmHandleProperties[0]['prop1'] == 'newValue1'
+            assert result.oldValues.cmHandleState == LCM_ADVISED
+            assert result.newValues.cmHandleState == LCM_READY
+            assert result.oldValues.cmHandleProperties[0].prop1 == 'value1'
+            assert result.newValues.cmHandleProperties[0].prop1 == 'newValue1'
     }
 
     def 'Create payload for delete operation.'() {
         given: 'a cm handle being deleted'
-            def currentCmHandle = new NcmpServiceCmHandle(
-                compositeState: new CompositeState(dataSyncEnabled: true, cmHandleState: CmHandleState.READY),
-                publicProperties: ['prop1': 'value1']
-            )
-            def targetCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch',
-                compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: CmHandleState.DELETED),
-                publicProperties: ['prop1': 'value1']
-            )
+            def currentCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch1', compositeState: defaultState, publicProperties: [prop1: 'value1'])
+            def targetCmHandle = new NcmpServiceCmHandle(cmHandleId: 'ch1', compositeState: new CompositeState(dataSyncEnabled: false, cmHandleState: DELETED), publicProperties: [prop1: 'value1'])
         when: 'payload is created for delete'
-            def result = PayloadFactory.createPayloadV1(LcmEventType.DELETE, currentCmHandle, targetCmHandle)
+            def result = PayloadFactory.createPayloadV1(DELETE, currentCmHandle, targetCmHandle)
         then: 'cmHandleId is populated'
-            assert result.cmHandleId == 'ch'
+            assert result.cmHandleId == 'ch1'
         and: 'no values are populated'
             assert result.oldValues == null
             assert result.newValues == null
     }
+
+    def 'Create V2 payload when #scenario changes.'() {
+        given: 'current and target cm handles'
+            def currentCmHandle = new NcmpServiceCmHandle(compositeState: defaultState, publicProperties: [:],(propertyName):currentValue)
+            def targetCmHandle  = new NcmpServiceCmHandle(compositeState: defaultState, publicProperties: [:],(propertyName):targetValue)
+        when: 'V2 payload is created'
+            def result = PayloadFactory.createPayloadV2(UPDATE, currentCmHandle, targetCmHandle)
+        then: 'changes are detected in V2 payload'
+            assert result.oldValues[propertyName] == currentValue
+            assert result.newValues[propertyName] == targetValue
+        where:
+            scenario                 | propertyName             | currentValue | targetValue
+            'alternateId'            | 'alternateId'            | 'old-alt-id' | 'new-alt-id'
+            'moduleSetTag'           | 'moduleSetTag'           | 'old-tag'    | 'new-tag'
+            'dataProducerIdentifier' | 'dataProducerIdentifier' | 'old-dpi'    | 'new-dpi'
+    }
+
+    def 'Create V2 payload when public properties change.'() {
+        given: 'current and target cm handles with different properties'
+            def currentCmHandle = new NcmpServiceCmHandle(compositeState: defaultState, publicProperties: [oldProp:'oldValue'])
+            def targetCmHandle  = new NcmpServiceCmHandle(compositeState: defaultState, publicProperties: [newProp:'newValue'])
+        when: 'V2 payload is created'
+            def result = PayloadFactory.createPayloadV2(UPDATE, currentCmHandle, targetCmHandle)
+        then: 'property changes are detected in V2 payload as "chHandleProperties"'
+            assert result.oldValues['cmHandleProperties'] == [oldProp:'oldValue']
+            assert result.newValues['cmHandleProperties'] == [newProp:'newValue']
+    }
+
+    def 'Create V2 payload when cm handle state changes and dataSyncEnabled #dataSyncScenario.'() {
+        given: 'current and target cm handles with different states'
+            def currentCmHandle = new NcmpServiceCmHandle(compositeState: defaultState)
+            def targetCmHandle  = new NcmpServiceCmHandle(compositeState: new CompositeState(dataSyncEnabled: targetDataSync, cmHandleState: ADVISED))
+        when: 'V2 payload is created'
+            def result = PayloadFactory.createPayloadV2(UPDATE, currentCmHandle, targetCmHandle)
+        then: 'state change is detected in V2 payload'
+            assert result.oldValues['cmHandleState'] == 'READY'
+            assert result.newValues['cmHandleState'] == 'ADVISED'
+        and: 'dataSyncEnabled is handled correctly'
+            assert result.oldValues['dataSyncEnabled'] == expectedOldDataSync
+            assert result.newValues['dataSyncEnabled'] == expectedNewDataSync
+        where:
+            dataSyncScenario | targetDataSync | expectedOldDataSync | expectedNewDataSync
+            'changes'        | false          | true                | false
+            'does not change'| true           | null                | null
+    }
 }
index 15f3a48..8f84c5f 100644 (file)
@@ -31,7 +31,11 @@ import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.inventory.models.LockReasonCategory
 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.events.lcm.LcmEventV1
+import org.onap.cps.ncmp.events.lcm.LcmEventV2
 import org.onap.cps.ncmp.impl.NetworkCmProxyInventoryFacadeImpl
+import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventProducer
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.test.util.ReflectionTestUtils
 import spock.util.concurrent.PollingConditions
 
 import java.time.Duration
@@ -43,6 +47,9 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
 
     KafkaConsumer<String, LegacyEvent> kafkaConsumer
 
+    @Autowired
+    LcmEventProducer lcmEventProducer
+
     def setup() {
         objectUnderTest = networkCmProxyInventoryFacade
         subscribeAndClearPreviousMessages('test-group', 'ncmp-events')
@@ -99,7 +106,7 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
                 assert oldValues == null
                 assert newValues.cmHandleState.value() == 'ADVISED'
                 assert newValues.dataSyncEnabled == null
-                assert newValues.cmHandleProperties[0] == [color:'green']
+                assert newValues.cmHandleProperties == [[color:'green']]
             }
         and: 'the next event is about update to READY state (new value), the old value for state is ADVISED'
             assert messages[1].event.oldValues.cmHandleState.value() == 'ADVISED'
@@ -124,6 +131,70 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase {
             }
     }
 
+    def 'CM Handle registration using V2 events.'() {
+        given: 'event schema version is set to v2'
+            def originalEventSchemaVersion = ReflectionTestUtils.getField(lcmEventProducer, 'eventSchemaVersion')
+            ReflectionTestUtils.setField(lcmEventProducer, 'eventSchemaVersion', 'v2')
+        and: 'DMI will return modules when requested'
+            def uniqueIdV2 = 'my-new-cm-handle-v2'
+            dmiDispatcher1.moduleNamesPerCmHandleId[uniqueIdV2] = ['M1', 'M2']
+        when: 'a CM-handle is registered for creation'
+            def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: uniqueIdV2,
+                                                           alternateId: 'fdn1',
+                                                           moduleSetTag: 'tag1',
+                                                           dataProducerIdentifier: 'prod1',
+                                                           publicProperties: [color:'green'])
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate])
+            def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistration(dmiPluginRegistration)
+        then: 'registration gives successful response'
+            assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(uniqueIdV2)]
+        and: 'CM-handle is initially in ADVISED state'
+            assert CmHandleState.ADVISED == objectUnderTest.getCmHandleCompositeState(uniqueIdV2).cmHandleState
+        then: 'the module sync watchdog is triggered'
+            moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+        then: 'CM-handle goes to READY state after module sync'
+            assert CmHandleState.READY == objectUnderTest.getCmHandleCompositeState(uniqueIdV2).cmHandleState
+        and: 'the CM-handle has expected modules'
+            assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences(uniqueIdV2).moduleName.sort()
+        then: 'get the last 2 messages'
+            def consumerRecords = getLatestConsumerRecordsWithMaxPollOf1Second(kafkaConsumer, 2)
+            def messages = []
+            consumerRecords.each { consumerRecord ->
+                messages.add(jsonObjectMapper.convertJsonString(consumerRecord.value().toString(), LcmEventV2))
+            }
+        and: 'both messages have the correct cmHandleId'
+            assert messages.event.every { it.cmHandleId == uniqueIdV2 }
+        and: 'the first lcm event has no old values and the initial attributes as new values state ADVISED'
+            with(messages[0].event) {
+                assert oldValues == null
+                assert newValues['cmHandleState'] == 'ADVISED'
+                assert newValues['dataSyncEnabled'] == null
+                assert newValues['cmHandleProperties'] == [color:'green']
+                assert newValues['alternateId'] == 'fdn1'
+                assert newValues['moduleSetTag'] == 'tag1'
+                assert newValues['dataProducerIdentifier'] == 'prod1'
+            }
+        and: 'the next event is about update to READY state (new value), the old value for state is ADVISED'
+            assert messages[1].event.oldValues['cmHandleState'] == 'ADVISED'
+            assert messages[1].event.newValues['cmHandleState'] == 'READY'
+        and: 'the cm handle (public) properties have not changed and are therefore null for old and new values'
+            assert messages[1].event.oldValues['cmHandleProperties'] == null
+            assert messages[1].event.newValues['cmHandleProperties'] == null
+        and: 'the data sync flag goes from undefined to false'
+            assert messages[1].event.oldValues['dataSyncEnabled'] == null
+            assert messages[1].event.newValues['dataSyncEnabled'] == false
+        and: 'alternateId, moduleSetTag, dataProducerIdentifier have not changed and are therefore null for old and new values'
+            assert messages[1].event.oldValues['alternateId'] == null
+            assert messages[1].event.newValues['alternateId'] == null
+            assert messages[1].event.oldValues['moduleSetTag'] == null
+            assert messages[1].event.newValues['moduleSetTag'] == null
+            assert messages[1].event.oldValues['dataProducerIdentifier'] == null
+            assert messages[1].event.newValues['dataProducerIdentifier'] == null
+        cleanup: 'restore original event schema version and deregister CM handle'
+            ReflectionTestUtils.setField(lcmEventProducer, 'eventSchemaVersion', originalEventSchemaVersion)
+            deregisterCmHandle(DMI1_URL, uniqueIdV2)
+    }
+
     def 'CM Handle registration with DMI error during module sync.'() {
         given: 'DMI is not available to handle requests'
             dmiDispatcher1.isAvailable = false
index ae302e8..a86bfa3 100644 (file)
@@ -29,7 +29,11 @@ import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
 import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle
 import org.onap.cps.ncmp.events.lcm.LcmEventV1
+import org.onap.cps.ncmp.events.lcm.LcmEventV2
 import org.onap.cps.ncmp.impl.NetworkCmProxyInventoryFacadeImpl
+import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventProducer
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.test.util.ReflectionTestUtils
 
 import java.time.Duration
 
@@ -39,6 +43,8 @@ class CmHandleUpdateSpec extends CpsIntegrationSpecBase {
 
     KafkaConsumer<String, LegacyEvent> kafkaConsumer
 
+    @Autowired
+    LcmEventProducer lcmEventProducer
 
     def setup() {
         objectUnderTest = networkCmProxyInventoryFacade
@@ -125,6 +131,43 @@ class CmHandleUpdateSpec extends CpsIntegrationSpecBase {
             deregisterCmHandle(DMI1_URL, cmHandleId)
     }
 
+    def 'CM Handle registration to verify changes in data producer identifier using V2 events'() {
+        given: 'event schema version is set to v2'
+            def originalEventSchemaVersion = ReflectionTestUtils.getField(lcmEventProducer, 'eventSchemaVersion')
+            ReflectionTestUtils.setField(lcmEventProducer, 'eventSchemaVersion', 'v2')
+        and: 'DMI will return modules when requested'
+            def cmHandleId = 'ch-id-for-update-v2'
+            dmiDispatcher1.moduleNamesPerCmHandleId[cmHandleId] = ['M1', 'M2']
+        when: 'a CM-handle is registered for creation'
+            def cmHandleToCreate = new NcmpServiceCmHandle(cmHandleId: cmHandleId)
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: [cmHandleToCreate])
+            def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistration(dmiPluginRegistration)
+        then: 'registration gives successful response'
+            assert dmiPluginRegistrationResponse.createdCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)]
+        then: 'the module sync watchdog is triggered'
+            moduleSyncWatchdog.moduleSyncAdvisedCmHandles()
+        and: 'flush and check there are 2 cm handle registration events (state transition from NONE to ADVISED and ADVISED to READY)'
+            assert getLatestConsumerRecordsWithMaxPollOf1Second(kafkaConsumer, 2).size() == 2
+        and: 'cm handle updated with the data producer identifier'
+            def cmHandleToUpdate = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dataProducerIdentifier: 'my-data-producer-id')
+            def dmiPluginRegistrationForUpdate = new DmiPluginRegistration(dmiPlugin: DMI1_URL, updatedCmHandles: [cmHandleToUpdate])
+            def dmiPluginRegistrationResponseForUpdate = objectUnderTest.updateDmiRegistration(dmiPluginRegistrationForUpdate)
+        then: 'registration gives successful response'
+            assert dmiPluginRegistrationResponseForUpdate.updatedCmHandles == [CmHandleRegistrationResponse.createSuccessResponse(cmHandleId)]
+        and: 'get the latest message'
+            def consumerRecords = getLatestConsumerRecordsWithMaxPollOf1Second(kafkaConsumer, 1)
+        and: 'the V2 message has the updated data producer identifier in newValues'
+            def notificationMessages = []
+            for (def consumerRecord : consumerRecords) {
+                notificationMessages.add(jsonObjectMapper.convertJsonString(consumerRecord.value().toString(), LcmEventV2))
+            }
+            assert notificationMessages[0].event.cmHandleId.contains(cmHandleId)
+            assert notificationMessages[0].event.newValues['dataProducerIdentifier'] == 'my-data-producer-id'
+        cleanup: 'restore original event schema version and deregister CM handle'
+            ReflectionTestUtils.setField(lcmEventProducer, 'eventSchemaVersion', originalEventSchemaVersion)
+            deregisterCmHandle(DMI1_URL, cmHandleId)
+    }
+
     def subscribeAndClearPreviousMessages(consumerGroupId, topicName) {
         kafkaConsumer = KafkaTestContainer.getLegacyEventConsumer(consumerGroupId)
         kafkaConsumer.subscribe([topicName])