Skip to content
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

[NT-727] Send checkout_id with checkout properties for tracking #1053

Merged
merged 23 commits into from
Feb 4, 2020

Conversation

Scollaco
Copy link
Contributor

@Scollaco Scollaco commented Jan 28, 2020

📲 What

  • Adds checkout_id property to the checkout properties group.

🤔 Why

  • Because we want to track the checkout id in the DataLake.
    For details: JIRA ticket

Note: Even though the ticket determines that all tracking events that require the checkout properties should contain this property, this is not applicable, since some of them will fire before the backing is created.

🛠 How

  • Changed the createBacking mutation to return checkout_id
  • Added checkoutId property on Checkout model
  • The checkoutPropertiesData function now receives an optional checkoutId as argument
  • Updated tests

✅ Acceptance criteria

  • After backing a project, the Thanks Page Viewed event should contain the checkout_id property.

@ifbarrera ifbarrera changed the title [NT-727] Create backing mutation checkout [NT-727] Send checkout_id with checkout properties for tracking Jan 28, 2020
Copy link
Contributor

@ifbarrera ifbarrera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add the id field to the UpdateBackingMutation response query - otherwise this change will break all update backing requests because we won't be able to deserialize the response.

@@ -2,11 +2,16 @@ import Foundation
import KsApi

public protocol StripeSCARequiring {
var checkoutId: String? { get }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the motivation for adding checkoutId in the StripeSCARequiring protocol? From my understanding, checkoutId is not actually required to determine SCA eligibility, so it's a bit misleading to add it in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd added there to reuse the existing login on the viewModel, but I guess you're right. I will change that.


return (data.project, data.reward, checkoutPropsData)
}

self.goToThanks = thanksPageData
.takeWhen(createBackingCompletionEvents)
self.goToThanks = createBackingCompletionEvents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this change needed? From what I can see the way it was before should still work, and is one line of code shorter 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to mention here... using takeWhen broke some other test and this solution was the one that made all of them pass 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might want to be pretty specific that this should only fire when the createBackingCompletionEvents emit 🤔 Do you know what broke/why?

Copy link
Contributor Author

@Scollaco Scollaco Jan 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that in some tests, thanksPageData emits before createBackingCompletionEvents and in others, like the one I've mentioned, the order changes and the test fails.
Using combineLatest we don't depend on the order that the signals emit and it avoids a race condition

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's just that combineLatest will also emit regardless of the order, in this case we just want to be sure that we only goToThanks when createBackingCompletionEvents emits. Given that this is tricky to do regression testing on I would try to avoid this change. Let's rather dig into why the tests break with your change? I can take a look with you too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see what's happening - previously the thanksPageData had all of the necessary data before the completion events emit. This changes that because now we want the checkoutId in the ThanksPageData which causes a bit of a race condition. I would move the creation of the thanksPageData to be a a signal that is takePairWhen createBackingCompletionEvents and retrieve the checkoutId right at the end.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ +1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option would be using zip instead of combineLatest. This guarantees that goToThanks only emits after both signals emit. But just for curiosity, what are the disadvantages of using .combineLatest in this case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the concern is that goToThanks could fire before createBackingCompletionEvents during a retry flow, causing us to send the user to thanks page before we've confirmed that we're clear to do so, particularly during the SCA flow. We might be prematurely sending users to the thanks page before they've completed the SCA auth flow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the disadvantages of using .combineLatest in this case?

I think the main concern is just that it doesn't accurately represent what we want to happen. Previously this was a takeWhen because that accurately describes the event, "take this data when this other thing happens". This has changed because we now require some data from the thing that has happened, but takePairWhen accurately describes that - "take some data from this other event, combine it with what we have, but only when that other thing happens". combineLatest on the other hand blurs this by saying "combine all this data when either of these things happen".

