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

Implement webview proxy for cross platform #1290

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}