Skip to content
Merged
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
11 changes: 10 additions & 1 deletion packages/codepush/__mocks__/react-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,16 @@ actualRN.NativeModules.DdRum = {
new Promise<string | undefined>(resolve =>
resolve('test-session-id')
)
) as jest.MockedFunction<DdRumType['getCurrentSessionId']>
) as jest.MockedFunction<DdRumType['getCurrentSessionId']>,
startFeatureOperation: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['startFeatureOperation']>,
succeedFeatureOperation: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['startFeatureOperation']>,
failFeatureOperation: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['failFeatureOperation']>
};

module.exports = actualRN;
11 changes: 10 additions & 1 deletion packages/core/__mocks__/react-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,16 @@ actualRN.NativeModules.DdRum = {
new Promise<string | undefined>(resolve =>
resolve('test-session-id')
)
) as jest.MockedFunction<DdRumType['getCurrentSessionId']>
) as jest.MockedFunction<DdRumType['getCurrentSessionId']>,
startFeatureOperation: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['startFeatureOperation']>,
succeedFeatureOperation: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['startFeatureOperation']>,
failFeatureOperation: jest.fn().mockImplementation(
() => new Promise<void>(resolve => resolve())
) as jest.MockedFunction<DdRumType['failFeatureOperation']>
};

module.exports = actualRN;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.datadog.android.rum.RumAttributes
import com.datadog.android.rum.RumErrorSource
import com.datadog.android.rum.RumResourceKind
import com.datadog.android.rum.RumResourceMethod
import com.datadog.android.rum.featureoperations.FailureReason
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
Expand Down Expand Up @@ -333,6 +334,59 @@ class DdRumImplementation(private val datadog: DatadogWrapper = DatadogSDKWrappe
}
}

/**
* Starts a Feature Operation.
*
* @param name Human-readable operation name (e.g., "login_flow").
* @param operationKey Optional key that uniquely identifies this operation instance.
* @param attributes Additional attributes to attach to the operation.
* @param promise Resolved with `null` when the call completes.
*/
fun startFeatureOperation(name: String, operationKey: String? = null, attributes: ReadableMap, promise: Promise) {
Comment thread
cdn34dd marked this conversation as resolved.
val attributesMap = attributes.toHashMap().toMutableMap()
datadog.getRumMonitor().startFeatureOperation(name, operationKey, attributesMap);
promise.resolve(null)
}

/**
* Marks a Feature Operation as successfully completed.
*
* @param name The name of the feature operation (for example, `"login_flow"`).
* @param operationKey The key of the operation instance to complete, if one was provided when starting it.
* @param attributes A map of custom attributes to attach to this completion event.
*/
fun succeedFeatureOperation(name: String, operationKey: String? = null, attributes: ReadableMap, promise: Promise) {
val attributesMap = attributes.toHashMap().toMutableMap()
datadog.getRumMonitor().succeedFeatureOperation(name, operationKey, attributesMap)
promise.resolve(null)
}


/**
* Marks a Feature Operation as failed.
*
* @param name The name of the feature operation (for example, `"login_flow"`).
* @param operationKey The key of the operation instance to fail, if one was provided when starting it.
* @param failureReason The reason for the failure. Possible values are defined in [FailureReason]
* (e.g., `FailureReason.ERROR`, `FailureReason.ABANDONED`, `FailureReason.OTHER`).
* @param attributes A map of custom attributes to attach to this failure event.
*/
fun failFeatureOperation(
name: String,
operationKey: String? = null,
failureReason: String,
attributes: ReadableMap,
promise: Promise
) {
val attributesMap = attributes.toHashMap().toMutableMap()
val reason = runCatching {
enumValueOf<FailureReason>(failureReason.uppercase())
}.getOrDefault(FailureReason.OTHER)

datadog.getRumMonitor().failFeatureOperation(name, operationKey, reason, attributesMap)
promise.resolve(null)
}

// region Internal

private fun String.asRumActionType(): RumActionType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package com.datadog.reactnative