@@ -7,6 +7,7 @@ final class UpdateBackingEnvelopeTests: XCTestCase {
{
"updateBacking": {
"checkout": {
"id": "2020",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the UpdateBackingEnvelope now requires a checkoutId to be present for deserialization, you'll need to also add it to the UpdateBackingMutation as well so that deserialization doesn't break for update backing.

@@ -352,7 +352,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
.switchMap { input in
AppEnvironment.current.apiService.updateBacking(input: input)
.ksr_delay(AppEnvironment.current.apiDelayInterval, on: AppEnvironment.current.scheduler)
.map { $0 as StripeSCARequiring }
.map { ($0.updateBacking.checkout.id, $0 as StripeSCARequiring) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to map out the checkout id in the updateBacking case? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because createOrUpdateEvent requires that createBackingEvents and updateBackingEvents have the same type (<(String, StripeSCARequiring), GraphError>.Event>) . So I saw some options here:
1- map out checkout id even if we won't use it
2- make the string of the tuple optional in both createBackingEvents and updateBackingEvents signals

Tried other solutions but wasn't able to make the compiler happy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I think just mapping the value in createOrUpdateEvent seems to work and doesn't require mapping out the checkoutId in the update events:

    let createOrUpdateEvent = Signal.merge(
      createBackingEvents.map { $0.map(second) },
      updateBackingEvents
    )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why I didn't think of that 🤦🏻‍♂️ Thanks, @ifbarrera !


return (data.project, data.reward, checkoutPropsData)
}

self.goToThanks = thanksPageData
.takeWhen(createBackingCompletionEvents)
self.goToThanks = createBackingCompletionEvents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ +1

@@ -352,7 +352,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
.switchMap { input in
AppEnvironment.current.apiService.updateBacking(input: input)
.ksr_delay(AppEnvironment.current.apiDelayInterval, on: AppEnvironment.current.scheduler)
.map { $0 as StripeSCARequiring }
.map { ($0.updateBacking.checkout.id, $0 as StripeSCARequiring) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I think just mapping the value in createOrUpdateEvent seems to work and doesn't require mapping out the checkoutId in the update events:

    let createOrUpdateEvent = Signal.merge(
      createBackingEvents.map { $0.map(second) },
      updateBackingEvents
    )

@@ -867,6 +882,7 @@ private func checkoutPropertiesData(from createBackingData: CreateBackingData, i

return Koala.CheckoutPropertiesData(
amount: amount,
checkoutId: checkoutId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While testing this I noticed that the checkoutId that's returned from Graph is the relayId. I think for tracking we actually want to send the Int value of the checkoutId 🤔

.map { data, isApplePay -> ThanksPageData in
let checkoutPropsData = checkoutPropertiesData(from: data, isApplePay: isApplePay)
.takeWhen(createBackingCompletionEvents)
.combineLatest(with: checkoutId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this change is actually the same thing as before, although we've moved things around this still combines the latest emissions from the two signals. Basically it reads like this "emit createBackingDataAndIsApplePay when createBackingCompletionEvents emits and combine its emission with checkoutId" which means that whenever checkoutId or createBackingCompletionEvents emit then this will emit. We just want to be explicit that this should only emit at all when createBackingCompletionEvents emits.

This is actually achievable just by moving things around slightly and carrying the checkoutId all the way through to createBackingCompletionEvents which will require a few changes higher up in the chain:

let thanksPageData = createBackingDataAndIsApplePay
  .takePairWhen(createBackingCompletionEvents)
  .map(unpack)
  .map { data, isApplePay, checkoutId -> ThanksPageData in
    let checkoutPropsData = checkoutPropertiesData(
      from: data,
      checkoutId: checkoutId,
      isApplePay: isApplePay
    )

    return (data.project, data.reward, checkoutPropsData)
  }

@Scollaco Scollaco merged commit 09457af into master Feb 4, 2020
@Scollaco Scollaco deleted the NT-727_createBacking_mutation_checkout_id branch February 4, 2020 21:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants