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

Merge feature/sr-web-view-support #1959

Merged
merged 30 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
44a906a
RUM-2484 Provide the SR privacy level through the DatadogEventBridge
mariusc83 Dec 21, 2023
bf58d3a
RUM-2590 Add recorded WebView scenario into the sample app
mariusc83 Dec 22, 2023
8d777ab
RUM-2483 Introduce the WebViewWireframeMapper
mariusc83 Dec 26, 2023
7803795
RUM-2487 Provide the WebViewRecordConsumer for the JS bridge
mariusc83 Dec 27, 2023
598cfe2
Do not force new batch when persisting SR records
mariusc83 Jan 2, 2024
668b59b
RUM-2622 Change the SR requestbody to match the new endpoint payload …
mariusc83 Jan 3, 2024
6d7088a
RUM-2572 WebView - force fullsnapshot in browser sdk when RUM view ch…
mariusc83 Jan 4, 2024
13a7578
Revert "Merge pull request #1807 from DataDog/mconstantin/rum-2572/fo…
mariusc83 Jan 12, 2024
474b8b0
RUM-2875 Introduce the FeatureContextUpdateLitener API
mariusc83 Jan 17, 2024
2470aa7
Merge pull request #1829 from DataDog/mconstantin/rum-2875/propagate-…
mariusc83 Jan 30, 2024
688c2a7
RUM-2485 Provide the parent container information for browser rum events
mariusc83 Jan 19, 2024
fce374b
Merge pull request #1831 from DataDog/mconstantin/rum-2485/provide-th…
mariusc83 Feb 1, 2024
0e6f41a
Merge branch 'develop' into sr-web-view-support/merge/develop
mariusc83 Feb 8, 2024
64b5013
Merge pull request #1849 from DataDog/sr-web-view-support/merge/develop
mariusc83 Feb 8, 2024
e22c43e
RUM-3281 add getCapabilities to the Webview bridge
xgouchet Feb 21, 2024
731551f
Merge pull request #1871 from DataDog/xgouchet/RUM-3281/webview_get_c…
xgouchet Feb 22, 2024
c3b45d2
RUM-3302 detect full snapshot from webview sr
xgouchet Mar 11, 2024
dec970a
Merge pull request #1908 from DataDog/xgouchet/RUM-3302/handle_has_fu…
xgouchet Mar 11, 2024
7b32a80
Merge branch 'develop' into xgouchet/merge_develop
xgouchet Mar 14, 2024
df4c3ca
Merge pull request #1915 from DataDog/xgouchet/merge_develop
xgouchet Mar 18, 2024
6ca9992
Merge remote-tracking branch 'origin/develop' into xgouchet/merge_dev…
xgouchet Mar 18, 2024
2027b2d
Merge pull request #1917 from DataDog/xgouchet/merge_develop_sr_webview
xgouchet Mar 18, 2024
319193a
Merge remote-tracking branch 'origin/develop' into xgouchet/merge_dev…
xgouchet Mar 19, 2024
26824c4
Merge pull request #1922 from DataDog/xgouchet/merge_develop_sr_webvi…
xgouchet Mar 19, 2024
ff69432
RUM-3532 add webview use cases in sample
xgouchet Mar 28, 2024
140ed18
RUM-3532 keep webviews hidden in Session Replay
xgouchet Mar 28, 2024
764ebdf
RUM-3532 fix PR comments
xgouchet Mar 29, 2024
04186b5
Merge pull request #1949 from DataDog/xgouchet/RUM-3532/keep_webview_…
xgouchet Mar 29, 2024
283c82b
Merge branch 'develop' into xgouchet/srwv_merge_develop
xgouchet Apr 2, 2024
da8c7a4
Merge pull request #1958 from DataDog/xgouchet/srwv_merge_develop
xgouchet Apr 2, 2024
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
4 changes: 4 additions & 0 deletions dd-sdk-android-core/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ interface com.datadog.android.api.feature.Feature
const val TRACING_FEATURE_NAME: String
const val SESSION_REPLAY_FEATURE_NAME: String
const val NDK_CRASH_REPORTS_FEATURE_NAME: String
interface com.datadog.android.api.feature.FeatureContextUpdateReceiver
fun onContextUpdate(String, Map<String, Any?>)
interface com.datadog.android.api.feature.FeatureEventReceiver
fun onReceive(Any)
interface com.datadog.android.api.feature.FeatureScope
Expand All @@ -110,6 +112,8 @@ interface com.datadog.android.api.feature.FeatureSdkCore : com.datadog.android.a
fun updateFeatureContext(String, (MutableMap<String, Any?>) -> Unit)
fun getFeatureContext(String): Map<String, Any?>
fun setEventReceiver(String, FeatureEventReceiver)
fun setContextUpdateReceiver(String, FeatureContextUpdateReceiver)
fun removeContextUpdateReceiver(String, FeatureContextUpdateReceiver)
fun removeEventReceiver(String)
fun createSingleThreadExecutorService(): java.util.concurrent.ExecutorService
fun createScheduledExecutorService(): java.util.concurrent.ScheduledExecutorService
Expand Down
6 changes: 6 additions & 0 deletions dd-sdk-android-core/api/dd-sdk-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ public final class com/datadog/android/api/feature/Feature$Companion {
public static final field TRACING_FEATURE_NAME Ljava/lang/String;
}

