From 113677e148ecbb6dce8cbf0e73f82b6d09b82626 Mon Sep 17 00:00:00 2001 From: Kevin Lind Date: Wed, 11 Jan 2023 12:23:09 -0800 Subject: [PATCH 1/2] Set pending shared state before updating identity map --- .../mobile/edge/identity/IdentityExtension.java | 9 +++++++-- .../edge/identity/IdentityExtensionTests.java | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) 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..da457fb5 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; @@ -268,8 +269,10 @@ void handleUpdateIdentities(@NonNull final Event event) { return; } + // Add pending shared state to avoid race condition between updating and reading identity map + final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); state.updateCustomerIdentifiers(map); - shareIdentityXDMSharedState(event); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); } /** @@ -296,8 +299,10 @@ void handleRemoveIdentity(@NonNull final Event event) { return; } + // Add pending shared state to avoid race condition between updating and reading identity map + final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); state.removeCustomerIdentifiers(map); - shareIdentityXDMSharedState(event); + resolver.resolve(state.getIdentityProperties().toXDMData(false)); } /** 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..71a9a126 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); + final ArgumentCaptor> stateCaptor = ArgumentCaptor.forClass(Map.class); + verify(mockExtensionApi).createPendingXDMSharedState(eq(updateIdentityEvent)); + verify(mockSharedStateResolver).resolve(stateCaptor.capture()); + assertEquals(properties.toXDMData(false), stateCaptor.getValue()); verify(mockExtensionApi, never()).dispatch(any()); } @@ -761,6 +769,8 @@ public Object answer(InvocationOnMock invocation) throws Throwable { .when(mockIdentityState) .removeCustomerIdentifiers(any()); + when(mockExtensionApi.createPendingXDMSharedState(any())).thenReturn(mockSharedStateResolver); + extension = new IdentityExtension(mockExtensionApi, mockIdentityState); // test @@ -779,7 +789,8 @@ public Object answer(InvocationOnMock invocation) throws Throwable { // verify shared state final Map expectedState = properties.toXDMData(false); final ArgumentCaptor> stateCaptor = ArgumentCaptor.forClass(Map.class); - verify(mockExtensionApi).createXDMSharedState(stateCaptor.capture(), eq(removeIdentityEvent)); + verify(mockExtensionApi).createPendingXDMSharedState(eq(removeIdentityEvent)); + verify(mockSharedStateResolver).resolve(stateCaptor.capture()); assertEquals(expectedState, stateCaptor.getValue()); } From 6952e75255e35a1d60349fd7c0663abf664eb8bf Mon Sep 17 00:00:00 2001 From: Kevin Lind Date: Wed, 11 Jan 2023 20:44:09 -0800 Subject: [PATCH 2/2] Create pending state when resetIdentities is called. --- .../edge/identity/IdentityExtension.java | 18 +++++-- .../edge/identity/IdentityExtensionTests.java | 47 ++++++++++++------- 2 files changed, 43 insertions(+), 22 deletions(-) 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 da457fb5..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 @@ -251,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; } @@ -266,11 +270,10 @@ 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; } - // Add pending shared state to avoid race condition between updating and reading identity map - final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); state.updateCustomerIdentifiers(map); resolver.resolve(state.getIdentityProperties().toXDMData(false)); } @@ -281,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; } @@ -296,11 +303,10 @@ 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; } - // Add pending shared state to avoid race condition between updating and reading identity map - final SharedStateResolver resolver = getApi().createPendingXDMSharedState(event); state.removeCustomerIdentifiers(map); resolver.resolve(state.getIdentityProperties().toXDMData(false)); } @@ -330,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 71a9a126..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 @@ -687,10 +687,10 @@ public void test_handleUpdateIdentities_whenValidData_updatesCustomerIdentifiers verify(mockIdentityState).updateCustomerIdentifiers(identityMapCaptor.capture()); assertEquals(identityXDM, identityMapCaptor.getValue().asXDMMap()); - final ArgumentCaptor> stateCaptor = ArgumentCaptor.forClass(Map.class); + // verify pending state is created and resolved verify(mockExtensionApi).createPendingXDMSharedState(eq(updateIdentityEvent)); - verify(mockSharedStateResolver).resolve(stateCaptor.capture()); - assertEquals(properties.toXDMData(false), stateCaptor.getValue()); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); + verify(mockExtensionApi, never()).dispatch(any()); } @@ -699,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); @@ -713,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 @@ -724,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 @@ -738,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))); } // ======================================================================================== @@ -786,12 +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 pending state is created and resolved verify(mockExtensionApi).createPendingXDMSharedState(eq(removeIdentityEvent)); - verify(mockSharedStateResolver).resolve(stateCaptor.capture()); - assertEquals(expectedState, stateCaptor.getValue()); + verify(mockSharedStateResolver).resolve(eq(properties.toXDMData(false))); } @Test @@ -803,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 @@ -812,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 @@ -828,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))); } // ======================================================================================== @@ -894,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); @@ -903,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))); } }