Skip to content

Commit c209a89

Browse files
authored
Merge pull request #2928 from DataDog/tvaleev/feature/RUM-12047-fix-missed-tags
[RUM-12047][FO]: fix missed tags
2 parents 5521696 + add9978 commit c209a89

File tree

5 files changed

+440
-1
lines changed

5 files changed

+440
-1
lines changed

detekt_custom_safe_calls.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ datadog:
845845
- "kotlin.collections.listOf(com.datadog.android.rum.model.LongTaskEvent.Interface)"
846846
- "kotlin.collections.listOf(com.datadog.android.rum.model.ResourceEvent.Interface)"
847847
- "kotlin.collections.listOf(com.datadog.android.rum.model.ViewEvent.Interface)"
848+
- "kotlin.collections.listOf(com.datadog.android.rum.model.VitalEvent.Interface)"
848849
- "kotlin.collections.listOf(com.datadog.android.sessionreplay.MapperTypeWrapper)"
849850
- "kotlin.collections.listOf(com.datadog.android.sessionreplay.internal.recorder.DefaultOptionSelectorDetector)"
850851
- "kotlin.collections.listOf(com.datadog.android.sessionreplay.material.internal.MaterialDrawableToColorMapper)"

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumEventExt.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,43 @@ internal fun NetworkInfo.toActionConnectivity(): ActionEvent.Connectivity {
350350
)
351351
}
352352

353+
internal fun NetworkInfo.toVitalConnectivity(): VitalEvent.Connectivity {
354+
val status = if (isConnected()) {
355+
VitalEvent.Status.CONNECTED
356+
} else {
357+
VitalEvent.Status.NOT_CONNECTED
358+
}
359+
val interfaces = when (connectivity) {
360+
NetworkInfo.Connectivity.NETWORK_ETHERNET -> listOf(VitalEvent.Interface.ETHERNET)
361+
NetworkInfo.Connectivity.NETWORK_WIFI -> listOf(VitalEvent.Interface.WIFI)
362+
NetworkInfo.Connectivity.NETWORK_WIMAX -> listOf(VitalEvent.Interface.WIMAX)
363+
NetworkInfo.Connectivity.NETWORK_BLUETOOTH -> listOf(VitalEvent.Interface.BLUETOOTH)
364+
NetworkInfo.Connectivity.NETWORK_2G,
365+
NetworkInfo.Connectivity.NETWORK_3G,
366+
NetworkInfo.Connectivity.NETWORK_4G,
367+
NetworkInfo.Connectivity.NETWORK_5G,
368+
NetworkInfo.Connectivity.NETWORK_MOBILE_OTHER,
369+
NetworkInfo.Connectivity.NETWORK_CELLULAR -> listOf(VitalEvent.Interface.CELLULAR)
370+
371+
NetworkInfo.Connectivity.NETWORK_OTHER -> listOf(VitalEvent.Interface.OTHER)
372+
NetworkInfo.Connectivity.NETWORK_NOT_CONNECTED -> emptyList()
373+
}
374+
375+
val cellular = if (cellularTechnology != null || carrierName != null) {
376+
VitalEvent.Cellular(
377+
technology = cellularTechnology,
378+
carrierName = carrierName
379+
)
380+
} else {
381+
null
382+
}
383+
return VitalEvent.Connectivity(
384+
status,
385+
interfaces,
386+
cellular = cellular
387+
)
388+
}
389+
353390
internal fun NetworkInfo.isConnected(): Boolean {
354391
return connectivity != NetworkInfo.Connectivity.NETWORK_NOT_CONNECTED
355392
}
@@ -418,6 +455,18 @@ internal fun DeviceType.toErrorSchemaType(): ErrorEvent.DeviceType {
418455
}
419456
}
420457

458+
internal fun DeviceType.toVitalSchemaType(): VitalEvent.DeviceType {
459+
return when (this) {
460+
DeviceType.MOBILE -> VitalEvent.DeviceType.MOBILE
461+
DeviceType.TABLET -> VitalEvent.DeviceType.TABLET
462+
DeviceType.TV -> VitalEvent.DeviceType.TV
463+
DeviceType.DESKTOP -> VitalEvent.DeviceType.DESKTOP
464+
DeviceType.GAMING_CONSOLE -> VitalEvent.DeviceType.GAMING_CONSOLE
465+
DeviceType.BOT -> VitalEvent.DeviceType.BOT
466+
DeviceType.OTHER -> VitalEvent.DeviceType.OTHER
467+
}
468+
}
469+
421470
// endregion
422471

