Skip to content

Commit d3fb366

Browse files
authored
enh: refactor setContexts and removeContexts to FFI/JNI (#3312)
* Refactor setContext and removeContext to FFI/JNI * Update scope * Integration test * Fix swift lint * CHANGELOG * Improve objc conversion * Fix tests * Remove native channel from coverage
1 parent a69a51f commit d3fb366

File tree

16 files changed

+2711
-135
lines changed

16 files changed

+2711
-135
lines changed

.github/actions/flutter-test/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ runs:
5151
# Remove native binding/bridge files from coverage.
5252
# These FFI/JNI bindings are currently not unit tested due to limitations of FFI/JNI mocking.
5353
dart run remove_from_coverage -f coverage/lcov.info -r 'binding.dart'
54+
dart run remove_from_coverage -f coverage/lcov.info -r 'lib/src/native/sentry_native_channel.dart'
5455
dart run remove_from_coverage -f coverage/lcov.info -r 'lib/src/native/java/sentry_native_java.dart'
5556
dart run remove_from_coverage -f coverage/lcov.info -r 'lib/src/native/cocoa/sentry_native_cocoa.dart'
5657
fi

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
### Enhancements
1111

12+
- Refactor `setContexts` and `removeContexts` to use FFI/JNI ([#3312](https://github.com/getsentry/sentry-dart/pull/3312))
1213
- Refactor `setUser` to use FFI/JNI ([#3295](https://github.com/getsentry/sentry-dart/pull/3295/))
1314
- Refactor native breadcrumbs sync to use FFI/JNI ([#3293](https://github.com/getsentry/sentry-dart/pull/3293/))
1415
- Refactor app hang and crash apis to use FFI/JNI ([#3289](https://github.com/getsentry/sentry-dart/pull/3289/))

packages/dart/lib/src/scope.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,17 @@ class Scope {
138138
}
139139

140140
/// add an entry to the Scope's contexts
141-
Future<void> setContexts(String key, dynamic value) async {
141+
FutureOr<void> setContexts(String key, dynamic value) {
142142
_setContextsSync(key, value);
143-
await _callScopeObservers(
143+
return _callScopeObservers(
144144
(scopeObserver) async => await scopeObserver.setContexts(key, value));
145145
}
146146

147147
/// Removes a value from the Scope's contexts
148-
Future<void> removeContexts(String key) async {
148+
FutureOr<void> removeContexts(String key) {
149149
_contexts.remove(key);
150150

151-
await _callScopeObservers(
151+
return _callScopeObservers(
152152
(scopeObserver) async => await scopeObserver.removeContexts(key));
153153
}
154154

packages/dart/lib/src/scope_observer.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import 'protocol/breadcrumb.dart';
44
import 'protocol/sentry_user.dart';
55

66
abstract class ScopeObserver {
7-
Future<void> setContexts(String key, dynamic value);
8-
Future<void> removeContexts(String key);
7+
FutureOr<void> setContexts(String key, dynamic value);
8+
FutureOr<void> removeContexts(String key);
99
FutureOr<void> setUser(SentryUser? user);
1010
FutureOr<void> addBreadcrumb(Breadcrumb breadcrumb);
1111
FutureOr<void> clearBreadcrumbs();

packages/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ class SentryFlutterPlugin :
6161
when (call.method) {
6262
"initNativeSdk" -> initNativeSdk(call, result)
6363
"closeNativeSdk" -> closeNativeSdk(result)
64-
"setContexts" -> setContexts(call.argument("key"), call.argument("value"), result)
65-
"removeContexts" -> removeContexts(call.argument("key"), result)
6664
"setExtra" -> setExtra(call.argument("key"), call.argument("value"), result)
6765
"removeExtra" -> removeExtra(call.argument("key"), result)
6866
"setTag" -> setTag(call.argument("key"), call.argument("value"), result)
@@ -141,37 +139,6 @@ class SentryFlutterPlugin :
141139
}
142140
}
143141

144-
private fun setContexts(
145-
key: String?,
146-
value: Any?,
147-
result: Result,
148-
) {
149-
if (key == null || value == null) {
150-
result.success("")
151-
return
152-
}
153-
Sentry.configureScope { scope ->
154-
scope.setContexts(key, value)
155-
156-
result.success("")
157-
}
158-
}
159-
160-
private fun removeContexts(
161-
key: String?,
162-
result: Result,
163-
) {
164-
if (key == null) {
165-
result.success("")
166-
return
167-
}
168-
Sentry.configureScope { scope ->
169-
scope.removeContexts(key)
170-
171-
result.success("")
172-
}
173-
}
174-
175142
private fun setExtra(
176143
key: String?,
177144
value: String?,

packages/flutter/example/integration_test/integration_test.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,62 @@ void main() {
737737
}
738738
});
739739

740+
testWidgets('setContexts and removedContexts sync to native', (tester) async {
741+
await restoreFlutterOnErrorAfter(() async {
742+
await setupSentryAndApp(tester);
743+
});
744+
745+
await Sentry.configureScope((scope) async {
746+
scope.setContexts('key1', 'randomValue');
747+
scope.setContexts('key2',
748+
{'String': 'Value', 'Bool': true, 'Int': 123, 'Double': 12.3});
749+
scope.setContexts('key3', true);
750+
scope.setContexts('key4', 12);
751+
scope.setContexts('key5', 12.3);
752+
});
753+
754+
var contexts = await SentryFlutter.native?.loadContexts();
755+
final values = contexts!['contexts'];
756+
expect(values, isNotNull, reason: 'Contexts are null');
757+
758+
if (Platform.isIOS) {
759+
expect(values['key1'], {'value': 'randomValue'}, reason: 'key1 mismatch');
760+
expect(values['key2'],
761+
{'String': 'Value', 'Bool': 1, 'Int': 123, 'Double': 12.3},
762+
reason: 'key2 mismatch');
763+
// bool values are mapped to num values of 1 or 0 during objc conversion
764+
expect(values['key3'], {'value': 1}, reason: 'key3 mismatch');
765+
expect(values['key4'], {'value': 12}, reason: 'key4 mismatch');
766+
expect(values['key5'], {'value': 12.3}, reason: 'key5 mismatch');
767+
} else if (Platform.isAndroid) {
768+
expect(values['key1'], 'randomValue', reason: 'key1 mismatch');
769+
expect(values['key2'],
770+
{'String': 'Value', 'Bool': true, 'Int': 123, 'Double': 12.3},
771+
reason: 'key2 mismatch');
772+
expect(values['key3'], true, reason: 'key3 mismatch');
773+
expect(values['key4'], 12, reason: 'key4 mismatch');
774+
expect(values['key5'], 12.3, reason: 'key5 mismatch');
775+
}
776+
777+
await Sentry.configureScope((scope) async {
778+
scope.removeContexts('key1');
779+
scope.removeContexts('key2');
780+
scope.removeContexts('key3');
781+
scope.removeContexts('key4');
782+
scope.removeContexts('key5');
783+
});
784+
785+
contexts = await SentryFlutter.native?.loadContexts();
786+
final removedValues = contexts!['contexts'];
787+
expect(removedValues, isNotNull, reason: 'Contexts are null');
788+
789+
expect(removedValues['key1'], isNull, reason: 'key1 should be removed');
790+
expect(removedValues['key2'], isNull, reason: 'key2 should be removed');
791+
expect(removedValues['key3'], isNull, reason: 'key3 should be removed');
792+
expect(removedValues['key4'], isNull, reason: 'key4 should be removed');
793+
expect(removedValues['key5'], isNull, reason: 'key5 should be removed');
794+
});
795+
740796
group('e2e', () {
741797
var output = find.byKey(const Key('output'));
742798
late Fixture fixture;

packages/flutter/ffi-cocoa.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ objc-interfaces:
3838
SentryScope:
3939
include:
4040
- 'clearBreadcrumbs'
41+
- 'setContextValue:forKey:'
42+
- 'removeContextForKey:'
4143
preamble: |
4244
// ignore_for_file: type=lint, unused_element
4345

packages/flutter/ffi-jni.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,7 @@ classes:
1919
- io.sentry.Sentry
2020
- io.sentry.Breadcrumb
2121
- io.sentry.ScopesAdapter
22+
- io.sentry.Scope
23+
- io.sentry.ScopeCallback
2224
- io.sentry.protocol.User
2325
- android.graphics.Bitmap

packages/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
6767
?? iso8601Formatter.date(from: iso8601String) // Parse date with low precision formatter for backward compatible
6868
}
6969

70-
// swiftlint:disable:next cyclomatic_complexity
7170
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
7271
switch call.method as String {
7372
case "initNativeSdk":
@@ -76,17 +75,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
7675
case "closeNativeSdk":
7776
closeNativeSdk(call, result: result)
7877

79-
case "setContexts":
80-
let arguments = call.arguments as? [String: Any?]
81-
let key = arguments?["key"] as? String
82-
let value = arguments?["value"] as? Any
83-
setContexts(key: key, value: value, result: result)
84-
85-
case "removeContexts":
86-
let arguments = call.arguments as? [String: Any?]
87-
let key = arguments?["key"] as? String
88-
removeContexts(key: key, result: result)
89-
9078
case "setExtra":
9179
let arguments = call.arguments as? [String: Any?]
9280
let key = arguments?["key"] as? String
@@ -266,39 +254,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
266254
return !name.isEmpty
267255
}
268256

269-
private func setContexts(key: String?, value: Any?, result: @escaping FlutterResult) {
270-
guard let key = key else {
271-
result("")
272-
return
273-
}
274-
275-
SentrySDK.configureScope { scope in
276-
if let dictionary = value as? [String: Any] {
277-
scope.setContext(value: dictionary, key: key)
278-
} else if let string = value as? String {
279-
scope.setContext(value: ["value": string], key: key)
280-
} else if let int = value as? Int {
281-
scope.setContext(value: ["value": int], key: key)
282-
} else if let double = value as? Double {
283-
scope.setContext(value: ["value": double], key: key)
284-
} else if let bool = value as? Bool {
285-
scope.setContext(value: ["value": bool], key: key)
286-
}
287-
result("")
288-
}
289-
}
290-
291-
private func removeContexts(key: String?, result: @escaping FlutterResult) {
292-
guard let key = key else {
293-
result("")
294-
return
295-
}
296-
SentrySDK.configureScope { scope in
297-
scope.removeContext(key: key)
298-
result("")
299-
}
300-
}
301-
302257
private func setExtra(key: String?, value: Any?, result: @escaping FlutterResult) {
303258
guard let key = key else {
304259
result("")

packages/flutter/lib/src/native/cocoa/binding.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,10 @@ final _objc_msgSend_1pl9qdv = objc.msgSendPointer
11571157
.asFunction<
11581158
void Function(
11591159
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<objc.ObjCSelector>)>();
1160+
late final _sel_setContextValue_forKey_ =
1161+
objc.registerName("setContextValue:forKey:");
1162+
late final _sel_removeContextForKey_ =
1163+
objc.registerName("removeContextForKey:");
11601164

11611165
/// SentryScope
11621166
class SentryScope extends objc.NSObject implements SentrySerializable {
@@ -1183,6 +1187,20 @@ class SentryScope extends objc.NSObject implements SentrySerializable {
11831187
void clearBreadcrumbs() {
11841188
_objc_msgSend_1pl9qdv(this.ref.pointer, _sel_clearBreadcrumbs);
11851189
}
1190+
1191+
/// Sets context values which will overwrite SentryEvent.context when event is
1192+
/// "enriched" with scope before sending event.
1193+
void setContextValue(objc.NSDictionary value,
1194+
{required objc.NSString forKey}) {
1195+
_objc_msgSend_pfv6jd(this.ref.pointer, _sel_setContextValue_forKey_,
1196+
value.ref.pointer, forKey.ref.pointer);
1197+
}
1198+
1199+
/// Remove the context for the specified key.
1200+
void removeContextForKey(objc.NSString key) {
1201+
_objc_msgSend_xtuoz7(
1202+
this.ref.pointer, _sel_removeContextForKey_, key.ref.pointer);
1203+
}
11861204
}
11871205

11881206
void _ObjCBlock_ffiVoid_SentryScope_fnPtrTrampoline(

0 commit comments

Comments
 (0)