Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] Testable iOS Core with proxy #560

Closed
wants to merge 6 commits into from
Closed
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 packages/core/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ dependencies {
}
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compileOnly "com.squareup.okhttp3:okhttp:3.12.13"

implementation "com.google.code.gson:gson:2.10.0"
implementation "com.datadoghq:dd-sdk-android-rum:2.4.0"
implementation "com.datadoghq:dd-sdk-android-logs:2.4.0"
implementation "com.datadoghq:dd-sdk-android-trace:2.4.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* 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.reactnative

import com.datadog.android.Datadog
import com.datadog.android.api.context.DatadogContext
import com.datadog.android.api.feature.FeatureScope
import com.datadog.android.api.feature.FeatureSdkCore
import com.datadog.android.api.storage.EventBatchWriter
import com.datadog.android.api.storage.RawBatchEvent
import com.datadog.android.core.InternalSdkCore
import com.facebook.react.bridge.Promise
import com.google.gson.Gson

/**
* The entry point to use Datadog's Core Tests feature.
*/
class DdCoreTestsImplementation() {
private val featureScopes = mutableMapOf<String, FeatureScopeInterceptor>()

fun clearData(promise: Promise) {
featureScopes["logs"]?.clearData()
featureScopes["rum"]?.clearData()
featureScopes["tracing"]?.clearData()
featureScopes["session-replay"]?.clearData()

promise.resolve(null)
}

fun getAllEventsData(feature: String, promise: Promise) {
val events = featureScopes[feature]?.eventsWritten()?.toList() ?: emptyList<Any>()
val eventsJson = Gson().toJson(events)
promise.resolve(eventsJson)
}

private fun registerFeature(name: String, core: FeatureSdkCore) {
val featureScope = core?.getFeature(name)
featureScope?.let {
val instrumentedScope = FeatureScopeInterceptor(featureScope, core!! as InternalSdkCore)
featureScopes[name] = instrumentedScope
}
}

fun startRecording(promise: Promise) {
val core = Datadog.getInstance() as FeatureSdkCore
registerFeature("rum", core)
registerFeature("logs", core)
registerFeature("tracing", core)
registerFeature("session-replay", core)
registerFeature("crash", core)

// Add reflection on the core to change the feature variable to be the one we created
core.javaClass.declaredFields.firstOrNull { it.name == "features" }?.let {
it.isAccessible = true
it.set(core, featureScopes)
}

promise.resolve(null)
}

companion object {
internal const val NAME = "DdCoreTests"
}
}

internal class FeatureScopeInterceptor(
private val featureScope: FeatureScope,
private val core: InternalSdkCore,
) : FeatureScope by featureScope {

private val eventsBatchInterceptor = EventBatchInterceptor()

fun eventsWritten(): List<String> {
return eventsBatchInterceptor.events
}

fun clearData() {
eventsBatchInterceptor.clearData()
}

// region FeatureScope

override fun withWriteContext(
forceNewBatch: Boolean,
callback: (DatadogContext, EventBatchWriter) -> Unit
) {
featureScope.withWriteContext(forceNewBatch, callback)

val context = core.getDatadogContext()!!
callback(context, eventsBatchInterceptor)
}

// endregion
}


