@datadog/mobile-react-native-session-replay 2.4.4-alpha.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DatadogSDKReactNativeSessionReplay.podspec +1 -1
- package/android/build.gradle +11 -1
- package/android/consumer-proguard-rules.pro +3 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +32 -27
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +15 -4
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolver.kt +8 -10
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayPrivacySettings.kt +90 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplaySDKWrapper.kt +14 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayWrapper.kt +10 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/extensions/ReactDrawablesExt.kt +190 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactEditTextMapper.kt +84 -10
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactNativeImageViewMapper.kt +106 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapper.kt +4 -5
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/resources/ReactDrawableCopier.kt +34 -0
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtils.kt +10 -23
- package/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +28 -4
- package/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +35 -3
- package/android/src/{main/kotlin/com/datadog/reactnative/sessionreplay/extensions/LongExt.kt → rn75/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt} +6 -8
- package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +101 -0
- package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +13 -0
- package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +101 -0
- package/android/src/rnlegacy/kotlin/com.datadog.reactnative.sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +88 -0
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +59 -50
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +7 -3
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolverTest.kt +2 -7
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapperTest.kt +2 -7
- package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtilsTest.kt +2 -1
- package/ios/Sources/DdSessionReplay.mm +46 -4
- package/ios/Sources/DdSessionReplayImplementation.swift +83 -11
- package/ios/Sources/RCTTextViewRecorder.swift +1 -1
- package/lib/commonjs/SessionReplay.js +78 -10
- package/lib/commonjs/SessionReplay.js.map +1 -1
- package/lib/commonjs/index.js +18 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/NativeDdSessionReplay.js.map +1 -1
- package/lib/module/SessionReplay.js +77 -9
- package/lib/module/SessionReplay.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/NativeDdSessionReplay.js.map +1 -1
- package/lib/typescript/SessionReplay.d.ts +73 -4
- package/lib/typescript/SessionReplay.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/nativeModulesTypes.d.ts +18 -3
- package/lib/typescript/nativeModulesTypes.d.ts.map +1 -1
- package/lib/typescript/specs/NativeDdSessionReplay.d.ts +15 -2
- package/lib/typescript/specs/NativeDdSessionReplay.d.ts.map +1 -1
- package/package.json +85 -84
- package/src/SessionReplay.ts +170 -23
- package/src/__tests__/SessionReplay.test.ts +94 -8
- package/src/index.ts +14 -2
- package/src/nativeModulesTypes.ts +27 -4
- package/src/specs/NativeDdSessionReplay.ts +21 -3
- package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +0 -66
|
@@ -7,10 +7,23 @@ export interface Spec extends TurboModule {
|
|
|
7
7
|
/**
|
|
8
8
|
* Enable session replay and start recording session.
|
|
9
9
|
* @param replaySampleRate: The sample rate applied for session replay.
|
|
10
|
-
* @param defaultPrivacyLevel: The privacy level used for replay.
|
|
11
10
|
* @param customEndpoint: Custom server url for sending replay data.
|
|
11
|
+
* @param imagePrivacyLevel: Defines the way images should be masked.
|
|
12
|
+
* @param touchPrivacyLevel: Defines the way user touches should be masked.
|
|
13
|
+
* @param textAndInputPrivacyLevel: Defines the way text and input should be masked.
|
|
14
|
+
* @param startRecordingImmediately: Whether the recording should start automatically when the feature is enabled.
|
|
15
|
+
* When `true`, the recording starts automatically; when `false` it doesn't,
|
|
16
|
+
* and the recording will need to be started manually. Default: `true`.
|
|
12
17
|
*/
|
|
13
|
-
enable(replaySampleRate: number,
|
|
18
|
+
enable(replaySampleRate: number, customEndpoint: string, imagePrivacyLevel: string, touchPrivacyLevel: string, textAndInputPrivacyLevel: string, startRecordingImmediately: boolean): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Manually start the recording of the current session.
|
|
21
|
+
*/
|
|
22
|
+
startRecording(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Manually stop the recording of the current session.
|
|
25
|
+
*/
|
|
26
|
+
stopRecording(): Promise<void>;
|
|
14
27
|
}
|
|
15
28
|
declare const _default: Spec | null;
|
|
16
29
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeDdSessionReplay.d.ts","sourceRoot":"","sources":["../../../src/specs/NativeDdSessionReplay.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;GAEG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAEhC
|
|
1
|
+
{"version":3,"file":"NativeDdSessionReplay.d.ts","sourceRoot":"","sources":["../../../src/specs/NativeDdSessionReplay.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;GAEG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,MAAM,CACF,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,wBAAwB,EAAE,MAAM,EAChC,yBAAyB,EAAE,OAAO,GACnC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;;AAGD,wBAAgE"}
|
package/package.json
CHANGED
|
@@ -1,90 +1,91 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
],
|
|
11
|
-
"author": "Datadog (https://github.com/DataDog)",
|
|
12
|
-
"homepage": "https://github.com/DataDog/dd-sdk-reactnative#readme",
|
|
13
|
-
"repository": "https://github.com/DataDog/dd-sdk-reactnative",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/DataDog/dd-sdk-reactnative/issues"
|
|
16
|
-
},
|
|
17
|
-
"license": "Apache-2.0",
|
|
18
|
-
"main": "lib/commonjs/index",
|
|
19
|
-
"files": [
|
|
20
|
-
"src/**",
|
|
21
|
-
"lib/**",
|
|
22
|
-
"android/build.gradle",
|
|
23
|
-
"android/detekt.yml",
|
|
24
|
-
"android/gradle.properties",
|
|
25
|
-
"android/src/**",
|
|
26
|
-
"ios/Sources/**",
|
|
27
|
-
"ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/xcsharedata",
|
|
28
|
-
"ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/*.xcworkspacedata",
|
|
29
|
-
"ios/DatadogSDKReactNativeSessionReplay.xcodeproj/*.pbxproj",
|
|
30
|
-
"DatadogSDKReactNativeSessionReplay.podspec"
|
|
31
|
-
],
|
|
32
|
-
"types": "lib/typescript/index.d.ts",
|
|
33
|
-
"react-native": "src/index",
|
|
34
|
-
"source": "src",
|
|
35
|
-
"module": "lib/module/index",
|
|
36
|
-
"publishConfig": {
|
|
37
|
-
"access": "restricted"
|
|
38
|
-
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"test": "jest",
|
|
41
|
-
"lint": "eslint .",
|
|
42
|
-
"prepare": "rm -rf lib && yarn bob build"
|
|
43
|
-
},
|
|
44
|
-
"peerDependencies": {
|
|
45
|
-
"react": ">=16.13.1",
|
|
46
|
-
"react-native": ">=0.63.4 <1.0"
|
|
47
|
-
},
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"@testing-library/react-native": "7.0.2",
|
|
50
|
-
"react-native-builder-bob": "0.26.0"
|
|
51
|
-
},
|
|
52
|
-
"jest": {
|
|
53
|
-
"preset": "react-native",
|
|
54
|
-
"moduleNameMapper": {
|
|
55
|
-
"@datadog/mobile-react-native": "<rootDir>../core/src"
|
|
56
|
-
},
|
|
57
|
-
"modulePathIgnorePatterns": [
|
|
58
|
-
"<rootDir>/lib/"
|
|
2
|
+
"name": "@datadog/mobile-react-native-session-replay",
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"description": "A client-side React Native module to enable session replay with Datadog",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"datadog",
|
|
7
|
+
"react-native",
|
|
8
|
+
"ios",
|
|
9
|
+
"android"
|
|
59
10
|
],
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
11
|
+
"author": "Datadog (https://github.com/DataDog)",
|
|
12
|
+
"homepage": "https://github.com/DataDog/dd-sdk-reactnative#readme",
|
|
13
|
+
"repository": "https://github.com/DataDog/dd-sdk-reactnative",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/DataDog/dd-sdk-reactnative/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"main": "lib/commonjs/index",
|
|
19
|
+
"files": [
|
|
20
|
+
"src/**",
|
|
21
|
+
"lib/**",
|
|
22
|
+
"android/build.gradle",
|
|
23
|
+
"android/detekt.yml",
|
|
24
|
+
"android/gradle.properties",
|
|
25
|
+
"android/consumer-proguard-rules.pro",
|
|
26
|
+
"android/src/**",
|
|
27
|
+
"ios/Sources/**",
|
|
28
|
+
"ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/xcsharedata",
|
|
29
|
+
"ios/DatadogSDKReactNativeSessionReplay.xcodeproj/project.xcworkspace/*.xcworkspacedata",
|
|
30
|
+
"ios/DatadogSDKReactNativeSessionReplay.xcodeproj/*.pbxproj",
|
|
31
|
+
"DatadogSDKReactNativeSessionReplay.podspec"
|
|
63
32
|
],
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
]
|
|
67
|
-
},
|
|
68
|
-
"react-native-builder-bob": {
|
|
33
|
+
"types": "lib/typescript/index.d.ts",
|
|
34
|
+
"react-native": "src/index",
|
|
69
35
|
"source": "src",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
36
|
+
"module": "lib/module/index",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "jest",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"prepare": "rm -rf lib && yarn bob build"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": ">=16.13.1",
|
|
47
|
+
"react-native": ">=0.63.4 <1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@testing-library/react-native": "7.0.2",
|
|
51
|
+
"react-native-builder-bob": "0.26.0"
|
|
52
|
+
},
|
|
53
|
+
"jest": {
|
|
54
|
+
"preset": "react-native",
|
|
55
|
+
"moduleNameMapper": {
|
|
56
|
+
"@datadog/mobile-react-native": "<rootDir>../core/src"
|
|
57
|
+
},
|
|
58
|
+
"modulePathIgnorePatterns": [
|
|
59
|
+
"<rootDir>/lib/"
|
|
60
|
+
],
|
|
61
|
+
"setupFiles": [
|
|
62
|
+
"./../../node_modules/react-native-gesture-handler/jestSetup.js",
|
|
63
|
+
"./../../jest.setup.js"
|
|
64
|
+
],
|
|
65
|
+
"transformIgnorePatterns": [
|
|
66
|
+
"jest-runner"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"react-native-builder-bob": {
|
|
70
|
+
"source": "src",
|
|
71
|
+
"output": "lib",
|
|
72
|
+
"targets": [
|
|
73
|
+
"commonjs",
|
|
74
|
+
"module",
|
|
75
|
+
[
|
|
76
|
+
"typescript",
|
|
77
|
+
{
|
|
78
|
+
"tsc": "./../../node_modules/.bin/tsc"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"codegenConfig": {
|
|
84
|
+
"name": "DdSDKReactNativeSessionReplay",
|
|
85
|
+
"type": "modules",
|
|
86
|
+
"jsSrcsDir": "./src/specs",
|
|
87
|
+
"android": {
|
|
88
|
+
"javaPackageName": "com.datadog.reactnative.sessionreplay"
|
|
78
89
|
}
|
|
79
|
-
]
|
|
80
|
-
]
|
|
81
|
-
},
|
|
82
|
-
"codegenConfig": {
|
|
83
|
-
"name": "DdSDKReactNativeSessionReplay",
|
|
84
|
-
"type": "modules",
|
|
85
|
-
"jsSrcsDir": "./src/specs",
|
|
86
|
-
"android": {
|
|
87
|
-
"javaPackageName": "com.datadog.reactnative.sessionreplay"
|
|
88
90
|
}
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
+
}
|
package/src/SessionReplay.ts
CHANGED
|
@@ -12,6 +12,49 @@ export enum SessionReplayPrivacy {
|
|
|
12
12
|
MASK_USER_INPUT = 'MASK_USER_INPUT'
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export enum ImagePrivacyLevel {
|
|
16
|
+
/**
|
|
17
|
+
* Only images that are bundled within the application will be recorded.
|
|
18
|
+
*
|
|
19
|
+
* On Android, all images larger than 100x100 dp will be masked, as we consider them non-bundled images.
|
|
20
|
+
*/
|
|
21
|
+
MASK_NON_BUNDLED_ONLY = 'MASK_NON_BUNDLED_ONLY',
|
|
22
|
+
/**
|
|
23
|
+
* No images will be recorded.
|
|
24
|
+
*/
|
|
25
|
+
MASK_ALL = 'MASK_ALL',
|
|
26
|
+
/**
|
|
27
|
+
* All images will be recorded, including the ones downloaded from the Internet or generated during the app runtime.
|
|
28
|
+
*/
|
|
29
|
+
MASK_NONE = 'MASK_NONE'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export enum TouchPrivacyLevel {
|
|
33
|
+
/**
|
|
34
|
+
* Show all user touches.
|
|
35
|
+
*/
|
|
36
|
+
SHOW = 'SHOW',
|
|
37
|
+
/**
|
|
38
|
+
* Hide all user touches.
|
|
39
|
+
*/
|
|
40
|
+
HIDE = 'HIDE'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export enum TextAndInputPrivacyLevel {
|
|
44
|
+
/**
|
|
45
|
+
* Show all texts except sensitive inputs (e.g password fields).
|
|
46
|
+
*/
|
|
47
|
+
MASK_SENSITIVE_INPUTS = 'MASK_SENSITIVE_INPUTS',
|
|
48
|
+
/**
|
|
49
|
+
* Mask all input fields (e.g text fields, switches, checkboxes).
|
|
50
|
+
*/
|
|
51
|
+
MASK_ALL_INPUTS = 'MASK_ALL_INPUTS',
|
|
52
|
+
/**
|
|
53
|
+
* Mask all texts and inputs (e.g labels).
|
|
54
|
+
*/
|
|
55
|
+
MASK_ALL = 'MASK_ALL'
|
|
56
|
+
}
|
|
57
|
+
|
|
15
58
|
/**
|
|
16
59
|
* The Session Replay configuration object.
|
|
17
60
|
*/
|
|
@@ -24,22 +67,70 @@ export interface SessionReplayConfiguration {
|
|
|
24
67
|
* Default value is `20`.
|
|
25
68
|
*/
|
|
26
69
|
replaySampleRate?: number;
|
|
70
|
+
|
|
27
71
|
/**
|
|
28
|
-
* Defines the way
|
|
29
|
-
*
|
|
30
|
-
* Default `SessionReplayPrivacy.MASK`.
|
|
72
|
+
* Defines the way images should be masked (Default: `MASK_ALL`)
|
|
31
73
|
*/
|
|
32
|
-
|
|
74
|
+
imagePrivacyLevel?: ImagePrivacyLevel;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Defines the way user touches (e.g tap) should be masked (Default: `HIDE`)
|
|
78
|
+
*/
|
|
79
|
+
touchPrivacyLevel?: TouchPrivacyLevel;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Defines the way text and input (e.g text fields, checkboxes) should be masked (Default: `MASK_ALL`)
|
|
83
|
+
*/
|
|
84
|
+
textAndInputPrivacyLevel?: TextAndInputPrivacyLevel;
|
|
85
|
+
|
|
33
86
|
/**
|
|
34
87
|
* Custom server url for sending replay data.
|
|
35
88
|
*/
|
|
36
89
|
customEndpoint?: string;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether the recording should start automatically when the feature is enabled.
|
|
93
|
+
* When `true`, the recording starts automatically.
|
|
94
|
+
* when `false` it doesn't, and the recording will need to be started manually.
|
|
95
|
+
* Default: `true`.
|
|
96
|
+
*/
|
|
97
|
+
startRecordingImmediately?: boolean;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Defines the way sensitive content (e.g. text) should be masked.
|
|
101
|
+
*
|
|
102
|
+
* Default `SessionReplayPrivacy.MASK`.
|
|
103
|
+
* @deprecated Use {@link imagePrivacyLevel}, {@link touchPrivacyLevel} and {@link textAndInputPrivacyLevel} instead.
|
|
104
|
+
* Note: setting this property (`defaultPrivacyLevel`) will override the individual privacy levels.
|
|
105
|
+
*/
|
|
106
|
+
defaultPrivacyLevel?: SessionReplayPrivacy;
|
|
37
107
|
}
|
|
38
108
|
|
|
39
|
-
|
|
109
|
+
type InternalBaseSessionReplayConfiguration = {
|
|
110
|
+
replaySampleRate: number;
|
|
111
|
+
customEndpoint: string;
|
|
112
|
+
startRecordingImmediately: boolean;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
type InternalPrivacySessionReplayConfiguration = {
|
|
116
|
+
imagePrivacyLevel: ImagePrivacyLevel;
|
|
117
|
+
touchPrivacyLevel: TouchPrivacyLevel;
|
|
118
|
+
textAndInputPrivacyLevel: TextAndInputPrivacyLevel;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
type InternalSessionReplayConfiguration = InternalBaseSessionReplayConfiguration &
|
|
122
|
+
InternalPrivacySessionReplayConfiguration;
|
|
123
|
+
|
|
124
|
+
const DEFAULTS: InternalSessionReplayConfiguration & {
|
|
125
|
+
defaultPrivacyLevel: SessionReplayPrivacy;
|
|
126
|
+
} = {
|
|
40
127
|
replaySampleRate: 0,
|
|
41
128
|
defaultPrivacyLevel: SessionReplayPrivacy.MASK,
|
|
42
|
-
customEndpoint: ''
|
|
129
|
+
customEndpoint: '',
|
|
130
|
+
imagePrivacyLevel: ImagePrivacyLevel.MASK_ALL,
|
|
131
|
+
touchPrivacyLevel: TouchPrivacyLevel.HIDE,
|
|
132
|
+
textAndInputPrivacyLevel: TextAndInputPrivacyLevel.MASK_ALL,
|
|
133
|
+
startRecordingImmediately: true
|
|
43
134
|
};
|
|
44
135
|
|
|
45
136
|
export class SessionReplayWrapper {
|
|
@@ -49,33 +140,69 @@ export class SessionReplayWrapper {
|
|
|
49
140
|
|
|
50
141
|
private buildConfiguration = (
|
|
51
142
|
configuration?: SessionReplayConfiguration
|
|
52
|
-
): {
|
|
53
|
-
replaySampleRate: number;
|
|
54
|
-
defaultPrivacyLevel: SessionReplayPrivacy;
|
|
55
|
-
customEndpoint: string;
|
|
56
|
-
} => {
|
|
143
|
+
): InternalSessionReplayConfiguration => {
|
|
57
144
|
if (!configuration) {
|
|
58
145
|
return DEFAULTS;
|
|
59
146
|
}
|
|
60
147
|
const {
|
|
61
148
|
replaySampleRate,
|
|
62
|
-
|
|
63
|
-
|
|
149
|
+
customEndpoint,
|
|
150
|
+
startRecordingImmediately
|
|
64
151
|
} = configuration;
|
|
65
|
-
|
|
152
|
+
|
|
153
|
+
const baseConfig: InternalBaseSessionReplayConfiguration = {
|
|
66
154
|
replaySampleRate:
|
|
67
155
|
replaySampleRate !== undefined
|
|
68
156
|
? replaySampleRate
|
|
69
157
|
: DEFAULTS.replaySampleRate,
|
|
70
|
-
defaultPrivacyLevel:
|
|
71
|
-
defaultPrivacyLevel !== undefined
|
|
72
|
-
? defaultPrivacyLevel
|
|
73
|
-
: DEFAULTS.defaultPrivacyLevel,
|
|
74
158
|
customEndpoint:
|
|
75
159
|
customEndpoint !== undefined
|
|
76
160
|
? customEndpoint
|
|
77
|
-
: DEFAULTS.customEndpoint
|
|
161
|
+
: DEFAULTS.customEndpoint,
|
|
162
|
+
startRecordingImmediately:
|
|
163
|
+
startRecordingImmediately !== undefined
|
|
164
|
+
? startRecordingImmediately
|
|
165
|
+
: DEFAULTS.startRecordingImmediately
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const privacyConfig: InternalPrivacySessionReplayConfiguration = {
|
|
169
|
+
imagePrivacyLevel:
|
|
170
|
+
configuration.imagePrivacyLevel ?? DEFAULTS.imagePrivacyLevel,
|
|
171
|
+
touchPrivacyLevel:
|
|
172
|
+
configuration.touchPrivacyLevel ?? DEFAULTS.touchPrivacyLevel,
|
|
173
|
+
textAndInputPrivacyLevel:
|
|
174
|
+
configuration.textAndInputPrivacyLevel ??
|
|
175
|
+
DEFAULTS.textAndInputPrivacyLevel
|
|
78
176
|
};
|
|
177
|
+
|
|
178
|
+
// Legacy Default Privacy Level property handling
|
|
179
|
+
if (configuration.defaultPrivacyLevel) {
|
|
180
|
+
switch (configuration.defaultPrivacyLevel) {
|
|
181
|
+
case SessionReplayPrivacy.MASK:
|
|
182
|
+
privacyConfig.imagePrivacyLevel =
|
|
183
|
+
ImagePrivacyLevel.MASK_ALL;
|
|
184
|
+
privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.HIDE;
|
|
185
|
+
privacyConfig.textAndInputPrivacyLevel =
|
|
186
|
+
TextAndInputPrivacyLevel.MASK_ALL;
|
|
187
|
+
break;
|
|
188
|
+
case SessionReplayPrivacy.MASK_USER_INPUT:
|
|
189
|
+
privacyConfig.imagePrivacyLevel =
|
|
190
|
+
ImagePrivacyLevel.MASK_NONE;
|
|
191
|
+
privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.HIDE;
|
|
192
|
+
privacyConfig.textAndInputPrivacyLevel =
|
|
193
|
+
TextAndInputPrivacyLevel.MASK_ALL_INPUTS;
|
|
194
|
+
break;
|
|
195
|
+
case SessionReplayPrivacy.ALLOW:
|
|
196
|
+
privacyConfig.imagePrivacyLevel =
|
|
197
|
+
ImagePrivacyLevel.MASK_NONE;
|
|
198
|
+
privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.SHOW;
|
|
199
|
+
privacyConfig.textAndInputPrivacyLevel =
|
|
200
|
+
TextAndInputPrivacyLevel.MASK_SENSITIVE_INPUTS;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { ...baseConfig, ...privacyConfig };
|
|
79
206
|
};
|
|
80
207
|
|
|
81
208
|
/**
|
|
@@ -85,16 +212,36 @@ export class SessionReplayWrapper {
|
|
|
85
212
|
enable = (configuration?: SessionReplayConfiguration): Promise<void> => {
|
|
86
213
|
const {
|
|
87
214
|
replaySampleRate,
|
|
88
|
-
|
|
89
|
-
|
|
215
|
+
customEndpoint,
|
|
216
|
+
imagePrivacyLevel,
|
|
217
|
+
touchPrivacyLevel,
|
|
218
|
+
textAndInputPrivacyLevel,
|
|
219
|
+
startRecordingImmediately
|
|
90
220
|
} = this.buildConfiguration(configuration);
|
|
91
221
|
|
|
92
222
|
return this.nativeSessionReplay.enable(
|
|
93
223
|
replaySampleRate,
|
|
94
|
-
|
|
95
|
-
|
|
224
|
+
customEndpoint,
|
|
225
|
+
imagePrivacyLevel,
|
|
226
|
+
touchPrivacyLevel,
|
|
227
|
+
textAndInputPrivacyLevel,
|
|
228
|
+
startRecordingImmediately
|
|
96
229
|
);
|
|
97
230
|
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Manually start the recording of the current session.
|
|
234
|
+
*/
|
|
235
|
+
startRecording = (): Promise<void> => {
|
|
236
|
+
return this.nativeSessionReplay.startRecording();
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Manually stop the recording of the current session.
|
|
241
|
+
*/
|
|
242
|
+
stopRecording = (): Promise<void> => {
|
|
243
|
+
return this.nativeSessionReplay.stopRecording();
|
|
244
|
+
};
|
|
98
245
|
}
|
|
99
246
|
|
|
100
247
|
export const SessionReplay = new SessionReplayWrapper();
|
|
@@ -6,7 +6,21 @@
|
|
|
6
6
|
|
|
7
7
|
import { NativeModules } from 'react-native';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
ImagePrivacyLevel,
|
|
11
|
+
SessionReplay,
|
|
12
|
+
SessionReplayPrivacy,
|
|
13
|
+
TextAndInputPrivacyLevel,
|
|
14
|
+
TouchPrivacyLevel
|
|
15
|
+
} from '../SessionReplay';
|
|
16
|
+
|
|
17
|
+
function getRandomEnumValue<
|
|
18
|
+
T extends { [s: string]: T[keyof T] } | ArrayLike<T[keyof T]>
|
|
19
|
+
>(enumObj: T): T[keyof T] {
|
|
20
|
+
const values = Object.values(enumObj) as T[keyof T][]; // Get all enum values
|
|
21
|
+
const randomIndex = Math.floor(Math.random() * values.length); // Generate a random index
|
|
22
|
+
return values[randomIndex]; // Return the random value
|
|
23
|
+
}
|
|
10
24
|
|
|
11
25
|
beforeEach(() => {
|
|
12
26
|
NativeModules.DdSessionReplay.enable.mockClear();
|
|
@@ -19,12 +33,15 @@ describe('SessionReplay', () => {
|
|
|
19
33
|
|
|
20
34
|
expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
|
|
21
35
|
0,
|
|
22
|
-
'
|
|
23
|
-
''
|
|
36
|
+
'',
|
|
37
|
+
'MASK_ALL',
|
|
38
|
+
'HIDE',
|
|
39
|
+
'MASK_ALL',
|
|
40
|
+
true
|
|
24
41
|
);
|
|
25
42
|
});
|
|
26
43
|
|
|
27
|
-
it('calls native session replay with provided configuration', () => {
|
|
44
|
+
it('calls native session replay with provided configuration { w defaultPrivacyLevel = ALLOW }', () => {
|
|
28
45
|
SessionReplay.enable({
|
|
29
46
|
replaySampleRate: 100,
|
|
30
47
|
defaultPrivacyLevel: SessionReplayPrivacy.ALLOW,
|
|
@@ -33,11 +50,77 @@ describe('SessionReplay', () => {
|
|
|
33
50
|
|
|
34
51
|
expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
|
|
35
52
|
100,
|
|
36
|
-
'
|
|
37
|
-
'
|
|
53
|
+
'https://session-replay.example.com',
|
|
54
|
+
'MASK_NONE',
|
|
55
|
+
'SHOW',
|
|
56
|
+
'MASK_SENSITIVE_INPUTS',
|
|
57
|
+
true
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('calls native session replay with provided configuration { w defaultPrivacyLevel = MASK }', () => {
|
|
62
|
+
SessionReplay.enable({
|
|
63
|
+
replaySampleRate: 100,
|
|
64
|
+
defaultPrivacyLevel: SessionReplayPrivacy.MASK,
|
|
65
|
+
customEndpoint: 'https://session-replay.example.com'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
|
|
69
|
+
100,
|
|
70
|
+
'https://session-replay.example.com',
|
|
71
|
+
'MASK_ALL',
|
|
72
|
+
'HIDE',
|
|
73
|
+
'MASK_ALL',
|
|
74
|
+
true
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('calls native session replay with provided configuration { w defaultPrivacyLevel = MASK_USER_INPUT }', () => {
|
|
79
|
+
SessionReplay.enable({
|
|
80
|
+
replaySampleRate: 100,
|
|
81
|
+
defaultPrivacyLevel: SessionReplayPrivacy.MASK_USER_INPUT,
|
|
82
|
+
customEndpoint: 'https://session-replay.example.com'
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
|
|
86
|
+
100,
|
|
87
|
+
'https://session-replay.example.com',
|
|
88
|
+
'MASK_NONE',
|
|
89
|
+
'HIDE',
|
|
90
|
+
'MASK_ALL_INPUTS',
|
|
91
|
+
true
|
|
38
92
|
);
|
|
39
93
|
});
|
|
40
94
|
|
|
95
|
+
it('calls native session replay with provided configuration { w random privacy levels }', () => {
|
|
96
|
+
const TIMES = 20;
|
|
97
|
+
|
|
98
|
+
const image = getRandomEnumValue(ImagePrivacyLevel);
|
|
99
|
+
const touch = getRandomEnumValue(TouchPrivacyLevel);
|
|
100
|
+
const textAndInput = getRandomEnumValue(TextAndInputPrivacyLevel);
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < TIMES; ++i) {
|
|
103
|
+
SessionReplay.enable({
|
|
104
|
+
replaySampleRate: 100,
|
|
105
|
+
customEndpoint: 'https://session-replay.example.com',
|
|
106
|
+
imagePrivacyLevel: image,
|
|
107
|
+
touchPrivacyLevel: touch,
|
|
108
|
+
textAndInputPrivacyLevel: textAndInput
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(
|
|
112
|
+
NativeModules.DdSessionReplay.enable
|
|
113
|
+
).toHaveBeenCalledWith(
|
|
114
|
+
100,
|
|
115
|
+
'https://session-replay.example.com',
|
|
116
|
+
image,
|
|
117
|
+
touch,
|
|
118
|
+
textAndInput,
|
|
119
|
+
true
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
41
124
|
it('calls native session replay with edge cases in configuration', () => {
|
|
42
125
|
SessionReplay.enable({
|
|
43
126
|
replaySampleRate: 0,
|
|
@@ -46,8 +129,11 @@ describe('SessionReplay', () => {
|
|
|
46
129
|
|
|
47
130
|
expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith(
|
|
48
131
|
0,
|
|
49
|
-
'
|
|
50
|
-
''
|
|
132
|
+
'',
|
|
133
|
+
'MASK_ALL',
|
|
134
|
+
'HIDE',
|
|
135
|
+
'MASK_ALL',
|
|
136
|
+
true
|
|
51
137
|
);
|
|
52
138
|
});
|
|
53
139
|
});
|
package/src/index.ts
CHANGED
|
@@ -5,8 +5,20 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { SessionReplayConfiguration } from './SessionReplay';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
SessionReplay,
|
|
10
|
+
SessionReplayPrivacy,
|
|
11
|
+
ImagePrivacyLevel,
|
|
12
|
+
TouchPrivacyLevel,
|
|
13
|
+
TextAndInputPrivacyLevel
|
|
14
|
+
} from './SessionReplay';
|
|
9
15
|
|
|
10
|
-
export {
|
|
16
|
+
export {
|
|
17
|
+
SessionReplay,
|
|
18
|
+
SessionReplayPrivacy,
|
|
19
|
+
ImagePrivacyLevel,
|
|
20
|
+
TouchPrivacyLevel,
|
|
21
|
+
TextAndInputPrivacyLevel
|
|
22
|
+
};
|
|
11
23
|
|
|
12
24
|
export type { SessionReplayConfiguration };
|
|
@@ -11,7 +11,12 @@ import type { Spec as NativeDdSessionReplay } from './specs/NativeDdSessionRepla
|
|
|
11
11
|
* As we cannot use enums or classes in the specs, we override methods using them here.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
type
|
|
14
|
+
type ImagePrivacyLevel = 'MASK_NON_BUNDLED_ONLY' | 'MASK_ALL' | 'MASK_NONE';
|
|
15
|
+
type TouchPrivacyLevel = 'SHOW' | 'HIDE';
|
|
16
|
+
type TextAndInputPrivacyLevel =
|
|
17
|
+
| 'MASK_SENSITIVE_INPUTS'
|
|
18
|
+
| 'MASK_ALL_INPUTS'
|
|
19
|
+
| 'MASK_ALL';
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* The entry point to use Datadog's Session Replay feature.
|
|
@@ -20,12 +25,30 @@ export interface NativeSessionReplayType extends NativeDdSessionReplay {
|
|
|
20
25
|
/**
|
|
21
26
|
* Enable session replay and start recording session.
|
|
22
27
|
* @param replaySampleRate: The sample rate applied for session replay.
|
|
23
|
-
* @param defaultPrivacyLevel: The privacy level used for replay.
|
|
24
28
|
* @param customEndpoint: Custom server url for sending replay data.
|
|
29
|
+
* @param imagePrivacyLevel: Defines the way images should be masked.
|
|
30
|
+
* @param touchPrivacyLevel: Defines the way user touches should be masked.
|
|
31
|
+
* @param textAndInputPrivacyLevel: Defines the way text and input should be masked.
|
|
32
|
+
* @param startRecordingImmediately: Whether the recording should start automatically when the feature is enabled.
|
|
33
|
+
* When `true`, the recording starts automatically; when `false` it doesn't, and the recording will need
|
|
34
|
+
* to be started manually. Default: `true`.
|
|
25
35
|
*/
|
|
26
36
|
enable(
|
|
27
37
|
replaySampleRate: number,
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
customEndpoint: string,
|
|
39
|
+
imagePrivacyLevel: ImagePrivacyLevel,
|
|
40
|
+
touchPrivacyLevel: TouchPrivacyLevel,
|
|
41
|
+
textAndInputPrivacyLevel: TextAndInputPrivacyLevel,
|
|
42
|
+
startRecordingImmediately: boolean
|
|
30
43
|
): Promise<void>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Manually start the recording of the current session.
|
|
47
|
+
*/
|
|
48
|
+
startRecording(): Promise<void>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Manually stop the recording of the current session.
|
|
52
|
+
*/
|
|
53
|
+
stopRecording(): Promise<void>;
|
|
31
54
|
}
|