diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8471f7..1aaea93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: name: Flutter version ${{ matrix.flutter_version }} strategy: matrix: - flutter_version: ['2.0.5', '1.20.4', '2.5.1'] + flutter_version: ['2.0.6', '2.2.3', '2.5.3', '2.8.0'] steps: - uses: actions/checkout@v2 @@ -91,7 +91,7 @@ jobs: name: Flutter version ${{ matrix.flutter_version }} (iOS) strategy: matrix: - flutter_version: ['1.20.4', '2.5.1'] + flutter_version: ['2.0.6', '2.2.3', '2.5.3', '2.8.0'] steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index ba114aa..0135026 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ local.properties **/pana-report **/.pubignore + +**/.DS_Store diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index dd816eb..0000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Defines a default set of lint rules enforced for -# projects at Google. For details and rationale, -# see https://github.com/dart-lang/pedantic#enabled-lints. -include: package:pedantic/analysis_options.yaml - -# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. -# Uncomment to specify additional rules. -# linter: -# rules: -# - camel_case_types - -analyzer: - exclude: - - 'example/**' diff --git a/rollbar_dart/CHANGELOG.md b/rollbar_dart/CHANGELOG.md index c14078a..a3fc616 100644 --- a/rollbar_dart/CHANGELOG.md +++ b/rollbar_dart/CHANGELOG.md @@ -1,3 +1,5 @@ -## 0.1.0-beta +## 0.2.0-beta +- Added null-safety. +## 0.1.0-beta - Initial version of rollbar-dart. diff --git a/rollbar_dart/analysis_options.yaml b/rollbar_dart/analysis_options.yaml deleted file mode 120000 index c9e0d9f..0000000 --- a/rollbar_dart/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -../analysis_options.yaml \ No newline at end of file diff --git a/rollbar_dart/analysis_options.yaml b/rollbar_dart/analysis_options.yaml new file mode 100644 index 0000000..e7f6299 --- /dev/null +++ b/rollbar_dart/analysis_options.yaml @@ -0,0 +1,32 @@ +# Defines a default set of lint rules enforced for +# projects at Google. For details and rationale, +# see https://github.com/dart-lang/pedantic#enabled-lints. + +# include: package:lints/core.yaml +include: package:lints/recommended.yaml + +# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. +# Uncomment to specify additional rules. +# linter: +# rules: +# - camel_case_types + +linter: + rules: + - always_declare_return_types + - prefer_single_quotes + - sort_child_properties_last + - unawaited_futures + - unsafe_html + - use_full_hex_values_for_flutter_colors + # - cancel_subscriptions + # - close_sinks + # - comment_references + # - one_member_abstracts + # - only_throw_errors + # - package_api_docs + # - prefer_final_in_for_each + +analyzer: + exclude: + - 'example/**' diff --git a/rollbar_dart/lib/rollbar.dart b/rollbar_dart/lib/rollbar.dart index d1555f4..f9841be 100644 --- a/rollbar_dart/lib/rollbar.dart +++ b/rollbar_dart/lib/rollbar.dart @@ -1,5 +1,6 @@ library rollbar; +export 'src/logging.dart'; export 'src/rollbar.dart'; export 'src/config.dart'; export 'src/sender.dart'; diff --git a/rollbar_dart/lib/src/api/payload/body.dart b/rollbar_dart/lib/src/api/payload/body.dart index 6727a14..12f9b67 100644 --- a/rollbar_dart/lib/src/api/payload/body.dart +++ b/rollbar_dart/lib/src/api/payload/body.dart @@ -4,13 +4,13 @@ import 'frame.dart'; /// Container class with the error or message to be sent to Rollbar. abstract class Body { Map toJson(); - List getTraces(); + List? getTraces(); static Body empty() { return Message()..body = ''; } - static Body fromMap(Map attributes) { + static Body? fromMap(Map attributes) { if (attributes.containsKey('trace')) { return TraceInfo.fromMap(attributes); } else if (attributes.containsKey('message')) { @@ -23,9 +23,9 @@ abstract class Body { /// An individual error with its corresponding stack trace if available. class TraceInfo implements Body { - List frames; - ExceptionInfo exception; - String rawTrace; + late List frames; + ExceptionInfo? exception; + String? rawTrace; TraceInfo() { frames = []; @@ -45,7 +45,7 @@ class TraceInfo implements Body { }; if (exception != null) { - traceInfo['trace']['exception'] = exception.toJson(); + traceInfo['trace']['exception'] = exception!.toJson(); } if (rawTrace != null) { @@ -55,7 +55,7 @@ class TraceInfo implements Body { return traceInfo; } - static TraceInfo fromMap(Map attributes) { + static TraceInfo? fromMap(Map attributes) { if (!attributes.containsKey('trace')) { return null; } @@ -87,13 +87,13 @@ class TraceInfo implements Body { /// A chain of multiple errors, where the first one on the list represents the /// root cause of the error. class TraceChain implements Body { - List traces; + List? traces; @override Map toJson() { return { - 'trace_chain': traces.map((v) { - return v.toJson()['trace']; + 'trace_chain': traces!.map((v) { + return v!.toJson()['trace']; }).toList() }; } @@ -107,21 +107,21 @@ class TraceChain implements Body { } @override - List getTraces() { + List? getTraces() { return traces; } } /// A text message to be sent to Rollbar. class Message implements Body { - String body; + String? body; @override List getTraces() { return []; } - static Message fromMap(Map attributes) { + static Message? fromMap(Map attributes) { if (!attributes.containsKey('message')) { return null; } diff --git a/rollbar_dart/lib/src/api/payload/client.dart b/rollbar_dart/lib/src/api/payload/client.dart index e9fa30c..2c248ff 100644 --- a/rollbar_dart/lib/src/api/payload/client.dart +++ b/rollbar_dart/lib/src/api/payload/client.dart @@ -1,15 +1,15 @@ class Client { - String locale; + String? locale; - String hostname; + String? hostname; - String os; + String? os; - String osVersion; + String? osVersion; - String rootPackage; + String? rootPackage; - Map dart; + Map? dart; /// Converts the object into a Json encodable map. /// diff --git a/rollbar_dart/lib/src/api/payload/data.dart b/rollbar_dart/lib/src/api/payload/data.dart index 7232495..557a019 100644 --- a/rollbar_dart/lib/src/api/payload/data.dart +++ b/rollbar_dart/lib/src/api/payload/data.dart @@ -4,19 +4,19 @@ import 'level.dart'; /// Contains the data for the occurrence to be sent to Rollbar. class Data { - Map notifier; - String environment; - Client client; - String platform; - String language; - String framework; - String codeVersion; - Level level; - int timestamp; - Body body; - Map custom; - Map platformPayload; - Map server; + late Map notifier; + String? environment; + late Client client; + String? platform; + late String language; + String? framework; + String? codeVersion; + Level? level; + int? timestamp; + late Body body; + Map? custom; + Map? platformPayload; + Map? server; Map toJson() { var result = { diff --git a/rollbar_dart/lib/src/api/payload/exception_info.dart b/rollbar_dart/lib/src/api/payload/exception_info.dart index 24be2f2..10f20c8 100644 --- a/rollbar_dart/lib/src/api/payload/exception_info.dart +++ b/rollbar_dart/lib/src/api/payload/exception_info.dart @@ -1,8 +1,8 @@ /// Contains all the error details except the stack trace. class ExceptionInfo { - String clazz; - String message; - String description; + String? clazz; + String? message; + String? description; Map toJson() { var result = {'class': clazz, 'message': message}; diff --git a/rollbar_dart/lib/src/api/payload/frame.dart b/rollbar_dart/lib/src/api/payload/frame.dart index a11a0d4..90f5cc8 100644 --- a/rollbar_dart/lib/src/api/payload/frame.dart +++ b/rollbar_dart/lib/src/api/payload/frame.dart @@ -1,10 +1,10 @@ /// Contains the information of a single frame in a stack trace. class Frame { - int colno; - int lineno; - String method; - String filename; - String className; + int? colno; + int? lineno; + String? method; + String? filename; + String? className; Map toJson() { var result = {}; @@ -29,10 +29,10 @@ class Frame { static Frame fromMap(Map attributes) { var result = Frame(); if (attributes.containsKey('colno')) { - result.colno = attributes['colno'] as int; + result.colno = attributes['colno'] as int?; } if (attributes.containsKey('lineno')) { - result.lineno = attributes['lineno'] as int; + result.lineno = attributes['lineno'] as int?; } if (attributes.containsKey('method')) { result.method = attributes['method']; diff --git a/rollbar_dart/lib/src/api/payload/level.dart b/rollbar_dart/lib/src/api/payload/level.dart index 1d0db2d..c4b9b5f 100644 --- a/rollbar_dart/lib/src/api/payload/level.dart +++ b/rollbar_dart/lib/src/api/payload/level.dart @@ -1,10 +1,10 @@ /// The level of an occurrence. enum Level { debug, info, warning, error, critical } -extension LevelExtension on Level { +extension LevelExtension on Level? { // The 'foundation' library, which contains `describeEnum`, is part of flutter, // so we don't want to add it as a dependency in rollbar-dart. - String get name { + String? get name { switch (this) { case Level.debug: return 'debug'; @@ -16,7 +16,8 @@ extension LevelExtension on Level { return 'error'; case Level.critical: return 'critical'; + default: + return null; } - return null; } } diff --git a/rollbar_dart/lib/src/api/payload/payload.dart b/rollbar_dart/lib/src/api/payload/payload.dart index 773bb2d..7cbf048 100644 --- a/rollbar_dart/lib/src/api/payload/payload.dart +++ b/rollbar_dart/lib/src/api/payload/payload.dart @@ -3,8 +3,8 @@ import 'data.dart'; /// Represents the payload to be sent to Rollbar. A successfully constructed Payload matches Rollbar's /// spec, and can be POSTed to the correct endpoint when serialized as JSON. class Payload { - String accessToken; - Data data; + String? accessToken; + late Data data; Map toJson() { return {'access_token': accessToken, 'data': data.toJson()}; diff --git a/rollbar_dart/lib/src/api/response.dart b/rollbar_dart/lib/src/api/response.dart index fb0ea8f..a32e814 100644 --- a/rollbar_dart/lib/src/api/response.dart +++ b/rollbar_dart/lib/src/api/response.dart @@ -1,8 +1,8 @@ /// Represents the response from the Rollbar API. class Response { - int err; - String message; - Result result; + int? err; + String? message; + Result? result; bool isError() { return err != null && err != 0; @@ -10,5 +10,5 @@ class Response { } class Result { - String uuid; + String? uuid; } diff --git a/rollbar_dart/lib/src/config.dart b/rollbar_dart/lib/src/config.dart index e53a7b5..0531ede 100644 --- a/rollbar_dart/lib/src/config.dart +++ b/rollbar_dart/lib/src/config.dart @@ -1,4 +1,4 @@ -import 'package:rollbar_dart/src/http_sender.dart'; +import 'http_sender.dart'; import 'transformer.dart'; import 'sender.dart'; @@ -7,13 +7,13 @@ import 'sender.dart'; class Config { final String accessToken; final String endpoint; - final String environment; - final String framework; - final String codeVersion; - final String package; - final bool handleUncaughtErrors; - final bool includePlatformLogs; - final Transformer Function(Config) transformer; + final String? environment; + final String? framework; + final String? codeVersion; + final String? package; + final bool? handleUncaughtErrors; + final bool? includePlatformLogs; + final Transformer Function(Config)? transformer; final Sender Function(Config) sender; Config._( @@ -66,23 +66,23 @@ class Config { class ConfigBuilder { final String accessToken; String endpoint = 'https://api.rollbar.com/api/1/item/'; - String environment; - String framework; - String codeVersion; - String package; + String? environment; + String? framework; + String? codeVersion; + String? package; - bool handleUncaughtErrors = false; - bool includePlatformLogs = false; + bool? handleUncaughtErrors = false; + bool? includePlatformLogs = false; /// If [handleUncaughtErrors] is enabled, the transformer function *must* be a static /// or free function, and cannot be a closure or instance function, since it will need /// to be passed to an error handler isolate as a message. - Transformer Function(Config) transformer; + Transformer Function(Config)? transformer; /// If [handleUncaughtErrors] is enabled, the sender factory *must* be a static /// or free function, and cannot be a closure or instance function, since it will need /// to be passed to an error handler isolate as a message. - Sender Function(Config) sender; + Sender Function(Config)? sender; ConfigBuilder(this.accessToken); diff --git a/rollbar_dart/lib/src/core_notifier.dart b/rollbar_dart/lib/src/core_notifier.dart index f8a30b9..a28e201 100644 --- a/rollbar_dart/lib/src/core_notifier.dart +++ b/rollbar_dart/lib/src/core_notifier.dart @@ -20,18 +20,20 @@ import 'sender.dart'; /// - Send the occurrence payload to Rollbar via a [Sender]. class CoreNotifier { final Config _config; - final Sender _sender; - final Transformer _transformer; + final Sender? _sender; + final Transformer? _transformer; - static const NOTIFIER_VERSION = '0.1.0-beta'; - static const NOTIFIER_NAME = 'rollbar-dart'; + // notifierVersion to be updated with each new release: + static const notifierVersion = '0.2.0-beta'; + + static const notifierName = 'rollbar-dart'; CoreNotifier(this._config) : _sender = _make(_config, _config.sender), _transformer = _make(_config, _config.transformer); - Future log( - Level level, dynamic error, StackTrace stackTrace, String message) async { + Future log(Level level, dynamic error, StackTrace? stackTrace, + String? message) async { var body = await _prepareBody(message, error, stackTrace); var client = Client() @@ -54,7 +56,7 @@ class CoreNotifier { ..codeVersion = _config.codeVersion ..client = client ..environment = _config.environment - ..notifier = {'version': NOTIFIER_VERSION, 'name': NOTIFIER_NAME}; + ..notifier = {'version': notifierVersion, 'name': notifierName}; if (client.rootPackage != null) { // Root detection compatibility, currently checked under the server element @@ -64,18 +66,18 @@ class CoreNotifier { } if (_transformer != null) { - data = await _transformer.transform(error, stackTrace, data); + data = await _transformer!.transform(error, stackTrace, data); } var payload = Payload() ..accessToken = _config.accessToken ..data = data; - return await _sender.send(payload.toJson()); + return await _sender!.send(payload.toJson()); } Future _prepareBody( - String message, dynamic error, StackTrace stackTrace) async { + String? message, dynamic error, StackTrace? stackTrace) async { if (error != null) { return await _prepareTracePayload(error, stackTrace, message); } else { @@ -84,7 +86,7 @@ class CoreNotifier { } Future _prepareTracePayload( - dynamic error, StackTrace trace, String description) async { + dynamic error, StackTrace? trace, String? description) async { var traceInfo = TraceInfo() ..exception = _getExceptionInfo(error, description); @@ -106,7 +108,7 @@ class CoreNotifier { return traceInfo; } - static ExceptionInfo _getExceptionInfo(dynamic error, String description) { + static ExceptionInfo _getExceptionInfo(dynamic error, String? description) { ExceptionInfo result; if (error is ExceptionInfo) { result = error; @@ -121,7 +123,7 @@ class CoreNotifier { return result; } - static T _make(Config config, T Function(Config) factoryFunction) { + static T? _make(Config config, T Function(Config)? factoryFunction) { if (factoryFunction == null) { return null; } else { diff --git a/rollbar_dart/lib/src/http_sender.dart b/rollbar_dart/lib/src/http_sender.dart index 25e61da..8ec9d42 100644 --- a/rollbar_dart/lib/src/http_sender.dart +++ b/rollbar_dart/lib/src/http_sender.dart @@ -1,7 +1,7 @@ import 'package:http/http.dart' as http; import 'dart:convert'; -import 'sender.dart'; import 'api/response.dart'; +import 'sender.dart'; /// Default HTTP [Sender] implementation. class HttpSender implements Sender { @@ -12,7 +12,11 @@ class HttpSender implements Sender { /// Sends the provided payload as the body of POST request to the configured endpoint. @override - Future send(Map payload) async { + Future send(Map? payload) async { + if (payload == null) { + return null; + } + final headers = { 'User-Agent': 'rollbar-dart', 'Content-Type': 'application/json', @@ -30,6 +34,7 @@ class HttpSender implements Sender { Future toRollbarResponse(Future response) async { Map data = json.decode((await response).body); + var result = Response(); if (data.containsKey('err')) { result.err = data['err'].toInt(); diff --git a/rollbar_dart/lib/src/logging.dart b/rollbar_dart/lib/src/logging.dart index 5335744..208f0a3 100644 --- a/rollbar_dart/lib/src/logging.dart +++ b/rollbar_dart/lib/src/logging.dart @@ -1,23 +1,26 @@ import 'dart:developer' as developer; -void info(String message, dynamic error) { - _log(800, message, error); -} +/// Internal SDK logging (for the SDK use only!!!): +class Logging { + static void info(String message, dynamic error) { + _log(800, message, error); + } -void warning(String message, dynamic error) { - _log(900, message, error); -} + static void warning(String message, dynamic error) { + _log(900, message, error); + } -void error(String message, dynamic error) { - _log(1000, message, error); -} + static void error(String message, dynamic error) { + _log(1000, message, error); + } -void _log(int level, String message, dynamic error) { - var logName = 'com.rollbar.rollbar-dart'; - if (error != null) { - developer.log(message, - level: level, name: logName, error: error.toString()); - } else { - developer.log(message, level: level, name: logName); + static void _log(int level, String message, dynamic error) { + var logName = 'com.rollbar.rollbar-dart'; + if (error != null) { + developer.log(message, + level: level, name: logName, error: error.toString()); + } else { + developer.log(message, level: level, name: logName); + } } } diff --git a/rollbar_dart/lib/src/rollbar.dart b/rollbar_dart/lib/src/rollbar.dart index f79b00f..8c31e5a 100644 --- a/rollbar_dart/lib/src/rollbar.dart +++ b/rollbar_dart/lib/src/rollbar.dart @@ -4,7 +4,7 @@ import 'dart:isolate'; import 'config.dart'; import 'core_notifier.dart'; import 'uncaught_error.dart'; -import 'logging.dart' as logging; +import 'logging.dart'; import 'api/response.dart'; import 'api/payload/level.dart'; @@ -22,7 +22,7 @@ class Rollbar { /// Some initialization operations are asynchronous, such as initializing /// the [Isolate] that handles uncaught errors. This future can be awaited /// to ensure all async initialization operations are complete. - Future ensureInitialized() async { + Future ensureInitialized() async { return _errorHandler; } @@ -32,7 +32,7 @@ class Rollbar { /// ```dart /// Isolate.current.addErrorListener(await rollbar.errorHandler); /// ``` - Future get errorHandler async { + Future? get errorHandler async { return (await _errorHandler).errorHandlerPort; } @@ -111,19 +111,21 @@ class Rollbar { try { return await action; } on Exception catch (e) { - logging.error('Internal error encountered while initializing Rollbar', e); + Logging.error('Internal error encountered while initializing Rollbar', e); rethrow; } } - static Future _processResponse(Future rollbarAction) async { + static Future _processResponse(Future rollbarAction) async { try { var response = await rollbarAction; - if (response.isError()) { - logging.error('Error while sending data to Rollbar', response.message); + if (response == null) { + Logging.error('No response while sending data to Rollbar', null); + } else if (response.isError()) { + Logging.error('Error while sending data to Rollbar', response.message); } } on Exception catch (e) { - logging.error( + Logging.error( 'Internal error encountered while sending data to Rollbar', e); rethrow; } diff --git a/rollbar_dart/lib/src/sender.dart b/rollbar_dart/lib/src/sender.dart index fa0cdfb..06dd447 100644 --- a/rollbar_dart/lib/src/sender.dart +++ b/rollbar_dart/lib/src/sender.dart @@ -2,5 +2,5 @@ import 'api/response.dart'; /// Sender interface to send payload data to Rollbar. abstract class Sender { - Future send(Map payload); + Future send(Map? payload); } diff --git a/rollbar_dart/lib/src/trace.dart b/rollbar_dart/lib/src/trace.dart index c569d21..6614381 100644 --- a/rollbar_dart/lib/src/trace.dart +++ b/rollbar_dart/lib/src/trace.dart @@ -35,10 +35,10 @@ ParseTraceResult _parseNativeTrace(String traceString) { } class ParseTraceResult { - Trace trace; + late Trace trace; /// The original, unparsed trace. It will only be present when the original trace /// might require further backend processing, eg. symbolication. Otherwise this /// value will be null. - String rawTrace; + String? rawTrace; } diff --git a/rollbar_dart/lib/src/transformer.dart b/rollbar_dart/lib/src/transformer.dart index 3f86606..6d16a35 100644 --- a/rollbar_dart/lib/src/transformer.dart +++ b/rollbar_dart/lib/src/transformer.dart @@ -7,5 +7,5 @@ abstract class Transformer { /// Implementations are free to mutate the [Data] object they receive, /// reuse some of its parts, or create an entirely new object, but a // `Data` instance *must* be returned in all cases. - Future transform(dynamic error, StackTrace trace, Data data); + Future transform(dynamic error, StackTrace? trace, Data data); } diff --git a/rollbar_dart/lib/src/uncaught_error.dart b/rollbar_dart/lib/src/uncaught_error.dart index e7ffa2b..2c1e3be 100644 --- a/rollbar_dart/lib/src/uncaught_error.dart +++ b/rollbar_dart/lib/src/uncaught_error.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'config.dart'; import 'core_notifier.dart'; -import 'logging.dart' as logging; +import 'logging.dart'; import 'api/payload/exception_info.dart'; import 'api/payload/level.dart'; @@ -13,7 +13,7 @@ import 'api/payload/level.dart'; class UncaughtErrorHandler { Config _config; - Future _errorPort; + late Future _errorPort; UncaughtErrorHandler._(this._config) { _errorPort = _getErrorMessageHandler(_config); @@ -22,15 +22,17 @@ class UncaughtErrorHandler { static Future build(Config config) async { var handler = UncaughtErrorHandler._(config); - if (config.handleUncaughtErrors) { + if (config.handleUncaughtErrors!) { var errorPort = await handler._errorPort; - Isolate.current.addErrorListener(errorPort); + if (errorPort != null) { + Isolate.current.addErrorListener(errorPort); + } } return handler; } - Future get errorHandlerPort { + Future get errorHandlerPort { return _errorPort; } @@ -44,8 +46,8 @@ class UncaughtErrorHandler { } } - Future _getErrorMessageHandler(Config config) async { - if (config.handleUncaughtErrors) { + Future _getErrorMessageHandler(Config config) async { + if (config.handleUncaughtErrors!) { var receivePort = ReceivePort(); await Isolate.spawn(_handleError, receivePort.sendPort); var errorPort = await receivePort.first; @@ -64,7 +66,7 @@ class UncaughtErrorHandler { sendPort.send(port.sendPort); - CoreNotifier rollbarCore; + CoreNotifier? rollbarCore; await for (var msg in port) { try { @@ -76,21 +78,21 @@ class UncaughtErrorHandler { var trace = _getTrace(msg[1]); await rollbarCore.log(Level.error, error, trace, null); } else { - logging.warning( + Logging.warning( 'An error has been reported to the uncaught error handler before the Rollbar instance was configured', error); } } } on Exception catch (e) { - logging.error('Failed to process rollbar error message', e); + Logging.error('Failed to process rollbar error message', e); } } } on Exception catch (e) { - logging.error('The rollbar uncaught error handler has crashed', e); + Logging.error('The rollbar uncaught error handler has crashed', e); } } - static StackTrace _getTrace(dynamic trace) { + static StackTrace? _getTrace(dynamic trace) { if (trace is StackTrace) { return trace; } else if (trace is String) { @@ -115,7 +117,7 @@ class UncaughtErrorHandler { static dynamic _tryParseError(String error) { var match = _exceptionClassPattern.firstMatch(error); if (match != null) { - var exceptionPart = match.group(0); + var exceptionPart = match.group(0)!; // `length - 1` to remove colon var clazz = exceptionPart.substring(0, exceptionPart.length - 1).trim(); diff --git a/rollbar_dart/pubspec.yaml b/rollbar_dart/pubspec.yaml index e6e6d20..bff54f0 100644 --- a/rollbar_dart/pubspec.yaml +++ b/rollbar_dart/pubspec.yaml @@ -1,19 +1,20 @@ name: rollbar_dart description: Connect your Dart applications to Rollbar for error reporting -version: 0.1.0-beta +version: 0.2.0-beta homepage: https://www.rollbar.com documentation: https://docs.rollbar.com/docs/flutter#dart repository: https://github.com/rollbar/rollbar-flutter environment: - sdk: '>=2.8.1 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: http: '>=0.12.2 <0.14.0' stack_trace: '>=1.9.5 <1.11.0' dev_dependencies: - pedantic: '>=1.9.2 <1.12.0' + lints: ^1.0.1 test: ^1.14.4 - mockito: '>=4.1.0 <=5.0.7' + build_runner: ^1.11.0 + mockito: '>=5.0.0 <6.0.0' pubspec_parse: '>=0.1.8 <=1.0.0' diff --git a/rollbar_dart/test/ReadMe.md b/rollbar_dart/test/ReadMe.md new file mode 100644 index 0000000..6964dac --- /dev/null +++ b/rollbar_dart/test/ReadMe.md @@ -0,0 +1,23 @@ +# References +https://pub.dev/documentation/mockito/latest/ + +# Notes + +## Useful Imports +import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; + + +## Generating Mocks + + // Annotation which generates the cat.mocks.dart library and the MockCat class. + @GenerateMocks([Cat]) + void main() { + // Create mock object. + var cat = MockCat(); + } + +To force the mock code generation run: + + dart run build_runner build + diff --git a/rollbar_dart/test/body_test.dart b/rollbar_dart/test/body_test.dart index 6c3ff82..cf69ef6 100644 --- a/rollbar_dart/test/body_test.dart +++ b/rollbar_dart/test/body_test.dart @@ -30,15 +30,18 @@ void main() { var asJson = jsonEncode(traceInfo.toJson()); - [(v) => TraceInfo.fromMap(v), (v) => Body.fromMap(v)].forEach((builder) { + for (var builder in [ + (v) => TraceInfo.fromMap(v), + (v) => Body.fromMap(v) + ]) { var recovered = builder(jsonDecode(asJson)) as TraceInfo; - expect(recovered.exception.clazz, equals('TestException')); - expect( - recovered.exception.message, equals('Attempted to test some code')); + expect(recovered.exception!.clazz, equals('TestException')); + expect(recovered.exception!.message, + equals('Attempted to test some code')); expect(recovered.frames.length, equals(2)); expect(recovered.rawTrace, equals('stack frame 1 2 3')); - }); + } }); test('TraceChain json roundtrip serialization test', () { @@ -70,16 +73,20 @@ void main() { var chain = TraceChain()..traces = [traceInfo1, traceInfo2]; var asJson = jsonEncode(chain.toJson()); - [(v) => TraceChain.fromMap(v), (v) => Body.fromMap(v)].forEach((builder) { + for (var builder in [ + (v) => TraceChain.fromMap(v), + (v) => Body.fromMap(v) + ]) { var recovered = builder(jsonDecode(asJson)) as TraceChain; - expect(recovered.traces.length, equals(2)); - expect(recovered.traces[0].frames.length, equals(2)); - expect(recovered.traces[0].frames[0].method, equals('inAChain')); + expect(recovered.traces!.length, equals(2)); + expect(recovered.traces![0]!.frames.length, equals(2)); + expect(recovered.traces![0]!.frames[0].method, equals('inAChain')); - expect(recovered.traces[1].frames.length, equals(1)); - expect(recovered.traces[1].frames[0].method, equals('_AnotherMethod')); - }); + expect(recovered.traces![1]!.frames.length, equals(1)); + expect( + recovered.traces![1]!.frames[0].method, equals('_AnotherMethod')); + } }); test('Message json roundtrip serialization test', () { @@ -87,11 +94,11 @@ void main() { var asJson = jsonEncode(message.toJson()); - [(v) => Message.fromMap(v), (v) => Body.fromMap(v)].forEach((builder) { + for (var builder in [(v) => Message.fromMap(v), (v) => Body.fromMap(v)]) { var recovered = builder(jsonDecode(asJson)) as Message; expect(recovered.body, equals('This is a test body')); - }); + } }); }); } diff --git a/rollbar_dart/test/client_server_utils.dart b/rollbar_dart/test/client_server_utils.dart index dba0339..1739b2b 100644 --- a/rollbar_dart/test/client_server_utils.dart +++ b/rollbar_dart/test/client_server_utils.dart @@ -6,11 +6,11 @@ import 'dart:isolate'; import 'package:rollbar_dart/rollbar.dart'; import 'package:rollbar_dart/src/api/response.dart'; -const PREFIX = 'http://raw:'; +const endpointPrefix = 'http://raw:'; Sender createTextSender(Config c) { - if (c.endpoint.startsWith(PREFIX)) { - var port = int.parse(c.endpoint.substring(PREFIX.length)); + if (c.endpoint.startsWith(endpointPrefix)) { + var port = int.parse(c.endpoint.substring(endpointPrefix.length)); return RawTextSender(port); } else { throw Exception('Invalid endpoint ${c.endpoint}'); @@ -20,15 +20,15 @@ Sender createTextSender(Config c) { /// Useful class to do some basic tests across isolates, without setting up a full blown /// HTTP server. It accepts and collects raw text over a TCP socket, 1 message per line. class RawTextSocket { - Isolate _isolate; - int _port; - ReceivePort _receivePort; + late Isolate _isolate; + int? _port; + late ReceivePort _receivePort; RawTextSocket._(); - String get endpoint => '$PREFIX$port'; + String get endpoint => '$endpointPrefix$port'; - Future _start() async { + Future _start() async { _receivePort = ReceivePort(); var tcpInfoPort = ReceivePort(); @@ -36,13 +36,13 @@ class RawTextSocket { _isolate = await Isolate.spawn( _server, [tcpInfoPort.sendPort, _receivePort.sendPort]); - int port = await tcpInfoPort.first; + var port = await tcpInfoPort.first; tcpInfoPort.close(); return port; } - int get port { + int? get port { return _port; } @@ -50,12 +50,12 @@ class RawTextSocket { /// do something like /// `var message = await server.messages.first.timeout(Duration(milliseconds: 500));` /// ...with an appropriate timeout for the scenario being tested. - Stream get messages { - return _receivePort.map((v) => v as String); + Stream get messages { + return _receivePort.map((v) => v as String?); } Future close() async { - final socket = await Socket.connect('localhost', port); + final socket = await Socket.connect('localhost', port!); try { socket.add('close\n'.codeUnits); } finally { @@ -113,7 +113,11 @@ class RawTextSender implements Sender { RawTextSender(this.port); @override - Future send(Map payload) async { + Future send(Map? payload) async { + if (payload == null) { + return null; + } + final socket = await Socket.connect('localhost', port); log('Client connected with port ${socket.port}'); try { diff --git a/rollbar_dart/test/core_notifier_test.dart b/rollbar_dart/test/core_notifier_test.dart index b284c8a..614164f 100644 --- a/rollbar_dart/test/core_notifier_test.dart +++ b/rollbar_dart/test/core_notifier_test.dart @@ -10,7 +10,7 @@ void main() { // Dart tests run from the current package directory, so we can just read pubspec.yaml var pubspecYaml = await File('pubspec.yaml').readAsString(); var pubspec = Pubspec.parse(pubspecYaml); - expect(CoreNotifier.NOTIFIER_VERSION, equals(pubspec.version.toString())); + expect(CoreNotifier.notifierVersion, equals(pubspec.version.toString())); }); }); } diff --git a/rollbar_dart/test/http_sender_test.dart b/rollbar_dart/test/http_sender_test.dart index a3f7bf7..37be5d3 100644 --- a/rollbar_dart/test/http_sender_test.dart +++ b/rollbar_dart/test/http_sender_test.dart @@ -2,11 +2,15 @@ import 'package:http/http.dart' as http; import 'package:rollbar_dart/src/http_sender.dart'; import 'package:test/test.dart'; import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; +import 'http_sender_test.mocks.dart'; + +@GenerateMocks([http.Response]) void main() { group('Response conversion', () { test('Can convert successful API response', () async { - var response = MockHttpResponse(); + var response = MockResponse(); when(response.body).thenReturn('''{ "err": 0, "result": { @@ -18,12 +22,12 @@ void main() { expect(rollbarResponse.err, equals(0)); expect(rollbarResponse.result, isNotNull); - expect(rollbarResponse.result.uuid, + expect(rollbarResponse.result!.uuid, equals('67ce3d7bfab14fd99218ae5c985071e7')); }); test('Can convert error API response', () async { - var response = MockHttpResponse(); + var response = MockResponse(); when(response.body).thenReturn('''{ "err": 1, "message": "invalid token" @@ -36,5 +40,3 @@ void main() { }); }); } - -class MockHttpResponse extends Mock implements http.Response {} diff --git a/rollbar_dart/test/http_sender_test.mocks.dart b/rollbar_dart/test/http_sender_test.mocks.dart new file mode 100644 index 0000000..c547932 --- /dev/null +++ b/rollbar_dart/test/http_sender_test.mocks.dart @@ -0,0 +1,50 @@ +// Mocks generated by Mockito 5.0.7 from annotations +// in rollbar_dart/test/http_sender_test.dart. +// Do not manually edit this file. + +import 'dart:typed_data' as _i2; + +import 'package:http/src/response.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references +// ignore_for_file: unnecessary_parenthesis + +// ignore_for_file: prefer_const_constructors + +// ignore_for_file: avoid_redundant_argument_values + +class _FakeUint8List extends _i1.Fake {} + +/// A class which mocks [Response]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockResponse extends _i1.Mock implements _i3.Response { + MockResponse() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.Uint8List get bodyBytes => + (super.noSuchMethod(Invocation.getter(#bodyBytes), + returnValue: _FakeUint8List()) as _i2.Uint8List); + @override + String get body => + (super.noSuchMethod(Invocation.getter(#body), returnValue: '') as String); + @override + int get statusCode => + (super.noSuchMethod(Invocation.getter(#statusCode), returnValue: 0) + as int); + @override + Map get headers => + (super.noSuchMethod(Invocation.getter(#headers), + returnValue: {}) as Map); + @override + bool get isRedirect => + (super.noSuchMethod(Invocation.getter(#isRedirect), returnValue: false) + as bool); + @override + bool get persistentConnection => + (super.noSuchMethod(Invocation.getter(#persistentConnection), + returnValue: false) as bool); +} diff --git a/rollbar_dart/test/rollbar_test.dart b/rollbar_dart/test/rollbar_test.dart index 56b1903..50e01dd 100644 --- a/rollbar_dart/test/rollbar_test.dart +++ b/rollbar_dart/test/rollbar_test.dart @@ -1,21 +1,16 @@ +import 'package:test/test.dart'; import 'package:mockito/mockito.dart'; + import 'package:rollbar_dart/rollbar.dart'; -import 'package:rollbar_dart/src/api/payload/body.dart'; -import 'package:rollbar_dart/src/api/payload/data.dart'; -import 'package:rollbar_dart/src/api/payload/exception_info.dart'; -import 'package:rollbar_dart/src/api/response.dart'; -import 'package:rollbar_dart/src/transformer.dart'; -import 'package:test/test.dart'; void main() { group('Rollbar notifier tests', () { - Rollbar rollbar; - Sender sender; + late Rollbar rollbar; + late Sender sender; setUp(() async { sender = MockSender(); - when(sender.send(any)) - .thenAnswer((_invocation) => Future.value(Response())); + when(sender.send(any)).thenAnswer((_) async => Response()); // handleUncaughtErrors must be set to false otherwise we can't use a closure with a // mock as the sender factory. var config = (ConfigBuilder('BlaBlaAccessToken') @@ -71,7 +66,7 @@ void main() { await rollbar.error(error, stackTrace); var payload = verify(await sender.send(captureAny)).captured.single; - Map data = payload['data']; + Map? data = payload['data']; expect(data, isNot(contains('code_version'))); expect(data, isNot(contains('framework'))); expect(data, isNot(contains('custom'))); @@ -111,10 +106,10 @@ void failingFunction() { class ExpandableTransformer implements Transformer { @override - Future transform(dynamic error, StackTrace trace, Data data) async { + Future transform(dynamic error, StackTrace? trace, Data data) async { // Simulates for example what we'd do with a PlatformException in Flutter if (error is ExpandableException) { - var traceChain = error.messages.map((message) { + List traceChain = error.messages.map((message) { return TraceInfo() ..frames = [] ..exception = (ExceptionInfo() @@ -122,7 +117,7 @@ class ExpandableTransformer implements Transformer { ..message = message); }).toList(); - traceChain.addAll(data.body.getTraces()); + traceChain.addAll(data.body.getTraces()!); data.body = TraceChain()..traces = traceChain; } return data; @@ -139,20 +134,26 @@ class ExpandableException implements Exception { } } -class MockSender extends Mock implements Sender {} +class MockSender extends Mock implements Sender { + @override + Future send(Map? payload) { + return super.noSuchMethod(Invocation.method(#send, [payload]), + returnValue: Future.value(Response())); + } +} -dynamic getPath(Map source, List path) { +dynamic getPath(Map? source, List path) { // We're in a test, let the bounds checker handle the edge case of an empty path for (var i = 0;; ++i) { var key = path[i]; expect(source, contains(key)); - var value = source[key]; + var value = source![key]; if (i == path.length - 1) { return value; } else { - source = value as Map; + source = value as Map?; } } } diff --git a/rollbar_dart/test/uncaught_error_handler_test.dart b/rollbar_dart/test/uncaught_error_handler_test.dart index 6d12f41..60983bd 100644 --- a/rollbar_dart/test/uncaught_error_handler_test.dart +++ b/rollbar_dart/test/uncaught_error_handler_test.dart @@ -10,8 +10,8 @@ import 'client_server_utils.dart'; void main() { group('UncaughtErrorHandler tests', () { - RawTextSocket _server; - UncaughtErrorHandler _handler; + late RawTextSocket _server; + late UncaughtErrorHandler _handler; setUp(() async { _server = await RawTextSocket.build(); @@ -29,12 +29,13 @@ void main() { try { await throwyMethodA(); } catch (error, trace) { - errorPort.send([error.toString(), trace.toString()]); + errorPort!.send([error.toString(), trace.toString()]); } var payloadJson = await _server.messages.first.timeout(Duration(milliseconds: 500)); - var payload = json.decode(payloadJson); + expect(payloadJson != null, equals(true)); + var payload = json.decode(payloadJson!); var data = payload['data']; expect(data['language'], equals('dart')); @@ -54,7 +55,8 @@ void main() { try { var payloadJson = await _server.messages.first.timeout(Duration(milliseconds: 500)); - var payload = json.decode(payloadJson); + expect(payloadJson != null, equals(true)); + var payload = json.decode(payloadJson!); var data = payload['data']; expect(data['language'], equals('dart')); @@ -97,8 +99,8 @@ Future _createErrorHandler(String endpoint) async { return await UncaughtErrorHandler.build(config); } -Future secondIsolateMethod(SendPort errorPort) async { - Isolate.current.addErrorListener(errorPort); +Future secondIsolateMethod(SendPort? errorPort) async { + Isolate.current.addErrorListener(errorPort!); // No try catch here, our handler should take care of it await inDifferentIsolate(); } diff --git a/rollbar_flutter/.gitignore b/rollbar_flutter/.gitignore index 68d8939..a20168a 100644 --- a/rollbar_flutter/.gitignore +++ b/rollbar_flutter/.gitignore @@ -1,5 +1,3 @@ -.DS_Store - # Files and directories created by pub .dart_tool/ .packages diff --git a/rollbar_flutter/CHANGELOG.md b/rollbar_flutter/CHANGELOG.md index 46e9d36..61ee1cb 100644 --- a/rollbar_flutter/CHANGELOG.md +++ b/rollbar_flutter/CHANGELOG.md @@ -1,3 +1,5 @@ -## 0.1.0-beta +## 0.2.0-beta +- Added null-safety. +## 0.1.0-beta - Initial version of rollbar-flutter. diff --git a/rollbar_flutter/analysis_options.yaml b/rollbar_flutter/analysis_options.yaml deleted file mode 120000 index c9e0d9f..0000000 --- a/rollbar_flutter/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -../analysis_options.yaml \ No newline at end of file diff --git a/rollbar_flutter/analysis_options.yaml b/rollbar_flutter/analysis_options.yaml new file mode 100644 index 0000000..0d22695 --- /dev/null +++ b/rollbar_flutter/analysis_options.yaml @@ -0,0 +1,31 @@ +# Defines a default set of lint rules enforced for +# projects at Google. For details and rationale, +# see https://github.com/dart-lang/pedantic#enabled-lints. + +include: package:flutter_lints/flutter.yaml + +# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. +# Uncomment to specify additional rules. +# linter: +# rules: +# - camel_case_types + +linter: + rules: + - always_declare_return_types + - prefer_single_quotes + - sort_child_properties_last + - unawaited_futures + - unsafe_html + - use_full_hex_values_for_flutter_colors + # - cancel_subscriptions + # - close_sinks + # - comment_references + # - one_member_abstracts + # - only_throw_errors + # - package_api_docs + # - prefer_final_in_for_each + +analyzer: + exclude: + - 'example/**' diff --git a/rollbar_flutter/example/lib/main.dart b/rollbar_flutter/example/lib/main.dart index 48b25ea..e0c087a 100644 --- a/rollbar_flutter/example/lib/main.dart +++ b/rollbar_flutter/example/lib/main.dart @@ -2,13 +2,15 @@ import 'dart:io' show Platform; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; + +import 'package:rollbar_dart/rollbar.dart'; import 'package:rollbar_flutter/rollbar.dart'; /// Example Flutter application using rollbar-flutter. Future main() async { var config = (ConfigBuilder('') ..environment = 'development' - ..codeVersion = '0.1.0' + ..codeVersion = '0.2.0' ..package = 'rollbar_flutter_example' ..handleUncaughtErrors = true ..includePlatformLogs = false) @@ -34,7 +36,7 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); + MyHomePage({Key? key = null, required this.title}) : super(key: key); final String title; diff --git a/rollbar_flutter/example/pubspec.yaml b/rollbar_flutter/example/pubspec.yaml index f6fa7a9..209d1de 100644 --- a/rollbar_flutter/example/pubspec.yaml +++ b/rollbar_flutter/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 0.1.0+1 publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: diff --git a/rollbar_flutter/lib/src/platform_transformer.dart b/rollbar_flutter/lib/src/platform_transformer.dart index 740e514..21aff94 100644 --- a/rollbar_flutter/lib/src/platform_transformer.dart +++ b/rollbar_flutter/lib/src/platform_transformer.dart @@ -2,21 +2,20 @@ import 'dart:convert'; import 'package:flutter/services.dart' show PlatformException; import 'package:rollbar_dart/rollbar.dart' - show Body, Data, RollbarPlatformInfo, TraceChain, Transformer; -import 'package:rollbar_dart/src/api/payload/body.dart'; + show Body, Data, RollbarPlatformInfo, TraceChain, TraceInfo, Transformer; /// This trasformer inspects some platform specific exception types, which /// carry additional occurrence details in their exception messages. /// This allows the Rollbar Dart notifier to report a complete trace including /// both platform specific and Dart frames. class PlatformTransformer implements Transformer { - final Transformer wrapped; + final Transformer? wrapped; final bool appendToChain; PlatformTransformer({this.wrapped, this.appendToChain = false}); @override - Future transform(dynamic error, StackTrace trace, Data data) async { + Future transform(dynamic error, StackTrace? trace, Data data) async { if (error is PlatformException) { if (RollbarPlatformInfo.isAndroid) { _enrichAndroidTrace(error, data); @@ -24,42 +23,41 @@ class PlatformTransformer implements Transformer { } if (wrapped != null) { - return await wrapped.transform(error, trace, data); + return await wrapped!.transform(error, trace, data); } return data; } - static const String ANDROID_TRACE_PAYLOAD_PREFIX = + static const String androidTracePayloadPrefix = 'com.rollbar.flutter.RollbarTracePayload:'; void _enrichAndroidTrace(PlatformException error, Data data) { // We cannot use error.stackTrace here, it will contain 'com.rollbar.flutter.RollbarTracePayload:' // only in debug mode, but not in release - if (error.message.startsWith(ANDROID_TRACE_PAYLOAD_PREFIX)) { - var message = - error.message.substring(ANDROID_TRACE_PAYLOAD_PREFIX.length); + if (error.message!.startsWith(androidTracePayloadPrefix)) { + var message = error.message!.substring(androidTracePayloadPrefix.length); _attachPlatformPayload(message, data); } } void _attachPlatformPayload(String message, Data data) { var embeddedPayload = jsonDecode(message); - var embeddedBody = embeddedPayload['data']['body'] as Map; + var embeddedBody = embeddedPayload['data']['body'] as Map?; if (appendToChain) { - data.body = _prependPlatformTraceToChain(data.body, embeddedBody); + data.body = _prependPlatformTraceToChain(data.body, embeddedBody!); } else { data.platformPayload = embeddedPayload; _restoreDartChainMessage( - data.body.getTraces(), Body.fromMap(embeddedBody).getTraces()); + data.body.getTraces(), Body.fromMap(embeddedBody!)!.getTraces()!); } } Body _prependPlatformTraceToChain(Body dartBody, Map embeddedBody) { - var embeddedChain = Body.fromMap(embeddedBody).getTraces(); + var embeddedChain = Body.fromMap(embeddedBody)!.getTraces()!; - var dartChain = dartBody.getTraces(); + var dartChain = dartBody.getTraces()!; _restoreDartChainMessage(dartChain, embeddedChain); @@ -69,16 +67,16 @@ class PlatformTransformer implements Transformer { // Fix message, we hijacked it on the platform side to carry the payload void _restoreDartChainMessage( - List dartChain, List embeddedChain) { + List? dartChain, List embeddedChain) { if (embeddedChain.isNotEmpty) { - dartChain.forEach((element) { - if (element.exception != null && - element.exception.message.startsWith('PlatformException') && - element.exception.message.contains(ANDROID_TRACE_PAYLOAD_PREFIX)) { - element.exception.message = - 'PlatformException(error, "${embeddedChain[0].exception.message}")'; + for (var element in dartChain!) { + if (element!.exception != null && + element.exception!.message!.startsWith('PlatformException') && + element.exception!.message!.contains(androidTracePayloadPrefix)) { + element.exception!.message = + 'PlatformException(error, "${embeddedChain[0]!.exception!.message}")'; } - }); + } } } } diff --git a/rollbar_flutter/lib/src/rollbar.dart b/rollbar_flutter/lib/src/rollbar.dart index 61d2dd5..486b425 100644 --- a/rollbar_flutter/lib/src/rollbar.dart +++ b/rollbar_flutter/lib/src/rollbar.dart @@ -4,9 +4,6 @@ import 'dart:isolate'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:rollbar_dart/rollbar.dart'; -import 'package:rollbar_dart/src/logging.dart' as logging; - -export 'package:rollbar_dart/rollbar.dart' show Config, ConfigBuilder; import 'platform_transformer.dart'; @@ -21,7 +18,7 @@ class RollbarFlutter extends Rollbar { static Future run( Config config, FutureOr Function(RollbarFlutter) action) async { - if (config.handleUncaughtErrors) { + if (config.handleUncaughtErrors!) { var rollbar = RollbarFlutter._(config); await runZonedGuarded(() async { @@ -29,13 +26,16 @@ class RollbarFlutter extends Rollbar { var previousOnError = FlutterError.onError; FlutterError.onError = (FlutterErrorDetails details) async { - await rollbar._unhandledError(details.exception, details.stack); + await rollbar._unhandledError(details.exception, details.stack!); if (previousOnError != null) { previousOnError.call(details); } }; - var errorHandler = await rollbar.errorHandler; + var errorHandler = await (rollbar.errorHandler as Future); + if (errorHandler == null) { + return; + } Isolate.current.addErrorListener(errorHandler); await rollbar._initializePlatformInstance(); @@ -67,9 +67,9 @@ class RollbarFlutter extends Rollbar { Future _unhandledError(dynamic exception, StackTrace trace) async { try { - await error(exception, trace); + await super.error(exception, trace); } on Exception catch (e) { - logging.error( + Logging.error( 'Internal error encountered while sending data to Rollbar', e); } } diff --git a/rollbar_flutter/pubspec.yaml b/rollbar_flutter/pubspec.yaml index 9a7a35e..ada1039 100644 --- a/rollbar_flutter/pubspec.yaml +++ b/rollbar_flutter/pubspec.yaml @@ -1,25 +1,32 @@ name: rollbar_flutter description: Connect your Flutter applications to Rollbar for error reporting. -version: 0.1.0-beta +version: 0.2.0-beta homepage: https://www.rollbar.com documentation: https://docs.rollbar.com/docs/flutter#flutter repository: https://github.com/rollbar/rollbar-flutter environment: - sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.20.0" + sdk: '>=2.12.0 <3.0.0' + flutter: '>=1.20.0' dependencies: flutter: sdk: flutter - rollbar_dart: ">=0.1.0-beta <0.2.0" - logging: '>=0.11.4 <1.1.0' + logging: ^1.0.2 + rollbar_dart: '>=0.2.0-beta <0.2.0' +dependency_overrides: + rollbar_dart: + path: ../rollbar_dart + dev_dependencies: + lints: ^1.0.1 + flutter_lints: ^1.0.4 flutter_test: sdk: flutter - pedantic: '>=1.9.0 <=1.11.0' - mockito: '>=4.1.0 <=5.0.7' + pedantic: ^1.11.1 + build_runner: ^1.11.0 + mockito: '>=5.0.0 <6.0.0' flutter: plugin: diff --git a/rollbar_flutter/test/platform_exception_utils.dart b/rollbar_flutter/test/platform_exception_utils.dart index 3b30220..ab47198 100644 --- a/rollbar_flutter/test/platform_exception_utils.dart +++ b/rollbar_flutter/test/platform_exception_utils.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:flutter/services.dart'; PlatformException createAndroidPlatformException( - {String topFrameMethod, + {String? topFrameMethod, bool includeLineNumber = true, bool createChain = false}) { var topTrace = { diff --git a/rollbar_flutter/test/platform_transformer_test.dart b/rollbar_flutter/test/platform_transformer_test.dart index e0bc313..b790bab 100644 --- a/rollbar_flutter/test/platform_transformer_test.dart +++ b/rollbar_flutter/test/platform_transformer_test.dart @@ -1,13 +1,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:rollbar_dart/rollbar.dart'; -import 'package:rollbar_dart/src/platform.dart'; import 'package:rollbar_flutter/src/platform_transformer.dart'; import 'platform_exception_utils.dart'; void main() { group('PlatformTransformer tests', () { - String expectedMessage; + String? expectedMessage; setUp(() { RollbarPlatformInfo.isAndroid = true; @@ -19,7 +18,7 @@ void main() { }); group('Platform transformer (appendToChain: true)', () { - PlatformTransformer transformer; + late PlatformTransformer transformer; setUp(() { transformer = PlatformTransformer(appendToChain: true); @@ -39,21 +38,21 @@ void main() { var updated = await transformer.transform(exception, StackTrace.empty, original); - var traces = updated.body.getTraces(); + var traces = updated.body.getTraces()!; expect(traces, hasLength(2)); - var rootCause = traces[0]; + var rootCause = traces[0]!; expect(rootCause.frames.length, greaterThan(1)); expect(rootCause.frames[0].method, equals('letsSeeYouParseThis')); - var dartTrace = traces[1]; + var dartTrace = traces[1]!; expect(dartTrace.frames, hasLength(2)); expect(dartTrace.frames[0].method, equals('testThis')); expect(dartTrace.frames[1].method, equals('what')); // The message was temporarily hijacked to transfer the platform payload, let's make sure // it's been restored - expect(dartTrace.exception.message, equals(expectedMessage)); + expect(dartTrace.exception!.message, equals(expectedMessage)); }); test( @@ -76,28 +75,28 @@ void main() { var updated = await transformer.transform(exception, StackTrace.empty, original); - var traces = updated.body.getTraces(); + var traces = updated.body.getTraces()!; expect(traces, hasLength(3)); - var rootCause = traces[0]; + var rootCause = traces[0]!; expect(rootCause.frames.length, greaterThan(1)); expect(rootCause.frames[0].method, equals('thisWillBeRethrown')); - var rethrownTrace = traces[1]; + var rethrownTrace = traces[1]!; expect(rethrownTrace.frames, hasLength(2)); expect(rethrownTrace.frames[0].method, equals('processError')); expect(rethrownTrace.frames[1].method, equals('catchAndThrow')); - var dartTrace = traces[2]; + var dartTrace = traces[2]!; expect(dartTrace.frames, hasLength(1)); expect(dartTrace.frames[0].method, equals('onTheDartSide')); - expect(dartTrace.exception.message, equals(expectedMessage)); + expect(dartTrace.exception!.message, equals(expectedMessage)); }); }); group('Platform transformer (appendToChain: false)', () { - PlatformTransformer transformer; + late PlatformTransformer transformer; setUp(() { transformer = PlatformTransformer(appendToChain: false); @@ -117,19 +116,19 @@ void main() { var updated = await transformer.transform(exception, StackTrace.empty, original); - var traces = updated.body.getTraces(); + var traces = updated.body.getTraces()!; expect(traces, hasLength(1)); - var dartTrace = traces[0]; + var dartTrace = traces[0]!; expect(dartTrace.frames, hasLength(1)); expect(dartTrace.frames[0].method, equals('thisFails')); - expect(dartTrace.exception.message, equals(expectedMessage)); + expect(dartTrace.exception!.message, equals(expectedMessage)); expect(updated.platformPayload, isNotNull); - expect(updated.platformPayload['data']['notifier']['name'], + expect(updated.platformPayload!['data']['notifier']['name'], equals('rollbar-java')); - var trace = updated.platformPayload['data']['body']['trace']; + var trace = updated.platformPayload!['data']['body']['trace']; expect(trace['frames'].length, equals(2)); expect(trace['frames'][0]['method'], equals('toBeAttached')); }); @@ -154,20 +153,20 @@ void main() { var updated = await transformer.transform(exception, StackTrace.empty, original); - var traces = updated.body.getTraces(); + var traces = updated.body.getTraces()!; expect(traces, hasLength(1)); - var dartTrace = traces[0]; + var dartTrace = traces[0]!; expect(dartTrace.frames, hasLength(1)); expect(dartTrace.frames[0].method, equals('attachedFailureChain')); - expect(dartTrace.exception.message, equals(expectedMessage)); + expect(dartTrace.exception!.message, equals(expectedMessage)); expect(updated.platformPayload, isNotNull); - expect(updated.platformPayload['data']['notifier']['name'], + expect(updated.platformPayload!['data']['notifier']['name'], equals('rollbar-java')); - var chain = updated.platformPayload['data']['body']['trace_chain']; + var chain = updated.platformPayload!['data']['body']['trace_chain']; expect(chain.length, equals(2)); var topTrace = chain[0]; diff --git a/rollbar_flutter/test/rollbar_flutter_test.dart b/rollbar_flutter/test/rollbar_flutter_test.dart index a2c85e7..fc4c0d5 100644 --- a/rollbar_flutter/test/rollbar_flutter_test.dart +++ b/rollbar_flutter/test/rollbar_flutter_test.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; +import 'package:rollbar_dart/rollbar_dart.dart'; import 'flutter1_workarounds.dart' as rbdart; import 'package:rollbar_flutter/rollbar.dart'; @@ -12,14 +13,13 @@ void main() { const channel = MethodChannel('rollbar_flutter'); TestWidgetsFlutterBinding.ensureInitialized(); - List callsReceived; - MockSender sender; + late List callsReceived; + late MockSender sender; setUp(() async { rbdart.RollbarPlatformInfo.isAndroid = true; sender = MockSender(); - when(sender.send(any)) - .thenAnswer((_invocation) => Future.value(rbdart.Response())); + when(sender.send(any)).thenAnswer((_) async => Response()); callsReceived = []; channel.setMockMethodCallHandler((MethodCall methodCall) async { @@ -117,7 +117,13 @@ Future _runRollbarFlutter( expect(run, equals(true)); } -class MockSender extends Mock implements rbdart.Sender {} +class MockSender extends Mock implements rbdart.Sender { + @override + Future send(Map? payload) { + return super.noSuchMethod(Invocation.method(#send, [payload]), + returnValue: Future.value(Response())); + } +} MockSender createMockSender(Config c) { return MockSender();