internal class EventBatchInterceptor: EventBatchWriter {
internal val events = mutableListOf<String>()

override fun currentMetadata(): ByteArray? {
TODO("Not yet implemented")
}

fun clearData() {
events.clear()
}

override fun write(
event: RawBatchEvent,
batchMetadata: ByteArray?
): Boolean {
val eventContent = String(event.data)

events.add(events.size,
eventContent
)

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class DdSdkReactNativePackage : TurboReactPackage() {
DdRumImplementation.NAME -> DdRum(reactContext)
DdTraceImplementation.NAME -> DdTrace(reactContext)
DdLogsImplementation.NAME -> DdLogs(reactContext)
DdCoreTestsImplementation.NAME -> DdCoreTests(reactContext)
else -> null
}
}
Expand All @@ -33,7 +34,8 @@ class DdSdkReactNativePackage : TurboReactPackage() {
DdSdkImplementation.NAME,
DdRumImplementation.NAME,
DdTraceImplementation.NAME,
DdLogsImplementation.NAME
DdLogsImplementation.NAME,
DdCoreTestsImplementation.NAME
).associateWith {
ReactModuleInfo(
it,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.reactnative

import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap

/**
* The entry point to use Datadog's Core Tests feature.
*/
class DdCoreTests(
reactContext: ReactApplicationContext,
) : NativeDdCoreTestsSpec(reactContext) {

private val implementation = DdCoreTestsImplementation()

override fun getName(): String = DdCoreTestsImplementation.NAME

@ReactMethod
override fun clearData(promise: Promise) {
implementation.clearData(promise)
}

@ReactMethod
override fun getAllEventsData(feature: String, promise: Promise) {
implementation.getAllEventsData(feature, promise)
}

@ReactMethod
override fun startRecording(promise: Promise) {
implementation.startRecording(promise)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.reactnative

import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap

/**
* The entry point to use Datadog's Core Tests feature.
*/
class DdCoreTests(
reactContext: ReactApplicationContext
) : ReactContextBaseJavaModule(reactContext) {

private val implementation = DdCoreTestsImplementation()

override fun getName(): String = DdCoreTestsImplementation.NAME

@ReactMethod
fun clearData(promise: Promise) {
implementation.clearData(promise)
}

@ReactMethod
fun getAllEventsData(feature: String, promise: Promise) {
implementation.getAllEventsData(feature, promise)
}

@ReactMethod
fun startRecording(promise: Promise) {
implementation.startRecording(promise)
}
}
24 changes: 24 additions & 0 deletions packages/core/ios/Sources/DdCoreTests.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.
*/

#import <Foundation/Foundation.h>
@class DdCoreTestsImplementation;

#ifdef RCT_NEW_ARCH_ENABLED

#import <DdSdkReactNative/DdSdkReactNative.h>
@interface DdCoreTests: NSObject <NativeDdCoreTestsSpec>

#else

#import <React/RCTBridgeModule.h>
@interface DdCoreTests : NSObject <RCTBridgeModule>

#endif

@property (nonatomic, strong) DdCoreTestsImplementation* ddCoreTestsImplementation;

@end
88 changes: 88 additions & 0 deletions packages/core/ios/Sources/DdCoreTests.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.
*/
// Import this first to prevent require cycles
#if __has_include("DatadogSDKReactNative-Swift.h")
#import <DatadogSDKReactNative-Swift.h>
#else
#import <DatadogSDKReactNative/DatadogSDKReactNative-Swift.h>
#endif
#import "DdCoreTests.h"


@implementation DdCoreTests

RCT_EXPORT_MODULE()

RCT_REMAP_METHOD(clearData, withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self clearData:resolve reject:reject];
}

RCT_REMAP_METHOD(getAllEvents, withFeature: (NSString *)feature
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self getAllEvents:feature resolve:resolve reject:reject];
}

RCT_REMAP_METHOD(getAllEventsData, withDataFeature: (NSString *)feature
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self getAllEventsData:feature resolve:resolve reject:reject];
}

RCT_REMAP_METHOD(startRecording, withResolver2:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
[self startRecording: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:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeDdCoreTestsSpecJSI>(params);
}
#endif

- (DdCoreTestsImplementation*)ddCoreTestsImplementation
{
if (_ddCoreTestsImplementation == nil) {
_ddCoreTestsImplementation = [[DdCoreTestsImplementation alloc] init];
}
return _ddCoreTestsImplementation;
}

+ (BOOL)requiresMainQueueSetup {
return NO;
}

- (dispatch_queue_t)methodQueue {
return [RNQueue getSharedQueue];
}


- (void)clearData:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddCoreTestsImplementation clearDataWithResolve:resolve reject:reject];
}

- (void)getAllEvents:(NSString *)feature resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddCoreTestsImplementation getAllEventsWithFeature:feature resolve:resolve reject:reject];
}

- (void)getAllEventsData:(NSString *)feature resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddCoreTestsImplementation getAllEventsDataWithFeature:feature resolve:resolve reject:reject];
}

- (void)startRecording:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[self.ddCoreTestsImplementation startRecordingWithResolve:resolve reject:reject];
}

@end
Loading