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

[WEB-863] Guard Segment Events Behind AppTrackingTransparency #1799

Merged
merged 11 commits into from Mar 14, 2023

Conversation

scottkicks
Copy link
Contributor

@scottkicks scottkicks commented Mar 6, 2023

πŸ“² What

We should only send segment analytic events if the member has given consent via the AppTrackingTransparency dialog

πŸ€” Why

If the member doesn't want KS to track their data, then we shouldn't.

πŸ›  How

  • Using AppTrackingTransparency library, we can access the current authorization status and use that to gate our KSRAnalytics.track() method.
  • This should also be behind the Consent Dialog feature flag

βœ… Acceptance criteria

  • If a member's consent permission status has been denied, or has yet to be determined, then no Segment calls should be made
  • If permission has been authorized (granted) Segment calls should continue being triggered as usual

@scottkicks scottkicks added this to the release-5.6.2 milestone Mar 6, 2023
@scottkicks scottkicks self-assigned this Mar 6, 2023
@scottkicks scottkicks force-pushed the guard-segment-behind-consent branch 2 times, most recently from 6b2e410 to fc53456 Compare March 6, 2023 21:07
@codecov
Copy link

codecov bot commented Mar 6, 2023

Codecov Report

Merging #1799 (beadd31) into main (2fb519b) will increase coverage by 0.00%.
The diff coverage is 89.28%.

@@           Coverage Diff            @@
##             main    #1799    +/-   ##
========================================
  Coverage   88.92%   88.93%            
========================================
  Files         887      888     +1     
  Lines       80981    81086   +105     
  Branches    21258    21279    +21     
========================================
+ Hits        72015    72114    +99     
- Misses       8204     8207     +3     
- Partials      762      765     +3     
Impacted Files Coverage Ξ”
...ary/ViewModels/PledgePaymentMethodsViewModel.swift 93.01% <0.00%> (-0.24%) ⬇️
Library/Tracking/AppTrackingTransparency.swift 5.00% <33.33%> (+5.00%) ⬆️
Library/ViewModels/ProjectPageViewModel.swift 93.55% <50.00%> (+0.01%) ⬆️
...ibrary/ViewModels/RewardsCollectionViewModel.swift 91.73% <50.00%> (+0.03%) ⬆️
Library/ViewModels/ThanksViewModel.swift 91.57% <50.00%> (+0.04%) ⬆️
...rary/TestHelpers/MockAppTrackingTransparency.swift 60.00% <60.00%> (ΓΈ)
Library/Tracking/KSRAnalyticsTests.swift 99.43% <96.34%> (-0.17%) ⬇️
Library/AppEnvironment.swift 96.18% <100.00%> (+0.03%) ⬆️
Library/Environment.swift 94.44% <100.00%> (+0.15%) ⬆️
Library/TestHelpers/TestCase.swift 90.14% <100.00%> (+0.58%) ⬆️
... and 3 more

πŸ“£ We’re building smart automated test selection to slash your CI/CD build times. Learn more

@scottkicks
Copy link
Contributor Author

based off of the Add CAPI events PR.
I'll merge this one in once that one is done.

@scottkicks scottkicks force-pushed the guard-segment-behind-consent branch from fc53456 to f1a39da Compare March 7, 2023 18:30
Base automatically changed from web-970-part-2 to main March 8, 2023 17:52
@scottkicks scottkicks force-pushed the guard-segment-behind-consent branch from 827ce82 to f9bfdea Compare March 8, 2023 18:21
@scottkicks scottkicks marked this pull request as ready for review March 8, 2023 20:12
@scottkicks scottkicks requested a review from msadoon March 8, 2023 20:12
Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Almost there, a few things here.

  1. Should we send the IDFA with every call now that's required for every call?
  2. Save the advertisingIdentifier to AppEnvironment so we can use AppEnvironment, instead of recreating the struct everywhere (defeats the purpose of adding it to AppEnvironment)

Library/AppEnvironment.swift Show resolved Hide resolved
@@ -0,0 +1,14 @@
import Library