423472
// region Source
@@ -507,6 +556,23 @@ internal fun ResourceEvent.ResourceEventSource.Companion.tryFromSource(
507556
}
508557
}
509558

559+
internal fun VitalEvent.VitalEventSource.Companion.tryFromSource(
560+
source: String,
561+
internalLogger: InternalLogger
562+
): VitalEvent.VitalEventSource? {
563+
return try {
564+
fromJson(source)
565+
} catch (e: NoSuchElementException) {
566+
internalLogger.log(
567+
InternalLogger.Level.ERROR,
568+
InternalLogger.Target.USER,
569+
{ UNKNOWN_SOURCE_WARNING_MESSAGE_FORMAT.format(Locale.US, source) },
570+
e
571+
)
572+
null
573+
}
574+
}
575+
510576
internal const val UNKNOWN_SOURCE_WARNING_MESSAGE_FORMAT = "You are using an unknown " +
511577
"source %s for your events"
512578

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ internal open class RumViewScope(
326326
syntheticsAttribute == null -> VitalEvent.VitalEventSessionType.USER
327327
else -> VitalEvent.VitalEventSessionType.SYNTHETICS
328328
}
329+
val batteryInfo = batteryInfoProvider.getState()
330+
val displayInfo = displayInfoProvider.getState()
331+
val user = datadogContext.userInfo
329332

330333
return VitalEvent(
331334
date = event.eventTime.timestamp + serverTimeOffsetInMs,
@@ -355,6 +358,49 @@ internal open class RumViewScope(
355358
name = rumContext.viewName,
356359
url = rumContext.viewUrl.orEmpty()
357360
),
361+
source = VitalEvent.VitalEventSource.tryFromSource(
362+
source = datadogContext.source,
363+
internalLogger = sdkCore.internalLogger
364+
),
365+
account = datadogContext.accountInfo?.let {
366+
VitalEvent.Account(
367+
id = it.id,
368+
name = it.name,
369+
additionalProperties = it.extraInfo.toMutableMap()
370+
)
371+
},
372+
usr = if (user.hasUserData()) {
373+
VitalEvent.Usr(
374+
id = user.id,
375+
name = user.name,
376+
email = user.email,
377+
anonymousId = user.anonymousId,
378+
additionalProperties = user.additionalProperties.toMutableMap()
379+
)
380+
} else {
381+
null
382+
},
383+
device = VitalEvent.Device(
384+
type = datadogContext.deviceInfo.deviceType.toVitalSchemaType(),
385+
name = datadogContext.deviceInfo.deviceName,
386+
model = datadogContext.deviceInfo.deviceModel,
387+
brand = datadogContext.deviceInfo.deviceBrand,
388+
architecture = datadogContext.deviceInfo.architecture,
389+
locales = datadogContext.deviceInfo.localeInfo.locales,
390+
timeZone = datadogContext.deviceInfo.localeInfo.timeZone,
391+
batteryLevel = batteryInfo.batteryLevel,
392+
powerSavingMode = batteryInfo.lowPowerMode,
393+
brightnessLevel = displayInfo.screenBrightness
394+
),
395+
os = VitalEvent.Os(
396+
name = datadogContext.deviceInfo.osName,
397+
version = datadogContext.deviceInfo.osVersion,
398+
versionMajor = datadogContext.deviceInfo.osMajorVersion
399+
),
400+
connectivity = datadogContext.networkInfo.toVitalConnectivity(),
401+
version = datadogContext.version,
402+
service = datadogContext.service,
403+
ddtags = buildDDTagsString(datadogContext),
358404
vital = VitalEvent.VitalEventVital(
359405
id = UUID.randomUUID().toString(),
360406
name = name,

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalEventAssert.kt

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
*/
66
package com.datadog.android.rum.assertj
77

8+
import com.datadog.android.api.context.AccountInfo
9+
import com.datadog.android.api.context.NetworkInfo
10+
import com.datadog.android.api.context.UserInfo
811
import com.datadog.android.rum.featureoperations.FailureReason
912
import com.datadog.android.rum.internal.domain.scope.RumSessionScope
13+
import com.datadog.android.rum.internal.domain.scope.isConnected
1014
import com.datadog.android.rum.internal.domain.scope.toSchemaFailureReason
1115
import com.datadog.android.rum.internal.domain.scope.toVitalSessionPrecondition
1216
import com.datadog.android.rum.model.VitalEvent
@@ -180,6 +184,207 @@ internal class VitalEventAssert(actual: VitalEvent) : AbstractObjectAssert<Vital
180184
).isEqualTo(resultId)
181185
}
182186