import com.datadog.android.rum.featureoperations.FailureReason
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
Expand Down Expand Up @@ -52,7 +53,12 @@ class DdRum(
* If not provided, current timestamp will be used.
*/
@ReactMethod
override fun stopView(key: String, context: ReadableMap, timestampMs: Double, promise: Promise) {
override fun stopView(
key: String,
context: ReadableMap,
timestampMs: Double,
promise: Promise
) {
implementation.stopView(key, context, timestampMs, promise)
}

Expand Down Expand Up @@ -276,4 +282,59 @@ class DdRum(
override fun getCurrentSessionId(promise: Promise) {
implementation.getCurrentSessionId(promise)
}

/**
* Starts a RUM Feature Operation.
*
* @param name Human-readable operation name (e.g., "login_flow").
* @param operationKey Optional key that uniquely identifies this operation instance.
* @param attributes Additional attributes to attach to the operation.
* @param promise Resolved with `null` when the call completes.
*/
@ReactMethod
override fun startFeatureOperation(
name: String,
operationKey: String?,
attributes: ReadableMap,
promise: Promise
) {
implementation.startFeatureOperation(name, operationKey, attributes, promise)
}

/**
* Marks a Feature Operation as successfully completed.
*
* @param name The name of the feature operation (for example, `"login_flow"`).
* @param operationKey The key of the operation instance to complete, if one was provided when starting it.
* @param attributes A map of custom attributes to attach to this completion event.
*/
@ReactMethod
override fun succeedFeatureOperation(
name: String,
operationKey: String?,
attributes: ReadableMap,
promise: Promise
) {
implementation.succeedFeatureOperation(name, operationKey, attributes, promise)
}

/**
* Marks a Feature Operation as failed.
*
* @param name The name of the feature operation (for example, `"login_flow"`).
* @param operationKey The key of the operation instance to fail, if one was provided when starting it.
* @param failureReason The reason for the failure. Possible values are defined in [FailureReason]
* (e.g., `FailureReason.ERROR`, `FailureReason.ABANDONED`, `FailureReason.OTHER`).
* @param attributes A map of custom attributes to attach to this failure event.
*/
@ReactMethod
override fun failFeatureOperation(
name: String,
operationKey: String?,
failureReason: String,
attributes: ReadableMap,
promise: Promise
) {
implementation.failFeatureOperation(name, operationKey, failureReason, attributes, promise)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package com.datadog.reactnative

import com.datadog.android.rum.featureoperations.FailureReason
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
Expand Down Expand Up @@ -266,4 +267,59 @@ class DdRum(
fun getCurrentSessionId(promise: Promise) {
implementation.getCurrentSessionId(promise)
}

/**
* Starts a RUM Feature Operation.
*
* @param name Human-readable operation name (e.g., "login_flow").
* @param operationKey Optional key that uniquely identifies this operation instance.
* @param attributes Additional attributes to attach to the operation.
* @param promise Resolved with `null` when the call completes.
*/
@ReactMethod
fun startFeatureOperation(
name: String,
operationKey: String? = null,
attributes: ReadableMap,
promise: Promise
) {
implementation.startFeatureOperation(name, operationKey, attributes, promise)
}

/**
* Marks a Feature Operation as successfully completed.
*
* @param name The name of the feature operation (for example, "login_flow").
* @param operationKey The key of the operation instance to complete, if one was provided.
* @param attributes A map of custom attributes to attach to this completion event.
*/
@ReactMethod
fun succeedFeatureOperation(
name: String,
operationKey: String? = null,
attributes: ReadableMap,
promise: Promise
) {
implementation.succeedFeatureOperation(name, operationKey, attributes, promise)
}

/**
* Marks a Feature Operation as failed.
*
* @param name The name of the feature operation (for example, "login_flow").
* @param operationKey The key of the operation instance to fail, if one was provided.
* @param failureReason The reason for the failure. Values are defined in [FailureReason]
* (e.g., `FailureReason.ERROR`, `FailureReason.ABANDONED`, `FailureReason.OTHER`).
* @param attributes A map of custom attributes to attach to this failure event.
*/
@ReactMethod
fun failFeatureOperation(
name: String,
operationKey: String? = null,
failureReason: String,
attributes: ReadableMap,
promise: Promise
) {
implementation.failFeatureOperation(name, operationKey, failureReason, attributes, promise)
}
}
43 changes: 43 additions & 0 deletions packages/core/ios/Sources/DdRum.mm
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,37 @@ @implementation DdRum
[self getCurrentSessionId:resolve reject:reject];
}

RCT_REMAP_METHOD(startFeatureOperation,
startWithName:(NSString*)name
withOperationKey:(NSString*)operationKey
withAttributes:(NSDictionary*)attributes
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self startFeatureOperation:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject];
}

RCT_REMAP_METHOD(succeedFeatureOperation,
succeedWithName:(NSString*)name
withOperationKey:(NSString*)operationKey
withAttributes:(NSDictionary*)attributes
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self succeedFeatureOperation:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject];
}

RCT_REMAP_METHOD(failFeatureOperation,
failWithName:(NSString*)name
withOperationKey:(NSString*)operationKey
withReason:(NSString*)reason
withAttributes:(NSDictionary*)attributes
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self failFeatureOperation:name operationKey:operationKey reason:reason attributes:attributes resolve:resolve reject:reject];
}