struct MockAppTrackingTransparencyService: AppTrackingTransparencyType {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, good work adding this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just check spelling here and can we put this in the TestHelpers folder.

Comment on lines 1242 to 1246
if featureConsentManagementDialogEnabled() {
guard AppEnvironment.current.appTrackingTransparency.authorizationStatus() == .authorized
else { return }
}

Copy link
Contributor

Choose a reason for hiding this comment

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

can be cleaner with

guard featureConsentManagementDialogEnabled(), AppEnvironment.current.appTrackingTransparency.authorizationStatus() == .authorized else { return }

Copy link
Contributor

Choose a reason for hiding this comment

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

So given we're requiring IDFA here, would KS want to get IDFA on every call? If that's the case then we can add it to userProperties which is sent up on every call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you think this as simple as adding
props["advertising_identifer"] = AppEnvironment.current.appTrackingTransparency.advertisingIdentifier
to the userProperties method in KSRAnalytics? Or would it require updating the user model with a new property and potentially add too much to this current PR?

@@ -398,7 +398,7 @@ public final class PledgePaymentMethodsViewModel: PledgePaymentMethodsViewModelT
let (project, _) = projectSignal

guard featureFacebookConversionsAPIEnabled(), project.sendMetaCapiEvents == true,
let externalId = AppTrackingTransparency.advertisingIdentifier() else { return }
let externalId = AppTrackingTransparency().advertisingIdentifier() else { return }
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we have it in the app environment, we can use AppEnvironment.current.appTrackingTransparency instead of creating a new instance of the struct. The only place we'd need to create that struct is after user is prompted and the dialog returns their answer.

@@ -348,7 +348,7 @@ public final class ProjectPageViewModel: ProjectPageViewModelType, ProjectPageVi
let (project, _) = projectAndRefTag

guard featureFacebookConversionsAPIEnabled(), project.sendMetaCapiEvents,
let externalId = AppTrackingTransparency.advertisingIdentifier() else { return }
let externalId = AppTrackingTransparency().advertisingIdentifier() else { return }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

@@ -236,7 +236,7 @@ public final class RewardsCollectionViewModel: RewardsCollectionViewModelType,
let (project, _) = projectAndRefTag

guard featureFacebookConversionsAPIEnabled(), project.sendMetaCapiEvents,
let externalId = AppTrackingTransparency.advertisingIdentifier() else { return }
let externalId = AppTrackingTransparency().advertisingIdentifier() else { return }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

@@ -218,7 +218,7 @@ public final class ThanksViewModel: ThanksViewModelType, ThanksViewModelInputs,
}

guard featureFacebookConversionsAPIEnabled(), project.sendMetaCapiEvents,
let externalId = AppTrackingTransparency.advertisingIdentifier() else { return }
let externalId = AppTrackingTransparency().advertisingIdentifier() else { return }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

Library/Tracking/KSRAnalyticsTests.swift Show resolved Hide resolved
@scottkicks scottkicks force-pushed the guard-segment-behind-consent branch from a96c6bf to 14c600a Compare March 9, 2023 17:09
@scottkicks scottkicks requested a review from msadoon March 9, 2023 17:16
@kickstarter kickstarter deleted a comment from nativeksr Mar 9, 2023
Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Ok all good, great work!
One minor thing - if you could quickly add appTrackingTransparency to replaceEnvironment static func on AppEnvironment that would make it consistent there as well.

@msadoon
Copy link
Contributor

msadoon commented Mar 9, 2023

Oh yea and get tests to pass ;)

@scottkicks scottkicks force-pushed the guard-segment-behind-consent branch from 06dfa83 to 033936d Compare March 9, 2023 20:45
Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Hey just asking for a few changes on the base TestCase

@@ -33,6 +33,9 @@ internal class TestCase: XCTestCase {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "GMT")!

self.optimizelyClient = MockOptimizelyClient()
|> \.features .~ [OptimizelyFeature.consentManagementDialogEnabled.rawValue: true]

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you remove this from the base TestCase and put it back into the override setUp for the individual test class? I was thinking about this method.

Copy link
Contributor

Choose a reason for hiding this comment

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

Create a class level variable MockOptimizelyClient in the override class setup func in PledgeViewModelTests for example, and then update it to false for whatever flags you need to be setup, then update the individual cases that need to be true as you've already done.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right. I was trying to avoid that because there are a few test files and it was easier to add it here. And then remove it once the feature is fully released. Removing it from one place would also be easier. What do you think about that?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea that's one way to do it, I just get a bit worried because we might forgot to remove it later. We've left flags in the app for >6 months after release, so that will effect all our testcases until we remove the flag and after that amount of time we might forgot our intent for this change in the base case. Just muddies up the codebase a bit more than necessary.

In general try to scope the change to just the bare minimum if possible. Plus I think it's just as easy to remove the mockOptimizelyClient in the PledgePageViewModelTests as opposed to TestCase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@msadoon Its a good point. We are making an effort to audit and clean up our flags as we think about moving to firebase as a manager though. There are a lot of test files (24) that have multiple tests (4-7) that are failing which feels like a lot of code changes for this.

I can make a ticket to remove as well so I don't forget to remove from TestCase. It also feels like there are so many places to make the update your suggesting that it would be just as easy to forget to remove this from some of them

Copy link
Contributor

@msadoon msadoon Mar 13, 2023

Choose a reason for hiding this comment

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

If it's not too much work, let's use the class level variables. I now we're trying to figure out Optimizely replacements and then yes we'll go ahead and rip out Optimizely everywhere for the replacement but that work isn't quite finalized yet.

I think the original creators intent of TestCase is not to add customizations of AppEnvironment.
We'll see that withEnvironment( in XCTestCase+AppEnvironment is intended to be customized for every test case.
Likely when we add the replacement for Optimizely, we'll follow this same approach and add it to the withEnvironment( function and customize it in every test case that needs it.

It shouldn't be too much boilerplate right? Because we're updating the class level mockOptimizelyClient not in every test. The only tests that need to change are the the ones that require a true flag. I understand its probably four different files, but it still feels like the tradeoff is worth it and sticks to existing conventions. Also when we have this new replacement for Optimizely, then those files are easy to change with the new dependency because we just go through AppEnvironment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, looking into this now, it's fine, just do it the way you have in TestCase, I'll approve this. The class level update doesn't really seem to work either.

Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Good to go!

@scottkicks scottkicks merged commit 3e59797 into main Mar 14, 2023
@scottkicks scottkicks deleted the guard-segment-behind-consent branch March 14, 2023 03:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants