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

Bugfix - send analytics even when BTAPIClient deallocated #1379

Merged
merged 26 commits into from
Jul 31, 2024

Conversation

scannillo
Copy link
Contributor

@scannillo scannillo commented Jul 29, 2024

See ticket DTMOBILES-882 for full details on bug & how to replicate.

Summary

  • This PR prevents our RepeatingTimer instance from getting wiped if a merchant is creating and destroying BTAPIClient instances often (ex: on button click)

Changes

  • Move BTAnalyticsService from "faux" singleton setup in BTAPIClient, to actual singleton
    • Use protocol abstraction to be able to mock BTAnalyticsService in our tests
    • Make RepeatingTimer and AnalyticsEventStorage properties non-static
  • Refactor BTAnalyticsService.sendAnalyticsEvent() method to require single FPTIBatchData.Event property vs 12 individual properties

Checklist

  • Added a changelog entry

Authors

@scannillo @richherrera @jaxdesmarais

@scannillo scannillo force-pushed the replicate-main-analytics-issue branch from 3493592 to 1b47adc Compare July 29, 2024 19:55
@scannillo scannillo force-pushed the replicate-main-analytics-issue branch from 1b47adc to 0515294 Compare July 29, 2024 19:58
@scannillo scannillo changed the title [DO NOT REVIEW] Bugfix - send analytics even when BTAPIClient deallocated Jul 29, 2024
@@ -0,0 +1,7 @@
/// Describes a class that batches and sends analytics events.
/// - Note: Specifically created to mock the `BTAnalyticsService` singleton.
protocol AnalyticsSendable: AnyObject {
Copy link
Contributor

Choose a reason for hiding this comment

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

🫶🏻

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you have a better name suggestion for this, please let me know

CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Contributor

@jaxdesmarais jaxdesmarais left a comment

Choose a reason for hiding this comment

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

🔥 will run some tests in the morning to verify but this looks great!

@@ -1,8 +1,10 @@
import Foundation

class BTAnalyticsService: Equatable {
class BTAnalyticsService: AnalyticsSendable {
Copy link
Contributor

Choose a reason for hiding this comment

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

Take it or leave it, but I know for other classes we have had the protocol conformance in an extension so we can add a MARK: to make it clear where the protocol starts/ends.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could! Since the protocol is just for testing purposes, I thought that would make the functionality of BTAnalyticsService harder to read - but open to whatever folks prefer!

Sources/BraintreeCore/Analytics/BTAnalyticsService.swift Outdated Show resolved Hide resolved
Sources/BraintreeCore/Analytics/BTAnalyticsService.swift Outdated Show resolved Hide resolved
Sources/BraintreeCore/Analytics/BTAnalyticsService.swift Outdated Show resolved Hide resolved
Sources/BraintreeCore/Analytics/BTAnalyticsService.swift Outdated Show resolved Hide resolved
Comment on lines -30 to -33
weak var analyticsService: BTAnalyticsService? {
get { BTAPIClient._analyticsService }
set { BTAPIClient._analyticsService = newValue }
}
Copy link
Contributor

Choose a reason for hiding this comment

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

:tears-of-joy:

Sources/BraintreeCore/BTAPIClient.swift Outdated Show resolved Hide resolved
@scannillo scannillo marked this pull request as ready for review July 30, 2024 13:18
@scannillo scannillo requested a review from a team as a code owner July 30, 2024 13:18
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Contributor

@jaxdesmarais jaxdesmarais left a comment

Choose a reason for hiding this comment

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

Ran some tests on device and was unable to replicate the bug! 🚀

Copy link
Contributor

@richherrera richherrera left a comment

Choose a reason for hiding this comment

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

🥇

Sources/BraintreeCore/BTAPIClient.swift Outdated Show resolved Hide resolved
startTime: Int? = nil
) {
/// - Parameter event: A single `FPTIBatchData.Event`
func sendAnalyticsEvent(_ event: FPTIBatchData.Event) {
Copy link
Contributor

Choose a reason for hiding this comment

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

sendAnalyticsEvent and performEventRequest could be merged, or if we decide to keep both, performEventRequest could be made private, thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have those as 2 separate functions for the purpose of unit testing right now. Since sendAnalyticsEvent launches an async background task, there is no way for our tests to know when it completes to run it's assertions. performEventRequest is acting as an "internal" version that delivers the async result, which we're calling in our unit tests.

We used this pattern in PPCP too to unit test background-type tasks. Of course open to future suggestions here for improving our tests!

let batchMetadata = FPTIBatchData.Metadata(
authorizationFingerprint: apiClient.authorization.type == .clientToken ? apiClient.authorization.bearer : nil,
authorizationFingerprint: apiClient?.authorization.type == .clientToken ? apiClient?.authorization.bearer : nil,
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 metadata and authorization are constants in BTAPIClient (they are var for testing purposes). In the future, we might consider removing the BTAPIClient dependency from here altogether and instead inject metadata, authorization, and Configuration

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeh this is a good callout. Though it did cross my mind going back to this PR (#1337) that we don't need a config to be present for analytics at the time of BTAPIClient.sendAnalytics() but rather right before we flush/batch upload the analytics after the 15 seconds timer.

If we inject the Config from the BTAPIClient, we might get back to calling the fetchOrReturnRemoteConfig more times than necessary

@scannillo scannillo force-pushed the replicate-main-analytics-issue branch from ed480e3 to 93c68e8 Compare July 30, 2024 18:43
@scannillo scannillo merged commit abdd1e9 into main Jul 31, 2024
7 of 8 checks passed
@scannillo scannillo deleted the replicate-main-analytics-issue branch July 31, 2024 12:58
scannillo added a commit that referenced this pull request Jul 31, 2024
- Move `BTAnalyticsService` from "faux" singleton setup in `BTAPIClient`, to actual singleton
    - Use protocol abstraction to be able to mock BTAnalyticsService in our tests
    - Make `RepeatingTimer` and `AnalyticsEventStorage `properties non-static
- Refactor `BTAnalyticsService.sendAnalyticsEvent()` method to require single `FPTIBatchData.Event` property vs 12 individual properties

---------

Signed-off-by: Jax DesMarais-Leder <[email protected]>
Signed-off-by: Rich Herrera <[email protected]>
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.

3 participants