public abstract interface class com/datadog/android/api/feature/FeatureContextUpdateReceiver {
public abstract fun onContextUpdate (Ljava/lang/String;Ljava/util/Map;)V
}

public abstract interface class com/datadog/android/api/feature/FeatureEventReceiver {
public abstract fun onReceive (Ljava/lang/Object;)V
}
Expand All @@ -332,7 +336,9 @@ public abstract interface class com/datadog/android/api/feature/FeatureSdkCore :
public abstract fun getFeatureContext (Ljava/lang/String;)Ljava/util/Map;
public abstract fun getInternalLogger ()Lcom/datadog/android/api/InternalLogger;
public abstract fun registerFeature (Lcom/datadog/android/api/feature/Feature;)V
public abstract fun removeContextUpdateReceiver (Ljava/lang/String;Lcom/datadog/android/api/feature/FeatureContextUpdateReceiver;)V
public abstract fun removeEventReceiver (Ljava/lang/String;)V
public abstract fun setContextUpdateReceiver (Ljava/lang/String;Lcom/datadog/android/api/feature/FeatureContextUpdateReceiver;)V
public abstract fun setEventReceiver (Ljava/lang/String;Lcom/datadog/android/api/feature/FeatureEventReceiver;)V
public abstract fun updateFeatureContext (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/

package com.datadog.android.api.feature

import androidx.annotation.AnyThread

/**
* Receiver for feature context updates.
*/
fun interface FeatureContextUpdateReceiver {

/**
* Called when the context for a feature is updated.
* @param featureName the name of the feature
* @param event the updated context
*/
@AnyThread
fun onContextUpdate(featureName: String, event: Map<String, Any?>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ interface FeatureSdkCore : SdkCore {
*/
fun setEventReceiver(featureName: String, receiver: FeatureEventReceiver)

/**
* Sets context update receiver for the given feature.
*
* @param featureName Feature name.
* @param listener Listener to remove.
*/
fun setContextUpdateReceiver(featureName: String, listener: FeatureContextUpdateReceiver)

/**
* Removes context update listener for the given feature.
*
* @param featureName Feature name.
* @param listener Listener to remove.
*/
fun removeContextUpdateReceiver(featureName: String, listener: FeatureContextUpdateReceiver)

/**
* Removes events receive for the given feature.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.datadog.android.api.context.NetworkInfo
import com.datadog.android.api.context.TimeInfo
import com.datadog.android.api.context.UserInfo
import com.datadog.android.api.feature.Feature
import com.datadog.android.api.feature.FeatureContextUpdateReceiver
import com.datadog.android.api.feature.FeatureEventReceiver
import com.datadog.android.api.feature.FeatureScope
import com.datadog.android.api.feature.FeatureSdkCore
Expand Down Expand Up @@ -197,6 +198,11 @@ internal class DatadogCore(
val mutableContext = featureContext.toMutableMap()
updateCallback(mutableContext)
it.setFeatureContext(featureName, mutableContext)
// notify all the other features
features.filter { it.key != featureName }
.forEach { (_, feature) ->
feature.notifyContextUpdated(featureName, mutableContext.toMap())
}
}
}
}
Expand Down Expand Up @@ -227,6 +233,23 @@ internal class DatadogCore(
}
}

override fun setContextUpdateReceiver(featureName: String, listener: FeatureContextUpdateReceiver) {
val feature = features[featureName]
if (feature == null) {
internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ MISSING_FEATURE_FOR_CONTEXT_UPDATE_LISTENER.format(Locale.US, featureName) }
)
} else {
feature.setContextUpdateListener(listener)
}
}

override fun removeContextUpdateReceiver(featureName: String, listener: FeatureContextUpdateReceiver) {
features[featureName]?.removeContextUpdateListener(listener)
}

/** @inheritDoc */
override fun removeEventReceiver(featureName: String) {
features[featureName]?.eventReceiver?.set(null)
Expand Down Expand Up @@ -515,6 +538,8 @@ internal class DatadogCore(

internal const val MISSING_FEATURE_FOR_EVENT_RECEIVER =
"Cannot add event receiver for feature \"%s\", it is not registered."
internal const val MISSING_FEATURE_FOR_CONTEXT_UPDATE_LISTENER =
"Cannot add event listener for feature \"%s\", it is not registered."
internal const val EVENT_RECEIVER_ALREADY_EXISTS =
"Feature \"%s\" already has event receiver registered, overwriting it."

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.datadog.android.api.context.DatadogContext
import com.datadog.android.api.context.NetworkInfo
import com.datadog.android.api.context.TimeInfo
import com.datadog.android.api.feature.Feature
import com.datadog.android.api.feature.FeatureContextUpdateReceiver
import com.datadog.android.api.feature.FeatureEventReceiver
import com.datadog.android.api.feature.FeatureScope
import com.datadog.android.core.internal.net.DefaultFirstPartyHostHeaderTypeResolver
Expand Down Expand Up @@ -103,6 +104,16 @@ internal object NoOpInternalSdkCore : InternalSdkCore {

override fun removeEventReceiver(featureName: String) = Unit

override fun setContextUpdateReceiver(
featureName: String,
listener: FeatureContextUpdateReceiver
) = Unit

override fun removeContextUpdateReceiver(
featureName: String,
listener: FeatureContextUpdateReceiver
) = Unit

override fun createSingleThreadExecutorService(): ExecutorService {
return NoOpExecutorService()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.content.Context
import com.datadog.android.api.InternalLogger
import com.datadog.android.api.context.DatadogContext
import com.datadog.android.api.feature.Feature
import com.datadog.android.api.feature.FeatureContextUpdateReceiver
import com.datadog.android.api.feature.FeatureEventReceiver
import com.datadog.android.api.feature.FeatureScope
import com.datadog.android.api.feature.StorageBackedFeature
Expand Down Expand Up @@ -42,7 +43,9 @@ import com.datadog.android.core.internal.persistence.file.advanced.FeatureFileOr
import com.datadog.android.core.internal.persistence.file.batch.BatchFileReaderWriter
import com.datadog.android.core.persistence.PersistenceStrategy
import com.datadog.android.privacy.TrackingConsentProviderCallback
import java.util.Collections
import java.util.Locale
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference

Expand All @@ -54,6 +57,10 @@ internal class SdkFeature(
) : FeatureScope {

internal val initialized = AtomicBoolean(false)

@Suppress("UnsafeThirdPartyFunctionCall") // the argument is always empty
internal val contextUpdateListeners =
Collections.newSetFromMap(ConcurrentHashMap<FeatureContextUpdateReceiver, Boolean>())
internal val eventReceiver = AtomicReference<FeatureEventReceiver>(null)
internal var storage: Storage = NoOpStorage()
internal var uploader: DataUploader = NoOpDataUploader()
Expand Down Expand Up @@ -166,6 +173,39 @@ internal class SdkFeature(

// endregion

// region Context Update Listener

internal fun notifyContextUpdated(featureName: String, context: Map<String, Any?>) {
contextUpdateListeners.forEach {
it.onContextUpdate(featureName, context)
}
}

internal fun setContextUpdateListener(listener: FeatureContextUpdateReceiver) {
synchronized(contextUpdateListeners) {
// the argument is always non - null, so we can suppress the warning
@Suppress("UnsafeThirdPartyFunctionCall")
if (contextUpdateListeners.contains(listener)) {
internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ CONTEXT_UPDATE_LISTENER_ALREADY_EXISTS.format(Locale.US, wrappedFeature.name) }
)
}
contextUpdateListeners.add(listener)
}
}

internal fun removeContextUpdateListener(listener: FeatureContextUpdateReceiver) {
synchronized(contextUpdateListeners) {
@Suppress("UnsafeThirdPartyFunctionCall")
// the argument is always non - null, so we can suppress the warning
contextUpdateListeners.remove(listener)
}
}

// endregion

// region Internal

private fun setupMetricsDispatcher(
Expand Down Expand Up @@ -318,6 +358,8 @@ internal class SdkFeature(
// endregion

companion object {
internal const val CONTEXT_UPDATE_LISTENER_ALREADY_EXISTS =
"Feature \"%s\" already has this listener registered."
const val NO_EVENT_RECEIVER =
"Feature \"%s\" has no event receiver registered, ignoring event."
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.datadog.android.api.context.NetworkInfo
import com.datadog.android.api.context.TimeInfo
import com.datadog.android.api.context.UserInfo
import com.datadog.android.api.feature.Feature
import com.datadog.android.api.feature.FeatureContextUpdateReceiver
import com.datadog.android.api.feature.FeatureEventReceiver
import com.datadog.android.core.configuration.Configuration
import com.datadog.android.core.internal.ContextProvider
Expand Down Expand Up @@ -62,10 +63,12 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.reset
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
import java.util.Locale
Expand Down Expand Up @@ -268,11 +271,17 @@ internal class DatadogCoreTest {
@MapForgery(
key = AdvancedForgery(string = [StringForgery(StringForgeryType.ALPHABETICAL)]),
value = AdvancedForgery(string = [StringForgery(StringForgeryType.ALPHABETICAL)])
) context: Map<String, String>
) context: Map<String, String>,
forge: Forge
) {
// Given
val mockContextProvider = mock<ContextProvider>()
testedCore.features[feature] = mock()
val mockFeature = mock<SdkFeature>()
val otherFeatures = mapOf(
forge.anAlphaNumericalString() to mock<SdkFeature>()
)
testedCore.features[feature] = mockFeature
testedCore.features.putAll(otherFeatures)
testedCore.coreFeature.contextProvider = mockContextProvider

// When
Expand All @@ -282,6 +291,11 @@ internal class DatadogCoreTest {

// Then
verify(mockContextProvider).setFeatureContext(feature, context)
otherFeatures.forEach { (_, otherFeature) ->
verify(otherFeature).notifyContextUpdated(feature, context)
verifyNoMoreInteractions(otherFeature)
}
verify(mockFeature, never()).notifyContextUpdated(feature, context)
}

@Test
Expand Down Expand Up @@ -385,6 +399,56 @@ internal class DatadogCoreTest {
verify(mockEventReceiverRef).set(null)
}

@Test
fun `𝕄 set context update listener 𝕎 setContextUpdateListener()`(
@StringForgery feature: String
) {
// Given
val mockFeature = mock<SdkFeature>()
val mockContextUpdateListener: FeatureContextUpdateReceiver = mock()
testedCore.features[feature] = mockFeature

// When
testedCore.setContextUpdateReceiver(feature, mockContextUpdateListener)

// Then
verify(mockFeature).setContextUpdateListener(mockContextUpdateListener)
}

@Test
fun `𝕄 notify no feature registered 𝕎 setContextUpdateListener() { feature is not registered }`(
@StringForgery feature: String
) {
// Given
val mockContextUpdateListener: FeatureContextUpdateReceiver = mock()

// When
testedCore.setContextUpdateReceiver(feature, mockContextUpdateListener)

// Then
mockInternalLogger.verifyLog(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
DatadogCore.MISSING_FEATURE_FOR_CONTEXT_UPDATE_LISTENER.format(Locale.US, feature)
)
}

@Test
fun `𝕄 remove context update listener 𝕎 removeContextUpdateListener()`(
@StringForgery feature: String
) {
// Given
val mockFeature = mock<SdkFeature>()
val mockContextUpdateListener: FeatureContextUpdateReceiver = mock()
testedCore.features[feature] = mockFeature

// When
testedCore.removeContextUpdateReceiver(feature, mockContextUpdateListener)

// Then
verify(mockFeature).removeContextUpdateListener(mockContextUpdateListener)
}

@Test
fun `𝕄 provide name 𝕎 name(){}`() {
// When+Then
Expand Down
Loading
Loading