diff --git a/code/edgeidentity/src/main/java/com/adobe/marketing/mobile/edge/identity/IdentityExtension.java b/code/edgeidentity/src/main/java/com/adobe/marketing/mobile/edge/identity/IdentityExtension.java index 81c7243f..ea699ae7 100644 --- a/code/edgeidentity/src/main/java/com/adobe/marketing/mobile/edge/identity/IdentityExtension.java +++ b/code/edgeidentity/src/main/java/com/adobe/marketing/mobile/edge/identity/IdentityExtension.java @@ -21,6 +21,7 @@ import com.adobe.marketing.mobile.Extension; import com.adobe.marketing.mobile.ExtensionApi; import com.adobe.marketing.mobile.SharedStateResolution; +import com.adobe.marketing.mobile.SharedStateResolver; import com.adobe.marketing.mobile.SharedStateResult; import com.adobe.marketing.mobile.SharedStateStatus; import com.adobe.marketing.mobile.services.Log; @@ -250,10 +251,14 @@ private void handleUrlVariableResponse( * @param event the edge update identity {@link Event} */ void handleUpdateIdentities(@NonNull final Event event) { + // Add pending shared state to avoid race condition between updating and reading identity map + final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); + final Map eventData = event.getEventData(); if (eventData == null) { Log.trace(LOG_TAG, LOG_SOURCE, "Cannot update identifiers, event data is null."); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); return; } @@ -265,11 +270,12 @@ void handleUpdateIdentities(@NonNull final Event event) { LOG_SOURCE, "Failed to update identifiers as no identifiers were found in the event data." ); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); return; } state.updateCustomerIdentifiers(map); - shareIdentityXDMSharedState(event); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); } /** @@ -278,10 +284,14 @@ void handleUpdateIdentities(@NonNull final Event event) { * @param event the edge remove identity request {@link Event} */ void handleRemoveIdentity(@NonNull final Event event) { + // Add pending shared state to avoid race condition between updating and reading identity map + final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); + final Map eventData = event.getEventData(); if (eventData == null) { Log.trace(LOG_TAG, LOG_SOURCE, "Cannot remove identifiers, event data is null."); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); return; } @@ -293,11 +303,12 @@ void handleRemoveIdentity(@NonNull final Event event) { LOG_SOURCE, "Failed to remove identifiers as no identifiers were found in the event data." ); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); return; } state.removeCustomerIdentifiers(map); - shareIdentityXDMSharedState(event); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); } /** @@ -325,8 +336,10 @@ private void handleGetIdentifiersRequest(@NonNull final Event event) { * @param event the identity request reset {@link Event} */ void handleRequestReset(@NonNull final Event event) { + // Add pending shared state to avoid race condition between updating and reading identity map + final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); state.resetIdentifiers(); - shareIdentityXDMSharedState(event); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); // dispatch reset complete event final Event responseEvent = new Event.Builder( diff --git a/code/edgeidentity/src/test/java/com/adobe/marketing/mobile/edge/identity/IdentityExtensionTests.java b/code/edgeidentity/src/test/java/com/adobe/marketing/mobile/edge/identity/IdentityExtensionTests.java index 9545273e..cd018e6f 100644 --- a/code/edgeidentity/src/test/java/com/adobe/marketing/mobile/edge/identity/IdentityExtensionTests.java +++ b/code/edgeidentity/src/test/java/com/adobe/marketing/mobile/edge/identity/IdentityExtensionTests.java @@ -31,6 +31,7 @@ import com.adobe.marketing.mobile.EventType; import com.adobe.marketing.mobile.ExtensionApi; import com.adobe.marketing.mobile.SharedStateResolution; +import com.adobe.marketing.mobile.SharedStateResolver; import com.adobe.marketing.mobile.SharedStateResult; import com.adobe.marketing.mobile.SharedStateStatus; import java.util.Collections; @@ -51,6 +52,9 @@ public class IdentityExtensionTests { @Mock ExtensionApi mockExtensionApi; + @Mock + SharedStateResolver mockSharedStateResolver; + @Mock IdentityState mockIdentityState; @@ -660,6 +664,7 @@ public void test_handleUpdateIdentities_whenValidData_updatesCustomerIdentifiers }) .when(mockIdentityState) .updateCustomerIdentifiers(any()); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); extension = new IdentityExtension(mockExtensionApi, mockIdentityState); @@ -682,7 +687,10 @@ public void test_handleUpdateIdentities_whenValidData_updatesCustomerIdentifiers verify(mockIdentityState).updateCustomerIdentifiers(identityMapCaptor.capture()); assertEquals(identityXDM, identityMapCaptor.getValue().asXDMMap()); - verify(mockExtensionApi).createXDMSharedState(properties.toXDMData(false), updateIdentityEvent); + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(updateIdentityEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); + verify(mockExtensionApi, never()).dispatch(any()); } @@ -691,6 +699,7 @@ public void test_handleUpdateIdentities_nullEventData_returns() { // setup final IdentityProperties properties = new IdentityProperties(); when(mockIdentityState.getIdentityProperties()).thenReturn(properties); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); extension = new IdentityExtension(mockExtensionApi, mockIdentityState); @@ -705,10 +714,12 @@ public void test_handleUpdateIdentities_nullEventData_returns() { // verify that identifiers are not updated verify(mockIdentityState, never()).updateCustomerIdentifiers(any()); - // verify that no shared state is created - verify(mockExtensionApi, never()).createXDMSharedState(any(), any()); // verify that no event is dispatched verify(mockExtensionApi, never()).dispatch(any()); + + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(updateIdentityEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } @Test @@ -716,6 +727,7 @@ public void test_handleUpdateIdentities_EmptyEventData_returns() { // setup final IdentityProperties properties = new IdentityProperties(); when(mockIdentityState.getIdentityProperties()).thenReturn(properties); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); extension = new IdentityExtension(mockExtensionApi, mockIdentityState); // test @@ -730,10 +742,12 @@ public void test_handleUpdateIdentities_EmptyEventData_returns() { // verify that identifiers are not updated verify(mockIdentityState, never()).updateCustomerIdentifiers(any()); - // verify that no shared state is created - verify(mockExtensionApi, never()).createXDMSharedState(any(), any()); // verify that no event is dispatched verify(mockExtensionApi, never()).dispatch(any()); + + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(updateIdentityEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } // ======================================================================================== @@ -761,6 +775,8 @@ public Object answer(InvocationOnMock invocation) throws Throwable { .when(mockIdentityState) .removeCustomerIdentifiers(any()); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); + extension = new IdentityExtension(mockExtensionApi, mockIdentityState); // test @@ -776,11 +792,9 @@ public Object answer(InvocationOnMock invocation) throws Throwable { removedIdentityMapCaptor.getValue().toString() ); - // verify shared state - final Map expectedState = properties.toXDMData(false); - final ArgumentCaptor> stateCaptor = ArgumentCaptor.forClass(Map.class); - verify(mockExtensionApi).createXDMSharedState(stateCaptor.capture(), eq(removeIdentityEvent)); - assertEquals(expectedState, stateCaptor.getValue()); + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(removeIdentityEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } @Test @@ -792,6 +806,7 @@ public void test_handleRemoveIdentity_whenNullData_returns() { ); final IdentityProperties properties = new IdentityProperties(identityXDM); when(mockIdentityState.getIdentityProperties()).thenReturn(properties); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); extension = new IdentityExtension(mockExtensionApi, mockIdentityState); // test @@ -801,13 +816,17 @@ public void test_handleRemoveIdentity_whenNullData_returns() { // verify identifiers not removed verify(mockIdentityState, never()).removeCustomerIdentifiers(any()); - // verify shared state is never created - verify(mockExtensionApi, never()).createXDMSharedState(any(), any()); + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(removeIdentityEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } @Test public void test_handleRemoveIdentity_eventWithEmptyData_returns() { // setup + final IdentityProperties properties = new IdentityProperties(); + when(mockIdentityState.getIdentityProperties()).thenReturn(properties); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); extension = new IdentityExtension(mockExtensionApi, mockIdentityState); // test @@ -817,8 +836,9 @@ public void test_handleRemoveIdentity_eventWithEmptyData_returns() { // verify identifiers not removed verify(mockIdentityState, never()).removeCustomerIdentifiers(any()); - // verify shared state is never created - verify(mockExtensionApi, never()).createXDMSharedState(any(), any()); + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(notARemoveIdentityEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } // ======================================================================================== @@ -883,6 +903,7 @@ public void test_handleRequestContent_EventIsNotAdIdEvent() { public void test_handleRequestReset() { final IdentityProperties properties = new IdentityProperties(); when(mockIdentityState.getIdentityProperties()).thenReturn(properties); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); extension = new IdentityExtension(mockExtensionApi, mockIdentityState); @@ -892,6 +913,9 @@ public void test_handleRequestReset() { extension.handleRequestReset(resetEvent); verify(mockIdentityState).resetIdentifiers(); - verify(mockExtensionApi).createXDMSharedState(properties.toXDMData(false), resetEvent); // will fail because of new ecid + + // verify pending state is created and resolved + verify(mockExtensionApi).createPendingXDMSharedState(eq(resetEvent)); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } }