Skip to content

Commit

Permalink
RUMM-2421 Processor - process FullSnapshotRecords and dispatch to per…
Browse files Browse the repository at this point in the history
…sister
  • Loading branch information
mariusc83 committed Aug 17, 2022
1 parent 48e737c commit 82339be
Show file tree
Hide file tree
Showing 25 changed files with 1,108 additions and 15 deletions.
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.sessionreplay.internal

import com.datadog.android.rum.GlobalRum
import com.datadog.android.sessionreplay.utils.RumContextProvider

internal class SessionReplayContextProvider : RumContextProvider {
override fun getRumContext(): com.datadog.android.sessionreplay.utils.SessionReplayRumContext {
return GlobalRum.getRumContext().let {
com.datadog.android.sessionreplay.utils.SessionReplayRumContext(
applicationId = it.applicationId,
sessionId = it.sessionId,
viewId = it.viewId
?: com.datadog.android.rum.internal.domain.RumContext.NULL_UUID
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.datadog.android.log.model.LogEvent
import com.datadog.android.privacy.TrackingConsent
import com.datadog.android.rum.internal.RumFeature
import com.datadog.android.sessionreplay.SessionReplayLifecycleCallback
import com.datadog.android.sessionreplay.internal.SessionReplayContextProvider
import com.datadog.android.sessionreplay.internal.SessionReplayFeature
import com.datadog.android.tracing.internal.TracingFeature
import com.datadog.android.v2.api.FeatureScope
Expand Down Expand Up @@ -297,7 +298,7 @@ internal class DatadogCore(
if (configuration != null) {
sessionReplayFeature = SessionReplayFeature(
coreFeature,
SessionReplayLifecycleCallback()
SessionReplayLifecycleCallback(SessionReplayContextProvider())
)
sessionReplayFeature?.initialize(appContext, configuration)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.sessionreplay.internal

import com.datadog.android.rum.GlobalRum
import com.datadog.android.rum.internal.domain.RumContext
import com.datadog.android.utils.config.GlobalRumMonitorTestConfiguration
import com.datadog.android.utils.forge.Configurator
import com.datadog.tools.unit.annotations.TestConfigurationsProvider
import com.datadog.tools.unit.extensions.TestConfigurationExtension
import com.datadog.tools.unit.extensions.config.TestConfiguration
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
import fr.xgouchet.elmyr.junit5.ForgeExtension
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.extension.Extensions
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.quality.Strictness

@Extensions(
ExtendWith(MockitoExtension::class),
ExtendWith(ForgeExtension::class),
ExtendWith(TestConfigurationExtension::class)
)
@MockitoSettings(strictness = Strictness.LENIENT)
@ForgeConfiguration(Configurator::class)
internal class SessionReplayContextProviderTest {

lateinit var testedSessionReplayContextProvider: SessionReplayContextProvider

@BeforeEach
fun `set up`() {
testedSessionReplayContextProvider = SessionReplayContextProvider()
}

@Test
fun `M provide a valid Rum context W getRumContext()`() {
// When
rumMonitor.context = rumMonitor.context.copy(viewId = UUID.randomUUID().toString())
GlobalRum.updateRumContext(rumMonitor.context)
val context = testedSessionReplayContextProvider.getRumContext()

// Then
assertThat(context.applicationId).isEqualTo(rumMonitor.context.applicationId)
assertThat(context.sessionId).isEqualTo(rumMonitor.context.sessionId)
assertThat(context.viewId).isEqualTo(rumMonitor.context.viewId)
}

@Test
fun `M provide an invalid Rum context W getRumContext() { RUM not initialized }`() {
// Given
GlobalRum.updateRumContext(RumContext())

// When
val context = testedSessionReplayContextProvider.getRumContext()

// Then
assertThat(context.applicationId).isEqualTo(RumContext.NULL_UUID)
assertThat(context.sessionId).isEqualTo(RumContext.NULL_UUID)
assertThat(context.viewId).isEqualTo(RumContext.NULL_UUID)
}

companion object {

val rumMonitor = GlobalRumMonitorTestConfiguration()

@TestConfigurationsProvider
@JvmStatic
fun getTestConfigurations(): List<TestConfiguration> {
return listOf(rumMonitor)
}
}
}
11 changes: 11 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,13 @@ datadog:
- "java.util.LinkedList.isEmpty()"
- "java.util.LinkedList.isNotEmpty()"
- "java.util.LinkedList.poll()"
- "java.util.LinkedList.addAll(kotlin.collections.Collection)"
- "java.util.LinkedHashMap.remove(kotlin.String)"
- "java.util.Stack.constructor()"
- "java.util.Stack.push(com.datadog.android.sessionreplay.recorder.Node)"
- "java.util.Stack.isNotEmpty()"
- "java.util.Stack.pop()"
- "java.util.Stack.push(com.datadog.android.sessionreplay.recorder.Node)"
# endregion
# region Java Concurrency
- "java.lang.Thread.UncaughtExceptionHandler.uncaughtException(java.lang.Thread, kotlin.Throwable)"
Expand Down Expand Up @@ -1036,6 +1042,10 @@ datadog:
- "kotlin.collections.List.toMap()"
- "kotlin.collections.List.toSet()"
- "kotlin.collections.List.withIndex()"
- "kotlin.collections.List.fold(com.google.gson.JsonArray, kotlin.Function2)"
- "kotlin.collections.List.filterIndexed(kotlin.Function2)"
- "kotlin.collections.List.subList(kotlin.Int, kotlin.Int)"
- "kotlin.collections.List.count()"
- "kotlin.collections.Map.containsKey(kotlin.String)"
- "kotlin.collections.Map.filterKeys(kotlin.Function1)"
- "kotlin.collections.Map.filterValues(kotlin.Function1)"
Expand All @@ -1055,6 +1065,7 @@ datadog:
- "kotlin.collections.MutableList.add(java.lang.ref.WeakReference)"
- "kotlin.collections.MutableList.add(kotlin.ByteArray)"
- "kotlin.collections.MutableList.add(kotlin.String)"
- "kotlin.collections.MutableList.add(com.datadog.android.sessionreplay.model.MobileSegment.MobileRecord)"
- "kotlin.collections.MutableList.add(com.datadog.android.sessionreplay.model.MobileSegment.Position)"
- "kotlin.collections.MutableList.addAll(kotlin.collections.Collection)"
- "kotlin.collections.MutableList.clear()"
Expand Down
7 changes: 7 additions & 0 deletions library/dd-sdk-android-session-replay/apiSurface
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class com.datadog.android.sessionreplay.SessionReplayLifecycleCallback : android.app.Application.ActivityLifecycleCallbacks
constructor(com.datadog.android.sessionreplay.utils.RumContextProvider)
override fun onActivityCreated(android.app.Activity, android.os.Bundle?)
override fun onActivityStarted(android.app.Activity)
override fun onActivityResumed(android.app.Activity)
Expand All @@ -8,6 +9,7 @@ class com.datadog.android.sessionreplay.SessionReplayLifecycleCallback : android
override fun onActivityDestroyed(android.app.Activity)
fun register(android.app.Application)
fun unregisterAndStopRecorders(android.app.Application)
companion object
data class com.datadog.android.sessionreplay.model.MobileSegment
constructor(Application, Session, View, kotlin.Long, kotlin.Long, kotlin.Long, kotlin.Long, kotlin.Boolean? = null, Source, kotlin.collections.List<MobileRecord>)
fun toJson(): com.google.gson.JsonElement
Expand Down Expand Up @@ -212,3 +214,8 @@ data class com.datadog.android.sessionreplay.model.MobileSegment
fun toJson(): com.google.gson.JsonElement
companion object
fun fromJson(kotlin.String): Vertical
interface com.datadog.android.sessionreplay.utils.RumContextProvider
fun getRumContext(): SessionReplayRumContext
data class com.datadog.android.sessionreplay.utils.SessionReplayRumContext
constructor(String = NULL_UUID, String = NULL_UUID, String = NULL_UUID)
companion object
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import android.os.Bundle
import com.datadog.android.sessionreplay.processor.SnapshotProcessor
import com.datadog.android.sessionreplay.recorder.Recorder
import com.datadog.android.sessionreplay.recorder.ScreenRecorder
import com.datadog.android.sessionreplay.utils.RumContextProvider
import com.datadog.android.sessionreplay.utils.SessionReplayTimeProvider
import java.util.WeakHashMap
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit

/**
* The SessionReplayLifecycleCallback.
Expand All @@ -21,10 +26,27 @@ import java.util.WeakHashMap
* This is only meant for internal usage and later will change visibility from public to internal.
*/
@SuppressWarnings("UndocumentedPublicFunction")
class SessionReplayLifecycleCallback :
class SessionReplayLifecycleCallback(rumContextProvider: RumContextProvider) :
Application.ActivityLifecycleCallbacks {

internal var recorder: Recorder = ScreenRecorder(SnapshotProcessor())
private val timeProvider = SessionReplayTimeProvider()

@Suppress("UnsafeThirdPartyFunctionCall") // workQueue can't be null
private val processorExecutorService = ThreadPoolExecutor(
CORE_DEFAULT_POOL_SIZE,
Runtime.getRuntime().availableProcessors(),
THREAD_POOL_MAX_KEEP_ALIVE_MS,
TimeUnit.MILLISECONDS,
LinkedBlockingDeque()
)
internal var recorder: Recorder = ScreenRecorder(
SnapshotProcessor(
rumContextProvider,
timeProvider,
processorExecutorService
),
timeProvider
)
internal val resumedActivities: WeakHashMap<Activity, Any?> = WeakHashMap()

// region callback
Expand Down Expand Up @@ -78,4 +100,9 @@ class SessionReplayLifecycleCallback :
}

// endregion

companion object {
private val THREAD_POOL_MAX_KEEP_ALIVE_MS = TimeUnit.SECONDS.toMillis(5)
private const val CORE_DEFAULT_POOL_SIZE = 1 // Only one thread will be kept alive
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.sessionreplay.processor

import com.datadog.android.sessionreplay.model.MobileSegment
import com.google.gson.JsonArray
import com.google.gson.JsonObject

internal data class EnrichedRecord(
val applicationId: String,
val sessionId: String,
val viewId: String,
val records: List<MobileSegment.MobileRecord>
) {

fun toJson(): String {
val json = JsonObject()
json.addProperty(APPLICATION_ID_KEY, applicationId)
json.addProperty(SESSION_ID_KEY, sessionId)
json.addProperty(VIEW_ID_KEY, viewId)
val recordsJsonArray = JsonArray()
records.map { it.toJson() }
.fold(recordsJsonArray) { acc, jsonElement ->
acc.add(jsonElement)
acc
}
json.add(RECORD_KEY, recordsJsonArray)
return json.toString()
}

companion object {
const val APPLICATION_ID_KEY = "application_id"
const val SESSION_ID_KEY = "session_id"
const val VIEW_ID_KEY = "view_id"
const val RECORD_KEY = "records"
}
}
Loading

0 comments on commit 82339be

Please sign in to comment.