-
-
Notifications
You must be signed in to change notification settings - Fork 245
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
feat(js-sdk): add script loader to set up Sentry Javascript SDK #2406
Merged
buenaflor
merged 77 commits into
feat/js-sdk-integration
from
feat/sentry-script-loader
Dec 10, 2024
Merged
Changes from 71 commits
Commits
Show all changes
77 commits
Select commit
Hold shift + click to select a range
4524383
update
buenaflor 02bde65
update
buenaflor 3a99531
updaet
buenaflor bb94847
Update sentry_js_sdk_version.dart
buenaflor c6c1cbd
temporary ci change
buenaflor b837667
fix test
buenaflor e535701
fix compilation
buenaflor 0f8c6fc
fix compilation
buenaflor 01c2738
fix
buenaflor e5179e4
fix test
buenaflor 59b4ab8
update
buenaflor 620071e
Merge branch 'main' into feat/sentry-script-loader
buenaflor c3328a4
fix test
buenaflor f64f749
fix test
buenaflor 9aba0c4
update
buenaflor 818f8a4
update
buenaflor 15bb56c
update
buenaflor e2b2251
fix analyze
buenaflor adad5d9
update
buenaflor e01a549
update
buenaflor 818d56e
update validation
buenaflor bd3c46e
update
buenaflor 797097b
update
buenaflor 009c8ea
update
buenaflor 3b3f78c
rethrow on automated test mode
buenaflor 8be2059
update rethrow
buenaflor 8de74d3
update
buenaflor ce02246
update tests
buenaflor fca9add
update appending
buenaflor b3009c8
update web
buenaflor 15d16db
add trusted types and add tests
buenaflor a7b6054
formatting
buenaflor 2ef43fd
update comment
buenaflor 21b1382
add as browser test
buenaflor af1f7cb
fix min_version
buenaflor dbfe736
fix warnings
buenaflor 8538c9c
add another test file
buenaflor 90aaedc
update
buenaflor c57b950
update comment
buenaflor 42aa1f8
try out web integration test
buenaflor 77dd5d8
update
buenaflor f9c4532
use ubuntu latest for web
buenaflor fab4044
see if it runs
buenaflor b128ede
update
buenaflor 3e3a87e
fix job
buenaflor de34883
fix job
buenaflor cb21548
run chromedriver in background
buenaflor fdd6353
run correct port
buenaflor 7115c4b
update
buenaflor 621da63
update
buenaflor 5f4db66
setup chrome action
buenaflor 927dc65
update
buenaflor a8f4b45
run chrome first
buenaflor d7daa56
try
buenaflor 57cf95f
update
buenaflor 09264b1
update
buenaflor e79977f
remove file
buenaflor a433e9f
update
buenaflor 4987b8a
update
buenaflor 5c237a2
fix tests
buenaflor 1c9010a
update
buenaflor cac20f3
update docs and test cases
buenaflor d441a0b
add todo
buenaflor fb2494f
update
buenaflor 5c4c498
update
buenaflor f78adc0
add doc and fix tests
buenaflor 0584a74
update tests
buenaflor 8597192
update
buenaflor 6c57cc6
update
buenaflor 485f196
Update web_sdk_test.dart
buenaflor bf38ab3
add test with automatedTestMode false
buenaflor cfd36a0
update
buenaflor 627ac06
remove debug
buenaflor 7f02fa2
remove fn
buenaflor 1a75a61
update restore flutter onError
buenaflor 8592e11
fix analyze
buenaflor 3f97c2e
Update flutter.yml
buenaflor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ on: | |
branches: | ||
- main | ||
- release/** | ||
- feat/** | ||
pull_request: | ||
paths: | ||
- '.github/workflows/flutter.yml' | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import 'package:integration_test/integration_test_driver.dart'; | ||
|
||
Future<void> main() => integrationDriver(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import 'package:sentry_flutter/sentry_flutter.dart'; | ||
|
||
const fakeDsn = 'https://abc@def.ingest.sentry.io/1234567'; | ||
|
||
void defaultTestOptionsInitializer(SentryFlutterOptions options) { | ||
options.dsn = fakeDsn; | ||
// ignore: invalid_use_of_internal_member | ||
options.automatedTestMode = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// ignore_for_file: invalid_use_of_internal_member | ||
|
||
@TestOn('browser') | ||
library flutter_test; | ||
|
||
// ignore: avoid_web_libraries_in_flutter | ||
import 'dart:js'; | ||
import 'dart:js_interop'; | ||
|
||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:integration_test/integration_test.dart'; | ||
import 'package:sentry_flutter/sentry_flutter.dart'; | ||
import 'package:sentry_flutter_example/main.dart' as app; | ||
|
||
import 'utils.dart'; | ||
|
||
// We can use dart:html, this is meant to be tested on Flutter Web and not WASM | ||
// This integration test can be changed later when we actually do support WASM | ||
|
||
void main() { | ||
group('Web SDK Integration', () { | ||
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); | ||
|
||
tearDown(() async { | ||
await Sentry.close(); | ||
}); | ||
|
||
testWidgets('Sentry JS SDK is callable', (tester) async { | ||
await SentryFlutter.init(defaultTestOptionsInitializer, | ||
appRunner: () async { | ||
await tester.pumpWidget(const app.MyApp()); | ||
}); | ||
|
||
const expectedMessage = 'test message'; | ||
final beforeSendFn = JsFunction.withThis((thisArg, event, hint) { | ||
final actualMessage = event['message']; | ||
expect(actualMessage, equals(expectedMessage)); | ||
|
||
return event; | ||
}); | ||
|
||
final Map<String, dynamic> options = { | ||
'dsn': app.exampleDsn, | ||
'beforeSend': beforeSendFn, | ||
'defaultIntegrations': [], | ||
}; | ||
|
||
final sentry = context['Sentry'] as JsObject; | ||
sentry.callMethod('init', [JsObject.jsify(options)]); | ||
|
||
sentry.callMethod('captureMessage', [expectedMessage.toJS]); | ||
buenaflor marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:meta/meta.dart'; | ||
|
||
import '../../sentry_flutter.dart'; | ||
import '../web/script_loader/sentry_script_loader.dart'; | ||
import '../web/sentry_js_bundle.dart'; | ||
|
||
class WebSdkIntegration implements Integration<SentryFlutterOptions> { | ||
WebSdkIntegration(this._scriptLoader); | ||
|
||
final SentryScriptLoader _scriptLoader; | ||
|
||
@internal | ||
static const name = 'webSdkIntegration'; | ||
|
||
@override | ||
FutureOr<void> call(Hub hub, SentryFlutterOptions options) async { | ||
try { | ||
final scripts = options.platformChecker.isDebugMode() | ||
? debugScripts | ||
: productionScripts; | ||
await _scriptLoader.loadWebSdk(scripts); | ||
|
||
options.sdk.addIntegration(name); | ||
} catch (exception, stackTrace) { | ||
options.logger( | ||
SentryLevel.fatal, | ||
'$name failed to be installed', | ||
exception: exception, | ||
stackTrace: stackTrace, | ||
); | ||
if (options.automatedTestMode) { | ||
rethrow; | ||
} | ||
} | ||
} | ||
|
||
@override | ||
FutureOr<void> close() { | ||
// no-op | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
flutter/lib/src/web/script_loader/html_script_dom_api.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import 'dart:async'; | ||
import 'dart:html'; | ||
import 'dart:js_util' as js_util; | ||
|
||
import '../../../sentry_flutter.dart'; | ||
import 'sentry_script_loader.dart'; | ||
|
||
Future<void> loadScript(String src, SentryOptions options, | ||
{String? integrity, | ||
String trustedTypePolicyName = defaultTrustedPolicyName}) { | ||
final completer = Completer<void>(); | ||
|
||
final script = ScriptElement() | ||
..crossOrigin = 'anonymous' | ||
..onLoad.listen((_) => completer.complete()) | ||
..onError.listen((event) => completer.completeError('Failed to load $src')); | ||
|
||
TrustedScriptUrl? trustedUrl; | ||
|
||
// If TrustedTypes are available, prepare a trusted URL | ||
final trustedTypes = js_util.getProperty<dynamic>(window, 'trustedTypes'); | ||
if (trustedTypes != null) { | ||
try { | ||
final policy = | ||
js_util.callMethod<dynamic>(trustedTypes as Object, 'createPolicy', [ | ||
trustedTypePolicyName, | ||
js_util.jsify({ | ||
'createScriptURL': (String url) => src, | ||
}) | ||
]); | ||
trustedUrl = | ||
js_util.callMethod(policy as Object, 'createScriptURL', [src]); | ||
} catch (e) { | ||
// will be caught by loadWebSdk | ||
throw TrustedTypesException(); | ||
} | ||
} | ||
|
||
if (trustedUrl != null) { | ||
js_util.setProperty(script, 'src', trustedUrl); | ||
} else { | ||
script.src = src; | ||
} | ||
|
||
if (integrity != null) { | ||
script.integrity = integrity; | ||
} | ||
|
||
// JS SDK needs to be loaded before everything else | ||
final head = document.head; | ||
if (head != null) { | ||
if (head.hasChildNodes()) { | ||
head.insertBefore(script, head.firstChild); | ||
} else { | ||
head.append(script); | ||
} | ||
} | ||
return completer.future; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import '../../../sentry_flutter.dart'; | ||
import 'sentry_script_loader.dart'; | ||
|
||
Future<void> loadScript(String src, SentryOptions options, | ||
{String? integrity, | ||
String trustedTypePolicyName = defaultTrustedPolicyName}) async {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export 'noop_script_dom_api.dart' | ||
if (dart.library.html) 'html_script_dom_api.dart' | ||
if (dart.library.js_interop) 'web_script_dom_api.dart'; |
60 changes: 60 additions & 0 deletions
60
flutter/lib/src/web/script_loader/sentry_script_loader.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:meta/meta.dart'; | ||
|
||
import '../../../sentry_flutter.dart'; | ||
import 'script_dom_api.dart'; | ||
|
||
@internal | ||
const String defaultTrustedPolicyName = 'sentry-dart'; | ||
|
||
class SentryScriptLoader { | ||
SentryScriptLoader(this._options); | ||
|
||
final SentryFlutterOptions _options; | ||
bool _scriptLoaded = false; | ||
|
||
/// Loads the scripts into the web page with support for Trusted Types security policy. | ||
/// | ||
/// The function handles three Trusted Types scenarios: | ||
/// 1. No Trusted Types configured - Scripts load normally | ||
/// 2. Custom Trusted Types policy - Uses provided policy name to create trusted URLs | ||
/// 3. Trusted Types forbidden - Scripts are not loaded | ||
/// | ||
/// The function is only executed successfully once and will be guarded by a flag afterwards. | ||
/// | ||
/// TrustedTypes implementation inspired by https://pub.dev/packages/google_identity_services_web | ||
Future<void> loadWebSdk(List<Map<String, String>> scripts, | ||
{String trustedTypePolicyName = defaultTrustedPolicyName}) async { | ||
if (_scriptLoaded) return; | ||
|
||
try { | ||
await Future.forEach(scripts, (Map<String, String> script) async { | ||
final url = script['url']; | ||
final integrity = script['integrity']; | ||
|
||
if (url != null) { | ||
await loadScript(url, _options, | ||
integrity: integrity, | ||
trustedTypePolicyName: trustedTypePolicyName); | ||
} | ||
}); | ||
|
||
_scriptLoaded = true; | ||
_options.logger(SentryLevel.debug, | ||
'JS SDK integration: all Sentry scripts loaded successfully.'); | ||
} catch (e) { | ||
_options.logger(SentryLevel.error, 'Failed to load Sentry scripts: $e'); | ||
if (_options.automatedTestMode) { | ||
rethrow; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Exception thrown if the Trusted Types feature is supported, enabled, and it | ||
/// has prevented this loader from injecting the Sentry JS SDK | ||
@internal | ||
class TrustedTypesException implements Exception { | ||
TrustedTypesException(); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will remove before merging, this is so ci runs since we merge into another feat branch