Skip to content

Commit

Permalink
ReactNativeAction DataSource (#829)
Browse files Browse the repository at this point in the history
  • Loading branch information
nelsitoPuglisi committed May 10, 2024
1 parent 7b31982 commit a1ef581
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import android.os.Build
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.embrace.android.embracesdk.Embrace
import io.embrace.android.embracesdk.IntegrationTestRule
import io.embrace.android.embracesdk.arch.schema.EmbType
import io.embrace.android.embracesdk.findSpanAttribute
import io.embrace.android.embracesdk.findSpansOfType
import io.embrace.android.embracesdk.internal.ApkToolsConfig
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
import io.embrace.android.embracesdk.recordSession
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
Expand Down Expand Up @@ -106,4 +110,36 @@ internal class ReactNativeInternalInterfaceTest {
assertEquals("999", checkNotNull(session?.appInfo?.javaScriptPatchNumber))
}
}

@Test
fun `react native action`() {
with(testRule) {
val message = checkNotNull(harness.recordSession {
embrace.reactNativeInternalInterface?.logRnAction(
"MyAction",
1000,
5000,
mapOf("key" to "value"),
100,
"SUCCESS"
)
})

val spans = message.findSpansOfType(EmbType.System.ReactNativeAction)

assertEquals(1, spans.size)

val span = spans.single()

assertEquals("emb-rn-action", span.name)
assertEquals("sys.rn_action", span.findSpanAttribute("emb.type"))
assertEquals("MyAction", span.findSpanAttribute("name"))
assertEquals("SUCCESS", span.findSpanAttribute("outcome"))
assertEquals("100", span.findSpanAttribute("payload_size"))
assertEquals("value", span.findSpanAttribute("emb.properties.key"))
assertEquals(1000, span.startTimeNanos.nanosToMillis())
assertEquals(5000, span.endTimeNanos.nanosToMillis())

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ internal sealed class EmbType(type: String, subtype: String?) : TelemetryType {
val embAndroidReactNativeCrashJsException = EmbraceAttributeKey("android.react_native_crash.js_exception")
}

internal object ReactNativeAction : System("rn_action", true)

internal object NativeCrash : System("android.native_crash", true) {
/**
* Exception coming from the native layer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.embrace.android.embracesdk.arch.schema

import io.embrace.android.embracesdk.internal.clock.millisToNanos
import io.embrace.android.embracesdk.internal.spans.toSessionPropertyAttributeName
import io.embrace.android.embracesdk.internal.utils.toNonNullMap
import io.embrace.android.embracesdk.network.EmbraceNetworkRequest
import io.embrace.android.embracesdk.payload.AppExitInfoData
Expand Down Expand Up @@ -313,4 +314,26 @@ internal sealed class SchemaType(
"emb.webview_info.tag" to tag
).toNonNullMap()
}

internal class ReactNativeAction(
name: String,
outcome: String,
payloadSize: Int,
properties: Map<String?, Any?>,
) : SchemaType(
telemetryType = EmbType.System.ReactNativeAction,
fixedObjectName = "rn-action"
) {
override val schemaAttributes = mapOf(
"name" to name,
"outcome" to outcome,
"payload_size" to payloadSize.toString(),
)
.plus(
properties
.mapKeys { it.key.toString().toSessionPropertyAttributeName() }
.mapValues { it.value.toString() }
)
.toNonNullMap()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.injection.DataSourceModule
import io.embrace.android.embracesdk.internal.clock.Clock
import io.embrace.android.embracesdk.internal.utils.Provider
import io.embrace.android.embracesdk.logging.EmbLogger
import io.embrace.android.embracesdk.payload.Breadcrumbs
import io.embrace.android.embracesdk.payload.PushNotificationBreadcrumb.NotificationType
import io.embrace.android.embracesdk.payload.TapBreadcrumb.TapBreadcrumbType
Expand All @@ -27,12 +26,9 @@ import io.embrace.android.embracesdk.session.lifecycle.ActivityLifecycleListener
internal class EmbraceBreadcrumbService(
private val clock: Clock,
private val configService: ConfigService,
private val dataSourceModuleProvider: Provider<DataSourceModule?>,
logger: EmbLogger
private val dataSourceModuleProvider: Provider<DataSourceModule?>
) : BreadcrumbService, ActivityLifecycleListener, MemoryCleanerListener {

private val rnBreadcrumbDataSource = RnBreadcrumbDataSource(configService, logger)

override fun logView(screen: String?, timestamp: Long) {
dataSourceModuleProvider()?.viewDataSource?.dataSource?.changeView(screen, false)
}
Expand Down Expand Up @@ -70,7 +66,9 @@ internal class EmbraceBreadcrumbService(
bytesSent: Int,
output: String
) {
rnBreadcrumbDataSource.logRnAction(name, startTime, endTime, properties, bytesSent, output)
dataSourceModuleProvider()?.rnActionDataSource?.dataSource?.apply {
this.logRnAction(name, startTime, endTime, properties, bytesSent, output)
}
}

override fun logWebView(url: String?, startTime: Long) {
Expand All @@ -79,9 +77,7 @@ internal class EmbraceBreadcrumbService(
}
}

override fun getBreadcrumbs() = Breadcrumbs(
rnActionBreadcrumbs = rnBreadcrumbDataSource.getCapturedData(),
)
override fun getBreadcrumbs() = Breadcrumbs()

override fun flushBreadcrumbs(): Breadcrumbs {
val breadcrumbs = getBreadcrumbs()
Expand Down Expand Up @@ -127,6 +123,5 @@ internal class EmbraceBreadcrumbService(
}

override fun cleanCollections() {
rnBreadcrumbDataSource.cleanCollections()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.embrace.android.embracesdk.capture.crumbs

import io.embrace.android.embracesdk.arch.datasource.SpanDataSourceImpl
import io.embrace.android.embracesdk.arch.limits.UpToLimitStrategy
import io.embrace.android.embracesdk.arch.schema.SchemaType
import io.embrace.android.embracesdk.config.behavior.BreadcrumbBehavior
import io.embrace.android.embracesdk.internal.spans.SpanService
import io.embrace.android.embracesdk.logging.EmbLogger

internal open class RnActionDataSource(
breadcrumbBehavior: BreadcrumbBehavior,
spanService: SpanService,
logger: EmbLogger
) : SpanDataSourceImpl(
spanService,
logger,
UpToLimitStrategy(logger) { breadcrumbBehavior.getCustomBreadcrumbLimit() }
) {
open fun logRnAction(
name: String?,
startTime: Long,
endTime: Long,
properties: Map<String?, Any?>,
bytesSent: Int,
output: String?
): Boolean = captureSpanData(
countsTowardsLimits = true,
inputValidation = { !name.isNullOrEmpty() },
captureAction = {
val schemaType = SchemaType.ReactNativeAction(
checkNotNull(name),
checkNotNull(output),
bytesSent,
properties
)
recordCompletedSpan(
name = schemaType.fixedObjectName,
startTimeMs = startTime,
endTimeMs = endTime,
type = schemaType.telemetryType,
private = false,
attributes = schemaType.attributes()
)
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,8 @@ internal class DataCaptureServiceModuleImpl @JvmOverloads constructor(
Systrace.traceSynchronous("breadcrumb-service-init") {
EmbraceBreadcrumbService(
initModule.clock,
configService,
{ dataSourceModule },
initModule.logger
)
configService
) { dataSourceModule }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.capture.aei.AeiDataSourceImpl
import io.embrace.android.embracesdk.capture.connectivity.NetworkStatusDataSource
import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbDataSource
import io.embrace.android.embracesdk.capture.crumbs.PushNotificationDataSource
import io.embrace.android.embracesdk.capture.crumbs.RnActionDataSource
import io.embrace.android.embracesdk.capture.crumbs.TapDataSource
import io.embrace.android.embracesdk.capture.crumbs.ViewDataSource
import io.embrace.android.embracesdk.capture.crumbs.WebViewUrlDataSource
Expand Down Expand Up @@ -48,6 +49,7 @@ internal interface DataSourceModule {
val memoryWarningDataSource: DataSourceState<MemoryWarningDataSource>
val networkStatusDataSource: DataSourceState<NetworkStatusDataSource>
val sigquitDataSource: DataSourceState<SigquitDataSource>
val rnActionDataSource: DataSourceState<RnActionDataSource>
}

internal class DataSourceModuleImpl(
Expand Down Expand Up @@ -211,6 +213,18 @@ internal class DataSourceModuleImpl(
)
}

override val rnActionDataSource: DataSourceState<RnActionDataSource> by dataSourceState {
DataSourceState(
factory = {
RnActionDataSource(
breadcrumbBehavior = configService.breadcrumbBehavior,
otelModule.spanService,
initModule.logger
)
}
)
}

private val configService = essentialServiceModule.configService

override fun getDataSources(): List<DataSourceState<*>> = values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ import io.embrace.android.embracesdk.fakes.FakeConfigService
import io.embrace.android.embracesdk.fakes.FakeProcessStateService
import io.embrace.android.embracesdk.fakes.FakeSpanService
import io.embrace.android.embracesdk.fakes.fakeBreadcrumbBehavior
import io.embrace.android.embracesdk.fakes.injection.fakeDataSourceModule
import io.embrace.android.embracesdk.fakes.system.mockActivity
import io.embrace.android.embracesdk.logging.EmbLoggerImpl
import io.embrace.android.embracesdk.session.EmbraceMemoryCleanerService
import io.embrace.android.embracesdk.session.lifecycle.ProcessStateService
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test

internal class EmbraceBreadcrumbServiceTest {

Expand Down Expand Up @@ -52,41 +49,6 @@ internal class EmbraceBreadcrumbServiceTest {
clock.tickSecond()
}

// TO DO: refactor BreadCrumbService to avoid accessing internal implementation
@Test
fun testCleanCollections() {
val service = initializeBreadcrumbService()
service.logRnAction("MyAction", 0, 5, mapOf("key" to "value"), 100, "success")

val breadcrumbs = service.getBreadcrumbs()
assertEquals(1, breadcrumbs.rnActionBreadcrumbs?.size)

service.cleanCollections()

val breadcrumbsAfterClean = service.getBreadcrumbs()
assertEquals(0, breadcrumbsAfterClean.rnActionBreadcrumbs?.size)
}

@Test
fun testLogRnAction() {
val service = initializeBreadcrumbService()
service.logRnAction("MyAction", 0, 5, mapOf("key" to "value"), 100, "success")

val crumbs = checkNotNull(service.getBreadcrumbs().rnActionBreadcrumbs)
val breadcrumb = checkNotNull(crumbs.single())
assertEquals("MyAction", breadcrumb.name)
assertEquals("success", breadcrumb.output)
assertEquals(100, breadcrumb.bytesSent)
assertEquals(mapOf("key" to "value"), breadcrumb.properties)
}

private fun initializeBreadcrumbService() = EmbraceBreadcrumbService(
clock,
configService,
{ fakeDataSourceModule() },
EmbLoggerImpl(),
)

companion object {
private const val MILLIS_FOR_2020_01_01 = 1577836800000L
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ internal class DataSourceModuleImplTest {
assertNotNull(module.memoryWarningDataSource)
assertNotNull(module.networkStatusDataSource)
assertNotNull(module.sigquitDataSource)
assertEquals(11, module.getDataSources().size)
assertNotNull(module.rnActionDataSource)
assertEquals(12, module.getDataSources().size)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
"sdc": "53",
"sdk": "__EMBRACE_TEST_IGNORE__"
},
"br": {
"rna": []
},
"br": {},
"d": "__EMBRACE_TEST_IGNORE__",
"p": {
"ds": {
Expand Down

0 comments on commit a1ef581

Please sign in to comment.