Skip to content
Draft
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: 1 addition & 1 deletion benchmarks/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2155,7 +2155,7 @@ SPEC CHECKSUMS:
KSCrash: 8c4464fd5da7de520f2ce4a00fdf63f169a80f18
OpenTelemetry-Swift-Api: 3be9043f6288eb4ac3cbe7548b5bd45fb4ed5849
OpenTelemetry-Swift-Sdk: d9fa7bc839350f5a81467a5e877f727a8a77712d
RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809
RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82
RCTDeprecation: be794de7dc6ed8f9f7fbf525f86e7651b8b68746
RCTRequired: a83787b092ec554c2eb6019ff3f5b8d125472b3b
RCTTypeSafety: 48ad3c858926b1c46f46a81a58822b476e178e2c
Expand Down
4 changes: 4 additions & 0 deletions example-new-architecture/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ import {APPLICATION_ID, CLIENT_TOKEN, ENVIRONMENT} from './ddCredentials';

const spanId = await DdTrace.startSpan('test span');
await DdTrace.finishSpan(spanId);

setTimeout(async () => {
await DdRum.reportAppFullyDisplayed();
}, 5000);
})();

function AppWithProviders() {
Expand Down
7 changes: 5 additions & 2 deletions example-new-architecture/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,13 @@ dependencies {
}

constraints {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.4.10") {
// Pinned to the project's own Kotlin version: these shims pull the real
// kotlin-stdlib up with them, and a stdlib newer than kotlinVersion above fails
// compilation with "incompatible version of Kotlin".
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$rootProject.ext.kotlinVersion") {
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
}
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.4.10") {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$rootProject.ext.kotlinVersion") {
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
}
}
Expand Down
2 changes: 1 addition & 1 deletion example-new-architecture/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1883,7 +1883,7 @@ SPEC CHECKSUMS:
hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11
KSCrash: 8c4464fd5da7de520f2ce4a00fdf63f169a80f18
OpenTelemetry-Swift-Api: 3be9043f6288eb4ac3cbe7548b5bd45fb4ed5849
RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1
RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17
RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83
RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716
RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f
Expand Down
7 changes: 5 additions & 2 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,13 @@ dependencies {
}