187+
fun hasSource(source: VitalEvent.VitalEventSource?) = apply {
188+
assertThat(actual.source)
189+
.overridingErrorMessage(
190+
"Expected event to have a source %s" +
191+
" instead it was %s",
192+
actual.source ?: "null",
193+
source ?: "null"
194+
)
195+
.isEqualTo(source)
196+
return this
197+
}
198+
199+
fun hasAccountInfo(expected: AccountInfo?) = apply {
200+
assertThat(actual.account?.id)
201+
.overridingErrorMessage(
202+
"Expected RUM event to have account.id ${expected?.id} " +
203+
"but was ${actual.account?.id}"
204+
)
205+
.isEqualTo(expected?.id)
206+
assertThat(actual.account?.name)
207+
.overridingErrorMessage(
208+
"Expected RUM event to have account.name ${expected?.name} " +
209+
"but was ${actual.account?.name}"
210+
)
211+
.isEqualTo(expected?.name)
212+
assertThat(actual.account?.additionalProperties)
213+
.overridingErrorMessage(
214+
"Expected event to have account additional " +
215+
"properties ${expected?.extraInfo} " +
216+
"but was ${actual.account?.additionalProperties}"
217+
)
218+
.containsExactlyInAnyOrderEntriesOf(expected?.extraInfo)
219+
}
220+
221+
fun hasUserInfo(expected: UserInfo?) = apply {
222+
assertThat(actual.usr?.id)
223+
.overridingErrorMessage(
224+
"Expected event to have usr.id ${expected?.id} " +
225+
"but was ${actual.usr?.id}"
226+
)
227+
.isEqualTo(expected?.id)
228+
assertThat(actual.usr?.name)
229+
.overridingErrorMessage(
230+
"Expected event to have usr.name ${expected?.name} " +
231+
"but was ${actual.usr?.name}"
232+
)
233+
.isEqualTo(expected?.name)
234+
assertThat(actual.usr?.email)
235+
.overridingErrorMessage(
236+
"Expected event to have usr.email ${expected?.email} " +
237+
"but was ${actual.usr?.email}"
238+
)
239+
.isEqualTo(expected?.email)
240+
assertThat(actual.usr?.additionalProperties)
241+
.overridingErrorMessage(
242+
"Expected event to have user additional" +
243+
" properties ${expected?.additionalProperties} " +
244+
"but was ${actual.usr?.additionalProperties}"
245+
)
246+
.containsExactlyInAnyOrderEntriesOf(expected?.additionalProperties)
247+
}
248+
249+
fun hasDeviceInfo(
250+
name: String,
251+
model: String,
252+
brand: String,
253+
type: VitalEvent.DeviceType,
254+
architecture: String
255+
) = apply {
256+
assertThat(actual.device?.name)
257+
.overridingErrorMessage(
258+
"Expected event data to have device.name $name but was ${actual.device?.name}"
259+
)
260+
.isEqualTo(name)
261+
assertThat(actual.device?.model)
262+
.overridingErrorMessage(
263+
"Expected event data to have device.model $model but was ${actual.device?.model}"
264+
)
265+
.isEqualTo(model)
266+
assertThat(actual.device?.brand)
267+
.overridingErrorMessage(
268+
"Expected event data to have device.brand $brand but was ${actual.device?.brand}"
269+
)
270+
.isEqualTo(brand)
271+
assertThat(actual.device?.type)
272+
.overridingErrorMessage(
273+
"Expected event data to have device.type $type but was ${actual.device?.type}"
274+
)
275+
.isEqualTo(type)
276+
assertThat(actual.device?.architecture)
277+
.overridingErrorMessage(
278+
"Expected event data to have device.architecture $architecture" +
279+
" but was ${actual.device?.architecture}"
280+
)
281+
.isEqualTo(architecture)
282+
}
283+
284+
fun hasOsInfo(
285+
name: String,
286+
version: String,
287+
versionMajor: String
288+
) = apply {
289+
assertThat(actual.os?.name)
290+
.overridingErrorMessage(
291+
"Expected event data to have os.name $name but was ${actual.os?.name}"
292+
)
293+
.isEqualTo(name)
294+
assertThat(actual.os?.version)
295+
.overridingErrorMessage(
296+
"Expected event data to have os.version $version but was ${actual.os?.version}"
297+
)
298+
.isEqualTo(version)
299+
assertThat(actual.os?.versionMajor)
300+
.overridingErrorMessage(
301+
"Expected event data to have os.version_major $versionMajor" +
302+
" but was ${actual.os?.versionMajor}"
303+
)
304+
.isEqualTo(versionMajor)
305+
}
306+
307+
fun hasConnectivityInfo(expected: NetworkInfo?) = apply {
308+
val expectedStatus = if (expected?.isConnected() == true) {
309+
VitalEvent.Status.CONNECTED
310+
} else {
311+
VitalEvent.Status.NOT_CONNECTED
312+
}
313+
val expectedInterfaces = when (expected?.connectivity) {
314+
NetworkInfo.Connectivity.NETWORK_ETHERNET -> listOf(VitalEvent.Interface.ETHERNET)
315+
NetworkInfo.Connectivity.NETWORK_WIFI -> listOf(VitalEvent.Interface.WIFI)
316+
NetworkInfo.Connectivity.NETWORK_WIMAX -> listOf(VitalEvent.Interface.WIMAX)
317+
NetworkInfo.Connectivity.NETWORK_BLUETOOTH -> listOf(VitalEvent.Interface.BLUETOOTH)
318+
NetworkInfo.Connectivity.NETWORK_2G,
319+
NetworkInfo.Connectivity.NETWORK_3G,
320+
NetworkInfo.Connectivity.NETWORK_4G,
321+
NetworkInfo.Connectivity.NETWORK_5G,
322+
NetworkInfo.Connectivity.NETWORK_MOBILE_OTHER,
323+
NetworkInfo.Connectivity.NETWORK_CELLULAR -> listOf(VitalEvent.Interface.CELLULAR)
324+
325+
NetworkInfo.Connectivity.NETWORK_OTHER -> listOf(VitalEvent.Interface.OTHER)
326+
NetworkInfo.Connectivity.NETWORK_NOT_CONNECTED -> emptyList()
327+
null -> null
328+
}
329+
330+
assertThat(actual.connectivity?.status)
331+
.overridingErrorMessage(
332+
"Expected RUM event to have connectivity.status $expectedStatus " +
333+
"but was ${actual.connectivity?.status}"
334+
)
335+
.isEqualTo(expectedStatus)
336+
337+
assertThat(actual.connectivity?.cellular?.technology)
338+
.overridingErrorMessage(
339+
"Expected RUM event to connectivity usr.cellular.technology " +
340+
"${expected?.cellularTechnology} " +
341+
"but was ${actual.connectivity?.cellular?.technology}"
342+
)
343+
.isEqualTo(expected?.cellularTechnology)
344+
345+
assertThat(actual.connectivity?.cellular?.carrierName)
346+
.overridingErrorMessage(
347+
"Expected RUM event to connectivity usr.cellular.carrierName " +
348+
"${expected?.carrierName} " +
349+
"but was ${actual.connectivity?.cellular?.carrierName}"
350+
)
351+
.isEqualTo(expected?.carrierName)
352+
353+
assertThat(actual.connectivity?.interfaces)
354+
.overridingErrorMessage(
355+
"Expected RUM event to have connectivity.interfaces $expectedInterfaces " +
356+
"but was ${actual.connectivity?.interfaces}"
357+
)
358+
.isEqualTo(expectedInterfaces)
359+
}
360+
361+
fun hasVersion(version: String?) = apply {
362+
assertThat(actual.version)
363+
.overridingErrorMessage(
364+
"Expected RUM event to have version: $version" +
365+
" but instead was: ${actual.version}"
366+
)
367+
.isEqualTo(version)
368+
}
369+
370+
fun hasServiceName(serviceName: String?) = apply {
371+
assertThat(actual.service)
372+
.overridingErrorMessage(
373+
"Expected RUM event to have serviceName: $serviceName" +
374+
" but instead was: ${actual.service}"
375+
)
376+
.isEqualTo(serviceName)
377+
}
378+
379+
fun hasDDTags(ddTags: String) = apply {
380+
assertThat(actual.ddtags)
381+
.overridingErrorMessage(
382+
"Expected RUM event to have ddTags: $ddTags" +
383+
" but instead was: ${actual.ddtags}"
384+
)
385+
.isEqualTo(ddTags)
386+
}
387+
183388
companion object {
184389
internal fun assertThat(actual: VitalEvent): VitalEventAssert = VitalEventAssert(actual)
185390
}

0 commit comments

Comments
 (0)