Skip to content

Commit

Permalink
Merge pull request #1290 from DataDog/louiszawadzki/rumm-3043/expose-…
Browse files Browse the repository at this point in the history
…webview-internals-for-cross-platform

Implement webview proxy for cross platform
  • Loading branch information
louiszawadzki authored Feb 22, 2023
2 parents dfd6c81 + f9dee9e commit 9edce6d
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 6 deletions.
2 changes: 2 additions & 0 deletions dd-sdk-android/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ class com.datadog.android._InternalProxy
fun debug(String)
fun error(String, Throwable? = null)
fun error(String, String?, String?)
class _WebviewProxy
val _telemetry: _TelemetryProxy
fun setCustomAppVersion(String)
fun consumeWebviewEvent(String)
companion object
fun setTelemetryConfigurationEventMapper(com.datadog.android.core.configuration.Configuration.Builder, com.datadog.android.event.EventMapper<com.datadog.android.telemetry.model.TelemetryConfigurationEvent>): com.datadog.android.core.configuration.Configuration.Builder
enum com.datadog.android.core.configuration.BatchSize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ object Datadog {
val _internal: _InternalProxy by lazy {
_InternalProxy(
telemetry,
(globalSdkCore as? DatadogCore)?.coreFeature
(globalSdkCore as? DatadogCore)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
package com.datadog.android

import com.datadog.android.core.configuration.Configuration
import com.datadog.android.core.internal.CoreFeature
import com.datadog.android.event.EventMapper
import com.datadog.android.telemetry.internal.Telemetry
import com.datadog.android.telemetry.model.TelemetryConfigurationEvent
import com.datadog.android.v2.core.DatadogCore
import com.datadog.android.webview.internal.MixedWebViewEventConsumer
import com.datadog.android.webview.internal.NoOpWebViewEventConsumer
import com.datadog.android.webview.internal.WebViewEventConsumer
import com.datadog.android.webview.internal.log.WebViewLogEventConsumer
import com.datadog.android.webview.internal.rum.WebViewRumEventConsumer
import com.datadog.android.webview.internal.rum.WebViewRumEventContextProvider

/**
* This class exposes internal methods that are used by other Datadog modules and cross platform
Expand All @@ -33,7 +39,7 @@ import com.datadog.android.telemetry.model.TelemetryConfigurationEvent
class _InternalProxy internal constructor(
telemetry: Telemetry,
// TODO RUMM-0000 Shouldn't be nullable
private val coreFeature: CoreFeature?
private val datadogCore: DatadogCore?
) {
class _TelemetryProxy internal constructor(private val telemetry: Telemetry) {

Expand All @@ -50,10 +56,43 @@ class _InternalProxy internal constructor(
}
}

class _WebviewProxy internal constructor(private val datadogCore: DatadogCore?) {
private fun buildWebViewEventConsumer(): WebViewEventConsumer<String> {
val core = datadogCore
val webViewRumFeature = core?.webViewRumFeature
val webViewLogsFeature = core?.webViewLogsFeature
val contextProvider = WebViewRumEventContextProvider()

if (webViewLogsFeature == null || webViewRumFeature == null) {
return NoOpWebViewEventConsumer()
} else {
return MixedWebViewEventConsumer(
WebViewRumEventConsumer(
sdkCore = core,
dataWriter = webViewRumFeature.dataWriter,
contextProvider = contextProvider
),
WebViewLogEventConsumer(
sdkCore = core,
userLogsWriter = webViewLogsFeature.dataWriter,
rumContextProvider = contextProvider
)
)
}
}

internal val webViewEventConsumer: WebViewEventConsumer<String> = buildWebViewEventConsumer()
}

private val _webview: _WebviewProxy = _WebviewProxy(datadogCore)
val _telemetry: _TelemetryProxy = _TelemetryProxy(telemetry)

fun setCustomAppVersion(version: String) {
coreFeature?.packageVersionProvider?.version = version
datadogCore?.coreFeature?.packageVersionProvider?.version = version
}

fun consumeWebviewEvent(event: String) {
_webview.webViewEventConsumer.consume(event)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,25 @@ package com.datadog.android
import com.datadog.android.core.internal.CoreFeature
import com.datadog.android.core.internal.system.AppVersionProvider
import com.datadog.android.telemetry.internal.Telemetry
import com.datadog.android.utils.assertj.JsonElementAssert
import com.datadog.android.utils.forge.Configurator
import com.datadog.android.v2.api.EventBatchWriter
import com.datadog.android.v2.api.FeatureScope
import com.datadog.android.v2.api.context.DatadogContext
import com.datadog.android.v2.core.DatadogCore
import com.datadog.android.v2.core.internal.storage.DataWriter
import com.datadog.android.webview.internal.MixedWebViewEventConsumer
import com.datadog.android.webview.internal.log.WebViewLogsFeature
import com.datadog.android.webview.internal.rum.WebViewRumEventConsumer
import com.datadog.android.webview.internal.rum.WebViewRumFeature
import com.google.gson.JsonObject
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.annotation.Forgery
import fr.xgouchet.elmyr.annotation.StringForgery
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
Expand All @@ -35,7 +49,7 @@ import org.mockito.quality.Strictness
internal class InternalProxyTest {

@Mock
lateinit var mockCoreFeature: CoreFeature
lateinit var mockCoreFeature: DatadogCore

@Test
fun `M proxy telemetry to RumMonitor W debug()`(
Expand Down Expand Up @@ -91,7 +105,9 @@ internal class InternalProxyTest {
) {
// Given
val mockAppVersionProvider = mock<AppVersionProvider>()
whenever(mockCoreFeature.packageVersionProvider) doReturn mockAppVersionProvider
val mockCore = mock<CoreFeature>()
whenever(mockCoreFeature.coreFeature) doReturn mockCore
whenever(mockCore.packageVersionProvider) doReturn mockAppVersionProvider
val proxy = _InternalProxy(telemetry = mock(), mockCoreFeature)

// When
Expand All @@ -100,4 +116,61 @@ internal class InternalProxyTest {
// Then
verify(mockAppVersionProvider).version = version
}

@Test
fun `M pass web view event to RumWebEventConsumer W consumeWebViewEvent()`(
forge: Forge
) {
// Given
val fakeBundledEvent = forge.getForgery<JsonObject>()
val fakeRumEventType = forge.anElementFrom(WebViewRumEventConsumer.RUM_EVENT_TYPES)
val fakeWebEvent = bundleWebEvent(fakeBundledEvent, fakeRumEventType)

val mockWebViewRumFeature = mock<WebViewRumFeature>()
val mockWebViewLogsFeature = mock<WebViewLogsFeature>()
val mockRumDataWriter = mock<DataWriter<Any>>()
val mockLogsDataWriter = mock<DataWriter<JsonObject>>()
whenever(mockCoreFeature.webViewRumFeature) doReturn mockWebViewRumFeature
whenever(mockCoreFeature.webViewLogsFeature) doReturn mockWebViewLogsFeature
whenever(mockWebViewRumFeature.dataWriter) doReturn mockRumDataWriter
whenever(mockWebViewLogsFeature.dataWriter) doReturn mockLogsDataWriter

val mockWebRumFeatureScope = mock<FeatureScope>()
val mockWebLogsFeatureScope = mock<FeatureScope>()
whenever(mockCoreFeature.getFeature(WebViewRumFeature.WEB_RUM_FEATURE_NAME))doReturn
mockWebRumFeatureScope
whenever(mockCoreFeature.getFeature(WebViewLogsFeature.WEB_LOGS_FEATURE_NAME)) doReturn
mockWebLogsFeatureScope

val mockDatadogContext = mock<DatadogContext>()
val mockEventBatchWriter = mock<EventBatchWriter>()
val proxy = _InternalProxy(telemetry = mock(), mockCoreFeature)

// When
proxy.consumeWebviewEvent(fakeWebEvent.toString())
argumentCaptor<(DatadogContext, EventBatchWriter) -> Unit> {
verify(mockWebRumFeatureScope).withWriteContext(any(), capture())
firstValue(mockDatadogContext, mockEventBatchWriter)
}

// Then
argumentCaptor<JsonObject> {
verify(mockRumDataWriter).write(any(), capture())
JsonElementAssert.assertThat(firstValue).isEqualTo(fakeBundledEvent)
}
}
}

private fun bundleWebEvent(
fakeBundledEvent: JsonObject?,
eventType: String?
): JsonObject {
val fakeWebEvent = JsonObject()
fakeBundledEvent?.let {
fakeWebEvent.add(MixedWebViewEventConsumer.EVENT_KEY, it)
}
eventType?.let {
fakeWebEvent.addProperty(MixedWebViewEventConsumer.EVENT_TYPE_KEY, it)
}
return fakeWebEvent
}

0 comments on commit 9edce6d

Please sign in to comment.