constraints {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.4.10") {
// Pinned to the project's own Kotlin version: these shims pull the real
// kotlin-stdlib up with them, and a stdlib newer than kotlinVersion above fails
// compilation with "incompatible version of Kotlin".
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$rootProject.ext.kotlinVersion") {
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
}
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.4.10") {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$rootProject.ext.kotlinVersion") {
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
}
}
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1987,7 +1987,7 @@ SPEC CHECKSUMS:
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
KSCrash: 8c4464fd5da7de520f2ce4a00fdf63f169a80f18
OpenTelemetry-Swift-Api: 3be9043f6288eb4ac3cbe7548b5bd45fb4ed5849
RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1
RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17
RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83
RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716
RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f
Expand Down
6 changes: 5 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import TraceScreen from './screens/TraceScreen';
import style from './screens/styles';
import { navigationRef } from './NavigationRoot';
import { DdRumReactNavigationTracking, NavigationTrackingOptions, ParamsTrackingPredicate, ViewNamePredicate, ViewTrackingPredicate } from '@datadog/mobile-react-navigation';
import { DatadogProvider, TrackingConsent, DdFlags, DdLogs, DdSdkReactNative } from '@datadog/mobile-react-native'
import { DdSdkReactNative, DatadogProvider, TrackingConsent, DdLogs, DdRum, DdFlags } from '@datadog/mobile-react-native'
import { OpenFeatureProvider } from '@openfeature/react-sdk';
import {
ImagePrivacyLevel,
Expand Down Expand Up @@ -96,6 +96,10 @@ const handleDatadogInitialization = async () => {
// ConfigurationWire, no network); the "Flags source" switch on the Home screen flips to
// the online provider (CDN) at runtime.
await setFlagsProvider('offline');

setTimeout(async () => {
await DdRum.reportAppFullyDisplayed();
}, 5000);
}

export default function App() {
Expand Down
5 changes: 5 additions & 0 deletions example/src/ddUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
DatadogProviderConfiguration,
DdLogs,
DdRum,
DdSdkReactNative,
CoreConfiguration,
SdkVerbosity,
Expand Down Expand Up @@ -87,6 +88,10 @@ export function initializeDatadog(trackingConsent: TrackingConsent) {
DdLogs.info('The RN Sdk was properly initialized')
DdSdkReactNative.setUserInfo({id: "1337", name: "Xavier", email: "xg@example.com", extraInfo: { type: "premium" } })
DdSdkReactNative.addAttributes({campaign: "ad-network"})

setTimeout(async () => {
await DdRum.reportAppFullyDisplayed();
}, 5000);
});

// Enable the Flags feature.
Expand Down
3 changes: 3 additions & 0 deletions packages/core/__mocks__/react-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ actualRN.NativeModules.DdRum = {
addViewLoadingTime: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['addViewLoadingTime']>,
reportAppFullyDisplayed: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['reportAppFullyDisplayed']>,
stopSession: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['stopSession']>,
Expand Down
18 changes: 10 additions & 8 deletions packages/core/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ dependencies {
// This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0.
// To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0
if (reactNativeMinorVersion < 76) {
implementation("com.datadoghq:dd-sdk-android-rum:3.12.1") {
implementation("com.datadoghq:dd-sdk-android-rum:3.13.1") {
exclude group: "androidx.metrics", module: "metrics-performance"
}
implementation "androidx.metrics:metrics-performance:1.0.0-beta01"
Expand All @@ -233,14 +233,16 @@ dependencies {
}
}
} else {
implementation "com.datadoghq:dd-sdk-android-rum:3.12.1"
implementation "com.datadoghq:dd-sdk-android-rum:3.13.1"
}
implementation "com.datadoghq:dd-sdk-android-logs:3.12.1"
implementation "com.datadoghq:dd-sdk-android-trace:3.12.1"
implementation "com.datadoghq:dd-sdk-android-webview:3.12.1"
implementation "com.datadoghq:dd-sdk-android-ndk:3.12.1"
implementation "com.datadoghq:dd-sdk-android-flags:3.12.1"
implementation "com.datadoghq:dd-sdk-android-internal:3.12.1"
implementation "com.datadoghq:dd-sdk-android-logs:3.13.1"
implementation "com.datadoghq:dd-sdk-android-trace:3.13.1"
implementation "com.datadoghq:dd-sdk-android-webview:3.13.1"
implementation "com.datadoghq:dd-sdk-android-ndk:3.13.1"
implementation "com.datadoghq:dd-sdk-android-flags:3.13.1"
implementation "com.datadoghq:dd-sdk-android-internal:3.13.1"
// TO DO: update once dd-sdk-android includes the prelaunch module
implementation "com.datadoghq:dd-sdk-android-rum-prelaunch:3.14.0"
implementation "com.google.code.gson:gson:2.11.0"
testImplementation "org.junit.platform:junit-platform-launcher:1.6.2"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ private fun buildEvaluationContext(
return EvaluationContext(targetingKey, parsed)
}

private const val SERIAL_ID_KEY = "serialId"

private fun convertUnparsedFlagToMap(
flagKey: String,
flag: UnparsedFlag,
Expand Down Expand Up @@ -222,6 +224,8 @@ private fun convertUnparsedFlagToMap(
"reason" to flag.reason,
"doLog" to flag.doLog,
"extraLogging" to flag.extraLogging.toMap(),
// Serialized as a String because the React Native bridge converts Long values to Double
SERIAL_ID_KEY to flag.serialId?.toString()
)
}

Expand All @@ -237,4 +241,7 @@ private fun convertMapToUnparsedFlag(map: Map<String, Any>): UnparsedFlag =
(map["extraLogging"] as? Map<String, Any>)?.toJSONObject()
?: JSONObject()
override val reason: String = map["reason"] as? String ?: ""
override val serialId: Long? =
(map[SERIAL_ID_KEY] as? String)?.toLongOrNull()
?: (map[SERIAL_ID_KEY] as? Number)?.toLong()
}
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,16 @@ class DdRumImplementation internal constructor(
promise.resolve(null)
}

/**
* This method can be used to mark the moment in time when the UI of the app is considered fully displayed.
* The duration between the application launch and this moment of time will be shown as TTFD (time to full display)
* in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session.
*/
fun reportAppFullyDisplayed(promise: Promise) {
datadog.getRumMonitor().reportAppFullyDisplayed()
promise.resolve(null)
}

/**
* Stops the current RUM Session.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ class DdRum(
implementation.addViewLoadingTime(overwrite, promise)
}

/**
* This method can be used to mark the moment in time when the UI of the app is considered fully displayed.
* The duration between the application launch and this moment of time will be shown as TTFD (time to full display)
* in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session.
*/
@ReactMethod
override fun reportAppFullyDisplayed(promise: Promise) {
implementation.reportAppFullyDisplayed(promise)
}

/**
* Stops the current RUM Session.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,16 @@ class DdRum(
implementation.addViewLoadingTime(overwrite, promise)
}

/**
* This method can be used to mark the moment in time when the UI of the app is considered fully displayed.
* The duration between the application launch and this moment of time will be shown as TTFD (time to full display)
* in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session.
*/
@ReactMethod
fun reportAppFullyDisplayed(promise: Promise) {
implementation.reportAppFullyDisplayed(promise)
}

/**
* Stops the current RUM Session.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,15 @@ internal class DdRumTest {
verify(mockRumMonitor).addViewLoadingTime(overwrite)
}

@Test
fun `M call reportAppFullyDisplayed w reportAppFullyDisplayed()`() {
// When
testedDdRum.reportAppFullyDisplayed(mockPromise)

// Then
verify(mockRumMonitor).reportAppFullyDisplayed()
}

@Test
fun `M call stopSession W stopSession()`() {
// When
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class MockRumMonitor : RumMonitor {
@ExperimentalRumApi
override fun addViewLoadingTime(overwrite: Boolean) {}

@ExperimentalRumApi
override fun reportAppFullyDisplayed() {}

override fun getCurrentSessionId(callback: (String?) -> Unit) {}

override fun addViewAttributes(attributes: Map<String, Any?>) {}
Expand Down Expand Up @@ -172,7 +175,4 @@ class MockRumMonitor : RumMonitor {
failureReason: OperationFailureReason,
attributes: Map<String, Any?>
) {}

@ExperimentalRumApi
override fun reportAppFullyDisplayed() {}
}
14 changes: 13 additions & 1 deletion packages/core/ios/Sources/DdRum.mm
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,15 @@ @implementation DdRum
[self addViewLoadingTime:overwrite resolve:resolve reject:reject];
}

RCT_REMAP_METHOD(stopSession, withResolve:(RCTPromiseResolveBlock)resolve
RCT_EXPORT_METHOD(reportAppFullyDisplayed:
(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self reportAppFullyDisplayed: resolve reject:reject];
}

RCT_EXPORT_METHOD(stopSession:
(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self stopSession:resolve reject:reject];
Expand Down Expand Up @@ -262,6 +270,10 @@ - (void)addViewLoadingTime:(BOOL)overwrite resolve:(RCTPromiseResolveBlock)resol
[self.ddRumImplementation addViewLoadingTimeWithOverwrite:overwrite resolve:resolve reject:reject];
}

- (void)reportAppFullyDisplayed:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddRumImplementation reportAppFullyDisplayedWithResolve:resolve reject: reject];
}

- (void)startAction:(NSString *)type name:(NSString *)name context:(NSDictionary *)context timestampMs:(double)timestampMs resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddRumImplementation startActionWithType:type name:name context:context timestampMs:timestampMs resolve:resolve reject:reject];
}
Expand Down
6 changes: 6 additions & 0 deletions packages/core/ios/Sources/DdRumImplementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ public class DdRumImplementation: NSObject {
resolve(nil)
}

@objc
public func reportAppFullyDisplayed(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
nativeRUM.reportAppFullyDisplayed()
resolve(nil)
}

@objc
public func stopSession(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {
nativeRUM.stopSession()
Expand Down
8 changes: 8 additions & 0 deletions packages/core/ios/Tests/DdRumTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,14 @@ internal class DdRumTests: XCTestCase {
XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 0)
}

func testReportAppFullyDisplayed() throws {
rum.reportAppFullyDisplayed(resolve: mockResolve, reject: mockReject)

XCTAssertEqual(mockNativeRUM.calledMethods.count, 1)
XCTAssertEqual(mockNativeRUM.calledMethods.last, .reportAppFullyDisplayed())
XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 0)
}

func testStopSession() throws {
rum.stopSession(resolve: mockResolve, reject: mockReject)

Expand Down
8 changes: 4 additions & 4 deletions packages/core/ios/Tests/MockRUMMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@
@testable import DatadogSDKReactNative

internal class MockRUMMonitor: RUMMonitorProtocol {
func reportAppFullyDisplayed() {
// not implemented
}

func currentSessionID(completion: @escaping (String?) -> Void) {
// not implemented
}
Expand Down Expand Up @@ -69,6 +65,7 @@ internal class MockRUMMonitor: RUMMonitorProtocol {
case addViewAttributes(_: Int? = nil) // We need an attribute for the case to be Equatable
case removeViewAttributes(keys: [String])
case addViewLoadingTime(overwrite: Bool)
case reportAppFullyDisplayed(_: Int? = nil) // We need an attribute for the case to be Equatable
case stopSession(_: Int? = nil) // We need an attribute for the case to be Equatable
case addResourceMetrics(resourceKey: String,
fetch: Interval,
Expand Down Expand Up @@ -151,6 +148,9 @@ internal class MockRUMMonitor: RUMMonitorProtocol {
func addViewLoadingTime(overwrite: Bool) {
calledMethods.append(.addViewLoadingTime(overwrite: overwrite))
}
func reportAppFullyDisplayed() {
calledMethods.append(.reportAppFullyDisplayed())
}
func stopSession() {
calledMethods.append(.stopSession())
}
Expand Down
3 changes: 3 additions & 0 deletions packages/core/jest/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ module.exports = {
addViewLoadingTime: jest
.fn()
.mockImplementation(() => new Promise(resolve => resolve())),
reportAppFullyDisplayed: jest
.fn()
.mockImplementation(() => new Promise(resolve => resolve())),
addFeatureFlagEvaluation: jest
.fn()
.mockImplementation(() => new Promise(resolve => resolve())),
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/rum/DdRum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,16 @@ class DdRumWrapper implements DdRumType {
);
};

reportAppFullyDisplayed = (): Promise<void> => {
InternalLog.log(
'Reporting App Fully Displayed (TTFD)',
SdkVerbosity.DEBUG
);
return bufferVoidNativeCall(() =>
this.nativeRum.reportAppFullyDisplayed()
);
};

stopSession = (): Promise<void> => {
InternalLog.log('Stopping RUM Session', SdkVerbosity.DEBUG);
clearCachedSessionId();
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/rum/__tests__/DdRum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1884,6 +1884,16 @@ describe('DdRum', () => {
});
});

describe('DdRum.reportAppFullyDisplayed', () => {
it('calls the native API', async () => {
await DdRum.reportAppFullyDisplayed();

expect(
NativeModules.DdRum.reportAppFullyDisplayed
).toHaveBeenCalledTimes(1);
});
});

describe('PropagatorTypes', () => {
it('matches with the native name of propagators', () => {
/**
Expand Down
Loading