-
Notifications
You must be signed in to change notification settings - Fork 663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Link holdback] Add ahead lookup call for exposure logging. #10565
base: master
Are you sure you want to change the base?
[Link holdback] Add ahead lookup call for exposure logging. #10565
Conversation
…osures-on-mpe' into carlosmuvi/add-v2-reporting-to-mpe
Diffuse output:
APK
DEX
|
val experimentsData = requireNotNull( | ||
elementsSession.experimentsData | ||
) { "Experiments data required to log exposures" } | ||
val holdbackOn = elementsSession.linkSettings?.linkGlobalHoldbackOn == true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK, we should instead look at elementsSession.experimentsData.experimentAssignments["link_global_holdback"]
to get the group
. In testmode, this key will not be present, so we treat that as though it were the control group. Confirming here though: https://stripe.slack.com/archives/C05RD8RBF5L/p1743774491875609?thread_ts=1743189375.360319&cid=C05RD8RBF5L
With this, I don't think we need the linkGlobalHoldbackOn
field on link_settings
for anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated it here - 8a30e76 (including removing linkGlobalHoldbackOn
)
group = group, | ||
name = "link_global_holdback", | ||
dimensions = mapOf( | ||
"integration_type" to "mpe", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we doing ios / android integration types? Or grouping all of MPE together?
"integration_type" to "mpe", | |
"integration_type" to "mpe_android", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…onsumer-for-exposure # Conflicts: # paymentsheet/src/main/java/com/stripe/android/common/analytics/experiment/LogLinkGlobalHoldbackExposure.kt # paymentsheet/src/main/java/com/stripe/android/common/analytics/experiment/LoggableExperiment.kt # paymentsheet/src/test/java/com/stripe/android/common/analytics/experiment/LogLinkGlobalHoldbackExposureTest.kt # paymentsheet/src/test/java/com/stripe/android/paymentsheet/analytics/DefaultEventReporterTest.kt
val avoidConsumerLoggingParams: Map<String, Boolean> = if (doNotLogConsumerFunnelEvent) { | ||
mapOf("TEMPORARY_AND_DANGEROUS__do_not_log_consumer_funnel_event" to true) | ||
} else { | ||
emptyMap() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is unfortunate, but should go away after the holdback.
|
||
internal class LogLinkGlobalHoldbackExposure @Inject constructor( | ||
internal interface LogLinkGlobalHoldbackExposure { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interface to facilitate adding a Fake.
@@ -45,6 +45,7 @@ interface ConsumersApiService { | |||
suspend fun lookupConsumerSession( | |||
email: String, | |||
requestSurface: String, | |||
doNotLogConsumerFunnelEvent: Boolean = false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Should we remove the default param here, so that callers are forced to think about which values to provide?
.map { it.exists } | ||
.fold( | ||
onSuccess = { it }, | ||
onFailure = { | ||
logger.error("Failed to check if user is returning", it) | ||
false | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: No need to fold
.
.map { it.exists } | |
.fold( | |
onSuccess = { it }, | |
onFailure = { | |
logger.error("Failed to check if user is returning", it) | |
false | |
} | |
) | |
.map { it.exists } | |
.onFailure { | |
logger.error("Failed to check if user is returning", it) | |
false | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onFailure does not allow error recovery, but getOrElse
does the trick!
|
||
val customerEmail = state.getEmail() | ||
|
||
val isReturningUser: Boolean = customerEmail?.let { isReturningUser(email = it) } == true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We can use to compiler to our advantage and clean this up a bit.
val isReturningUser: Boolean = customerEmail?.let { isReturningUser(email = it) } == true | |
val isReturningUser = customerEmail != null && isReturningUser(customerEmail) |
paymentMethodMetadata.linkState?.configuration?.customerInfo?.email | ||
?: config.defaultBillingDetails?.email | ||
?: customer?.let { | ||
customerRepository.retrieveCustomer( | ||
CustomerRepository.CustomerInfo( | ||
id = it.id, | ||
ephemeralKeySecret = it.ephemeralKeySecret, | ||
customerSessionClientSecret = it.customerSessionClientSecret | ||
) | ||
) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have similar code here to determine the actual customer email.
Can we extract that into a method and then use it in both the PaymentElementLoader
and in here?
@@ -27,6 +27,15 @@ internal interface LinkRepository { | |||
email: String, | |||
): Result<ConsumerSessionLookup> | |||
|
|||
/** | |||
* Performs a lookup of a consumer session without triggering any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Performs a lookup of a consumer session without triggering any | |
* Performs a lookup of a consumer session without triggering any |
val exposureCall = eventReporter.experimentExposureCalls.awaitItem() | ||
assertTrue(exposureCall.experiment is LoggableExperiment.LinkGlobalHoldback) | ||
assertEquals(exposureCall.experiment.group, "holdback") | ||
assertTrue((exposureCall.experiment.isReturningLinkConsumer)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
((…))
Let’s use Truth’s assertThat()
instead.
val exposureCall = eventReporter.experimentExposureCalls.awaitItem() | ||
assertTrue(exposureCall.experiment is LoggableExperiment.LinkGlobalHoldback) | ||
assertEquals(exposureCall.experiment.group, "holdback") | ||
assertFalse((exposureCall.experiment.isReturningLinkConsumer)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
((…))
Let’s use Truth’s assertThat()
instead.
…onsumer-for-exposure
@tillh-stripe addressed all feedback here! |
Summary
no-cache
,no-backend-logging
lookup if we can retrieve an email when retrieving the session. On a follow-up PR, this will be put behind a killswitch so we can turn it off after the experiment is done.Motivation
Testing
Screenshots
Changelog