// Thanks to this guard, we won't compile this code when we build for the old architecture.
#ifdef RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
Expand Down Expand Up @@ -257,4 +288,16 @@ - (void)stopView:(NSString *)key context:(NSDictionary *)context timestampMs:(do
[self.ddRumImplementation stopViewWithKey:key context:context timestampMs:timestampMs resolve:resolve reject:reject];
}

- (void) startFeatureOperation:(NSString *)name operationKey:(NSString *)operationKey attributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddRumImplementation startFeatureOperationWithName:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject];
}

- (void) succeedFeatureOperation:(NSString *)name operationKey:(NSString *)operationKey attributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddRumImplementation succeedFeatureOperationWithName:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject];
}

- (void) failFeatureOperation:(NSString *)name operationKey:(NSString *)operationKey reason:(NSString *)reason attributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddRumImplementation failFeatureOperationWithName:name operationKey:operationKey reason:reason attributes:attributes resolve:resolve reject:reject];
}

@end
51 changes: 51 additions & 0 deletions packages/core/ios/Sources/DdRumImplementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ private extension RUMMethod {
}
}

internal extension RUMFeatureOperationFailureReason {
init(from string: String) {
switch string.lowercased() {
case "error": self = .error
case "abandoned": self = .abandoned
default: self = .other
}
}
}

@objc
public class DdRumImplementation: NSObject {
internal static let timestampKey = "_dd.timestamp"
Expand Down Expand Up @@ -236,6 +246,47 @@ public class DdRumImplementation: NSObject {
resolve(sessionId)
}
}

@objc
public func startFeatureOperation(
name: String,
operationKey: String?,
attributes: NSDictionary,
resolve: @escaping (Any?) -> Void,
reject: RCTPromiseRejectBlock
){
let castedAttributes = castAttributesToSwift(attributes)
nativeRUM.startFeatureOperation(name: name, operationKey: operationKey, attributes: castedAttributes)
resolve(nil)
}

@objc
public func succeedFeatureOperation(
name: String,
operationKey: String?,
attributes: NSDictionary,
resolve: @escaping (Any?) -> Void,
reject: RCTPromiseRejectBlock
){
let castedAttributes = castAttributesToSwift(attributes)
nativeRUM.succeedFeatureOperation(name: name, operationKey: operationKey, attributes: castedAttributes)
resolve(nil)
}

@objc
public func failFeatureOperation(
name: String,
operationKey: String?,
reason: String,
attributes: NSDictionary,
resolve: @escaping (Any?) -> Void,
reject: RCTPromiseRejectBlock
){
let castedAttributes = castAttributesToSwift(attributes)
nativeRUM.failFeatureOperation(name: name, operationKey: operationKey,
reason: RUMFeatureOperationFailureReason(from: reason), attributes: castedAttributes)
resolve(nil)
}

// MARK: - Private methods

Expand Down
9 changes: 9 additions & 0 deletions packages/core/jest/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ module.exports = {
.mockImplementation(
() => new Promise(resolve => resolve('test-session-id'))
),
startFeatureOperation: jest
.fn()
.mockImplementation(() => new Promise(resolve => resolve())),
succeedFeatureOperation: jest
.fn()
.mockImplementation(() => new Promise(resolve => resolve())),
failFeatureOperation: jest
.fn()
.mockImplementation(() => new Promise() < (resolve => resolve())),
setTimeProvider: jest.fn().mockImplementation(() => {}),
timeProvider: jest.fn().mockReturnValue(undefined),
getTracingContext: jest.fn().mockReturnValue(undefined),
Expand Down
Loading