Skip to content

Commit

Permalink
Merge branch 'main' into paypal-app-switch-feature
Browse files Browse the repository at this point in the history
  • Loading branch information
richherrera committed Sep 19, 2024
2 parents 19ea4a3 + 4f70845 commit 2b7ff74
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.ListenableWorker
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.braintreepayments.api.sharedutils.Time
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
Expand All @@ -20,7 +21,8 @@ internal class AnalyticsClient(
private val analyticsDatabase: AnalyticsDatabase = AnalyticsDatabase.getInstance(context.applicationContext),
private val workManager: WorkManager = WorkManager.getInstance(context.applicationContext),
private val deviceInspector: DeviceInspector = DeviceInspector(),
private val analyticsParamRepository: AnalyticsParamRepository = AnalyticsParamRepository.instance
private val analyticsParamRepository: AnalyticsParamRepository = AnalyticsParamRepository.instance,
private val time: Time = Time()
) {
private val applicationContext = context.applicationContext

Expand Down Expand Up @@ -121,6 +123,7 @@ internal class AnalyticsClient(
)
val analyticsRequest =
createFPTIPayload(authorization, eventBlobs, metadata)

httpClient.post(
FPTI_ANALYTICS_URL,
analyticsRequest.toString(),
Expand All @@ -137,27 +140,11 @@ internal class AnalyticsClient(
}
}

fun reportCrash(
context: Context?,
configuration: Configuration?,
integration: IntegrationType?,
authorization: Authorization?
) {
reportCrash(
context,
configuration,
integration,
System.currentTimeMillis(),
authorization
)
}

@VisibleForTesting
fun reportCrash(
context: Context?,
configuration: Configuration?,
integration: IntegrationType?,
timestamp: Long,
authorization: Authorization?
) {
if (authorization == null) {
Expand All @@ -169,7 +156,10 @@ internal class AnalyticsClient(
sessionId = analyticsParamRepository.sessionId,
integration = integration
)
val event = AnalyticsEvent(name = "crash", timestamp = timestamp)
val event = AnalyticsEvent(
name = "crash",
timestamp = time.currentTime
)
val eventJSON = mapAnalyticsEventToFPTIEventJSON(event)
val eventBlobs = listOf(
AnalyticsEventBlob(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package com.braintreepayments.api.core

internal data class AnalyticsEvent(
val name: String,
val timestamp: Long,
val payPalContextId: String? = null,
val linkType: String? = null,
val isVaultRequest: Boolean = false,
val startTime: Long? = null,
val endTime: Long? = null,
val endpoint: String? = null,
val timestamp: Long = System.currentTimeMillis(),
val endpoint: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.annotation.VisibleForTesting
import com.braintreepayments.api.sharedutils.HttpResponseCallback
import com.braintreepayments.api.sharedutils.HttpResponseTiming
import com.braintreepayments.api.sharedutils.ManifestValidator
import com.braintreepayments.api.sharedutils.Time
import org.json.JSONException
import org.json.JSONObject

Expand Down Expand Up @@ -38,6 +39,7 @@ class BraintreeClient @VisibleForTesting internal constructor(
private val graphQLClient: BraintreeGraphQLClient,
private val configurationLoader: ConfigurationLoader,
private val manifestValidator: ManifestValidator,
private val time: Time,
private val returnUrlScheme: String,
private val braintreeDeepLinkReturnUrlScheme: String,
/**
Expand All @@ -50,7 +52,10 @@ class BraintreeClient @VisibleForTesting internal constructor(
private var launchesBrowserSwitchAsNewTask: Boolean = false

// NOTE: this constructor is used to make dependency injection easy
internal constructor(params: BraintreeClientParams) : this(
internal constructor(
params: BraintreeClientParams,
time: Time = Time()
) : this(
applicationContext = params.applicationContext,
integrationType = params.integrationType,
authorization = params.authorization,
Expand All @@ -59,6 +64,7 @@ class BraintreeClient @VisibleForTesting internal constructor(
graphQLClient = params.graphQLClient,
configurationLoader = params.configurationLoader,
manifestValidator = params.manifestValidator,
time = time,
returnUrlScheme = params.returnUrlScheme,
braintreeDeepLinkReturnUrlScheme = params.braintreeReturnUrlScheme,
appLinkReturnUri = params.appLinkReturnUri
Expand Down Expand Up @@ -132,15 +138,17 @@ class BraintreeClient @VisibleForTesting internal constructor(
eventName: String,
params: AnalyticsEventParams = AnalyticsEventParams()
) {
val timestamp = time.currentTime
getConfiguration { configuration, _ ->
val event = AnalyticsEvent(
eventName,
params.payPalContextId,
params.linkType,
params.isVaultRequest,
params.startTime,
params.endTime,
params.endpoint
name = eventName,
timestamp = timestamp,
payPalContextId = params.payPalContextId,
linkType = params.linkType,
isVaultRequest = params.isVaultRequest,
startTime = params.startTime,
endTime = params.endTime,
endpoint = params.endpoint,
)
sendAnalyticsEvent(event, configuration, authorization)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.braintreepayments.api.core.AnalyticsClient.Companion.WORK_INPUT_KEY_S
import com.braintreepayments.api.core.AnalyticsClient.Companion.WORK_NAME_ANALYTICS_UPLOAD
import com.braintreepayments.api.core.Authorization.Companion.fromString
import com.braintreepayments.api.core.Configuration.Companion.fromJson
import com.braintreepayments.api.sharedutils.Time
import com.braintreepayments.api.testutils.Fixtures
import io.mockk.*
import org.json.JSONException
Expand All @@ -30,6 +31,7 @@ class AnalyticsClientUnitTest {
private lateinit var httpClient: BraintreeHttpClient
private lateinit var deviceInspector: DeviceInspector
private lateinit var analyticsParamRepository: AnalyticsParamRepository
private lateinit var time: Time
private lateinit var eventName: String
private lateinit var sessionId: String
private lateinit var payPalContextId: String
Expand All @@ -39,6 +41,8 @@ class AnalyticsClientUnitTest {
private lateinit var analyticsDatabase: AnalyticsDatabase
private lateinit var analyticsEventBlobDao: AnalyticsEventBlobDao

private lateinit var sut: AnalyticsClient

private var timestamp: Long = 0

@Before
Expand All @@ -59,9 +63,22 @@ class AnalyticsClientUnitTest {
analyticsDatabase = mockk(relaxed = true)
analyticsEventBlobDao = mockk(relaxed = true)
workManager = mockk(relaxed = true)
time = mockk(relaxed = true)

every { analyticsDatabase.analyticsEventBlobDao() } returns analyticsEventBlobDao
every { analyticsParamRepository.sessionId } returns sessionId

every { time.currentTime } returns 123

sut = AnalyticsClient(
context = context,
httpClient = httpClient,
analyticsDatabase = analyticsDatabase,
workManager = workManager,
deviceInspector = deviceInspector,
analyticsParamRepository = analyticsParamRepository,
time = time
)
}

@Test
Expand All @@ -77,13 +94,7 @@ class AnalyticsClientUnitTest {
} returns mockk()

val event = AnalyticsEvent(eventName, timestamp = 123)
val sut = AnalyticsClient(
context = context,
httpClient = httpClient,
analyticsDatabase = analyticsDatabase,
workManager = workManager,
deviceInspector = deviceInspector
)

sut.sendEvent(configuration, event, integration, authorization)

val workSpec = workRequestSlot.captured.workSpec
Expand Down Expand Up @@ -120,8 +131,7 @@ class AnalyticsClientUnitTest {
isVaultRequest = true,
timestamp = 456
)
val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)

sut.sendEvent(configuration, event, integration, authorization)

val workSpec = workRequestSlot.captured.workSpec
Expand Down Expand Up @@ -156,17 +166,13 @@ class AnalyticsClientUnitTest {
.putString(WORK_INPUT_KEY_ANALYTICS_JSON, JSONObject().toString())
.putString(WORK_INPUT_KEY_SESSION_ID, JSONObject().toString())
.build()
val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsWrite(inputData)
assertTrue(result is ListenableWorker.Result.Success)
}

@Test
fun writeAnalytics_whenAnalyticsJSONIsMissing_returnsSuccess() {
val inputData = Data.Builder().build()
val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsWrite(inputData)
assertTrue(result is ListenableWorker.Result.Failure)
}
Expand All @@ -181,8 +187,7 @@ class AnalyticsClientUnitTest {
.putString(WORK_INPUT_KEY_ANALYTICS_JSON, json)
.putString(WORK_INPUT_KEY_SESSION_ID, sessionId)
.build()
val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)

sut.performAnalyticsWrite(inputData)

val blob = analyticsEventBlobSlot.captured
Expand Down Expand Up @@ -239,8 +244,6 @@ class AnalyticsClientUnitTest {
)
}

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
sut.performAnalyticsUpload(inputData)

// language=JSON
Expand Down Expand Up @@ -287,8 +290,6 @@ class AnalyticsClientUnitTest {
.putString(AnalyticsClient.WORK_INPUT_KEY_INTEGRATION, integration.stringValue)
.build()

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsUpload(inputData)
assertTrue(result is ListenableWorker.Result.Failure)

Expand All @@ -305,8 +306,6 @@ class AnalyticsClientUnitTest {
.putString(AnalyticsClient.WORK_INPUT_KEY_INTEGRATION, integration.stringValue)
.build()

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsUpload(inputData)
assertTrue(result is ListenableWorker.Result.Failure)

Expand Down Expand Up @@ -342,8 +341,6 @@ class AnalyticsClientUnitTest {
val analyticsJSONSlot = slot<String>()
every { httpClient.post(any(), capture(analyticsJSONSlot), any(), any()) }

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
sut.performAnalyticsUpload(inputData)

val analyticsJson = JSONObject(analyticsJSONSlot.captured)
Expand All @@ -362,8 +359,6 @@ class AnalyticsClientUnitTest {
.putString(AnalyticsClient.WORK_INPUT_KEY_INTEGRATION, integration.stringValue)
.build()

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsUpload(inputData)
assertTrue(result is ListenableWorker.Result.Failure)

Expand All @@ -380,8 +375,6 @@ class AnalyticsClientUnitTest {
.putString(AnalyticsClient.WORK_INPUT_KEY_SESSION_ID, sessionId)
.build()

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsUpload(inputData)
assertTrue(result is ListenableWorker.Result.Failure)

Expand Down Expand Up @@ -412,8 +405,6 @@ class AnalyticsClientUnitTest {
)
every { analyticsEventBlobDao.getBlobsBySessionId(sessionId) } returns blobs

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
sut.performAnalyticsUpload(inputData)

verify { analyticsEventBlobDao.deleteEventBlobs(blobs) }
Expand Down Expand Up @@ -444,8 +435,6 @@ class AnalyticsClientUnitTest {
val httpError = Exception("error")
every { httpClient.post(any(), any(), any(), any()) } throws httpError

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val result = sut.performAnalyticsUpload(inputData)
assertTrue(result is ListenableWorker.Result.Failure)
}
Expand All @@ -472,15 +461,7 @@ class AnalyticsClientUnitTest {
)
} returns Unit

val sut = AnalyticsClient(
context = context,
httpClient = httpClient,
analyticsDatabase = analyticsDatabase,
workManager = workManager,
deviceInspector = deviceInspector,
analyticsParamRepository = analyticsParamRepository
)
sut.reportCrash(context, configuration, integration, 123, authorization)
sut.reportCrash(context, configuration, integration, authorization)

// language=JSON
val expectedJSON = """
Expand Down Expand Up @@ -531,29 +512,18 @@ class AnalyticsClientUnitTest {
deviceInspector.getDeviceMetadata(context, configuration, sessionId, integration)
} returns metadata

val sut =
AnalyticsClient(context, httpClient, analyticsDatabase, workManager, deviceInspector)
val event = AnalyticsEvent(eventName)
val event = AnalyticsEvent(eventName, timestamp)
sut.sendEvent(configuration, event, integration, authorization)

sut.reportCrash(context, configuration, integration, 123, null)
sut.reportCrash(context, configuration, integration, null)

// or confirmVerified(httpClient)
verify { httpClient wasNot Called }
}

@Test
fun `sendEvent enqueues work to upload analytic events with sessionId in the name`() {
val sut = AnalyticsClient(
context = context,
httpClient = httpClient,
analyticsDatabase = analyticsDatabase,
workManager = workManager,
deviceInspector = deviceInspector,
analyticsParamRepository = analyticsParamRepository
)

sut.sendEvent(configuration, AnalyticsEvent("event-name"), integration, authorization)
sut.sendEvent(configuration, AnalyticsEvent("event-name", timestamp), integration, authorization)

verify {
workManager.enqueueUniqueWork(
Expand Down
Loading

0 comments on commit 2b7ff74

Please sign in to comment.