diff --git a/.gitignore b/.gitignore index 3c2252a2439a..c57b12a34b3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,14 @@ .DS_Store .atom/ .idea -.dart_tool/ .packages .pub/ +pubspec.lock + Podfile.lock Pods/ GeneratedPluginRegistrant.h GeneratedPluginRegistrant.m + GeneratedPluginRegistrant.java -pubspec.lock -packages/sentry/build/ -packages/sentry/android/ -packages/sentry/ios/ + diff --git a/packages/sentry/.idea/modules.xml b/packages/sentry/.idea/modules.xml deleted file mode 100644 index 96a30c72817d..000000000000 --- a/packages/sentry/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/packages/sentry/.idea/sentry.iml b/packages/sentry/.idea/sentry.iml deleted file mode 100644 index 7457fc5904d9..000000000000 --- a/packages/sentry/.idea/sentry.iml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/sentry/.idea/vcs.xml b/packages/sentry/.idea/vcs.xml deleted file mode 100644 index 94a25f7f4cb4..000000000000 --- a/packages/sentry/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/sentry/.travis.yml b/packages/sentry/.travis.yml deleted file mode 100644 index b3a6410de40a..000000000000 --- a/packages/sentry/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: dart -dart: - # - stable # there's no Dart 2 on the stable channel yet - - dev -script: ./tool/presubmit.sh diff --git a/packages/sentry/AUTHORS b/packages/sentry/AUTHORS deleted file mode 100644 index fa93e5ec4e78..000000000000 --- a/packages/sentry/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to package:sentry. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -Simon Lightfoot diff --git a/packages/sentry/CHANGELOG.md b/packages/sentry/CHANGELOG.md deleted file mode 100644 index 527ec150ece8..000000000000 --- a/packages/sentry/CHANGELOG.md +++ /dev/null @@ -1,58 +0,0 @@ -# package:sentry changelog - -## 2.1.1 - -- Defensively copy internal maps event attributes to - avoid shared mutable state (https://github.com/flutter/sentry/commit/044e4c1f43c2d199ed206e5529e2a630c90e4434) - -## 2.1.0 - -- Support DNS format without secret key. -- Remove dependency on `package:quiver`. -- The `clock` argument to `SentryClient` constructor _should_ now be - `ClockProvider` (but still accepts `Clock` for backwards compatibility). - -## 2.0.2 - -- Add support for user context in Sentry events. - -## 2.0.1 - -- Invert stack frames to be compatible with Sentry's default culprit detection. - -## 2.0.0 - -- Fixed deprecation warnings for Dart 2 -- Refactored tests to work with Dart 2 - -## 1.0.0 - -- first and last Dart 1-compatible release (we may fix bugs on a separate branch if there's demand) -- fix code for Dart 2 - -## 0.0.6 - -- use UTC in the `timestamp` field - -## 0.0.5 - -- remove sub-seconds from the timestamp - -## 0.0.4 - -- parse and report async gaps in stack traces - -## 0.0.3 - -- environment attributes -- auto-generate event_id and timestamp for events - -## 0.0.2 - -- parse and report stack traces -- use x-sentry-error HTTP response header -- gzip outgoing payloads by default - -## 0.0.1 - -- basic ability to send exception reports to Sentry.io diff --git a/packages/sentry/LICENSE b/packages/sentry/LICENSE deleted file mode 100644 index 6f2d1444dd98..000000000000 --- a/packages/sentry/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/sentry/PATENTS b/packages/sentry/PATENTS deleted file mode 100644 index ac39faf67938..000000000000 --- a/packages/sentry/PATENTS +++ /dev/null @@ -1,17 +0,0 @@ -Google hereby grants to you a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this -section) patent license to make, have made, use, offer to sell, sell, -import, transfer, and otherwise run, modify and propagate the contents -of this implementation, where such license applies only to those -patent claims, both currently owned by Google and acquired in the -future, licensable by Google that are necessarily infringed by this -implementation. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute -or order or agree to the institution of patent litigation or any other -patent enforcement activity against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that this -implementation constitutes direct or contributory patent infringement, -or inducement of patent infringement, then any patent rights granted -to you under this License for this implementation shall terminate as -of the date such litigation is filed. \ No newline at end of file diff --git a/packages/sentry/README.md b/packages/sentry/README.md deleted file mode 100644 index 1bd7d9579eca..000000000000 --- a/packages/sentry/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Sentry.io client for Dart - -[![Build Status](https://travis-ci.org/flutter/sentry.svg?branch=master)](https://travis-ci.org/flutter/sentry) - -Use this library in your Dart programs (Flutter, command-line and (TBD) AngularDart) to report errors thrown by your -program to https://sentry.io error tracking service. - -## Versions - -`>=0.0.0 <2.0.0` is the range of versions compatible with Dart 1. - -`>=2.0.0 <3.0.0` is the range of versions compatible with Dart 2. - -## Usage - -Sign up for a Sentry.io account and get a DSN at http://sentry.io. - -Add `sentry` dependency to your `pubspec.yaml`: - -```yaml -dependencies: - sentry: any -``` - -In your Dart code, import `package:sentry/sentry.dart` and create a `SentryClient` using the DSN issued by Sentry.io: - -```dart -import 'package:sentry/sentry.dart'; - -final SentryClient sentry = new SentryClient(dsn: YOUR_DSN); -``` - -In an exception handler, call `captureException()`: - -```dart -main() async { - try { - doSomethingThatMightThrowAnError(); - } catch(error, stackTrace) { - await sentry.captureException( - exception: error, - stackTrace: stackTrace, - ); - } -} -``` - -## Tips for catching errors - -- use a `try/catch` block -- create a `Zone` with an error handler, e.g. using [runZoned][run_zoned] -- in Flutter, use [FlutterError.onError][flutter_error] -- use `Isolate.current.addErrorListener` to capture uncaught errors in the root zone - -[run_zoned]: https://api.dartlang.org/stable/dart-async/runZoned.html -[flutter_error]: https://docs.flutter.io/flutter/foundation/FlutterError/onError.html - -## Found a bug? - -Please file it at https://github.com/flutter/flutter/issues/new diff --git a/packages/sentry/bin/test.dart b/packages/sentry/bin/test.dart deleted file mode 100644 index bbeadbe1fcd0..000000000000 --- a/packages/sentry/bin/test.dart +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:io'; - -import 'package:sentry/sentry.dart'; - -/// Sends a test exception report to Sentry.io using this Dart client. -Future main(List rawArgs) async { - if (rawArgs.length != 1) { - stderr.writeln( - 'Expected exactly one argument, which is the DSN issued by Sentry.io to your project.'); - exit(1); - } - - final String dsn = rawArgs.single; - final SentryClient client = new SentryClient(dsn: dsn); - - try { - await foo(); - } catch (error, stackTrace) { - print('Reporting the following stack trace: '); - print(stackTrace); - final SentryResponse response = await client.captureException( - exception: error, - stackTrace: stackTrace, - ); - - if (response.isSuccessful) { - print('SUCCESS\nid: ${response.eventId}'); - } else { - print('FAILURE: ${response.error}'); - } - } finally { - await client.close(); - } -} - -Future foo() async { - await bar(); -} - -Future bar() async { - await baz(); -} - -Future baz() async { - throw new StateError('This is a test error'); -} diff --git a/packages/sentry/lib/sentry.dart b/packages/sentry/lib/sentry.dart deleted file mode 100644 index ccf20f884b83..000000000000 --- a/packages/sentry/lib/sentry.dart +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// A pure Dart client for Sentry.io crash reporting. -library sentry; - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:http/http.dart'; -import 'package:meta/meta.dart'; -import 'package:usage/uuid/uuid.dart'; - -import 'src/stack_trace.dart'; -import 'src/utils.dart'; -import 'src/version.dart'; - -export 'src/version.dart'; - -/// Used to provide timestamp for logging. -typedef ClockProvider = DateTime Function(); - -/// Logs crash reports and events to the Sentry.io service. -class SentryClient { - /// Sentry.io client identifier for _this_ client. - @visibleForTesting - static const String sentryClient = '$sdkName/$sdkVersion'; - - /// The default logger name used if no other value is supplied. - static const String defaultLoggerName = 'SentryClient'; - - /// Instantiates a client using [dsn] issued to your project by Sentry.io as - /// the endpoint for submitting events. - /// - /// [environmentAttributes] contain event attributes that do not change over - /// the course of a program's lifecycle. These attributes will be added to - /// all events captured via this client. The following attributes often fall - /// under this category: [Event.loggerName], [Event.serverName], - /// [Event.release], [Event.environment]. - /// - /// If [compressPayload] is `true` the outgoing HTTP payloads are compressed - /// using gzip. Otherwise, the payloads are sent in plain UTF8-encoded JSON - /// text. If not specified, the compression is enabled by default. - /// - /// If [httpClient] is provided, it is used instead of the default client to - /// make HTTP calls to Sentry.io. This is useful in tests. - /// - /// If [clock] is provided, it is used to get time instead of the system - /// clock. This is useful in tests. Should be an implementation of [ClockProvider]. - /// This parameter is dynamic to maintain backwards compatibility with - /// previous use of [Clock](https://pub.dartlang.org/documentation/quiver/latest/quiver.time/Clock-class.html) - /// from [`package:quiver`](https://pub.dartlang.org/packages/quiver). - /// - /// If [uuidGenerator] is provided, it is used to generate the "event_id" - /// field instead of the built-in random UUID v4 generator. This is useful in - /// tests. - factory SentryClient({ - @required String dsn, - Event environmentAttributes, - bool compressPayload, - Client httpClient, - dynamic clock, - UuidGenerator uuidGenerator, - }) { - httpClient ??= new Client(); - clock ??= _getUtcDateTime; - uuidGenerator ??= _generateUuidV4WithoutDashes; - compressPayload ??= true; - - final ClockProvider clockProvider = - clock is ClockProvider ? clock : clock.get; - - final Uri uri = Uri.parse(dsn); - final List userInfo = uri.userInfo.split(':'); - - assert(() { - if (uri.pathSegments.isEmpty) - throw new ArgumentError( - 'Project ID not found in the URI path of the DSN URI: $dsn'); - - return true; - }()); - - final String publicKey = userInfo[0]; - final String secretKey = userInfo.length >= 2 ? userInfo[1] : null; - final String projectId = uri.pathSegments.last; - - return new SentryClient._( - httpClient: httpClient, - clock: clockProvider, - uuidGenerator: uuidGenerator, - environmentAttributes: environmentAttributes, - dsnUri: uri, - publicKey: publicKey, - secretKey: secretKey, - projectId: projectId, - compressPayload: compressPayload, - ); - } - - SentryClient._({ - @required Client httpClient, - @required ClockProvider clock, - @required UuidGenerator uuidGenerator, - @required this.environmentAttributes, - @required this.dsnUri, - @required this.publicKey, - this.secretKey, - @required this.compressPayload, - @required this.projectId, - }) : _httpClient = httpClient, - _clock = clock, - _uuidGenerator = uuidGenerator; - - final Client _httpClient; - final ClockProvider _clock; - final UuidGenerator _uuidGenerator; - - /// Contains [Event] attributes that are automatically mixed into all events - /// captured through this client. - /// - /// This event is designed to contain static values that do not change from - /// event to event, such as local operating system version, the version of - /// Dart/Flutter SDK, etc. These attributes have lower precedence than those - /// supplied in the even passed to [capture]. - final Event environmentAttributes; - - /// Whether to compress payloads sent to Sentry.io. - final bool compressPayload; - - /// The DSN URI. - @visibleForTesting - final Uri dsnUri; - - /// The Sentry.io public key for the project. - @visibleForTesting - final String publicKey; - - /// The Sentry.io secret key for the project. - @visibleForTesting - final String secretKey; - - /// The ID issued by Sentry.io to your project. - /// - /// Attached to the event payload. - final String projectId; - - /// Information about the current user. - /// - /// This information is sent with every logged event. If the value - /// of this field is updated, all subsequent events will carry the - /// new information. - /// - /// [Event.userContext] overrides the [User] context set here. - /// - /// See also: - /// * https://docs.sentry.io/learn/context/#capturing-the-user - User userContext; - - @visibleForTesting - String get postUri => - '${dsnUri.scheme}://${dsnUri.host}/api/$projectId/store/'; - - /// Reports an [event] to Sentry.io. - Future capture({@required Event event}) async { - final DateTime now = _clock(); - String authHeader = 'Sentry sentry_version=6, sentry_client=$sentryClient, ' - 'sentry_timestamp=${now.millisecondsSinceEpoch}, sentry_key=$publicKey'; - if (secretKey != null) { - authHeader += ', sentry_secret=$secretKey'; - } - - final Map headers = { - 'User-Agent': '$sentryClient', - 'Content-Type': 'application/json', - 'X-Sentry-Auth': authHeader, - }; - - final Map data = { - 'project': projectId, - 'event_id': _uuidGenerator(), - 'timestamp': formatDateAsIso8601WithSecondPrecision(now), - 'logger': defaultLoggerName, - }; - - if (environmentAttributes != null) - mergeAttributes(environmentAttributes.toJson(), into: data); - - // Merge the user context. - if (userContext != null) { - mergeAttributes({'user': userContext.toJson()}, into: data); - } - mergeAttributes(event.toJson(), into: data); - - List body = utf8.encode(json.encode(data)); - if (compressPayload) { - headers['Content-Encoding'] = 'gzip'; - body = GZIP.encode(body); - } - - final Response response = - await _httpClient.post(postUri, headers: headers, body: body); - - if (response.statusCode != 200) { - String errorMessage = - 'Sentry.io responded with HTTP ${response.statusCode}'; - if (response.headers['x-sentry-error'] != null) - errorMessage += ': ${response.headers['x-sentry-error']}'; - return new SentryResponse.failure(errorMessage); - } - - final String eventId = json.decode(response.body)['id']; - return new SentryResponse.success(eventId: eventId); - } - - /// Reports the [exception] and optionally its [stackTrace] to Sentry.io. - Future captureException({ - @required dynamic exception, - dynamic stackTrace, - }) { - final Event event = new Event( - exception: exception, - stackTrace: stackTrace, - ); - return capture(event: event); - } - - Future close() async { - _httpClient.close(); - } - - @override - String toString() => '$SentryClient("$postUri")'; -} - -/// A response from Sentry.io. -/// -/// If [isSuccessful] the [eventId] field will contain the ID assigned to the -/// captured event by the Sentry.io backend. Otherwise, the [error] field will -/// contain the description of the error. -@immutable -class SentryResponse { - const SentryResponse.success({@required this.eventId}) - : isSuccessful = true, - error = null; - - const SentryResponse.failure(this.error) - : isSuccessful = false, - eventId = null; - - /// Whether event was submitted successfully. - final bool isSuccessful; - - /// The ID Sentry.io assigned to the submitted event for future reference. - final String eventId; - - /// Error message, if the response is not successful. - final String error; -} - -typedef UuidGenerator = String Function(); - -String _generateUuidV4WithoutDashes() => - new Uuid().generateV4().replaceAll('-', ''); - -/// Severity of the logged [Event]. -@immutable -class SeverityLevel { - static const fatal = const SeverityLevel._('fatal'); - static const error = const SeverityLevel._('error'); - static const warning = const SeverityLevel._('warning'); - static const info = const SeverityLevel._('info'); - static const debug = const SeverityLevel._('debug'); - - const SeverityLevel._(this.name); - - /// API name of the level as it is encoded in the JSON protocol. - final String name; -} - -/// Sentry does not take a timezone and instead expects the date-time to be -/// submitted in UTC timezone. -DateTime _getUtcDateTime() => new DateTime.now().toUtc(); - -/// An event to be reported to Sentry.io. -@immutable -class Event { - /// Refers to the default fingerprinting algorithm. - /// - /// You do not need to specify this value unless you supplement the default - /// fingerprint with custom fingerprints. - static const String defaultFingerprint = '{{ default }}'; - - /// Creates an event. - const Event({ - this.loggerName, - this.serverName, - this.release, - this.environment, - this.message, - this.exception, - this.stackTrace, - this.level, - this.culprit, - this.tags, - this.extra, - this.fingerprint, - this.userContext, - }); - - /// The logger that logged the event. - final String loggerName; - - /// Identifies the server that logged this event. - final String serverName; - - /// The version of the application that logged the event. - final String release; - - /// The environment that logged the event, e.g. "production", "staging". - final String environment; - - /// Event message. - /// - /// Generally an event either contains a [message] or an [exception]. - final String message; - - /// An object that was thrown. - /// - /// It's `runtimeType` and `toString()` are logged. If this behavior is - /// undesirable, consider using a custom formatted [message] instead. - final dynamic exception; - - /// The stack trace corresponding to the thrown [exception]. - /// - /// Can be `null`, a [String], or a [StackTrace]. - final dynamic stackTrace; - - /// How important this event is. - final SeverityLevel level; - - /// What caused this event to be logged. - final String culprit; - - /// Name/value pairs that events can be searched by. - final Map tags; - - /// Arbitrary name/value pairs attached to the event. - /// - /// Sentry.io docs do not talk about restrictions on the values, other than - /// they must be JSON-serializable. - final Map extra; - - /// Information about the current user. - /// - /// The value in this field overrides the user context - /// set in [SentryClient.userContext] for this logged event. - final User userContext; - - /// Used to deduplicate events by grouping ones with the same fingerprint - /// together. - /// - /// If not specified a default deduplication fingerprint is used. The default - /// fingerprint may be supplemented by additional fingerprints by specifying - /// multiple values. The default fingerprint can be specified by adding - /// [defaultFingerprint] to the list in addition to your custom values. - /// - /// Examples: - /// - /// // A completely custom fingerprint: - /// var custom = ['foo', 'bar', 'baz']; - /// // A fingerprint that supplements the default one with value 'foo': - /// var supplemented = [Event.defaultFingerprint, 'foo']; - final List fingerprint; - - /// Serializes this event to JSON. - Map toJson() { - final Map json = { - 'platform': sdkPlatform, - 'sdk': { - 'version': sdkVersion, - 'name': sdkName, - }, - }; - - if (loggerName != null) json['logger'] = loggerName; - - if (serverName != null) json['server_name'] = serverName; - - if (release != null) json['release'] = release; - - if (environment != null) json['environment'] = environment; - - if (message != null) json['message'] = message; - - if (exception != null) { - json['exception'] = [ - { - 'type': '${exception.runtimeType}', - 'value': '$exception', - } - ]; - } - - if (stackTrace != null) { - json['stacktrace'] = { - 'frames': encodeStackTrace(stackTrace), - }; - } - - if (level != null) json['level'] = level.name; - - if (culprit != null) json['culprit'] = culprit; - - if (tags != null && tags.isNotEmpty) json['tags'] = tags; - - if (extra != null && extra.isNotEmpty) json['extra'] = extra; - - Map userContextMap; - if (userContext != null && - (userContextMap = userContext.toJson()).isNotEmpty) - json['user'] = userContextMap; - - if (fingerprint != null && fingerprint.isNotEmpty) - json['fingerprint'] = fingerprint; - - return json; - } -} - -/// Describes the current user associated with the application, such as the -/// currently signed in user. -/// -/// The user can be specified globally in the [SentryClient.userContext] field, -/// or per event in the [Event.userContext] field. -/// -/// You should provide at least either an [id] (a unique identifier for an -/// authenticated user) or [ipAddress] (their IP address). -/// -/// Conforms to the User Interface contract for Sentry -/// https://docs.sentry.io/clientdev/interfaces/user/. -/// -/// The outgoing JSON representation is: -/// -/// ``` -/// "user": { -/// "id": "unique_id", -/// "username": "my_user", -/// "email": "foo@example.com", -/// "ip_address": "127.0.0.1", -/// "subscription": "basic" -/// } -/// ``` -class User { - /// A unique identifier of the user. - final String id; - - /// The username of the user. - final String username; - - /// The email address of the user. - final String email; - - /// The IP of the user. - final String ipAddress; - - /// Any other user context information that may be helpful. - /// - /// These keys are stored as extra information but not specifically processed - /// by Sentry. - final Map extras; - - /// At a minimum you must set an [id] or an [ipAddress]. - const User({this.id, this.username, this.email, this.ipAddress, this.extras}) - : assert(id != null || ipAddress != null); - - /// Produces a [Map] that can be serialized to JSON. - Map toJson() { - return { - "id": id, - "username": username, - "email": email, - "ip_address": ipAddress, - "extras": extras, - }; - } -} diff --git a/packages/sentry/lib/src/stack_trace.dart b/packages/sentry/lib/src/stack_trace.dart deleted file mode 100644 index d1175d69213f..000000000000 --- a/packages/sentry/lib/src/stack_trace.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:stack_trace/stack_trace.dart'; - -/// Sentry.io JSON encoding of a stack frame for the asynchronous suspension, -/// which is the gap between asynchronous calls. -const Map asynchronousGapFrameJson = const { - 'abs_path': '', -}; - -/// Encodes [stackTrace] as JSON in the Sentry.io format. -/// -/// [stackTrace] must be [String] or [StackTrace]. -List> encodeStackTrace(dynamic stackTrace) { - assert(stackTrace is String || stackTrace is StackTrace); - final Chain chain = stackTrace is StackTrace - ? new Chain.forTrace(stackTrace) - : new Chain.parse(stackTrace); - - final List> frames = >[]; - for (int t = 0; t < chain.traces.length; t += 1) { - frames.addAll(chain.traces[t].frames.map(encodeStackTraceFrame)); - if (t < chain.traces.length - 1) frames.add(asynchronousGapFrameJson); - } - return frames.reversed.toList(); -} - -Map encodeStackTraceFrame(Frame frame) { - final Map json = { - 'abs_path': _absolutePathForCrashReport(frame), - 'function': frame.member, - 'lineno': frame.line, - 'in_app': !frame.isCore, - }; - - if (frame.uri.pathSegments.isNotEmpty) - json['filename'] = frame.uri.pathSegments.last; - - return json; -} - -/// A stack frame's code path may be one of "file:", "dart:" and "package:". -/// -/// Absolute file paths may contain personally identifiable information, and -/// therefore are stripped to only send the base file name. For example, -/// "/foo/bar/baz.dart" is reported as "baz.dart". -/// -/// "dart:" and "package:" imports are always relative and are OK to send in -/// full. -String _absolutePathForCrashReport(Frame frame) { - if (frame.uri.scheme != 'dart' && frame.uri.scheme != 'package') - return frame.uri.pathSegments.last; - - return '${frame.uri}'; -} diff --git a/packages/sentry/lib/src/utils.dart b/packages/sentry/lib/src/utils.dart deleted file mode 100644 index 0e5e93e18113..000000000000 --- a/packages/sentry/lib/src/utils.dart +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:meta/meta.dart'; - -/// Recursively merges [attributes] [into] another map of attributes. -/// -/// [attributes] take precedence over the target map. Recursion takes place -/// along [Map] values only. All other types are overwritten entirely. -void mergeAttributes(Map attributes, - {@required Map into}) { - assert(attributes != null && into != null); - attributes.forEach((String name, dynamic value) { - dynamic targetValue = into[name]; - if (value is Map) { - if (targetValue is! Map) { - // Let mergeAttributes make a deep copy, because assigning a reference - // of 'value' will expose 'value' to be mutated by further merges. - into[name] = targetValue = {}; - } - mergeAttributes(value, into: targetValue); - } else { - into[name] = value; - } - }); -} - -String formatDateAsIso8601WithSecondPrecision(DateTime date) { - String iso = date.toIso8601String(); - final millisecondSeparatorIndex = iso.lastIndexOf('.'); - if (millisecondSeparatorIndex != -1) - iso = iso.substring(0, millisecondSeparatorIndex); - return iso; -} diff --git a/packages/sentry/lib/src/version.dart b/packages/sentry/lib/src/version.dart deleted file mode 100644 index b7b759e2bc41..000000000000 --- a/packages/sentry/lib/src/version.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Sentry.io has a concept of "SDK", which refers to the client library or -/// tool used to submit events to Sentry.io. -/// -/// This library contains Sentry.io SDK constants used by this package. -library version; - -/// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '2.1.1'; - -/// The SDK name reported to Sentry.io in the submitted events. -const String sdkName = 'dart'; - -/// The name of the SDK platform reported to Sentry.io in the submitted events. -const String sdkPlatform = 'dart'; diff --git a/packages/sentry/pubspec.yaml b/packages/sentry/pubspec.yaml deleted file mode 100644 index 6e133bb0ce88..000000000000 --- a/packages/sentry/pubspec.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: sentry -version: 2.1.1 -description: A pure Dart Sentry.io client. -author: Flutter Authors -homepage: https://github.com/flutter/sentry - -environment: - sdk: ">=2.0.0-dev.28.0 <3.0.0" - -dependencies: - http: ">=0.11.0 <2.0.0" - meta: ">=1.0.0 <2.0.0" - stack_trace: ">=1.0.0 <2.0.0" - usage: ">=3.0.0 <4.0.0" - -dev_dependencies: - args: ">=0.13.0 <2.0.0" - test: ">=0.12.0 <2.0.0" - yaml: ">=2.1.0 <3.0.0" - mockito: ">=2.0.0 <4.0.0" diff --git a/packages/sentry/test/sentry_test.dart b/packages/sentry/test/sentry_test.dart deleted file mode 100644 index 1a61518ed1f1..000000000000 --- a/packages/sentry/test/sentry_test.dart +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; -import 'dart:io'; - -import 'package:http/http.dart'; -import 'package:sentry/sentry.dart'; -import 'package:test/test.dart'; - -const String _testDsn = 'https://public:secret@sentry.example.com/1'; -const String _testDsnWithoutSecret = 'https://public@sentry.example.com/1'; - -void main() { - group('$SentryClient', () { - test('can parse DSN', () async { - final SentryClient client = new SentryClient(dsn: _testDsn); - expect(client.dsnUri, Uri.parse(_testDsn)); - expect(client.postUri, 'https://sentry.example.com/api/1/store/'); - expect(client.publicKey, 'public'); - expect(client.secretKey, 'secret'); - expect(client.projectId, '1'); - await client.close(); - }); - - test('can parse DSN without secret', () async { - final SentryClient client = new SentryClient(dsn: _testDsnWithoutSecret); - expect(client.dsnUri, Uri.parse(_testDsnWithoutSecret)); - expect(client.postUri, 'https://sentry.example.com/api/1/store/'); - expect(client.publicKey, 'public'); - expect(client.secretKey, null); - expect(client.projectId, '1'); - await client.close(); - }); - - test('sends client auth header without secret', () async { - final MockClient httpMock = new MockClient(); - final ClockProvider fakeClockProvider = - () => new DateTime.utc(2017, 1, 2); - - Map headers; - - httpMock.answerWith((Invocation invocation) async { - if (invocation.memberName == #close) { - return null; - } - if (invocation.memberName == #post) { - headers = invocation.namedArguments[#headers]; - return new Response('{"id": "test-event-id"}', 200); - } - fail('Unexpected invocation of ${invocation.memberName} in HttpMock'); - }); - - final SentryClient client = new SentryClient( - dsn: _testDsnWithoutSecret, - httpClient: httpMock, - clock: fakeClockProvider, - compressPayload: false, - uuidGenerator: () => 'X' * 32, - environmentAttributes: const Event( - serverName: 'test.server.com', - release: '1.2.3', - environment: 'staging', - ), - ); - - try { - throw new ArgumentError('Test error'); - } catch (error, stackTrace) { - final SentryResponse response = await client.captureException( - exception: error, stackTrace: stackTrace); - expect(response.isSuccessful, true); - expect(response.eventId, 'test-event-id'); - expect(response.error, null); - } - - final Map expectedHeaders = { - 'User-Agent': '$sdkName/$sdkVersion', - 'Content-Type': 'application/json', - 'X-Sentry-Auth': 'Sentry sentry_version=6, ' - 'sentry_client=${SentryClient.sentryClient}, ' - 'sentry_timestamp=${fakeClockProvider().millisecondsSinceEpoch}, ' - 'sentry_key=public', - }; - - expect(headers, expectedHeaders); - - await client.close(); - }); - - testCaptureException(bool compressPayload) async { - final MockClient httpMock = new MockClient(); - final ClockProvider fakeClockProvider = - () => new DateTime.utc(2017, 1, 2); - - String postUri; - Map headers; - List body; - httpMock.answerWith((Invocation invocation) async { - if (invocation.memberName == #close) { - return null; - } - if (invocation.memberName == #post) { - postUri = invocation.positionalArguments.single; - headers = invocation.namedArguments[#headers]; - body = invocation.namedArguments[#body]; - return new Response('{"id": "test-event-id"}', 200); - } - fail('Unexpected invocation of ${invocation.memberName} in HttpMock'); - }); - - final SentryClient client = new SentryClient( - dsn: _testDsn, - httpClient: httpMock, - clock: fakeClockProvider, - uuidGenerator: () => 'X' * 32, - compressPayload: compressPayload, - environmentAttributes: const Event( - serverName: 'test.server.com', - release: '1.2.3', - environment: 'staging', - ), - ); - - try { - throw new ArgumentError('Test error'); - } catch (error, stackTrace) { - final SentryResponse response = await client.captureException( - exception: error, stackTrace: stackTrace); - expect(response.isSuccessful, true); - expect(response.eventId, 'test-event-id'); - expect(response.error, null); - } - - expect(postUri, client.postUri); - - final Map expectedHeaders = { - 'User-Agent': '$sdkName/$sdkVersion', - 'Content-Type': 'application/json', - 'X-Sentry-Auth': 'Sentry sentry_version=6, ' - 'sentry_client=${SentryClient.sentryClient}, ' - 'sentry_timestamp=${fakeClockProvider().millisecondsSinceEpoch}, ' - 'sentry_key=public, ' - 'sentry_secret=secret', - }; - - if (compressPayload) expectedHeaders['Content-Encoding'] = 'gzip'; - - expect(headers, expectedHeaders); - - Map data; - if (compressPayload) { - data = json.decode(utf8.decode(GZIP.decode(body))); - } else { - data = json.decode(utf8.decode(body)); - } - final Map stacktrace = data.remove('stacktrace'); - expect(stacktrace['frames'], const isInstanceOf()); - expect(stacktrace['frames'], isNotEmpty); - - final Map topFrame = - (stacktrace['frames'] as Iterable).last; - expect(topFrame.keys, - ['abs_path', 'function', 'lineno', 'in_app', 'filename']); - expect(topFrame['abs_path'], 'sentry_test.dart'); - expect(topFrame['function'], 'main..testCaptureException'); - expect(topFrame['lineno'], greaterThan(0)); - expect(topFrame['in_app'], true); - expect(topFrame['filename'], 'sentry_test.dart'); - - expect(data, { - 'project': '1', - 'event_id': 'X' * 32, - 'timestamp': '2017-01-02T00:00:00', - 'platform': 'dart', - 'exception': [ - {'type': 'ArgumentError', 'value': 'Invalid argument(s): Test error'} - ], - 'sdk': {'version': sdkVersion, 'name': 'dart'}, - 'logger': SentryClient.defaultLoggerName, - 'server_name': 'test.server.com', - 'release': '1.2.3', - 'environment': 'staging', - }); - - await client.close(); - } - - test('sends an exception report (compressed)', () async { - await testCaptureException(true); - }); - - test('sends an exception report (uncompressed)', () async { - await testCaptureException(false); - }); - - test('reads error message from the x-sentry-error header', () async { - final MockClient httpMock = new MockClient(); - final ClockProvider fakeClockProvider = - () => new DateTime.utc(2017, 1, 2); - - httpMock.answerWith((Invocation invocation) async { - if (invocation.memberName == #close) { - return null; - } - if (invocation.memberName == #post) { - return new Response('', 401, headers: { - 'x-sentry-error': 'Invalid api key', - }); - } - fail('Unexpected invocation of ${invocation.memberName} in HttpMock'); - }); - - final SentryClient client = new SentryClient( - dsn: _testDsn, - httpClient: httpMock, - clock: fakeClockProvider, - uuidGenerator: () => 'X' * 32, - compressPayload: false, - environmentAttributes: const Event( - serverName: 'test.server.com', - release: '1.2.3', - environment: 'staging', - ), - ); - - try { - throw new ArgumentError('Test error'); - } catch (error, stackTrace) { - final SentryResponse response = await client.captureException( - exception: error, stackTrace: stackTrace); - expect(response.isSuccessful, false); - expect(response.eventId, null); - expect(response.error, - 'Sentry.io responded with HTTP 401: Invalid api key'); - } - - await client.close(); - }); - - test('$Event userContext overrides client', () async { - final MockClient httpMock = new MockClient(); - final ClockProvider fakeClockProvider = - () => new DateTime.utc(2017, 1, 2); - - String loggedUserId; // used to find out what user context was sent - httpMock.answerWith((Invocation invocation) async { - if (invocation.memberName == #close) { - return null; - } - if (invocation.memberName == #post) { - // parse the body and detect which user context was sent - var bodyData = invocation.namedArguments[new Symbol("body")]; - var decoded = new Utf8Codec().decode(bodyData); - var decodedJson = new JsonDecoder().convert(decoded); - loggedUserId = decodedJson['user']['id']; - return new Response('', 401, headers: { - 'x-sentry-error': 'Invalid api key', - }); - } - fail('Unexpected invocation of ${invocation.memberName} in HttpMock'); - }); - - final clientUserContext = new User( - id: "client_user", - username: "username", - email: "email@email.com", - ipAddress: "127.0.0.1"); - final eventUserContext = new User( - id: "event_user", - username: "username", - email: "email@email.com", - ipAddress: "127.0.0.1", - extras: {"foo": "bar"}); - - final SentryClient client = new SentryClient( - dsn: _testDsn, - httpClient: httpMock, - clock: fakeClockProvider, - uuidGenerator: () => 'X' * 32, - compressPayload: false, - environmentAttributes: const Event( - serverName: 'test.server.com', - release: '1.2.3', - environment: 'staging', - ), - ); - client.userContext = clientUserContext; - - try { - throw new ArgumentError('Test error'); - } catch (error, stackTrace) { - final eventWithoutContext = - new Event(exception: error, stackTrace: stackTrace); - final eventWithContext = new Event( - exception: error, - stackTrace: stackTrace, - userContext: eventUserContext); - await client.capture(event: eventWithoutContext); - expect(loggedUserId, clientUserContext.id); - await client.capture(event: eventWithContext); - expect(loggedUserId, eventUserContext.id); - } - - await client.close(); - }); - }); - - group('$Event', () { - test('serializes to JSON', () { - final user = new User( - id: "user_id", - username: "username", - email: "email@email.com", - ipAddress: "127.0.0.1", - extras: {"foo": "bar"}); - expect( - new Event( - message: 'test-message', - exception: new StateError('test-error'), - level: SeverityLevel.debug, - culprit: 'Professor Moriarty', - tags: { - 'a': 'b', - 'c': 'd', - }, - extra: { - 'e': 'f', - 'g': 2, - }, - fingerprint: [Event.defaultFingerprint, 'foo'], - userContext: user, - ).toJson(), - { - 'platform': 'dart', - 'sdk': {'version': sdkVersion, 'name': 'dart'}, - 'message': 'test-message', - 'exception': [ - {'type': 'StateError', 'value': 'Bad state: test-error'} - ], - 'level': 'debug', - 'culprit': 'Professor Moriarty', - 'tags': {'a': 'b', 'c': 'd'}, - 'extra': {'e': 'f', 'g': 2}, - 'fingerprint': ['{{ default }}', 'foo'], - 'user': { - 'id': 'user_id', - 'username': 'username', - 'email': 'email@email.com', - 'ip_address': '127.0.0.1', - 'extras': {'foo': 'bar'} - }, - }, - ); - }); - }); -} - -typedef Answer = dynamic Function(Invocation invocation); - -class MockClient implements Client { - Answer _answer; - - void answerWith(Answer answer) { - _answer = answer; - } - - noSuchMethod(Invocation invocation) { - return _answer(invocation); - } -} diff --git a/packages/sentry/test/stack_trace_test.dart b/packages/sentry/test/stack_trace_test.dart deleted file mode 100644 index 3523f5c91c11..000000000000 --- a/packages/sentry/test/stack_trace_test.dart +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:sentry/src/stack_trace.dart'; -import 'package:stack_trace/stack_trace.dart'; -import 'package:test/test.dart'; - -void main() { - group('encodeStackTraceFrame', () { - test('marks dart: frames as not app frames', () { - final Frame frame = new Frame(Uri.parse('dart:core'), 1, 2, 'buzz'); - expect(encodeStackTraceFrame(frame), { - 'abs_path': 'dart:core', - 'function': 'buzz', - 'lineno': 1, - 'in_app': false, - 'filename': 'core' - }); - }); - - test('cleanses absolute paths', () { - final Frame frame = - new Frame(Uri.parse('file://foo/bar/baz.dart'), 1, 2, 'buzz'); - expect(encodeStackTraceFrame(frame)['abs_path'], 'baz.dart'); - }); - }); - - group('encodeStackTrace', () { - test('encodes a simple stack trace', () { - expect(encodeStackTrace(''' -#0 baz (file:///pathto/test.dart:50:3) -#1 bar (file:///pathto/test.dart:46:9) - '''), [ - { - 'abs_path': 'test.dart', - 'function': 'bar', - 'lineno': 46, - 'in_app': true, - 'filename': 'test.dart' - }, - { - 'abs_path': 'test.dart', - 'function': 'baz', - 'lineno': 50, - 'in_app': true, - 'filename': 'test.dart' - }, - ]); - }); - - test('encodes an asynchronous stack trace', () { - expect(encodeStackTrace(''' -#0 baz (file:///pathto/test.dart:50:3) - -#1 bar (file:///pathto/test.dart:46:9) - '''), [ - { - 'abs_path': 'test.dart', - 'function': 'bar', - 'lineno': 46, - 'in_app': true, - 'filename': 'test.dart' - }, - { - 'abs_path': '', - }, - { - 'abs_path': 'test.dart', - 'function': 'baz', - 'lineno': 50, - 'in_app': true, - 'filename': 'test.dart' - }, - ]); - }); - }); -} diff --git a/packages/sentry/test/utils_test.dart b/packages/sentry/test/utils_test.dart deleted file mode 100644 index e86a57a2aae1..000000000000 --- a/packages/sentry/test/utils_test.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:test/test.dart'; - -import 'package:sentry/src/utils.dart'; - -void main() { - group('mergeAttributes', () { - test('merges attributes', () { - final Map target = { - 'overwritten': 1, - 'unchanged': 2, - 'recursed': { - 'overwritten_child': [1, 2, 3], - 'unchanged_child': 'qwerty', - }, - }; - - final Map attributes = { - 'overwritten': 2, - 'recursed': { - 'overwritten_child': [4, 5, 6], - }, - }; - - mergeAttributes(attributes, into: target); - expect(target, { - 'overwritten': 2, - 'unchanged': 2, - 'recursed': { - 'overwritten_child': [4, 5, 6], - 'unchanged_child': 'qwerty', - }, - }); - }); - - test('does not allow overriding original maps', () { - final environment = { - 'extra': { - 'device': 'Pixel 2', - }, - }; - - final event = { - 'extra': { - 'widget': 'Scaffold', - }, - }; - - final target = {}; - mergeAttributes(environment, into: target); - mergeAttributes(event, into: target); - expect(environment['extra'], {'device': 'Pixel 2'}); - }); - }); - - group('formatDateAsIso8601WithSecondPrecision', () { - test('strips sub-millisecond parts', () { - final DateTime testDate = - new DateTime.fromMillisecondsSinceEpoch(1502467721598, isUtc: true); - expect(testDate.toIso8601String(), '2017-08-11T16:08:41.598Z'); - expect(formatDateAsIso8601WithSecondPrecision(testDate), - '2017-08-11T16:08:41'); - }); - }); -} diff --git a/packages/sentry/test/version_test.dart b/packages/sentry/test/version_test.dart deleted file mode 100644 index 4f32a66ea3cc..000000000000 --- a/packages/sentry/test/version_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:sentry/sentry.dart'; -import 'package:test/test.dart'; -import 'package:yaml/yaml.dart' as yaml; - -void main() { - group('sdkVersion', () { - test('matches that of pubspec.yaml', () { - final dynamic pubspec = - yaml.loadYaml(new File('pubspec.yaml').readAsStringSync()); - expect(sdkVersion, pubspec['version']); - }); - }); -} diff --git a/packages/sentry/tool/dart2_test.sh b/packages/sentry/tool/dart2_test.sh deleted file mode 100755 index d9f38d362c20..000000000000 --- a/packages/sentry/tool/dart2_test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -# Temporary workaround until Pub supports --preview-dart-2 flag -set -e -set -x -for filename in test/*_test.dart; do - dart --preview-dart-2 --enable_asserts "$filename" -done diff --git a/packages/sentry/tool/presubmit.sh b/packages/sentry/tool/presubmit.sh deleted file mode 100755 index a464495fd042..000000000000 --- a/packages/sentry/tool/presubmit.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -e -set -x - -pub get -dartanalyzer --strong --fatal-warnings ./ -pub run test --platform vm -./tool/dart2_test.sh -dartfmt -n --set-exit-if-changed ./