diff --git a/.craft.yml b/.craft.yml index 94d538b336..b456b09aab 100644 --- a/.craft.yml +++ b/.craft.yml @@ -11,6 +11,7 @@ targets: dio: file: sqflite: + drift: - name: github - name: registry sdks: @@ -20,3 +21,5 @@ targets: pub:sentry_dio: pub:sentry_file: pub:sentry_sqflite: + # This needs to be published on pub.dev first before uncommenting + # pub:sentry_drift: diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index cca626a0e4..20d028f0d9 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -11,6 +11,7 @@ on: - "dio/**" - "file/**" - "sqflite/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.github/workflows/dio.yml b/.github/workflows/dio.yml index 05e21b4fad..801138c407 100644 --- a/.github/workflows/dio.yml +++ b/.github/workflows/dio.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "file/**" - "sqflite/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.github/workflows/drift.yml b/.github/workflows/drift.yml new file mode 100644 index 0000000000..3f2285bb7c --- /dev/null +++ b/.github/workflows/drift.yml @@ -0,0 +1,114 @@ +name: sentry-drift +on: + push: + branches: + - main + - release/** + pull_request: + paths-ignore: + - "**/*.md" + - "logging/**" + - "flutter/**" + - "dio/**" + - "file/**" + - "sqflite/**" + +jobs: + cancel-previous-workflow: + runs-on: ubuntu-latest + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@01ce38bf961b4e243a6342cbade0dbc8ba3f0432 # pin@0.12.0 + with: + access_token: ${{ github.token }} + + build: + name: ${{ matrix.target }} | ${{ matrix.os }} | ${{ matrix.sdk }} + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + defaults: + run: + shell: bash + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + target: ["ios", "android", "macos", "linux", "windows"] + sdk: ["stable", "beta"] + exclude: + - os: ubuntu-latest + target: ios + - os: ubuntu-latest + target: macos + - os: ubuntu-latest + target: windows + - os: windows-latest + target: ios + - os: windows-latest + target: android + - os: windows-latest + target: macos + - os: windows-latest + target: linux + # macos-latest is taking hours due to limited resources + - os: macos-latest + target: android + - os: macos-latest + target: linux + - os: macos-latest + target: windows + # Bad CPU type in executable + - os: macos-latest + sdk: beta + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v3 + if: ${{ matrix.target == 'android' }} + with: + java-version: "11" + distribution: "adopt" + + # Install required dependencies for Flutter on Linux on Ubuntu + - name: "Setup Linux" + run: | + sudo apt update + sudo apt install -y cmake dbus libblkid-dev libgtk-3-dev liblzma-dev ninja-build pkg-config xvfb + sudo apt install -y network-manager upower + if: matrix.os == 'ubuntu-latest' && matrix.target == 'linux' + + - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa # pin@v2.10.0 + with: + channel: ${{ matrix.sdk }} + + - run: flutter upgrade + + - name: Pub Get + run: | + cd drift + flutter pub get + + - name: Test VM with coverage + run: | + cd drift + flutter test --coverage --test-randomize-ordering-seed=random + + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # pin@v3 + if: runner.os == 'Linux' && matrix.sdk == 'stable' && matrix.target == 'linux' + with: + name: sentry_drift + file: ./drift/coverage/lcov.info + functionalities: "search" # remove after https://github.com/codecov/codecov-action/issues/600 + + - uses: VeryGoodOpenSource/very_good_coverage@e5c91bc7ce9843e87c800b3bcafdfb86fbe28491 # pin@v2.1.0 + if: runner.os == 'Linux' && matrix.sdk == 'stable' && matrix.target == 'linux' + with: + path: "./drift/coverage/lcov.info" + min_coverage: 80 + + analyze: + uses: ./.github/workflows/analyze.yml + with: + package: drift + sdk: flutter \ No newline at end of file diff --git a/.github/workflows/e2e_dart.yml b/.github/workflows/e2e_dart.yml index 08493f67b3..62827e69c3 100644 --- a/.github/workflows/e2e_dart.yml +++ b/.github/workflows/e2e_dart.yml @@ -12,6 +12,7 @@ on: - "flutter/**" - "file/**" - "sqflite/**" + - "drift/**" env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} diff --git a/.github/workflows/file.yml b/.github/workflows/file.yml index b0a6d5b728..a4e17af787 100644 --- a/.github/workflows/file.yml +++ b/.github/workflows/file.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "dio/**" - "sqflite/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 834ce13a1f..d81896825e 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -11,6 +11,7 @@ on: - "dio/**" - "file/**" - "sqflite/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.github/workflows/logging.yml b/.github/workflows/logging.yml index 8be3df1b95..3696428cd8 100644 --- a/.github/workflows/logging.yml +++ b/.github/workflows/logging.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "file/**" - "sqflite/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.github/workflows/min_version_test.yml b/.github/workflows/min_version_test.yml index 23078eb1b4..8499838d41 100644 --- a/.github/workflows/min_version_test.yml +++ b/.github/workflows/min_version_test.yml @@ -9,6 +9,7 @@ on: - "**/*.md" - "file/**" - "sqflite/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.github/workflows/sqflite.yml b/.github/workflows/sqflite.yml index 65e4c3f7a8..b1a1858756 100644 --- a/.github/workflows/sqflite.yml +++ b/.github/workflows/sqflite.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "dio/**" - "file/**" + - "drift/**" jobs: cancel-previous-workflow: diff --git a/.gitignore b/.gitignore index 558e422972..55f9d96c39 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ dio/coverage/* file/coverage/* flutter/coverage/* sqflite/coverage/* +drift/coverage/* pubspec.lock Podfile.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 51543538ed..7fe44ecf20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Features +- Add APM integration for Drift ([#1709](https://github.com/getsentry/sentry-dart/pull/1709)) - StackTraces in `PlatformException.message` will get nicely formatted too when present ([#1716](https://github.com/getsentry/sentry-dart/pull/1716)) - Breadcrumbs for database operations ([#1656](https://github.com/getsentry/sentry-dart/pull/1656)) - Add `attachScreenshotOnlyWhenResumed` to options ([#1700](https://github.com/getsentry/sentry-dart/pull/1700)) diff --git a/dart/lib/src/sentry_trace_origins.dart b/dart/lib/src/sentry_trace_origins.dart index e3fd4dbafc..8910292b74 100644 --- a/dart/lib/src/sentry_trace_origins.dart +++ b/dart/lib/src/sentry_trace_origins.dart @@ -18,4 +18,7 @@ class SentryTraceOrigins { 'auto.db.sqflite.database_executor'; static const autoDbSqfliteDatabaseFactory = 'auto.db.sqflite.database_factory'; + static const autoDbDriftQueryExecutor = 'auto.db.drift.query.executor'; + static const autoDbDriftTransactionExecutor = + 'auto.db.drift.transaction.executor'; } diff --git a/drift/.gitignore b/drift/.gitignore new file mode 100644 index 0000000000..ba521d5a39 --- /dev/null +++ b/drift/.gitignore @@ -0,0 +1,14 @@ +# Omit committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ diff --git a/drift/CHANGELOG.md b/drift/CHANGELOG.md new file mode 100644 index 0000000000..c640f46f6b --- /dev/null +++ b/drift/CHANGELOG.md @@ -0,0 +1,1596 @@ +# Changelog + +## 7.12.0 + +### Enhancements + +- Log warning if both tracesSampleRate and tracesSampler are set ([#1701](https://github.com/getsentry/sentry-dart/pull/1701)) +- Better Flutter framework stack traces - we now collect Flutter framework debug symbols for iOS, macOS and Android automatically on the Sentry server ([#1673](https://github.com/getsentry/sentry-dart/pull/1673)) + +### Features + +- Initial (alpha) support for profiling on iOS and macOS ([#1611](https://github.com/getsentry/sentry-dart/pull/1611)) + +## 7.11.0 + +### Fixes + +- Session: missing mechanism.handled is considered crash ([#3353](https://github.com/getsentry/sentry-cocoa/pull/3353)) + +### Features + +- Breadcrumbs for file I/O operations ([#1649](https://github.com/getsentry/sentry-dart/pull/1649)) + +### Dependencies + +- Enable compatibility with uuid v4 ([#1647](https://github.com/getsentry/sentry-dart/pull/1647)) +- Bump Android SDK from v6.29.0 to v6.32.0 ([#1660](https://github.com/getsentry/sentry-dart/pull/1660), [#1676](https://github.com/getsentry/sentry-dart/pull/1676), [#1688](https://github.com/getsentry/sentry-dart/pull/1688)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6320) + - [diff](https://github.com/getsentry/sentry-java/compare/6.29.0...6.32.0) +- Bump Cocoa SDK from v8.11.0 to v8.14.2 ([#1650](https://github.com/getsentry/sentry-dart/pull/1650), [#1655](https://github.com/getsentry/sentry-dart/pull/1655), [#1677](https://github.com/getsentry/sentry-dart/pull/1677), [#1691](https://github.com/getsentry/sentry-dart/pull/1691)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8142) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.11.0...8.14.2) + +## 7.10.1 + +### Enhancements + +- Add Sampling Decision to Trace Envelope Header ([#1639](https://github.com/getsentry/sentry-dart/pull/1639)) +- Add http.request.method attribute to http spans data ([#1633](https://github.com/getsentry/sentry-dart/pull/1633)) +- Add db.system and db.name attributes to db spans data ([#1629](https://github.com/getsentry/sentry-dart/pull/1629)) +- Log SDK errors to the console if the log level is `fatal` even if `debug` is disabled ([#1635](https://github.com/getsentry/sentry-dart/pull/1635)) + +### Features + +- Tracing without performance ([#1621](https://github.com/getsentry/sentry-dart/pull/1621)) + +### Fixes + +- Normalize data properties of `SentryUser` and `Breadcrumb` before sending over method channel ([#1591](https://github.com/getsentry/sentry-dart/pull/1591)) +- Fixing memory leak issue in SentryFlutterPlugin (Android Plugin) ([#1588](https://github.com/getsentry/sentry-dart/pull/1588)) +- Discard empty stack frames ([#1625](https://github.com/getsentry/sentry-dart/pull/1625)) +- Disable scope sync for cloned scopes ([#1628](https://github.com/getsentry/sentry-dart/pull/1628)) + +### Dependencies + +- Bump Android SDK from v6.25.2 to v6.29.0 ([#1586](https://github.com/getsentry/sentry-dart/pull/1586), [#1630](https://github.com/getsentry/sentry-dart/pull/1630)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6290) + - [diff](https://github.com/getsentry/sentry-java/compare/6.25.2...6.29.0) +- Bump Cocoa SDK from v8.9.1 to v8.11.0 ([#1584](https://github.com/getsentry/sentry-dart/pull/1584), [#1606](https://github.com/getsentry/sentry-dart/pull/1606), [#1626](https://github.com/getsentry/sentry-dart/pull/1626)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8110) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.9.1...8.11.0) + +## 7.9.0 + +### Features + +- Send trace origin ([#1534](https://github.com/getsentry/sentry-dart/pull/1534)) + +[Trace origin](https://develop.sentry.dev/sdk/performance/trace-origin/) indicates what created a trace or a span. Not all transactions and spans contain enough information to tell whether the user or what precisely in the SDK created it. Origin solves this problem. The SDK now sends origin for transactions and spans. + +- Add `appHangTimeoutInterval` to `SentryFlutterOptions` ([#1568](https://github.com/getsentry/sentry-dart/pull/1568)) +- DioEventProcessor: Append http response body ([#1557](https://github.com/getsentry/sentry-dart/pull/1557)) + - This is opt-in and depends on `maxResponseBodySize` + - Only for `dio` package + +### Dependencies + +- Bump Cocoa SDK from v8.8.0 to v8.9.1 ([#1553](https://github.com/getsentry/sentry-dart/pull/1553)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#891) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.8.0...8.9.1) +- Bump Android SDK from v6.23.0 to v6.25.2 ([#1554](https://github.com/getsentry/sentry-dart/pull/1554)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6252) + - [diff](https://github.com/getsentry/sentry-java/compare/6.23.0...6.25.2) + +## 7.8.0 + +### Enhancements + +- Add `apiTarget` field to `SentryRequest` and `data` field to `SentryResponse` ([#1517](https://github.com/getsentry/sentry-dart/pull/1517)) + +### Dependencies + +- Bump Android SDK from v6.21.0 to v6.23.0 ([#1512](https://github.com/getsentry/sentry-dart/pull/1512), [#1520](https://github.com/getsentry/sentry-dart/pull/1520)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6230) + - [diff](https://github.com/getsentry/sentry-java/compare/6.21.0...6.23.0) +- Bump Cocoa SDK from v8.7.3 to v8.8.0 ([#1521](https://github.com/getsentry/sentry-dart/pull/1521)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#880) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.3...8.8.0) + +## 7.7.0 + +### Fixes + +- Enums use its name instead of non exhaustive switches ([##1506](https://github.com/getsentry/sentry-dart/pull/#1506)) + +### Enhancements + +- Add http fields to `span.data` ([#1497](https://github.com/getsentry/sentry-dart/pull/1497)) + - Set `http.response.status_code` + - Set `http.response_content_length` +- Improve `SentryException#value`, remove stringified stack trace ([##1470](https://github.com/getsentry/sentry-dart/pull/#1470)) + +### Dependencies + +- Bump Android SDK from v6.20.0 to v6.21.0 ([#1500](https://github.com/getsentry/sentry-dart/pull/1500)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6210) + - [diff](https://github.com/getsentry/sentry-java/compare/6.20.0...6.21.0) + +## 7.6.3 + +### Fixes + +- Check if the Native SDKs are enabled when using `autoInitializeNativeSdk=false` ([#1489](https://github.com/getsentry/sentry-dart/pull/1489)) +- Align http method to span convention ([#1477](https://github.com/getsentry/sentry-dart/pull/1477)) +- Wrapped methods return a `Future` instead of executing right away ([#1476](https://github.com/getsentry/sentry-dart/pull/1476)) + - Relates to ([#1462](https://github.com/getsentry/sentry-dart/pull/1462)) +- Fix readTimeoutMillis wrongly configures connectionTimeoutMillis instead of the correct field ([#1485](https://github.com/getsentry/sentry-dart/pull/1485)) + +### Dependencies + +- Bump Android SDK from v6.19.0 to v6.20.0 ([#1466](https://github.com/getsentry/sentry-dart/pull/1466), [#1491](https://github.com/getsentry/sentry-dart/pull/1491)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6200) + - [diff](https://github.com/getsentry/sentry-java/compare/6.19.0...6.20.0) +- Bump Cocoa SDK from v8.7.2 to v8.7.3 ([#1487](https://github.com/getsentry/sentry-dart/pull/1487)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#873) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.2...8.7.3) + +## 7.6.2 + +### Enhancements + +- `SentryAssetBundle` returns Future by default ([#1462](https://github.com/getsentry/sentry-dart/pull/1462)) + +### Features + +- Support `http` >= 1.0.0 ([#1475](https://github.com/getsentry/sentry-dart/pull/1475)) + +### Dependencies + +- Bump Android SDK from v6.18.1 to v6.19.0 ([#1455](https://github.com/getsentry/sentry-dart/pull/1455)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6190) + - [diff](https://github.com/getsentry/sentry-java/compare/6.18.1...6.19.0) +- Bump Cocoa SDK from v8.7.1 to v8.7.2 ([#1458](https://github.com/getsentry/sentry-dart/pull/1458)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#872) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.7.1...8.7.2) + +## 7.6.1 + +### Features + +- Add `sent_at` to envelope header ([#1428](https://github.com/getsentry/sentry-dart/pull/1428)) + +### Fixes + +- Fix battery level conversion for iOS 16.4 ([#1433](https://github.com/getsentry/sentry-dart/pull/1433)) +- Adds a namespace for compatibility with AGP 8.0. ([#1427](https://github.com/getsentry/sentry-dart/pull/1427)) +- Avoid dependency conflict with package_info_plus v4 ([#1440](https://github.com/getsentry/sentry-dart/pull/1440)) + +### Breaking Changes + +- Android `minSdkVersion` is now 19 (Flutter already defines 19-20 as best effort) +- Deprecate `extra` in favor of `contexts` ([#1435](https://github.com/getsentry/sentry-dart/pull/1435)) + +### Dependencies + +- Bump Cocoa SDK from v8.5.0 to v8.7.1 ([#1449](https://github.com/getsentry/sentry-dart/pull/1449)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#871) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.5.0...8.7.1) + +## 7.5.2 + +### Fixes + +- Fix `event.origin` and `event.environment` on unhandled exceptions ([#1419](https://github.com/getsentry/sentry-dart/pull/1419)) +- Fix authority redaction ([#1424](https://github.com/getsentry/sentry-dart/pull/1424)) + +### Dependencies + +- Bump Android SDK from v6.17.0 to v6.18.1 ([#1415](https://github.com/getsentry/sentry-dart/pull/1415)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6181) + - [diff](https://github.com/getsentry/sentry-java/compare/6.17.0...6.18.1) + +## 7.5.1 + +### Fixes + +- Fallback Uri parsing to `unknown` if its invalid ([#1414](https://github.com/getsentry/sentry-dart/pull/1414)) + +## 7.5.0 + +### Features + +- Add `SentryIOOverridesIntegration` that automatically wraps `File` into `SentryFile` ([#1362](https://github.com/getsentry/sentry-dart/pull/1362)) + +```dart +import 'package:sentry_file/sentry_file.dart'; + +// SDK init. options +options.addIntegration(SentryIOOverridesIntegration()); +``` + +- Add `enableTracing` option ([#1395](https://github.com/getsentry/sentry-dart/pull/1395)) + - This change is backwards compatible. The default is `null` meaning existing behaviour remains unchanged (setting either `tracesSampleRate` or `tracesSampler` enables performance). + - If set to `true`, performance is enabled, even if no `tracesSampleRate` or `tracesSampler` have been configured. + - If set to `true`, sampler will use default sample rate of 1.0, if no `tracesSampleRate` is set. + - If set to `false` performance is disabled, regardless of `tracesSampleRate` and `tracesSampler` options. + +```dart +// SDK init. options +options.enableTracing = true; +``` + +- Sync `connectionTimeout` and `readTimeout` to Android ([#1397](https://github.com/getsentry/sentry-dart/pull/1397)) + +```dart +// SDK init. options +options.connectionTimeout = Duration(seconds: 10); +options.readTimeout = Duration(seconds: 10); +``` + +- Set User `name` and `geo` in native plugins ([#1393](https://github.com/getsentry/sentry-dart/pull/1393)) + +```dart +Sentry.configureScope( + (scope) => scope.setUser(SentryUser( + id: '1234', + name: 'Jane Doe', + email: 'jane.doe@example.com', + geo: SentryGeo( + city: 'Vienna', + countryCode: 'AT', + region: 'Austria', + ))), +); +``` + +- Add processor count to device info ([#1402](https://github.com/getsentry/sentry-dart/pull/1402)) +- Add attachments to `Hint` ([#1404](https://github.com/getsentry/sentry-dart/pull/1404)) + +```dart +import 'dart:convert'; + +options.beforeSend = (event, {hint}) { + final text = 'This event should not be sent happen in prod. Investigate.'; + final textAttachment = SentryAttachment.fromIntList( + utf8.encode(text), + 'event_info.txt', + contentType: 'text/plain', + ); + hint?.attachments.add(textAttachment); + return event; +}; +``` + +### Fixes + +- Screenshots and View Hierarchy should only be added to errors ([#1385](https://github.com/getsentry/sentry-dart/pull/1385)) + - View Hierarchy is removed from Web errors since we don't symbolicate minified View Hierarchy yet. +- More improvements related to not awaiting `FutureOr` if it's not a future ([#1385](https://github.com/getsentry/sentry-dart/pull/1385)) +- Do not report only async gap frames for logging calls ([#1398](https://github.com/getsentry/sentry-dart/pull/1398)) + +### Dependencies + +- Bump Cocoa SDK from v8.4.0 to v8.5.0 ([#1394](https://github.com/getsentry/sentry-dart/pull/1394)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#850) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.5.0) + +## 7.4.2 + +### Fixes + +- Fix breadcrumbs not being sent on Android web ([#1378](https://github.com/getsentry/sentry-dart/pull/1378)) + +## 7.4.1 + +### Fixes + +- Fix Dart web builds breaking due to `dart:io` imports when using `SentryIsolate` or `SentryIsolateExtension` ([#1371](https://github.com/getsentry/sentry-dart/pull/1371)) + - When using `SentryIsolate` or `SentryIsolateExtension`, import `sentry_io.dart`. +- Export `SentryBaggage` ([#1377](https://github.com/getsentry/sentry-dart/pull/1377)) +- Remove breadcrumbs from transaction to avoid duplication ([#1366](https://github.com/getsentry/sentry-dart/pull/1366)) + +### Dependencies + +- Bump Cocoa SDK from v8.3.3 to v8.4.0 ([#1379](https://github.com/getsentry/sentry-dart/pull/1379)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#840) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.3.3...8.4.0) +- Bump Android SDK from v6.16.0 to v6.17.0 ([#1374](https://github.com/getsentry/sentry-dart/pull/1374)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6170) + - [diff](https://github.com/getsentry/sentry-java/compare/6.16.0...6.17.0) + +## 7.4.0 + +### Features + +- SentryUserInteractionWidget: add support for PopupMenuButton and PopupMenuItem ([#1361](https://github.com/getsentry/sentry-dart/pull/1361)) + +### Fixes + +- Fix `SentryUserInteractionWidget` throwing when Sentry is not enabled ([#1363](https://github.com/getsentry/sentry-dart/pull/1363)) +- Fix enableAutoNativeBreadcrumbs and enableNativeCrashHandling sync flags ([#1367](https://github.com/getsentry/sentry-dart/pull/1367)) + +## 7.3.0 + +### Features + +- Sanitize sensitive data from URLs (span desc, span data, crumbs, client errors) ([#1327](https://github.com/getsentry/sentry-dart/pull/1327)) + +### Dependencies + +- Bump Cocoa SDK from v8.3.1 to v8.3.3 ([#1350](https://github.com/getsentry/sentry-dart/pull/1350), [#1355](https://github.com/getsentry/sentry-dart/pull/1355)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#833) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.3.1...8.3.3) + +### Fixes + +- Sync missing properties to the Native SDKs ([#1354](https://github.com/getsentry/sentry-dart/pull/1354)) + +## 7.2.0 + +### Features + +- sqflite Support for Flutter ([#1306](https://github.com/getsentry/sentry-dart/pull/1306)) + +### Fixes + +- `DioErrorExtractor` no longer extracts `DioError.stackTrace` which is done via `DioStackTraceExtractor` instead ([#1344](https://github.com/getsentry/sentry-dart/pull/1344)) +- LoadImageListIntegration won't throw bad state if there is no exceptions in the event ([#1347](https://github.com/getsentry/sentry-dart/pull/1347)) + +### Dependencies + +- Bump Android SDK from v6.15.0 to v6.16.0 ([#1342](https://github.com/getsentry/sentry-dart/pull/1342)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6160) + - [diff](https://github.com/getsentry/sentry-java/compare/6.15.0...6.16.0) + +## 7.1.0 + +### Features + +- Exception StackTrace Extractor ([#1335](https://github.com/getsentry/sentry-dart/pull/1335)) + +### Dependencies + +- Bump Cocoa SDK from v8.0.0 to v8.3.1 ([#1331](https://github.com/getsentry/sentry-dart/pull/1331)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#831) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.0.0...8.3.1) + +### Fixes + +- SentryUserInteractionWidget checks if the Elements are mounted before comparing them ([#1339](https://github.com/getsentry/sentry-dart/pull/1339)) + +## 7.0.0 + +### Features + +- Platform Exception Event Processor ([#1297](https://github.com/getsentry/sentry-dart/pull/1297)) +- Support failedRequestTargets for HTTP Client errors ([#1285](https://github.com/getsentry/sentry-dart/pull/1285)) + - Captures errors for the default range `500-599` if `captureFailedRequests` is enabled +- Sentry Isolate Extension ([#1266](https://github.com/getsentry/sentry-dart/pull/1266)) +- Allow sentry user to control resolution of captured Flutter screenshots ([#1288](https://github.com/getsentry/sentry-dart/pull/1288)) +- Support beforeSendTransaction ([#1238](https://github.com/getsentry/sentry-dart/pull/1238)) +- Add In Foreground to App context ([#1260](https://github.com/getsentry/sentry-dart/pull/1260)) +- Error Cause Extractor ([#1198](https://github.com/getsentry/sentry-dart/pull/1198), [#1236](https://github.com/getsentry/sentry-dart/pull/1236)) + - Add `throwable` to `SentryException` +- Dart 3 Support ([#1220](https://github.com/getsentry/sentry-dart/pull/1220)) +- Introduce `Hint` data bag ([#1136](https://github.com/getsentry/sentry-dart/pull/1136)) +- Use `Hint` for screenshots ([#1165](https://github.com/getsentry/sentry-dart/pull/1165)) +- Support custom units for custom measurements ([#1181](https://github.com/getsentry/sentry-dart/pull/1181)) + +### Enhancements + +- Replace `toImage` with `toImageSync` for Flutter >= 3.7 ([1268](https://github.com/getsentry/sentry-dart/pull/1268)) +- Don't await `FutureOr` if it's not a future. This should marginally improve the performance ([#1310](https://github.com/getsentry/sentry-dart/pull/1310)) +- Replace `StackTrace.empty` with `StackTrace.current` ([#1183](https://github.com/getsentry/sentry-dart/pull/1183)) + +### Breaking Changes + +[Dart Migration guide](https://docs.sentry.io/platforms/dart/migration/#migrating-from-sentry-618x-to-sentry-700). + +[Flutter Migration guide](https://docs.sentry.io/platforms/flutter/migration/#migrating-from-sentry_flutter-618x-to-sentry-700). + +- Enable enableNdkScopeSync by default ([#1276](https://github.com/getsentry/sentry-dart/pull/1276)) +- Update `sentry_dio` to dio v5 ([#1282](https://github.com/getsentry/sentry-dart/pull/1282)) +- Remove deprecated fields ([#1227](https://github.com/getsentry/sentry-dart/pull/1227)) + - Remove deprecated fields from the `Scope` class. + - `user(SentryUser? user)`, using the `setUser(SentryUser? user)` instead. + - `attachements`, using the `attachments` instead. + - Remove deprecated field from the `SentryFlutterOptions` class. + - `anrTimeoutIntervalMillis`, using the `anrTimeoutInterval` instead. + - `autoSessionTrackingIntervalMillis`, using the `autoSessionTrackingInterval` instead. +- Rename APM tracking feature flags to tracing ([#1222](https://github.com/getsentry/sentry-dart/pull/1222)) + - Rename + - enableAutoPerformanceTracking to enableAutoPerformanceTracing + - enableOutOfMemoryTracking to enableWatchdogTerminationTracking +- Enable APM features by default ([#1217](https://github.com/getsentry/sentry-dart/pull/1217)) + - Enable by default + - captureFailedRequests + - enableStructuredDataTracing + - enableUserInteractionTracing +- Mark transaction as internal_error in case of unhandled errors ([#1218](https://github.com/getsentry/sentry-dart/pull/1218)) +- Removed various deprecated fields ([#1036](https://github.com/getsentry/sentry-dart/pull/1036)): + - Removed the following fields from the `device` context + - language + - timezone + - screenResolution + - theme + - Removed isolate name from Dart context. It's now reported via the threads interface. It can be enabled via `options.attachThreads` +- Use `sentryClientName` instead of `sdk.identifier` ([#1135](https://github.com/getsentry/sentry-dart/pull/1135)) +- Refactor `BindingUtils` to `BindingWrapper` to enable the use of custom bindings ([#1184](https://github.com/getsentry/sentry-dart/pull/1184)) +- Bump Flutter min to 3.0.0 and Dart to 2.17.0 ([#1180](https://github.com/getsentry/sentry-dart/pull/1180)) + +### Dependencies + +- Bump Cocoa SDK from 7.31.5 to 8.0.0 + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#800) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.31.5...8.0.0) + +### Fixes + +- View hierarchy reads size from RenderBox only ([#1258](https://github.com/getsentry/sentry-dart/pull/1258)) +- Try to avoid ConcurrentModificationError by not using a Future.forEach ([#1259](https://github.com/getsentry/sentry-dart/pull/1259)) +- isWeb check for WASM ([#1249](https://github.com/getsentry/sentry-dart/pull/1249)) +- Don't suppress error logs ([#1228](https://github.com/getsentry/sentry-dart/pull/1228)) +- Fix: Remove `SentryOptions` related parameters from classes which also take `Hub` as a parameter (#816) + +## 7.0.0-rc.2 + +### Features + +- Platform Exception Event Processor ([#1297](https://github.com/getsentry/sentry-dart/pull/1297)) +- Support failedRequestTargets for HTTP Client errors ([#1285](https://github.com/getsentry/sentry-dart/pull/1285)) + - Captures errors for the default range `500-599` if `captureFailedRequests` is enabled +- Sentry Isolate Extension ([#1266](https://github.com/getsentry/sentry-dart/pull/1266)) +- Allow sentry user to control resolution of captured Flutter screenshots ([#1288](https://github.com/getsentry/sentry-dart/pull/1288)) + +### Enhancements + +- Replace `toImage` with `toImageSync` for Flutter >= 3.7 ([1268](https://github.com/getsentry/sentry-dart/pull/1268)) +- Don't await `FutureOr` if it's not a future. This should marginally improve the performance ([#1310](https://github.com/getsentry/sentry-dart/pull/1310)) + +## 6.22.0 + +### Features + +- Add proguard_uui property to SentryFlutterOptions to set proguard information at runtime ([#1312](https://github.com/getsentry/sentry-dart/pull/1312)) + +### Fixes + +- Change podspec `EXCLUDED_ARCHS` value to allow podfiles to add more excluded architetures ([#1303](https://github.com/getsentry/sentry-dart/pull/1303)) + +### Dependencies + +- Bump Android SDK from v6.13.1 to v6.15.0 ([#1287](https://github.com/getsentry/sentry-dart/pull/1287), [#1311](https://github.com/getsentry/sentry-dart/pull/1311)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6150) + - [diff](https://github.com/getsentry/sentry-java/compare/6.13.1...6.15.0) + +## 7.0.0-rc.1 + +### Breaking Changes + +- Enable enableNdkScopeSync by default ([#1276](https://github.com/getsentry/sentry-dart/pull/1276)) +- Update `sentry_dio` to dio v5 ([#1282](https://github.com/getsentry/sentry-dart/pull/1282)) + +### Dependencies + +- Bump Android SDK from v6.13.1 to v6.14.0 ([#1287](https://github.com/getsentry/sentry-dart/pull/1287)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6140) + - [diff](https://github.com/getsentry/sentry-java/compare/6.13.1...6.14.0) + +## 6.21.0 + +### Features + +- Implement `loadStructuredBinaryData` from updated AssetBundle ([#1272](https://github.com/getsentry/sentry-dart/pull/1272)) + +### Dependencies + +- Bump Android SDK from v6.13.0 to v6.13.1 ([#1273](https://github.com/getsentry/sentry-dart/pull/1273)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6131) + - [diff](https://github.com/getsentry/sentry-java/compare/6.13.0...6.13.1) + +### Fixes + +- Pass processed Breadcrumb to scope observer ([#1298](https://github.com/getsentry/sentry-dart/pull/1298)) +- Remove duplicated breadcrumbs when syncing with iOS/macOS ([#1283](https://github.com/getsentry/sentry-dart/pull/1283)) + +## 6.20.1 + +### Fixes + +- Set client name with version in Android SDK ([#1274](https://github.com/getsentry/sentry-dart/pull/1274)) + +## 7.0.0-beta.4 + +### Features + +- Support beforeSendTransaction ([#1238](https://github.com/getsentry/sentry-dart/pull/1238)) +- Add In Foreground to App context ([#1260](https://github.com/getsentry/sentry-dart/pull/1260)) + +### Fixes + +- View hierarchy reads size from RenderBox only ([#1258](https://github.com/getsentry/sentry-dart/pull/1258)) +- Try to avoid ConcurrentModificationError by not using a Future.forEach ([#1259](https://github.com/getsentry/sentry-dart/pull/1259)) + +### Dependencies + +- Bump Android SDK from v6.12.1 to v6.13.0 ([#1250](https://github.com/getsentry/sentry-dart/pull/1250)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6130) + - [diff](https://github.com/getsentry/sentry-java/compare/6.12.1...6.13.0) + +## 7.0.0-beta.1 + +### Fixes + +- isWeb check for WASM ([#1249](https://github.com/getsentry/sentry-dart/pull/1249)) + +## 7.0.0-alpha.5 + +### Features + +- Error Cause Extractor ([#1198](https://github.com/getsentry/sentry-dart/pull/1198), [#1236](https://github.com/getsentry/sentry-dart/pull/1236)) + - Add `throwable` to `SentryException` + +### Fixes + +- Don't suppress error logs ([#1228](https://github.com/getsentry/sentry-dart/pull/1228)) +- Fix export for `BindingWrapper` ([#1234](https://github.com/getsentry/sentry-dart/pull/1234)) + +## 7.0.0-alpha.4 + +### Breaking Changes + +- Remove deprecated fields ([#1227](https://github.com/getsentry/sentry-dart/pull/1227)) + - Remove deprecated fields from the `Scope` class. + - `user(SentryUser? user)`, using the `setUser(SentryUser? user)` instead. + - `attachements`, using the `attachments` instead. + - Remove deprecated field from the `SentryFlutterOptions` class. + - `anrTimeoutIntervalMillis`, using the `anrTimeoutInterval` instead. + - `autoSessionTrackingIntervalMillis`, using the `autoSessionTrackingInterval` instead. + +### Dependencies + +- Bump Cocoa SDK from 8.0.0-rc.1 to 8.0.0 + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#800) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.0.0-rc.1...8.0.0) + +## 7.0.0-alpha.3 + +### Breaking Changes + +- Rename APM tracking feature flags to tracing ([#1222](https://github.com/getsentry/sentry-dart/pull/1222)) + - Rename + - enableAutoPerformanceTracking to enableAutoPerformanceTracing + - enableOutOfMemoryTracking to enableWatchdogTerminationTracking + +### Enhancements + +- Migrate to sentry cocoa v8 ([#1197](https://github.com/getsentry/sentry-dart/pull/1197)) + +## 7.0.0-alpha.2 + +### Features + +- Dart 3 Support ([#1220](https://github.com/getsentry/sentry-dart/pull/1220)) + +### Breaking Changes + +- Enable APM features by default ([#1217](https://github.com/getsentry/sentry-dart/pull/1217)) + - Enable by default + - captureFailedRequests + - enableStructuredDataTracing + - enableUserInteractionTracing +- Mark transaction as internal_error in case of unhandled errors ([#1218](https://github.com/getsentry/sentry-dart/pull/1218)) + +## 7.0.0-alpha.1 + +### Features + +- Feat: Introduce `Hint` data bag ([#1136](https://github.com/getsentry/sentry-dart/pull/1136)) +- Feat: Use `Hint` for screenshots ([#1165](https://github.com/getsentry/sentry-dart/pull/1165)) +- Feat: Support custom units for custom measurements ([#1181](https://github.com/getsentry/sentry-dart/pull/1181)) + +### Fixes + +- Fix: Remove `SentryOptions` related parameters from classes which also take `Hub` as a parameter (#816) + +### Enhancements + +- Enha: Replace `StackTrace.empty` with `StackTrace.current` ([#1183](https://github.com/getsentry/sentry-dart/pull/1183)) + +### Breaking Changes + +- Removed various deprecated fields ([#1036](https://github.com/getsentry/sentry-dart/pull/1036)): + - Removed the following fields from the `device` context + - language + - timezone + - screenResolution + - theme + - Removed isolate name from Dart context. It's now reported via the threads interface. It can be enabled via `options.attachThreads` +- Use `sentryClientName` instead of `sdk.identifier` ([#1135](https://github.com/getsentry/sentry-dart/pull/1135)) +- Refactor `BindingUtils` to `BindingWrapper` to enable the use of custom bindings ([#1184](https://github.com/getsentry/sentry-dart/pull/1184)) +- Bump Flutter min to 3.0.0 and Dart to 2.17.0 ([#1180](https://github.com/getsentry/sentry-dart/pull/1180)) + +## 6.19.0 + +### Fixes + +- intl is now more version permissive (>=0.17.0 <1.0.0) ([#1247](https://github.com/getsentry/sentry-dart/pull/1247)) + +### Breaking Changes: + +- sentry_file now requires Dart >= 2.19 ([#1240](https://github.com/getsentry/sentry-dart/pull/1240)) + +## 6.18.3 + +### Fixes + +- Fix Pod target for iOS ([#1237](https://github.com/getsentry/sentry-dart/pull/1237)) + +### Dependencies + +- Bump Android SDK from v6.11.0 to v6.12.1 ([#1225](https://github.com/getsentry/sentry-dart/pull/1225), [#1230](https://github.com/getsentry/sentry-dart/pull/1230)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6121) + - [diff](https://github.com/getsentry/sentry-java/compare/6.11.0...6.12.1) + +## 6.18.2 + +### Fixes + +- enableUserInteractionTracing sometimes finds the wrong widget ([#1212](https://github.com/getsentry/sentry-dart/pull/1212)) +- Only call method channels on native platforms ([#1196](https://github.com/getsentry/sentry-dart/pull/1196)) + +### Dependencies + +- Bump Android SDK from v6.9.2 to v6.11.0 ([#1194](https://github.com/getsentry/sentry-dart/pull/1194), [#1209](https://github.com/getsentry/sentry-dart/pull/1209)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#6110) + - [diff](https://github.com/getsentry/sentry-java/compare/6.9.2...6.11.0) +- Bump Cocoa SDK from v7.31.3 to v7.31.5 ([#1190](https://github.com/getsentry/sentry-dart/pull/1190), [#1207](https://github.com/getsentry/sentry-dart/pull/1207)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/8.0.0/CHANGELOG.md#7315) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.31.3...7.31.5) + +## 6.18.1 + +### Fixes + +- Missing slow and frozen frames for Auto transactions ([#1172](https://github.com/getsentry/sentry-dart/pull/1172)) + +### Dependencies + +- Bump Android SDK from v6.9.1 to v6.9.2 ([#1167](https://github.com/getsentry/sentry-dart/pull/1167)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#692) + - [diff](https://github.com/getsentry/sentry-java/compare/6.9.1...6.9.2) + +## 6.18.0 + +### Features + +- Tracing for File IO integration ([#1160](https://github.com/getsentry/sentry-dart/pull/1160)) + +### Dependencies + +- Bump Cocoa SDK from v7.31.2 to v7.31.3 ([#1157](https://github.com/getsentry/sentry-dart/pull/1157)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7313) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.31.2...7.31.3) +- Bump Android SDK from v6.8.0 to v6.9.1 ([#1159](https://github.com/getsentry/sentry-dart/pull/1159)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#691) + - [diff](https://github.com/getsentry/sentry-java/compare/6.8.0...6.9.1) + +## 6.17.0 + +### Fixes + +- Capture Future errors for Flutter Web automatically ([#1152](https://github.com/getsentry/sentry-dart/pull/1152)) + +### Features + +- User Interaction transactions and breadcrumbs ([#1137](https://github.com/getsentry/sentry-dart/pull/1137)) + +## 6.16.1 + +### Fixes + +- Do not attach headers if Span is NoOp ([#1143](https://github.com/getsentry/sentry-dart/pull/1143)) + +### Dependencies + +- Bump Cocoa SDK from v7.31.1 to v7.31.2 ([#1146](https://github.com/getsentry/sentry-dart/pull/1146)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7312) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.31.1...7.31.2) +- Bump Android SDK from v6.7.1 to v6.8.0 ([#1147](https://github.com/getsentry/sentry-dart/pull/1147)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#680) + - [diff](https://github.com/getsentry/sentry-java/compare/6.7.1...6.8.0) + +## 6.16.0 + +### Features + +- Add request context to `HttpException`, `SocketException` and `NetworkImageLoadException` ([#1118](https://github.com/getsentry/sentry-dart/pull/1118)) +- `SocketException` and `FileSystemException` with `OSError`s report the `OSError` as root exception ([#1118](https://github.com/getsentry/sentry-dart/pull/1118)) + +### Fixes + +- VendorId should be a String ([#1112](https://github.com/getsentry/sentry-dart/pull/1112)) +- Disable `enableUserInteractionBreadcrumbs` on Android when `enableAutoNativeBreadcrumbs` is disabled ([#1131](https://github.com/getsentry/sentry-dart/pull/1131)) +- Transaction name is reset after the transaction finishes ([#1125](https://github.com/getsentry/sentry-dart/pull/1125)) + +### Dependencies + +- Bump Cocoa SDK from v7.30.2 to v7.31.1 ([#1132](https://github.com/getsentry/sentry-dart/pull/1132), [#1139](https://github.com/getsentry/sentry-dart/pull/1139)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7311) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.30.2...7.31.1) +- Bump Android SDK from v6.7.0 to v6.7.1 ([#1112](https://github.com/getsentry/sentry-dart/pull/1112)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#671) + - [diff](https://github.com/getsentry/sentry-java/compare/6.7.0...6.7.1) + +## 6.15.1 + +### Dependencies + +- Bump Cocoa SDK from v7.30.1 to v7.30.2 ([#1113](https://github.com/getsentry/sentry-dart/pull/1113)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7302) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.30.1...7.30.2) + +## 6.15.0 + +### Features + +- Feat: Screenshot Attachment ([#1088](https://github.com/getsentry/sentry-dart/pull/1088)) + +### Fixes + +- Merging of integrations and packages ([#1111](https://github.com/getsentry/sentry-dart/pull/1111)) +- Add missing `fragment` for HTTP Client Errors ([#1102](https://github.com/getsentry/sentry-dart/pull/1102)) +- Sync user name and geo for Android ([#1102](https://github.com/getsentry/sentry-dart/pull/1102)) +- Add mechanism to Dio Http Client error ([#1114](https://github.com/getsentry/sentry-dart/pull/1114)) + +### Dependencies + +- Bump Android SDK from v6.6.0 to v6.7.0 ([#1105](https://github.com/getsentry/sentry-dart/pull/1105)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#670) + - [diff](https://github.com/getsentry/sentry-java/compare/6.6.0...6.7.0) +- Bump Cocoa SDK from v7.30.0 to v7.30.1 ([#1104](https://github.com/getsentry/sentry-dart/pull/1104)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7301) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.30.0...7.30.1) + +## 6.14.0 + +### Features + +- Capture response information in `SentryHttpClient` ([#1095](https://github.com/getsentry/sentry-dart/pull/1095)) + +### Changes + +- Remove experimental `SentryResponse` fields: `url`, `body`, `redirected`, `status` ([#1095](https://github.com/getsentry/sentry-dart/pull/1095)) +- `SentryHttpClient` request body capture checks default PII capture setting, same as the DIO integration ([#1095](https://github.com/getsentry/sentry-dart/pull/1095)) + +### Dependencies + +- Bump Android SDK from v6.5.0 to v6.6.0 ([#1090](https://github.com/getsentry/sentry-dart/pull/1090)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#660) + - [diff](https://github.com/getsentry/sentry-java/compare/6.5.0...6.6.0) +- Bump Cocoa SDK from v7.28.0 to v7.30.0 ([#1089](https://github.com/getsentry/sentry-dart/pull/1089), [#1101](https://github.com/getsentry/sentry-dart/pull/1101)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7300) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.28.0...7.30.0) + +## 6.13.1 + +### Fixes + +- Avoid dependency conflict with package_info_plus v3 ([#1084](https://github.com/getsentry/sentry-dart/pull/1084)) + +## 6.13.0 + +### Features + +- Use PlatformDispatcher.onError in Flutter 3.3 ([#1039](https://github.com/getsentry/sentry-dart/pull/1039)) + +### Fixes + +- Bring protocol up to date with latest Sentry protocol ([#1038](https://github.com/getsentry/sentry-dart/pull/1038)) + +### Dependencies + +- Bump Cocoa SDK from v7.27.1 to v7.28.0 ([#1080](https://github.com/getsentry/sentry-dart/pull/1080)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7280) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.27.1...7.28.0) + +## 6.12.2 + +### Fixes + +- Avoid dependency conflict with package_info_plus v2 ([#1068](https://github.com/getsentry/sentry-dart/pull/1068)) + +## 6.12.1 + +### Dependencies + +- Bump Android SDK from v6.4.3 to v6.5.0 ([#1062](https://github.com/getsentry/sentry-dart/pull/1062), [#1064](https://github.com/getsentry/sentry-dart/pull/1064)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#650) + - [diff](https://github.com/getsentry/sentry-java/compare/6.4.3...6.5.0) + +## 6.12.0 + +### Fixes + +- Handle traces sampler exception ([#1040](https://github.com/getsentry/sentry-dart/pull/1040)) +- tracePropagationTargets ignores invalid Regex ([#1043](https://github.com/getsentry/sentry-dart/pull/1043)) +- SentryDevice cast error ([#1059](https://github.com/getsentry/sentry-dart/pull/1059)) + +### Features + +- Added [Flutter renderer](https://docs.flutter.dev/development/platform-integration/web/renderers) information to events ([#1035](https://github.com/getsentry/sentry-dart/pull/1035)) +- Added missing DSN field into the SentryEnvelopeHeader ([#1050](https://github.com/getsentry/sentry-dart/pull/1050)) + +### Dependencies + +- Bump Android SDK from v6.4.2 to v6.4.3 ([#1048](https://github.com/getsentry/sentry-dart/pull/1048)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#643) + - [diff](https://github.com/getsentry/sentry-java/compare/6.4.2...6.4.3) +- Bump Cocoa SDK from v7.27.0 to v7.27.1 ([#1049](https://github.com/getsentry/sentry-dart/pull/1049)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7271) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.27.0...7.27.1) + +## 6.11.2 + +### Fixes + +- Tracer does not allow setting measurement if finished ([#1026](https://github.com/getsentry/sentry-dart/pull/1026)) +- Add missing measurements units ([#1033](https://github.com/getsentry/sentry-dart/pull/1033)) + +### Features + +- Bump Cocoa SDK from v7.26.0 to v7.27.0 ([#1030](https://github.com/getsentry/sentry-dart/pull/1030)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7270) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.26.0...7.27.0) + +## 6.11.1 + +### Fixes + +- Align span spec for serialize ops ([#1024](https://github.com/getsentry/sentry-dart/pull/1024)) +- Pin sentry version ([#1020](https://github.com/getsentry/sentry-dart/pull/1020)) + +### Features + +- Bump Cocoa SDK from v7.25.1 to v7.26.0 ([#1023](https://github.com/getsentry/sentry-dart/pull/1023)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7260) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.25.1...7.26.0) + +## 6.11.0 + +### Fixes + +- Scope cloning method was not setting the user ([#1013](https://github.com/getsentry/sentry-dart/pull/1013)) + +### Features + +- Dynamic sampling ([#1004](https://github.com/getsentry/sentry-dart/pull/1004)) +- Set custom measurements on transactions ([#1011](https://github.com/getsentry/sentry-dart/pull/1011)) + +## 6.10.0 + +### Fixes + +- Capture Callback Exceptions ([#990](https://github.com/getsentry/sentry-dart/pull/990)) +- Allow routeNameExtractor to set transaction names ([#1005](https://github.com/getsentry/sentry-dart/pull/1005)) + +### Features + +- Prepare future support for iOS and macOS obfuscated app symbolication using dSYM (requires Flutter `master` channel) ([#823](https://github.com/getsentry/sentry-dart/pull/823)) +- Bump Android SDK from v6.3.1 to v6.4.2 ([#989](https://github.com/getsentry/sentry-dart/pull/989), [#1009](https://github.com/getsentry/sentry-dart/pull/1009)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#642) + - [diff](https://github.com/getsentry/sentry-java/compare/6.3.1...6.4.2) +- Bump Cocoa SDK from v7.23.0 to v7.25.1 ([#993](https://github.com/getsentry/sentry-dart/pull/993), [#996](https://github.com/getsentry/sentry-dart/pull/996), [#1000](https://github.com/getsentry/sentry-dart/pull/1000), [#1007](https://github.com/getsentry/sentry-dart/pull/1007)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7251) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.23.0...7.25.1) + +## 6.9.1 + +### Fixes + +* Scope.clone incorrectly accesses tags ([#978](https://github.com/getsentry/sentry-dart/pull/978)) +* beforeBreadcrumb was not adding the mutated breadcrumb ([#982](https://github.com/getsentry/sentry-dart/pull/982)) + +### Features + +- Bump Cocoa SDK to v7.23.0 ([#968](https://github.com/getsentry/sentry-dart/pull/968)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7230) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.22.0...7.23.0) +- Bump Android SDK from v6.3.0 to v6.3.1 ([#976](https://github.com/getsentry/sentry-dart/pull/976)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#631) + - [diff](https://github.com/getsentry/sentry-java/compare/6.3.0...6.3.1) + +## 6.9.0 + +### Features + +* Bump Flutter's min. supported version from 1.17.0 to 2.0.0 ([#966](https://github.com/getsentry/sentry-dart/pull/966)) + +This should not break anything since the Dart's min. version is already 2.12.0 and Flutter 2.0.0 uses Dart 2.12.0 + +### Fixes + +* Back compatibility of Object.hash for Dart 2.12.0 ([#966](https://github.com/getsentry/sentry-dart/pull/966)) +* Fix back compatibility for OnErrorIntegration integration ([#965](https://github.com/getsentry/sentry-dart/pull/965)) + +## 6.8.1 + +### Fixes + +* `Scope#setContexts` pasing a List value would't not work ([#932](https://github.com/getsentry/sentry-dart/pull/932)) + +### Features + +* Add integration for `PlatformDispatcher.onError` ([#915](https://github.com/getsentry/sentry-dart/pull/915)) +- Bump Cocoa SDK to v7.22.0 ([#960](https://github.com/getsentry/sentry-dart/pull/960)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7220) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.21.0...7.22.0) + +## 6.8.0 + +### Fixes + +* Missing OS context for iOS events ([#958](https://github.com/getsentry/sentry-dart/pull/958)) +* Fix: `Scope#clone` calls the Native bridges again via the `scopeObserver` ([#959](https://github.com/getsentry/sentry-dart/pull/959)) + +### Features + +* Dio Integration adds response data ([#934](https://github.com/getsentry/sentry-dart/pull/934)) + +## 6.7.0 + +### Fixes + +* Maps with Key Object, Object would fail during serialization if not String, Object ([#935](https://github.com/getsentry/sentry-dart/pull/935)) +* Breadcrumbs "Concurrent Modification" ([#948](https://github.com/getsentry/sentry-dart/pull/948)) +* Duplicative Screen size changed breadcrumbs ([#888](https://github.com/getsentry/sentry-dart/pull/888)) +* Duplicated Android Breadcrumbs with no Mechanism ([#954](https://github.com/getsentry/sentry-dart/pull/954)) +* Fix windows native method need default result ([#943](https://github.com/getsentry/sentry-dart/pull/943)) +* Add request instead of response data to `SentryRequest` in `DioEventProcessor` [#933](https://github.com/getsentry/sentry-dart/pull/933) + +### Features + +- Bump Android SDK to v6.3.0 ([#945](https://github.com/getsentry/sentry-dart/pull/945), [#950](https://github.com/getsentry/sentry-dart/pull/950)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#630) + - [diff](https://github.com/getsentry/sentry-java/compare/6.1.4...6.3.0) +- Bump Cocoa SDK to v7.21.0 ([#947](https://github.com/getsentry/sentry-dart/pull/947)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7210) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.19.0...7.21.0) + +## 6.6.3 + +### Fixes + +* Context Escape with ScopeCallback ([#925](https://github.com/getsentry/sentry-dart/pull/925)) + +## 6.6.2 + +### Features + +- Bump Android SDK to v6.1.4 ([#900](https://github.com/getsentry/sentry-dart/pull/900)) + - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#614) + - [diff](https://github.com/getsentry/sentry-java/compare/6.1.2...6.1.4) +- Bump Cocoa SDK to v7.19.0 ([#901](https://github.com/getsentry/sentry-dart/pull/901), [#928](https://github.com/getsentry/sentry-dart/pull/928)) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/master/CHANGELOG.md#7190) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/7.18.0...7.19.0) + +### Fixes + +* Send DidBecomeActiveNotification when OOM enabled (#905) +* `dio.addSentry` hangs if `dsn` is empty and SDK NoOp ([#920](https://github.com/getsentry/sentry-dart/pull/920)) +* addBreadcrumb throws on Android API < 24 because of NewApi usage ([#923](https://github.com/getsentry/sentry-dart/pull/923)) +* [`sentry_dio`](https://pub.dev/packages/sentry_dio) is promoted to GA and not experimental anymore ([#914](https://github.com/getsentry/sentry-dart/pull/914)) + +## 6.6.1 + +### Fixes + +* Filter out app starts with more than 60s (#895) + +## 6.6.0 + +### Fixes + +* Bump: Sentry-Cocoa to 7.18.0 and Sentry-Android to 6.1.2 (#892) +* Fix: Add missing iOS contexts (#761) +* Fix serialization of threads (#844) +* Fix: `SentryAssetBundle` on Flutter >= 3.1 (#877) + +### Features + +* Feat: Client Reports (#829) +* Feat: Allow manual init of the Native SDK (#765) +* Feat: Attach Isolate name to thread context (#847) +* Feat: Add Android thread to platform stacktraces (#853) +* Feat: Sync Scope to Native (#858) + +### Sentry Self-hosted Compatibility + +* Starting with version `6.6.0` of `sentry`, [Sentry's version >= v21.9.0](https://github.com/getsentry/self-hosted/releases) is required or you have to manually disable sending client reports via the `sendClientReports` option. This only applies to self-hosted Sentry. If you are using [sentry.io](https://sentry.io), no action is needed. + +## 6.6.0-beta.4 + +* Bump: Sentry-Cocoa to 7.17.0 and Sentry-Android to 6.1.1 (#891) + +## 6.6.0-beta.3 + +* Bump: Sentry-Cocoa to 7.16.1 (#886) + +## 6.6.0-beta.2 + +* Fix: Add user setter back in the scope (#883) +* Fix: clear method sets all properties synchronously (#882) + +## 6.6.0-beta.1 + +* Feat: Sync Scope to Native (#858) + +## 6.6.0-alpha.3 + +* Feat: Attach Isolate name to thread context (#847) +* Fix: `SentryAssetBundle` on Flutter >= 3.1 (#877) +* Feat: Add Android thread to platform stacktraces (#853) +* Fix: Rename auto initialize property (#857) +* Bump: Sentry-Android to 6.0.0 (#879) + +## 6.6.0-alpha.2 + +* Fix serialization of threads (#844) +* Feat: Allow manual init of the Native SDK (#765) + +## 6.6.0-alpha.1 + +* Feat: Client Reports (#829) +* Fix: Add missing iOS contexts (#761) + +### Sentry Self-hosted Compatibility + +* Starting with version `6.6.0` of `sentry`, [Sentry's version >= v21.9.0](https://github.com/getsentry/self-hosted/releases) is required or you have to manually disable sending client reports via the `sendClientReports` option. This only applies to self-hosted Sentry. If you are using [sentry.io](https://sentry.io), no action is needed. + +## 6.5.1 + +* Update event contexts (#838) + +## 6.5.0 + +* No documented changes. + +## 6.5.0-beta.2 + +* Fix: Do not set the transaction to scope if no op (#828) + +## 6.5.0-beta.1 + +* No documented changes. + +## 6.5.0-alpha.3 + +* Feat: Support for platform stacktraces on Android (#788) + +## 6.5.0-alpha.2 + +* Bump: Sentry-Android to 5.7.0 and Sentry-Cocoa to 7.11.0 (#796) +* Fix: Dio event processor safelly bails if no DioError in the exception list (#795) + +## 6.5.0-alpha.1 + +* Feat: Mobile Vitals - Native App Start (#749) +* Feat: Mobile Vitals - Native Frames (#772) + +## 6.4.0 + +### Various fixes & improvements + +* Fix: Missing userId on iOS when userId is not set (#782) by @marandaneto +* Allow to set startTimestamp & endTimestamp manually to SentrySpan (#676) by @fatihergin + +## 6.4.0-beta.3 + +* Feat: Allow to set startTimestamp & endTimestamp manually to SentrySpan (#676) +* Bump: Sentry-Cocoa to 7.10.0 (#777) +* Feat: Additional Dart/Flutter context information (#778) +* Bump: Kotlin plugin to 1.5.31 (#763) +* Fix: Missing userId on iOS when userId is not set (#782) + +## 6.4.0-beta.2 + +* No documented changes. + +## 6.4.0-beta.1 + +* Fix: Disable log by default in debug mode (#753) +* [Dio] Ref: Replace FailedRequestAdapter with FailedRequestInterceptor (#728) +* Fix: Add missing return values - dart analyzer (#742) +* Feat: Add `DioEventProcessor` which improves DioError crash reports (#718) +* Fix: Do not report duplicated packages and integrations (#760) +* Feat: Allow manual init of the Native SDK or no Native SDK at all (#765) + +## 6.3.0 + +* Feat: Support maxSpan for performance API and expose SentryOptions through Hub (#716) +* Fix: await ZonedGuard integration to run (#732) +* Fix: `sentry_logging` incorrectly setting SDK name (#725) +* Bump: Sentry-Android to 5.6.1 and Sentry-Cocoa to 7.9.0 (#736) +* Feat: Support Attachment.addToTransactions (#709) +* Fix: captureTransaction should return emptyId when transaction is discarded (#713) +* Add `SentryAssetBundle` for automatic spans for asset loading (#685) +* Fix: `maxRequestBodySize` should be `never` by default when using the FailedRequestClientAdapter directly (#701) +* Feat: Add support for [Dio](https://pub.dev/packages/dio) (#688) +* Fix: Use correct data/extras type in tracer (#693) +* Fix: Do not throw when Throwable type is not supported for associating errors to a transaction (#692) +* Feat: Automatically create transactions when navigating between screens (#643) + +## 6.3.0-beta.4 + +* Feat: Support Attachment.addToTransactions (#709) +* Fix: captureTransaction should return emptyId when transaction is discarded (#713) + +## 6.3.0-beta.3 + +* Feat: Auto transactions duration trimming (#702) +* Add `SentryAssetBundle` for automatic spans for asset loading (#685) +* Feat: Configure idle transaction duration (#705) +* Fix: `maxRequestBodySize` should be `never` by default when using the FailedRequestClientAdapter directly (#701) + +## 6.3.0-beta.2 + +* Feat: Improve configuration options of `SentryNavigatorObserver` (#684) +* Feat: Add support for [Dio](https://pub.dev/packages/dio) (#688) +* Bump: Sentry-Android to 5.5.2 and Sentry-Cocoa to 7.8.0 (#696) + +## 6.3.0-beta.1 + +* Enha: Replace flutter default root name '/' with 'root' (#678) +* Fix: Use 'navigation' instead of 'ui.load' for auto transaction operation (#675) +* Fix: Use correct data/extras type in tracer (#693) +* Fix: Do not throw when Throwable type is not supported for associating errors to a transaction (#692) + +## 6.3.0-alpha.1 + +* Feat: Automatically create transactions when navigating between screens (#643) + +## 6.2.2 + +* Fix: ConcurrentModificationError in when finishing span (#664) +* Feat: Add enableNdkScopeSync Android support (#665) + +## 6.2.1 + +* Fix: `sentry_logging` works now on web (#660) +* Fix: `sentry_logging` timestamps are in UTC (#660) +* Fix: `sentry_logging` Level.Off is never recorded (#660) +* Fix: Rate limiting fallback to retryAfterHeader (#658) + +## 6.2.0 + +* Feat: Integration for `logging` (#631) +* Feat: Add logger name to `SentryLogger` and send errors in integrations to the registered logger (#641) + +## 6.1.2 + +* Fix: Remove is Enum check to support older Dart versions (#635) + +## 6.1.1 + +* Fix: Transaction serialization if not encodable (#633) + +## 6.1.0 + +* Bump: Sentry-Android to 5.3.0 and Sentry-Cocoa to 7.5.1 (#629) +* Fix: event.origin tag for macOS and other Apple platforms (#622) +* Feat: Add current route as transaction (#615) +* Feat: Add Breadcrumbs for Flutters `debugPrint` (#618) +* Feat: Enrich Dart context with isolate name (#600) +* Feat: Sentry Performance for HTTP client (#603) +* Performance API for Dart/Flutter (#530) + +### Breaking Changes: + +* `SentryEvent` inherits from the `SentryEventLike` mixin +* `Scope#transaction` sets and reads from the `Scope#span` object if bound to the Scope + +## 6.1.0-beta.1 + +* Feat: Add current route as transaction (#615) +* Feat: Add Breadcrumbs for Flutters `debugPrint` (#618) + +## 6.1.0-alpha.2 + +* Bump Sentry Android SDK to [5.2.0](https://github.com/getsentry/sentry-dart/pull/594) (#594) + - [changelog](https://github.com/getsentry/sentry-java/blob/5.2.0/CHANGELOG.md) + - [diff](https://github.com/getsentry/sentry-java/compare/5.1.2...5.2.0) +* Feat: Enrich Dart context with isolate name (#600) +* Feat: Sentry Performance for HTTP client (#603) + +## 6.1.0-alpha.1 + +* Performance API for Dart/Flutter (#530) + +### Breaking Changes: + +* `SentryEvent` inherits from the `SentryEventLike` mixin +* `Scope#transaction` sets and reads from the `Scope#span` object if bound to the Scope + +## 6.0.1 + +* Fix: Set custom SentryHttpClientError when HTTP error is captured without an exception (#580) +* Bump: Android AGP 4.1 (#586) +* Bump: Sentry Cocoa to 7.3.0 (#589) + +## 6.0.0 + +* Fix: Update `SentryUser` according to docs (#561) +* Feat: Enable or disable reporting of packages (#563) +* Bump: Sentry-Cocoa to 7.2.7 (#578) +* Bump: Sentry-Android to 5.1.2 (#578) +* Fix: Read Sentry config from environment variables as fallback (#567) + +## 6.0.0-beta.4 + +### Breaking Changes: + +* Feat: Lists of exceptions and threads (#524) +* Feat: Collect more information for exceptions collected via `FlutterError.onError` (#538) +* Feat: Add maxAttachmentSize option (#553) +* Feat: HTTP breadcrumbs have the request & response size if available (#552) + +## 6.0.0-beta.3 + +* Fix: Re-initialization of Flutter SDK (#526) +* Enhancement: Call `toString()` on all non-serializable fields (#528) +* Fix: Always call `Flutter.onError` in order to not swallow messages (#533) +* Bump: Android SDK to 5.1.0-beta.6 (#535) + +## 6.0.0-beta.2 + +* Fix: Serialization of Flutter Context (#520) +* Feat: Add support for attachments (#505) +* Feat: Add support for User Feedback (#506) + +## 6.0.0-beta.1 + +* Feat: Browser detection (#502) +* Feat: Enrich events with more context (#452) +* Feat: Add Culture Context (#491) +* Feat: Add DeduplicationEventProcessor (#498) +* Feat: Capture failed requests as event (#473) +* Feat: `beforeSend` callback accepts async code (#494) + +### Breaking Changes: + +* Ref: EventProcessor changed to an interface (#489) +* Feat: Support envelope based transport for events (#391) + * The method signature of `Transport` changed from `Future send(SentryEvent event)` to `Future send(SentryEnvelope envelope)` +* Remove `Sentry.currentHub` (#490) +* Ref: Rename `cacheDirSize` to `maxCacheItems` and add `maxCacheItems` for iOS (#495) +* Ref: Add error and stacktrace parameter to logger (#503) +* Feat: Change timespans to Durations in SentryOptions (#504) +* Feat: `beforeSend` callback accepts async code (#494) + +### Sentry Self Hosted Compatibility + +* Since version `6.0.0` of the `sentry`, [Sentry's version >= v20.6.0](https://github.com/getsentry/self-hosted/releases) is required. This only applies to on-premise Sentry, if you are using sentry.io no action is needed. + +## 5.1.0 + +* Fix: Merge user from event and scope (#467) +* Feature: Allow setting of default values for in-app-frames via `SentryOptions.considerInAppFramesByDefault` (#482) +* Bump: sentry-android to v5.0.1 (#486) +* Bump: Sentry-Cocoa to 7.1.3 for iOS and macOS (#488) + +## 5.1.0-beta.1 + +* Fix: `Sentry.close()` closes native SDK integrations (#388) +* Feat: Support for macOS (#389) +* Feat: Support for Linux (#402) +* Feat: Support for Windows (#407) +* Fix: Mark `Sentry.currentHub` as deprecated (#406) +* Fix: Set console logger as default logger in debug mode (#413) +* Fix: Use name from pubspec.yaml for release if package id is not available (#411) +* Feat: `SentryHttpClient` tracks the duration which a request takes and logs failed requests (#414) +* Bump: sentry-cocoa to v7.0.0 (#424) +* Feat: Support for Out-of-Memory-Tracking on macOS/iOS (#424) +* Fix: Trim `\u0000` from Windows package info (#420) +* Feature: Log calls to `print()` as Breadcrumbs (#439) +* Fix: `dist` was read from `SENTRY_DSN`, now it's read from `SENTRY_DIST` (#442) +* Bump: sentry-cocoa to v7.0.3 (#445) +* Fix: Fix adding integrations on web (#450) +* Fix: Use `log()` instead of `print()` for SDK logging (#453) +* Bump: sentry-android to v5.0.0-beta.2 (#457) +* Feature: Add `withScope` callback to capture methods (#463) +* Fix: Add missing properties `language`, `screenHeightPixels` and `screenWidthPixels` to `SentryDevice` (#465) + +### Sentry Self Hosted Compatibility + +* This version of the `sentry` Dart package requires [Sentry server >= v20.6.0](https://github.com/getsentry/self-hosted/releases). This only applies to on-premise Sentry, if you are using sentry.io no action is needed. + +## 5.0.0 + +* Sound null safety +* Fix: event.origin and event.environment tags have wrong value for iOS (#365) and (#369) +* Fix: Fix deprecated `registrar.messenger` call in `SentryFlutterWeb` (#364) +* Fix: Enable breadcrumb recording mechanism based on platform (#366) +* Feat: Send default PII options (#360) +* Bump: sentry-cocoa to v6.2.1 (#360) +* Feat: Migration from `package_info` to `package_info_plus` plugin (#370) +* Fix: Set `SentryOptions.debug` in `sentry` (#376) +* Fix: Read all environment variables in `sentry` (#375) + +### Breaking Changes: + +* Return type of `Sentry.close()` changed from `void` to `Future` and `Integration.close()` changed from `void` to `FutureOr` (#395) +* Remove deprecated member `enableLifecycleBreadcrumbs`. Use `enableAppLifecycleBreadcrumbs` instead. (#366) + +## 4.1.0-nullsafety.1 + +* Bump: sentry-android to v4.3.0 (#343) +* Fix: Multiple FlutterError.onError calls in FlutterErrorIntegration (#345) +* Fix: Pass hint to EventProcessors (#356) +* Fix: EventProcessors were not dropping events when returning null (#353) + +### Breaking Changes: + +* Fix: Plugin Registrant class moved to barrel file (#358) + * This changed the import from `import 'package:sentry_flutter/src/sentry_flutter_web.dart';` + to `import 'package:sentry_flutter/sentry_flutter_web.dart';` + * This could lead to breaking changes. Typically it shouldn't because the referencing file is auto-generated. +* Fix: Prefix classes with Sentry (#357) + * A couple of classes were often conflicting with user's code. + Thus this change renames the following classes: + * `App` -> `SentryApp` + * `Browser` -> `SentryBrowser` + * `Device` -> `SentryDevice` + * `Gpu` -> `SentryGpu` + * `Integration` -> `SentryIntegration` + * `Message` -> `SentryMessage` + * `OperatingSystem` -> `SentryOperatingSystem` + * `Request` -> `SentryRequest` + * `User` -> `SentryUser` + * `Orientation` -> `SentryOrientation` + +## 4.1.0-nullsafety.0 + +* Fix: Do not append stack trace to the exception if there are no frames +* Fix: Empty DSN disables the SDK and runs the App +* Feat: sentry and sentry_flutter null-safety thanks to @ueman and @fzyzcjy + +## 4.0.6 + +* Fix: captureMessage defaults SentryLevel to info +* Fix: SentryEvent.throwable returns the unwrapped throwable instead of the throwableMechanism +* Feat: Support enableNativeCrashHandling on iOS + +## 4.0.5 + +* Bump: sentry-android to v4.0.0 +* Fix: Pana Flutter upper bound deprecation +* Fix: sentry_flutter static analysis (pana) using stable version + +## 4.0.4 + +* Fix: Call WidgetsFlutterBinding.ensureInitialized() within runZoneGuarded + +## 4.0.3 + +* Fix: Auto session tracking start on iOS #274 +* Bump: Sentry-cocoa to 6.1.4 + +## 4.0.2 + +* Fix: Mark session as `errored` in iOS #270 +* Fix: Pass auto session tracking interval to iOS +* Fix: Deprecated binaryMessenger (MethodChannel member) for Flutter Web +* Ref: Make `WidgetsFlutterBinding.ensureInitialized();` the first thing the Sentry SDK calls. +* Bump: Sentry-cocoa to 6.0.12 +* Feat: Respect FlutterError silent flag #248 +* Bump: Android SDK to v3.2.1 #273 + +## 4.0.1 + +* Ref: Changed category of Flutter lifecycle tracking [#240](https://github.com/getsentry/sentry-dart/issues/240) +* Fix: Envelope length should be based on the UTF8 array instead of String length + +## 4.0.0 + +Release of Sentry's new SDK for Dart/Flutter. + +New features not offered by <= v4.0.0: + +### Dart SDK + +* Sentry's [Unified API](https://develop.sentry.dev/sdk/unified-api/). +* Complete Sentry's [Protocol](https://develop.sentry.dev/sdk/event-payloads/) available. +* [Dart SDK](https://docs.sentry.io/platforms/dart/) docs. +* Automatic [HTTP Breadcrumbs](https://docs.sentry.io/platforms/dart/usage/advanced-usage/#automatic-breadcrumbs) for [http.Client](https://pub.dev/documentation/http/latest/http/Client-class.html) +* No boilerplate for `runZonedGuarded` and `Isolate.current.addErrorListener` +* All events are enriched with [Scope's Contexts](https://develop.sentry.dev/sdk/event-payloads/#scope-interfaces), this includes Breadcrumbs, tags, User, etc... + +### Flutter SDK + +* The Flutter SDK is built on top of the Dart SDK, so it includes all the available features, plus +* [Flutter SDK](https://docs.sentry.io/platforms/flutter/) docs. +* Automatic [NavigatorObserver Breadcrumbs](https://docs.sentry.io/platforms/flutter/usage/advanced-usage/#automatic-breadcrumbs) +* Automatic [Device's Breadcrumbs](https://docs.sentry.io/platforms/flutter/usage/advanced-usage/#automatic-breadcrumbs) through the Android and iOS SDKs or via Sentry's `WidgetsBindingObserver` wrapper +* No boilerplate for `FlutterError.onError` +* All events are enriched with [Contexts's data](https://develop.sentry.dev/sdk/event-payloads/contexts/), this includes Device's, OS, App info, etc... +* Offline caching +* [Release health](https://docs.sentry.io/product/releases/health/) +* Captures not only Dart and Flutter errors, but also errors caused on the native platforms, Like Kotlin, Java, C and C++ for Android and Swift, ObjC, C, C++ for iOS +* Supports Fatal crashes, Event is going to be sent on App's restart +* Supports `split-debug-info` for Android only +* Flutter Android, iOS and limited support for Flutter Web + +Improvements: + +* Feat: Added a copyWith method to all the protocol classes + +Packages were released on [sentry pubdev](https://pub.dev/packages/sentry) and [sentry_flutter pubdev](https://pub.dev/packages/sentry_flutter) + +### Sentry Self Hosted Compatibility + +* Since version `4.0.0` of the `sentry_flutter`, [Sentry's version >= v20.6.0](https://github.com/getsentry/self-hosted/releases) is required. This only applies to on-premise Sentry, if you are using sentry.io no action is needed. + +## 4.0.0-beta.2 + +* Ref: Remove duplicated attachStackTrace field +* Fix: Flutter Configurations should be able to mutate the SentryFlutterOptions +* Enhancement: Add SentryWidgetsBindingObserver, an Integration that captures certain window and device events. +* Enhancement: Set `options.environment` on SDK init based on the flags (kReleaseMode, kDebugMode, kProfileMode or SENTRY_ENVIRONMENT). +* Feature: SentryHttpClient to capture HTTP requests as breadcrumbs +* Ref: Only assign non-null option values in Android native integration in order preserve default values +* Enhancement: Add 'attachThreads' in options. When enabled, threads are attached to all logged events for Android +* Ref: Rename typedef `Logger` to `SentryLogger` to prevent name clashes with logging packages +* Fix: Scope Event processors should be awaited +* Fix: Package usage as git dependency + +### Breaking changes + +* `Logger` typedef is renamed to `SentryLogger` +* `attachStackTrace` is renamed to `attachStacktrace` + +## 4.0.0-beta.1 + +* Fix: StackTrace frames with 'package' uri.scheme are inApp by default #185 +* Fix: Missing App's StackTrace frames for Flutter errors +* Enhancement: Add isolateErrorIntegration and runZonedGuardedIntegration to default integrations in sentry-dart +* Fix: Breadcrumb list is a plain list instead of a values list #201 +* Ref: Remove deprecated classes (Flutter Plugin for Android) and cleaning up #186 +* Fix: Handle immutable event lists and maps +* Fix: NDK integration was being disabled by a typo +* Fix: Missing toList for debug meta #192 +* Enhancement: NavigationObserver to record Breadcrumbs for navigation events #197 +* Fix: Integrations should be closeable +* Feat: Support split-debug-info for Android #191 +* Fix: the event payload must never serialize null or empty fields +* Ref: Make hints optional + +### Breaking changes + +* `Sentry.init` and `SentryFlutter.init` have an optional callback argument which runs the host App after Sentry initialization. +* `Integration` is an `Interface` instead of a pure Function +* `Hints` are optional arguments +* Sentry Dart SDK adds an `IsolateError` handler by default + +## 4.0.0-alpha.2 + +* Enhancement: `Contexts` were added to the `Scope` #154 +* Fix: App. would hang if `debug` mode was enabled and refactoring ##157 +* Enhancement: Sentry Protocol v7 +* Enhancement: Added missing Protocol fields, `Request`, `SentryStackTrace`...) #155 +* Feat: Added `attachStackTrace` options to attach stack traces on `captureMessage` calls +* Feat: Flutter SDK has the Native SDKs embedded (Android and Apple) #158 + +### Breaking changes + +* `Sentry.init` returns a `Future`. +* Dart min. SDK is `2.8.0` +* Flutter min. SDK is `1.17.0` +* Timestamp has millis precision. +* For better groupping, add your own package to the `addInAppInclude` list, e.g. `options.addInAppInclude('sentry_flutter_example');` +* A few classes of the `Protocol` were renamed. + +### Sentry Self Hosted Compatibility + +* Since version `4.0.0` of the `sentry_flutter`, `Sentry` version >= `v20.6.0` is required. This only applies to on-premise Sentry, if you are using sentry.io no action is needed. + +## 4.0.0-alpha.1 + +First Release of Sentry's new SDK for Dart/Flutter. + +New features not offered by <= v4.0.0: + +* Sentry's [Unified API](https://develop.sentry.dev/sdk/unified-api/). +* Complete Sentry [Protocol](https://develop.sentry.dev/sdk/event-payloads/) available. +* Docs and Migration is under review on this [PR](https://github.com/getsentry/sentry-docs/pull/2599) +* For all the breaking changes follow this [PR](https://github.com/getsentry/sentry-dart/pull/117), they'll be soon available on the Migration page. + +Packages were released on [pubdev](https://pub.dev/packages/sentry) + +We'd love to get feedback and we'll work in getting the GA 4.0.0 out soon. +Until then, the stable SDK offered by Sentry is at version [3.0.1](https://github.com/getsentry/sentry-dart/releases/tag/3.0.1) + +## 3.0.1 + +* Add support for Contexts in Sentry events + +## 3.0.0+1 + +* `pubspec.yaml` and example code clean-up. + +## 3.0.0 + +* Support Web + * `SentryClient` from `package:sentry/sentry.dart` with conditional import + * `SentryBrowserClient` for web from `package:sentry/browser_client.dart` + * `SentryIOClient` for VM and Flutter from `package:sentry/io_client.dart` + +## 2.3.1 + +* Support non-standard port numbers and paths in DSN URL. + +## 2.3.0 + +* Add [breadcrumb](https://docs.sentry.io/development/sdk-dev/event-payloads/breadcrumbs/) support. + +## 2.2.0 + +* Add a `stackFrameFilter` argument to `SentryClient`'s `capture` method (96be842). +* Clean-up code using pre-Dart 2 API (91c7706, b01ebf8). + +## 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/drift/LICENSE b/drift/LICENSE new file mode 100644 index 0000000000..2a6964d84d --- /dev/null +++ b/drift/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Sentry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/drift/README.md b/drift/README.md new file mode 100644 index 0000000000..4255e1bb76 --- /dev/null +++ b/drift/README.md @@ -0,0 +1,73 @@ +

+ + + +
+

+ +Sentry integration for `drift` package +=========== + +- Sign up for a Sentry.io account and get a DSN at https://sentry.io. + +- Follow the installing instructions on [pub.dev](https://pub.dev/packages/sentry/install). + +- Initialize the Sentry SDK using the DSN issued by Sentry.io. + +- Call... + +```dart +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry_drift/sentry_drift.dart'; + +import '../test/test_database.dart'; + +Future main() async { + // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io + const dsn = + 'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562'; + + await Sentry.init( + (options) { + options.dsn = dsn; + options.tracesSampleRate = 1.0; + options.debug = true; + }, + appRunner: runApp, // Init your App. + ); +} + +Future runApp() async { + final tr = + Sentry.startTransaction('drift', 'op', bindToScope: true); + final executor = SentryQueryExecutor( + () => NativeDatabase.memory(), + databaseName: 'my_db_name', + ); + final db = AppDatabase(executor); + + await db.into(db.todoItems).insert( + TodoItemsCompanion.insert( + title: 'This is a test thing', + content: 'test', + ), + ); + + final items = await db.select(db.todoItems).get(); + print(items); + + await db.close(); + + await tr.finish(status: const SpanStatus.ok()); +} +``` + +#### Resources + +* [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dart/) +* [![Forum](https://img.shields.io/badge/forum-sentry-green.svg)](https://forum.sentry.io/c/sdks) +* [![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr) +* [![Stack Overflow](https://img.shields.io/badge/stack%20overflow-sentry-green.svg)](https://stackoverflow.com/questions/tagged/sentry) +* [![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry) diff --git a/drift/analysis_options.yaml b/drift/analysis_options.yaml new file mode 100644 index 0000000000..28ddbe2636 --- /dev/null +++ b/drift/analysis_options.yaml @@ -0,0 +1,34 @@ +include: package:lints/recommended.yaml + +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + errors: + # treat missing required parameters as a warning (not a hint) + missing_required_param: error + # treat missing returns as a warning (not a hint) + missing_return: error + # allow having TODOs in the code + todo: ignore + # allow self-reference to deprecated members (we do this because otherwise we have + # to annotate every member in every test, assert, etc, when we deprecate something) + deprecated_member_use_from_same_package: warning + # ignore sentry/path on pubspec as we change it on deployment + invalid_dependency: ignore + unnecessary_import: ignore + exclude: + - example/** + - test/mocks/mocks.mocks.dart + +linter: + rules: + - prefer_final_locals + - public_member_api_docs + - prefer_single_quotes + - prefer_relative_imports + - unnecessary_brace_in_string_interps + - implementation_imports + - require_trailing_commas + - unawaited_futures diff --git a/drift/dartdoc_options.yaml b/drift/dartdoc_options.yaml new file mode 100644 index 0000000000..20edf7f942 --- /dev/null +++ b/drift/dartdoc_options.yaml @@ -0,0 +1,3 @@ +dartdoc: + errors: + - unresolved-doc-reference diff --git a/drift/example/database.dart b/drift/example/database.dart new file mode 100644 index 0000000000..71a467b984 --- /dev/null +++ b/drift/example/database.dart @@ -0,0 +1,21 @@ +import 'dart:io'; + +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; + +part 'database.g.dart'; + +class TodoItems extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get title => text().withLength(min: 6, max: 32)(); + TextColumn get content => text().named('body')(); + IntColumn get category => integer().nullable()(); +} + +@DriftDatabase(tables: [TodoItems]) +class AppDatabase extends _$AppDatabase { + AppDatabase(super.e); + + @override + int get schemaVersion => 1; +} diff --git a/drift/example/database.g.dart b/drift/example/database.g.dart new file mode 100644 index 0000000000..1f9d234456 --- /dev/null +++ b/drift/example/database.g.dart @@ -0,0 +1,269 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ignore_for_file: type=lint +class $TodoItemsTable extends TodoItems + with TableInfo<$TodoItemsTable, TodoItem> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TodoItemsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _titleMeta = const VerificationMeta('title'); + @override + late final GeneratedColumn title = GeneratedColumn( + 'title', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 32), + type: DriftSqlType.string, + requiredDuringInsert: true); + static const VerificationMeta _contentMeta = + const VerificationMeta('content'); + @override + late final GeneratedColumn content = GeneratedColumn( + 'body', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _categoryMeta = + const VerificationMeta('category'); + @override + late final GeneratedColumn category = GeneratedColumn( + 'category', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + @override + List get $columns => [id, title, content, category]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'todo_items'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('title')) { + context.handle( + _titleMeta, title.isAcceptableOrUnknown(data['title']!, _titleMeta)); + } else if (isInserting) { + context.missing(_titleMeta); + } + if (data.containsKey('body')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['body']!, _contentMeta)); + } else if (isInserting) { + context.missing(_contentMeta); + } + if (data.containsKey('category')) { + context.handle(_categoryMeta, + category.isAcceptableOrUnknown(data['category']!, _categoryMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TodoItem map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TodoItem( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + title: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}title'])!, + content: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}body'])!, + category: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}category']), + ); + } + + @override + $TodoItemsTable createAlias(String alias) { + return $TodoItemsTable(attachedDatabase, alias); + } +} + +class TodoItem extends DataClass implements Insertable { + final int id; + final String title; + final String content; + final int? category; + const TodoItem( + {required this.id, + required this.title, + required this.content, + this.category}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['title'] = Variable(title); + map['body'] = Variable(content); + if (!nullToAbsent || category != null) { + map['category'] = Variable(category); + } + return map; + } + + TodoItemsCompanion toCompanion(bool nullToAbsent) { + return TodoItemsCompanion( + id: Value(id), + title: Value(title), + content: Value(content), + category: category == null && nullToAbsent + ? const Value.absent() + : Value(category), + ); + } + + factory TodoItem.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TodoItem( + id: serializer.fromJson(json['id']), + title: serializer.fromJson(json['title']), + content: serializer.fromJson(json['content']), + category: serializer.fromJson(json['category']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'title': serializer.toJson(title), + 'content': serializer.toJson(content), + 'category': serializer.toJson(category), + }; + } + + TodoItem copyWith( + {int? id, + String? title, + String? content, + Value category = const Value.absent()}) => + TodoItem( + id: id ?? this.id, + title: title ?? this.title, + content: content ?? this.content, + category: category.present ? category.value : this.category, + ); + @override + String toString() { + return (StringBuffer('TodoItem(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('content: $content, ') + ..write('category: $category') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, title, content, category); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TodoItem && + other.id == this.id && + other.title == this.title && + other.content == this.content && + other.category == this.category); +} + +class TodoItemsCompanion extends UpdateCompanion { + final Value id; + final Value title; + final Value content; + final Value category; + const TodoItemsCompanion({ + this.id = const Value.absent(), + this.title = const Value.absent(), + this.content = const Value.absent(), + this.category = const Value.absent(), + }); + TodoItemsCompanion.insert({ + this.id = const Value.absent(), + required String title, + required String content, + this.category = const Value.absent(), + }) : title = Value(title), + content = Value(content); + static Insertable custom({ + Expression? id, + Expression? title, + Expression? content, + Expression? category, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (title != null) 'title': title, + if (content != null) 'body': content, + if (category != null) 'category': category, + }); + } + + TodoItemsCompanion copyWith( + {Value? id, + Value? title, + Value? content, + Value? category}) { + return TodoItemsCompanion( + id: id ?? this.id, + title: title ?? this.title, + content: content ?? this.content, + category: category ?? this.category, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (title.present) { + map['title'] = Variable(title.value); + } + if (content.present) { + map['body'] = Variable(content.value); + } + if (category.present) { + map['category'] = Variable(category.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TodoItemsCompanion(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('content: $content, ') + ..write('category: $category') + ..write(')')) + .toString(); + } +} + +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + late final $TodoItemsTable todoItems = $TodoItemsTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [todoItems]; +} diff --git a/drift/example/example.dart b/drift/example/example.dart new file mode 100644 index 0000000000..bfe8b6d8e6 --- /dev/null +++ b/drift/example/example.dart @@ -0,0 +1,44 @@ +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry_drift/sentry_drift.dart'; + +import 'database.dart'; + +Future main() async { + // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io + const dsn = + 'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562'; + + await Sentry.init( + (options) { + options.dsn = dsn; + options.tracesSampleRate = 1.0; + options.debug = true; + }, + appRunner: runApp, // Init your App. + ); +} + +Future runApp() async { + final tr = Sentry.startTransaction('drift', 'op', bindToScope: true); + final executor = SentryQueryExecutor( + () => NativeDatabase.memory(), + databaseName: 'your_db_name', + ); + final db = AppDatabase(executor); + + await db.into(db.todoItems).insert( + TodoItemsCompanion.insert( + title: 'This is a test thing', + content: 'test', + ), + ); + + final items = await db.select(db.todoItems).get(); + print(items); + + await db.close(); + + await tr.finish(status: const SpanStatus.ok()); +} diff --git a/drift/lib/sentry_drift.dart b/drift/lib/sentry_drift.dart new file mode 100644 index 0000000000..ba84220b42 --- /dev/null +++ b/drift/lib/sentry_drift.dart @@ -0,0 +1,3 @@ +library sentry_drift; + +export 'src/sentry_query_executor.dart'; diff --git a/drift/lib/src/sentry_query_executor.dart b/drift/lib/src/sentry_query_executor.dart new file mode 100644 index 0000000000..29697e3c92 --- /dev/null +++ b/drift/lib/src/sentry_query_executor.dart @@ -0,0 +1,179 @@ +import 'dart:async'; + +import 'package:drift/backends.dart'; +import 'package:drift/drift.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; +import 'sentry_span_helper.dart'; +import 'sentry_transaction_executor.dart'; + +/// Signature of a function that opens a database connection when instructed to. +typedef DatabaseOpener = FutureOr Function(); + +/// The Sentry Query Executor. +/// +/// If the constructor parameter queryExecutor is null, [LazyDatabase] will be +/// used as a default. +@experimental +class SentryQueryExecutor extends QueryExecutor { + Hub _hub; + + final _spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbDriftQueryExecutor, + ); + + final QueryExecutor _executor; + + final String _dbName; + + @internal + // ignore: public_member_api_docs + static const dbNameKey = 'db.name'; + + @internal + // ignore: public_member_api_docs + static const dbOp = 'db'; + + @internal + // ignore: public_member_api_docs + static const dbSystemKey = 'db.system'; + + @internal + // ignore: public_member_api_docs + static const dbSystem = 'sqlite'; + + bool _isOpen = false; + + /// Declares a [SentryQueryExecutor] that will run [opener] when the database is + /// first requested to be opened. You must specify the same [dialect] as the + /// underlying database has + SentryQueryExecutor( + DatabaseOpener opener, { + @internal Hub? hub, + @internal QueryExecutor? queryExecutor, + required String databaseName, + }) : _hub = hub ?? HubAdapter(), + _dbName = databaseName, + _executor = queryExecutor ?? LazyDatabase(opener) { + _spanHelper.setHub(_hub); + } + + /// @nodoc + @internal + void setHub(Hub hub) { + _hub = hub; + _spanHelper.setHub(hub); + } + + @override + TransactionExecutor beginTransaction() { + final transactionExecutor = _executor.beginTransaction(); + final sentryTransactionExecutor = SentryTransactionExecutor( + transactionExecutor, + _hub, + dbName: _dbName, + ); + sentryTransactionExecutor.beginTransaction(); + return sentryTransactionExecutor; + } + + @override + Future runBatched(BatchedStatements statements) { + return _spanHelper.asyncWrapInSpan( + statements.toString(), + () async { + return await _executor.runBatched(statements); + }, + dbName: _dbName, + ); + } + + @override + Future ensureOpen(QueryExecutorUser user) { + if (_isOpen) { + return Future.value(true); + } + return _spanHelper.asyncWrapInSpan( + 'Open DB: $_dbName', + () async { + final res = await _executor.ensureOpen(user); + _isOpen = true; + return res; + }, + dbName: _dbName, + ); + } + + @override + Future runCustom(String statement, [List? args]) { + return _spanHelper.asyncWrapInSpan( + statement, + () async { + return await _executor.runCustom(statement, args); + }, + dbName: _dbName, + ); + } + + @override + Future runDelete(String statement, List args) { + return _spanHelper.asyncWrapInSpan( + statement, + () async { + return await _executor.runDelete(statement, args); + }, + dbName: _dbName, + ); + } + + @override + Future runInsert(String statement, List args) { + return _spanHelper.asyncWrapInSpan( + statement, + () async { + return await _executor.runInsert(statement, args); + }, + dbName: _dbName, + ); + } + + @override + Future>> runSelect( + String statement, + List args, + ) { + return _spanHelper.asyncWrapInSpan( + statement, + () async { + return await _executor.runSelect(statement, args); + }, + dbName: _dbName, + ); + } + + @override + Future runUpdate(String statement, List args) { + return _spanHelper.asyncWrapInSpan( + statement, + () async { + return await _executor.runUpdate(statement, args); + }, + dbName: _dbName, + ); + } + + @override + Future close() { + return _spanHelper.asyncWrapInSpan( + 'Close DB: $_dbName', + () async { + return await _executor.close(); + }, + dbName: _dbName, + ); + } + + @override + SqlDialect get dialect => _executor.dialect; +} diff --git a/drift/lib/src/sentry_span_helper.dart b/drift/lib/src/sentry_span_helper.dart new file mode 100644 index 0000000000..762bd24d08 --- /dev/null +++ b/drift/lib/src/sentry_span_helper.dart @@ -0,0 +1,155 @@ +import 'package:meta/meta.dart'; + +import 'package:sentry/sentry.dart'; + +import 'sentry_query_executor.dart'; + +/// @nodoc +@internal +class SentrySpanHelper { + /// @nodoc + Hub _hub = HubAdapter(); + + /// @nodoc + final String _origin; + + /// @nodoc + SentrySpanHelper(this._origin); + + /// @nodoc + void setHub(Hub hub) { + _hub = hub; + } + + /// @nodoc + @internal + Future asyncWrapInSpan( + String description, + Future Function() execute, { + String? dbName, + bool useTransactionSpan = false, + }) async { + ISentrySpan? currentSpan = _hub.getSpan(); + if (useTransactionSpan) { + currentSpan = transactionSpan; + } + final span = currentSpan?.startChild( + SentryQueryExecutor.dbOp, + description: description, + ); + + // ignore: invalid_use_of_internal_member + span?.origin = _origin; + + span?.setData( + SentryQueryExecutor.dbSystemKey, + SentryQueryExecutor.dbSystem, + ); + + if (dbName != null) { + span?.setData(SentryQueryExecutor.dbNameKey, dbName); + } + + try { + final result = await execute(); + span?.status = SpanStatus.ok(); + + return result; + } catch (exception) { + span?.throwable = exception; + span?.status = SpanStatus.internalError(); + + rethrow; + } finally { + await span?.finish(); + } + } + + /// This span is used for the database transaction. + @internal + ISentrySpan? transactionSpan; + + /// @nodoc + @internal + T beginTransaction( + String description, + T Function() execute, { + String? dbName, + }) { + final currentSpan = _hub.getSpan(); + final span = currentSpan?.startChild( + SentryQueryExecutor.dbOp, + description: description, + ); + + // ignore: invalid_use_of_internal_member + span?.origin = _origin; + + span?.setData( + SentryQueryExecutor.dbSystemKey, + SentryQueryExecutor.dbSystem, + ); + + if (dbName != null) { + span?.setData(SentryQueryExecutor.dbNameKey, dbName); + } + + try { + final result = execute(); + span?.status = SpanStatus.unknown(); + + return result; + } catch (exception) { + span?.throwable = exception; + span?.status = SpanStatus.internalError(); + + rethrow; + } finally { + transactionSpan = span; + } + } + + /// @nodoc + @internal + Future finishTransaction( + Future Function() execute, { + String? dbName, + }) async { + try { + final result = await execute(); + transactionSpan?.status = SpanStatus.ok(); + + return result; + } catch (exception) { + transactionSpan?.throwable = exception; + transactionSpan?.status = SpanStatus.internalError(); + + rethrow; + } finally { + await transactionSpan?.finish(); + transactionSpan = null; + } + } + + /// @nodoc + @internal + Future abortTransaction( + Future Function() execute, { + String? dbName, + }) async { + try { + final result = await execute(); + transactionSpan?.status = SpanStatus.aborted(); + + return result; + } catch (exception) { + transactionSpan?.throwable = exception; + transactionSpan?.status = SpanStatus.internalError(); + + rethrow; + } finally { + await transactionSpan?.finish(); + transactionSpan = null; + } + } +} diff --git a/drift/lib/src/sentry_transaction_executor.dart b/drift/lib/src/sentry_transaction_executor.dart new file mode 100644 index 0000000000..afd7625524 --- /dev/null +++ b/drift/lib/src/sentry_transaction_executor.dart @@ -0,0 +1,155 @@ +import 'package:drift/backends.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; +import 'sentry_span_helper.dart'; + +/// @nodoc +@internal +class SentryTransactionExecutor extends TransactionExecutor { + final TransactionExecutor _executor; + + final Hub _hub; + + final _spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + + final String? _dbName; + + bool _isOpen = false; + + final _withinTransactionDescription = 'Within transaction: '; + + /// @nodoc + SentryTransactionExecutor(this._executor, Hub hub, {@internal String? dbName}) + : _hub = hub, + _dbName = dbName { + _spanHelper.setHub(_hub); + } + + @override + TransactionExecutor beginTransaction() { + return _spanHelper.beginTransaction( + 'transaction', + () { + return _executor.beginTransaction(); + }, + dbName: _dbName, + ); + } + + @override + Future rollback() { + return _spanHelper.abortTransaction(() async { + return await _executor.rollback(); + }); + } + + @override + Future send() { + return _spanHelper.finishTransaction(() async { + return await _executor.send(); + }); + } + + @override + SqlDialect get dialect => _executor.dialect; + + @override + Future ensureOpen(QueryExecutorUser user) { + if (_isOpen) { + return Future.value(true); + } + return _spanHelper.asyncWrapInSpan( + 'Open transaction', + () async { + final res = await _executor.ensureOpen(user); + _isOpen = true; + return res; + }, + dbName: _dbName, + ); + } + + @override + Future runBatched(BatchedStatements statements) { + return _spanHelper.asyncWrapInSpan( + 'batch', + () async { + return await _executor.runBatched(statements); + }, + dbName: _dbName, + ); + } + + @override + Future runCustom(String statement, [List? args]) { + return _spanHelper.asyncWrapInSpan( + _spanDescriptionForOperations(statement), + () async { + return _executor.runCustom(statement, args); + }, + dbName: _dbName, + useTransactionSpan: true, + ); + } + + @override + Future runDelete(String statement, List args) { + return _spanHelper.asyncWrapInSpan( + _spanDescriptionForOperations(statement), + () async { + return _executor.runDelete(statement, args); + }, + dbName: _dbName, + useTransactionSpan: true, + ); + } + + @override + Future runInsert(String statement, List args) { + return _spanHelper.asyncWrapInSpan( + _spanDescriptionForOperations(statement), + () async { + return _executor.runInsert(statement, args); + }, + dbName: _dbName, + useTransactionSpan: true, + ); + } + + @override + Future>> runSelect( + String statement, + List args, + ) { + return _spanHelper.asyncWrapInSpan( + _spanDescriptionForOperations(statement), + () async { + return _executor.runSelect(statement, args); + }, + dbName: _dbName, + useTransactionSpan: true, + ); + } + + @override + Future runUpdate(String statement, List args) { + return _spanHelper.asyncWrapInSpan( + _spanDescriptionForOperations(statement), + () async { + return _executor.runUpdate(statement, args); + }, + dbName: _dbName, + useTransactionSpan: true, + ); + } + + @override + bool get supportsNestedTransactions => _executor.supportsNestedTransactions; + + String _spanDescriptionForOperations(String operation) { + return '$_withinTransactionDescription$operation'; + } +} diff --git a/drift/pubspec.yaml b/drift/pubspec.yaml new file mode 100644 index 0000000000..abcecf2ca1 --- /dev/null +++ b/drift/pubspec.yaml @@ -0,0 +1,28 @@ +name: sentry_drift +description: An integration which adds support for performance tracing for the drift package. +version: 7.12.0 +homepage: https://docs.sentry.io/platforms/flutter/ +repository: https://github.com/getsentry/sentry-dart +issue_tracker: https://github.com/getsentry/sentry-dart/issues + +environment: + sdk: '>=2.17.0 <4.0.0' + flutter: '>=3.3.0' + +dependencies: + sentry: 7.12.0 + meta: ^1.3.0 + drift: ^2.13.0 + +dev_dependencies: + lints: ^3.0.0 + flutter_test: + sdk: flutter + coverage: ^1.3.0 + mockito: ^5.1.0 + build_runner: ^2.4.6 + drift_dev: ^2.13.0 + yaml: ^3.1.0 # needed for version match (code and pubspec) + sqlite3_flutter_libs: ^0.5.0 + sqlite3: ^2.1.0 + archive: ^3.1.2 \ No newline at end of file diff --git a/drift/pubspec_overrides.yaml b/drift/pubspec_overrides.yaml new file mode 100644 index 0000000000..16e71d16f0 --- /dev/null +++ b/drift/pubspec_overrides.yaml @@ -0,0 +1,3 @@ +dependency_overrides: + sentry: + path: ../dart diff --git a/drift/test/mocks/mocks.dart b/drift/test/mocks/mocks.dart new file mode 100644 index 0000000000..4842f97455 --- /dev/null +++ b/drift/test/mocks/mocks.dart @@ -0,0 +1,10 @@ +import 'package:drift/drift.dart'; +import 'package:mockito/annotations.dart'; +import 'package:sentry/sentry.dart'; + +@GenerateMocks([ + Hub, + LazyDatabase, + TransactionExecutor, +]) +void main() {} diff --git a/drift/test/mocks/mocks.mocks.dart b/drift/test/mocks/mocks.mocks.dart new file mode 100644 index 0000000000..f6a88c359a --- /dev/null +++ b/drift/test/mocks/mocks.mocks.dart @@ -0,0 +1,769 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in sentry_drift/test/mocks/mocks.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i5; + +import 'package:drift/backends.dart' as _i3; +import 'package:drift/drift.dart' as _i6; +import 'package:mockito/mockito.dart' as _i1; +import 'package:sentry/sentry.dart' as _i2; +import 'package:sentry/src/profiling.dart' as _i4; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeSentryOptions_0 extends _i1.SmartFake implements _i2.SentryOptions { + _FakeSentryOptions_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSentryId_1 extends _i1.SmartFake implements _i2.SentryId { + _FakeSentryId_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeScope_2 extends _i1.SmartFake implements _i2.Scope { + _FakeScope_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeHub_3 extends _i1.SmartFake implements _i2.Hub { + _FakeHub_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeISentrySpan_4 extends _i1.SmartFake implements _i2.ISentrySpan { + _FakeISentrySpan_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeQueryExecutor_5 extends _i1.SmartFake implements _i3.QueryExecutor { + _FakeQueryExecutor_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeTransactionExecutor_6 extends _i1.SmartFake + implements _i3.TransactionExecutor { + _FakeTransactionExecutor_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [Hub]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockHub extends _i1.Mock implements _i2.Hub { + MockHub() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.SentryOptions get options => (super.noSuchMethod( + Invocation.getter(#options), + returnValue: _FakeSentryOptions_0( + this, + Invocation.getter(#options), + ), + ) as _i2.SentryOptions); + + @override + bool get isEnabled => (super.noSuchMethod( + Invocation.getter(#isEnabled), + returnValue: false, + ) as bool); + + @override + _i2.SentryId get lastEventId => (super.noSuchMethod( + Invocation.getter(#lastEventId), + returnValue: _FakeSentryId_1( + this, + Invocation.getter(#lastEventId), + ), + ) as _i2.SentryId); + + @override + _i2.Scope get scope => (super.noSuchMethod( + Invocation.getter(#scope), + returnValue: _FakeScope_2( + this, + Invocation.getter(#scope), + ), + ) as _i2.Scope); + + @override + set profilerFactory(_i4.SentryProfilerFactory? value) => super.noSuchMethod( + Invocation.setter( + #profilerFactory, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i5.Future<_i2.SentryId> captureEvent( + _i2.SentryEvent? event, { + dynamic stackTrace, + _i2.Hint? hint, + _i2.ScopeCallback? withScope, + }) => + (super.noSuchMethod( + Invocation.method( + #captureEvent, + [event], + { + #stackTrace: stackTrace, + #hint: hint, + #withScope: withScope, + }, + ), + returnValue: _i5.Future<_i2.SentryId>.value(_FakeSentryId_1( + this, + Invocation.method( + #captureEvent, + [event], + { + #stackTrace: stackTrace, + #hint: hint, + #withScope: withScope, + }, + ), + )), + ) as _i5.Future<_i2.SentryId>); + + @override + _i5.Future<_i2.SentryId> captureException( + dynamic throwable, { + dynamic stackTrace, + _i2.Hint? hint, + _i2.ScopeCallback? withScope, + }) => + (super.noSuchMethod( + Invocation.method( + #captureException, + [throwable], + { + #stackTrace: stackTrace, + #hint: hint, + #withScope: withScope, + }, + ), + returnValue: _i5.Future<_i2.SentryId>.value(_FakeSentryId_1( + this, + Invocation.method( + #captureException, + [throwable], + { + #stackTrace: stackTrace, + #hint: hint, + #withScope: withScope, + }, + ), + )), + ) as _i5.Future<_i2.SentryId>); + + @override + _i5.Future<_i2.SentryId> captureMessage( + String? message, { + _i2.SentryLevel? level, + String? template, + List? params, + _i2.Hint? hint, + _i2.ScopeCallback? withScope, + }) => + (super.noSuchMethod( + Invocation.method( + #captureMessage, + [message], + { + #level: level, + #template: template, + #params: params, + #hint: hint, + #withScope: withScope, + }, + ), + returnValue: _i5.Future<_i2.SentryId>.value(_FakeSentryId_1( + this, + Invocation.method( + #captureMessage, + [message], + { + #level: level, + #template: template, + #params: params, + #hint: hint, + #withScope: withScope, + }, + ), + )), + ) as _i5.Future<_i2.SentryId>); + + @override + _i5.Future captureUserFeedback(_i2.SentryUserFeedback? userFeedback) => + (super.noSuchMethod( + Invocation.method( + #captureUserFeedback, + [userFeedback], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future addBreadcrumb( + _i2.Breadcrumb? crumb, { + _i2.Hint? hint, + }) => + (super.noSuchMethod( + Invocation.method( + #addBreadcrumb, + [crumb], + {#hint: hint}, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + void bindClient(_i2.SentryClient? client) => super.noSuchMethod( + Invocation.method( + #bindClient, + [client], + ), + returnValueForMissingStub: null, + ); + + @override + _i2.Hub clone() => (super.noSuchMethod( + Invocation.method( + #clone, + [], + ), + returnValue: _FakeHub_3( + this, + Invocation.method( + #clone, + [], + ), + ), + ) as _i2.Hub); + + @override + _i5.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.FutureOr configureScope(_i2.ScopeCallback? callback) => + (super.noSuchMethod(Invocation.method( + #configureScope, + [callback], + )) as _i5.FutureOr); + + @override + _i2.ISentrySpan startTransaction( + String? name, + String? operation, { + String? description, + DateTime? startTimestamp, + bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, + bool? trimEnd, + _i2.OnTransactionFinish? onFinish, + Map? customSamplingContext, + }) => + (super.noSuchMethod( + Invocation.method( + #startTransaction, + [ + name, + operation, + ], + { + #description: description, + #startTimestamp: startTimestamp, + #bindToScope: bindToScope, + #waitForChildren: waitForChildren, + #autoFinishAfter: autoFinishAfter, + #trimEnd: trimEnd, + #onFinish: onFinish, + #customSamplingContext: customSamplingContext, + }, + ), + returnValue: _FakeISentrySpan_4( + this, + Invocation.method( + #startTransaction, + [ + name, + operation, + ], + { + #description: description, + #startTimestamp: startTimestamp, + #bindToScope: bindToScope, + #waitForChildren: waitForChildren, + #autoFinishAfter: autoFinishAfter, + #trimEnd: trimEnd, + #onFinish: onFinish, + #customSamplingContext: customSamplingContext, + }, + ), + ), + ) as _i2.ISentrySpan); + + @override + _i2.ISentrySpan startTransactionWithContext( + _i2.SentryTransactionContext? transactionContext, { + Map? customSamplingContext, + DateTime? startTimestamp, + bool? bindToScope, + bool? waitForChildren, + Duration? autoFinishAfter, + bool? trimEnd, + _i2.OnTransactionFinish? onFinish, + }) => + (super.noSuchMethod( + Invocation.method( + #startTransactionWithContext, + [transactionContext], + { + #customSamplingContext: customSamplingContext, + #startTimestamp: startTimestamp, + #bindToScope: bindToScope, + #waitForChildren: waitForChildren, + #autoFinishAfter: autoFinishAfter, + #trimEnd: trimEnd, + #onFinish: onFinish, + }, + ), + returnValue: _FakeISentrySpan_4( + this, + Invocation.method( + #startTransactionWithContext, + [transactionContext], + { + #customSamplingContext: customSamplingContext, + #startTimestamp: startTimestamp, + #bindToScope: bindToScope, + #waitForChildren: waitForChildren, + #autoFinishAfter: autoFinishAfter, + #trimEnd: trimEnd, + #onFinish: onFinish, + }, + ), + ), + ) as _i2.ISentrySpan); + + @override + _i5.Future<_i2.SentryId> captureTransaction( + _i2.SentryTransaction? transaction, { + _i2.SentryTraceContextHeader? traceContext, + }) => + (super.noSuchMethod( + Invocation.method( + #captureTransaction, + [transaction], + {#traceContext: traceContext}, + ), + returnValue: _i5.Future<_i2.SentryId>.value(_FakeSentryId_1( + this, + Invocation.method( + #captureTransaction, + [transaction], + {#traceContext: traceContext}, + ), + )), + ) as _i5.Future<_i2.SentryId>); + + @override + void setSpanContext( + dynamic throwable, + _i2.ISentrySpan? span, + String? transaction, + ) => + super.noSuchMethod( + Invocation.method( + #setSpanContext, + [ + throwable, + span, + transaction, + ], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [LazyDatabase]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockLazyDatabase extends _i1.Mock implements _i6.LazyDatabase { + MockLazyDatabase() { + _i1.throwOnMissingStub(this); + } + + @override + _i6.DatabaseOpener get opener => (super.noSuchMethod( + Invocation.getter(#opener), + returnValue: () => + _i5.Future<_i3.QueryExecutor>.value(_FakeQueryExecutor_5( + this, + Invocation.getter(#opener), + )), + ) as _i6.DatabaseOpener); + + @override + _i3.SqlDialect get dialect => (super.noSuchMethod( + Invocation.getter(#dialect), + returnValue: _i3.SqlDialect.sqlite, + ) as _i3.SqlDialect); + + @override + _i3.TransactionExecutor beginTransaction() => (super.noSuchMethod( + Invocation.method( + #beginTransaction, + [], + ), + returnValue: _FakeTransactionExecutor_6( + this, + Invocation.method( + #beginTransaction, + [], + ), + ), + ) as _i3.TransactionExecutor); + + @override + _i5.Future ensureOpen(_i3.QueryExecutorUser? user) => + (super.noSuchMethod( + Invocation.method( + #ensureOpen, + [user], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Future runBatched(_i3.BatchedStatements? statements) => + (super.noSuchMethod( + Invocation.method( + #runBatched, + [statements], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future runCustom( + String? statement, [ + List? args, + ]) => + (super.noSuchMethod( + Invocation.method( + #runCustom, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future runDelete( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runDelete, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future runInsert( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runInsert, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future>> runSelect( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runSelect, + [ + statement, + args, + ], + ), + returnValue: _i5.Future>>.value( + >[]), + ) as _i5.Future>>); + + @override + _i5.Future runUpdate( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runUpdate, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} + +/// A class which mocks [TransactionExecutor]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTransactionExecutor extends _i1.Mock + implements _i3.TransactionExecutor { + MockTransactionExecutor() { + _i1.throwOnMissingStub(this); + } + + @override + bool get supportsNestedTransactions => (super.noSuchMethod( + Invocation.getter(#supportsNestedTransactions), + returnValue: false, + ) as bool); + + @override + _i3.SqlDialect get dialect => (super.noSuchMethod( + Invocation.getter(#dialect), + returnValue: _i3.SqlDialect.sqlite, + ) as _i3.SqlDialect); + + @override + _i5.Future send() => (super.noSuchMethod( + Invocation.method( + #send, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future rollback() => (super.noSuchMethod( + Invocation.method( + #rollback, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future ensureOpen(_i3.QueryExecutorUser? user) => + (super.noSuchMethod( + Invocation.method( + #ensureOpen, + [user], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + _i5.Future>> runSelect( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runSelect, + [ + statement, + args, + ], + ), + returnValue: _i5.Future>>.value( + >[]), + ) as _i5.Future>>); + + @override + _i5.Future runInsert( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runInsert, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future runUpdate( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runUpdate, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future runDelete( + String? statement, + List? args, + ) => + (super.noSuchMethod( + Invocation.method( + #runDelete, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future runCustom( + String? statement, [ + List? args, + ]) => + (super.noSuchMethod( + Invocation.method( + #runCustom, + [ + statement, + args, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future runBatched(_i3.BatchedStatements? statements) => + (super.noSuchMethod( + Invocation.method( + #runBatched, + [statements], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i3.TransactionExecutor beginTransaction() => (super.noSuchMethod( + Invocation.method( + #beginTransaction, + [], + ), + returnValue: _FakeTransactionExecutor_6( + this, + Invocation.method( + #beginTransaction, + [], + ), + ), + ) as _i3.TransactionExecutor); + + @override + _i5.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} diff --git a/drift/test/sentry_database_test.dart b/drift/test/sentry_database_test.dart new file mode 100644 index 0000000000..2b3dd96536 --- /dev/null +++ b/drift/test/sentry_database_test.dart @@ -0,0 +1,645 @@ +// ignore_for_file: invalid_use_of_internal_member + +@TestOn('vm') + +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry/src/sentry_tracer.dart'; +import 'package:sentry_drift/src/sentry_query_executor.dart'; +import 'package:sentry_drift/src/sentry_transaction_executor.dart'; +import 'package:sqlite3/open.dart'; + +import 'mocks/mocks.mocks.dart'; +import 'test_database.dart'; +import 'utils/windows_helper.dart'; + +void main() { + open.overrideFor(OperatingSystem.windows, openOnWindows); + + final expectedInsertStatement = + 'INSERT INTO "todo_items" ("title", "body") VALUES (?, ?)'; + final expectedUpdateStatement = + 'UPDATE "todo_items" SET "title" = ?, "body" = ? WHERE "title" = ?;'; + final expectedSelectStatement = 'SELECT * FROM todo_items'; + final expectedDeleteStatement = 'DELETE FROM "todo_items";'; + final expectedCloseStatement = 'Close DB: ${Fixture.dbName}'; + final expectedOpenStatement = 'Open DB: ${Fixture.dbName}'; + final expectedTransactionStatement = 'transaction'; + final withinTransactionDescription = 'Within transaction: '; + + void verifySpan( + String description, + SentrySpan? span, { + String origin = SentryTraceOrigins.autoDbDriftQueryExecutor, + SpanStatus? status, + }) { + status ??= SpanStatus.ok(); + expect(span?.context.operation, SentryQueryExecutor.dbOp); + expect(span?.context.description, description); + expect(span?.status, status); + expect(span?.origin, origin); + expect( + span?.data[SentryQueryExecutor.dbSystemKey], + SentryQueryExecutor.dbSystem, + ); + expect( + span?.data[SentryQueryExecutor.dbNameKey], + Fixture.dbName, + ); + } + + void verifyErrorSpan( + String description, + Exception exception, + SentrySpan? span, { + String origin = SentryTraceOrigins.autoDbDriftQueryExecutor, + }) { + expect(span?.context.operation, SentryQueryExecutor.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.internalError()); + expect(span?.origin, origin); + expect( + span?.data[SentryQueryExecutor.dbSystemKey], + SentryQueryExecutor.dbSystem, + ); + expect( + span?.data[SentryQueryExecutor.dbNameKey], + Fixture.dbName, + ); + + expect(span?.throwable, exception); + } + + Future insertRow(AppDatabase sut, {bool withError = false}) { + if (withError) { + return sut.into(sut.todoItems).insert( + TodoItemsCompanion.insert( + title: '', + content: '', + ), + ); + } else { + return sut.into(sut.todoItems).insert( + TodoItemsCompanion.insert( + title: 'todo: finish drift setup', + content: 'We can now write queries and define our own tables.', + ), + ); + } + } + + Future updateRow(AppDatabase sut, {bool withError = false}) { + if (withError) { + return (sut.update(sut.todoItems) + ..where((tbl) => tbl.title.equals('doesnt exist'))) + .write( + TodoItemsCompanion( + title: Value('after update'), + content: Value('We can now write queries and define our own tables.'), + ), + ); + } else { + return (sut.update(sut.todoItems) + ..where((tbl) => tbl.title.equals('todo: finish drift setup'))) + .write( + TodoItemsCompanion( + title: Value('after update'), + content: Value('We can now write queries and define our own tables.'), + ), + ); + } + } + + group('adds span', () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + await fixture.setUp(); + + when(fixture.hub.options).thenReturn(fixture.options); + when(fixture.hub.getSpan()).thenReturn(fixture.tracer); + }); + + tearDown(() async { + await fixture.tearDown(); + }); + + test('open span is only added once', () async { + final sut = fixture.sut; + + await insertRow(sut); + await insertRow(sut); + await insertRow(sut); + + final openSpansCount = fixture.tracer.children + .where( + (element) => element.context.description == expectedOpenStatement, + ) + .length; + + expect(openSpansCount, 1); + }); + + test('insert adds span', () async { + final sut = fixture.sut; + + await insertRow(sut); + + verifySpan( + expectedInsertStatement, + fixture.getCreatedSpan(), + ); + }); + + test('update adds span', () async { + final sut = fixture.sut; + + await insertRow(sut); + await updateRow(sut); + + verifySpan( + expectedUpdateStatement, + fixture.getCreatedSpan(), + ); + }); + + test('custom adds span', () async { + final sut = fixture.sut; + + await sut.customStatement('SELECT * FROM todo_items'); + + verifySpan( + expectedSelectStatement, + fixture.getCreatedSpan(), + ); + }); + + test('delete adds span', () async { + final sut = fixture.sut; + + await insertRow(sut); + await fixture.sut.delete(fixture.sut.todoItems).go(); + + verifySpan( + expectedDeleteStatement, + fixture.getCreatedSpan(), + ); + }); + + test('transaction adds insert spans', () async { + final sut = fixture.sut; + + await sut.transaction(() async { + await insertRow(sut); + await insertRow(sut); + }); + + final insertSpanCount = fixture.tracer.children + .where( + (element) => + element.context.description == + '$withinTransactionDescription$expectedInsertStatement', + ) + .length; + expect(insertSpanCount, 2); + + verifySpan( + '$withinTransactionDescription$expectedInsertStatement', + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + + verifySpan( + expectedTransactionStatement, + fixture.getCreatedSpanByDescription(expectedTransactionStatement), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('transaction adds update spans', () async { + final sut = fixture.sut; + + await sut.transaction(() async { + await insertRow(sut); + await updateRow(sut); + }); + + final updateSpanCount = fixture.tracer.children + .where( + (element) => + element.context.description == + '$withinTransactionDescription$expectedUpdateStatement', + ) + .length; + expect(updateSpanCount, 1); + + verifySpan( + '$withinTransactionDescription$expectedUpdateStatement', + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + + verifySpan( + expectedTransactionStatement, + fixture.getCreatedSpanByDescription(expectedTransactionStatement), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('transaction adds delete spans', () async { + final sut = fixture.sut; + + await sut.transaction(() async { + await insertRow(sut); + await fixture.sut.delete(fixture.sut.todoItems).go(); + }); + + final deleteSpanCount = fixture.tracer.children + .where( + (element) => + element.context.description == + '$withinTransactionDescription$expectedDeleteStatement', + ) + .length; + expect(deleteSpanCount, 1); + + verifySpan( + '$withinTransactionDescription$expectedDeleteStatement', + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + + verifySpan( + expectedTransactionStatement, + fixture.getCreatedSpanByDescription(expectedTransactionStatement), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('transaction adds custom spans', () async { + final sut = fixture.sut; + + await sut.transaction(() async { + await insertRow(sut); + await sut.customStatement('SELECT * FROM todo_items'); + }); + + final customSpanCount = fixture.tracer.children + .where( + (element) => + element.context.description == + '$withinTransactionDescription$expectedSelectStatement', + ) + .length; + expect(customSpanCount, 1); + + verifySpan( + '$withinTransactionDescription$expectedSelectStatement', + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + + verifySpan( + expectedTransactionStatement, + fixture.getCreatedSpanByDescription(expectedTransactionStatement), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('transaction rollback adds span', () async { + final sut = fixture.sut; + + await insertRow(sut); + await insertRow(sut); + + try { + await sut.transaction(() async { + await insertRow(sut, withError: true); + }); + } catch (_) {} + + verifySpan( + expectedTransactionStatement, + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + status: SpanStatus.aborted(), + ); + }); + + test('batch adds span', () async { + final sut = fixture.sut; + + await sut.batch((batch) async { + await insertRow(sut); + await insertRow(sut); + }); + + verifySpan( + 'batch', + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('close adds span', () async { + final sut = fixture.sut; + + await sut.close(); + + verifySpan( + 'Close DB: ${Fixture.dbName}', + fixture.getCreatedSpan(), + ); + }); + + test('open adds span', () async { + final sut = fixture.sut; + + // SentryDriftDatabase is by default lazily opened by default so it won't + // create a span until it is actually used. + await sut.select(sut.todoItems).get(); + + verifySpan( + expectedOpenStatement, + fixture.getCreatedSpanByDescription(expectedOpenStatement), + ); + }); + }); + + group('does not add span', () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + await fixture.setUp(); + + when(fixture.hub.options).thenReturn(fixture.options); + when(fixture.hub.getSpan()).thenReturn(fixture.tracer); + }); + + tearDown(() async { + await fixture.tearDown(); + }); + + test('does not add open span if db is not used', () async { + fixture.sut; + + expect(fixture.tracer.children.isEmpty, true); + }); + + test('batch does not add span for failed operations', () async { + final sut = fixture.sut; + + try { + await sut.batch((batch) async { + await insertRow(sut, withError: true); + await insertRow(sut); + }); + } catch (_) {} + + expect(fixture.tracer.children.isEmpty, true); + }); + }); + + group('adds error span', () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + await fixture.setUp(injectMock: true); + + when(fixture.hub.options).thenReturn(fixture.options); + when(fixture.hub.getSpan()).thenReturn(fixture.tracer); + when(fixture.mockLazyDatabase.ensureOpen(any)) + .thenAnswer((_) => Future.value(true)); + }); + + tearDown(() async { + await fixture.tearDown(); + }); + + test('throwing runInsert throws error span', () async { + when(fixture.mockLazyDatabase.runInsert(any, any)) + .thenThrow(fixture.exception); + + try { + await insertRow(fixture.sut); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedInsertStatement, + fixture.exception, + fixture.getCreatedSpan(), + ); + }); + + test('throwing runUpdate throws error span', () async { + when(fixture.mockLazyDatabase.runUpdate(any, any)) + .thenThrow(fixture.exception); + + try { + await updateRow(fixture.sut); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedUpdateStatement, + fixture.exception, + fixture.getCreatedSpan(), + ); + }); + + test('throwing runCustom throws error span', () async { + when(fixture.mockLazyDatabase.runCustom(any, any)) + .thenThrow(fixture.exception); + + try { + await fixture.sut.customStatement('SELECT * FROM todo_items'); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedSelectStatement, + fixture.exception, + fixture.getCreatedSpan(), + ); + }); + + test('throwing transaction throws error span', () async { + final mockTransactionExecutor = MockTransactionExecutor(); + when(mockTransactionExecutor.beginTransaction()) + .thenThrow(fixture.exception); + + try { + // We need to move it inside the try/catch becaue SentryTransactionExecutor + // starts beginTransaction() directly after init + final SentryTransactionExecutor transactionExecutor = + SentryTransactionExecutor( + mockTransactionExecutor, + fixture.hub, + dbName: Fixture.dbName, + ); + + when(fixture.mockLazyDatabase.beginTransaction()) + .thenReturn(transactionExecutor); + + await fixture.sut.transaction(() async { + await insertRow(fixture.sut); + }); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedTransactionStatement, + fixture.exception, + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('throwing batch throws error span', () async { + final mockTransactionExecutor = MockTransactionExecutor(); + when(mockTransactionExecutor.beginTransaction()) + .thenThrow(fixture.exception); + + // We need to move it inside the try/catch becaue SentryTransactionExecutor + // starts beginTransaction() directly after init + final SentryTransactionExecutor transactionExecutor = + SentryTransactionExecutor( + mockTransactionExecutor, + fixture.hub, + dbName: Fixture.dbName, + ); + + when(fixture.mockLazyDatabase.beginTransaction()) + .thenReturn(transactionExecutor); + + when(fixture.mockLazyDatabase.runInsert(any, any)) + .thenAnswer((realInvocation) => Future.value(1)); + + try { + await fixture.sut.batch((batch) async { + await insertRow(fixture.sut); + }); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedTransactionStatement, + fixture.exception, + fixture.getCreatedSpan(), + origin: SentryTraceOrigins.autoDbDriftTransactionExecutor, + ); + }); + + test('throwing close throws error span', () async { + when(fixture.mockLazyDatabase.close()).thenThrow(fixture.exception); + when(fixture.mockLazyDatabase.runInsert(any, any)) + .thenAnswer((_) => Future.value(1)); + + try { + await insertRow(fixture.sut); + await fixture.sut.close(); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedCloseStatement, + fixture.exception, + fixture.getCreatedSpan(), + ); + + when(fixture.mockLazyDatabase.close()).thenAnswer((_) => Future.value()); + }); + + test('throwing ensureOpen throws error span', () async { + when(fixture.mockLazyDatabase.ensureOpen(any)) + .thenThrow(fixture.exception); + + try { + await fixture.sut.select(fixture.sut.todoItems).get(); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedOpenStatement, + fixture.exception, + fixture.getCreatedSpanByDescription(expectedOpenStatement), + ); + }); + + test('throwing runDelete throws error span', () async { + when(fixture.mockLazyDatabase.runDelete(any, any)) + .thenThrow(fixture.exception); + + try { + await fixture.sut.delete(fixture.sut.todoItems).go(); + } catch (exception) { + expect(exception, fixture.exception); + } + + verifyErrorSpan( + expectedDeleteStatement, + fixture.exception, + fixture.getCreatedSpan(), + ); + }); + }); +} + +class Fixture { + final options = SentryOptions(); + final hub = MockHub(); + static final dbName = 'people-drift-impl'; + final exception = Exception('fixture-exception'); + final _context = SentryTransactionContext('name', 'operation'); + late final tracer = SentryTracer(_context, hub); + late AppDatabase sut; + final mockLazyDatabase = MockLazyDatabase(); + + Future setUp({bool injectMock = false}) async { + sut = AppDatabase(openConnection(injectMock: injectMock)); + } + + Future tearDown() async { + await sut.close(); + } + + SentrySpan? getCreatedSpan() { + return tracer.children.last; + } + + SentrySpan? getCreatedSpanByDescription(String description) { + return tracer.children + .firstWhere((element) => element.context.description == description); + } + + SentryQueryExecutor openConnection({bool injectMock = false}) { + if (injectMock) { + final executor = + SentryQueryExecutor(() => mockLazyDatabase, databaseName: dbName); + executor.setHub(hub); + return executor; + } else { + return SentryQueryExecutor( + () { + return NativeDatabase.memory(); + }, + hub: hub, + databaseName: dbName, + ); + } + } +} diff --git a/drift/test/sqlite3.dll b/drift/test/sqlite3.dll new file mode 100644 index 0000000000..1239c35203 Binary files /dev/null and b/drift/test/sqlite3.dll differ diff --git a/drift/test/test_database.dart b/drift/test/test_database.dart new file mode 100644 index 0000000000..4bf7d7a55a --- /dev/null +++ b/drift/test/test_database.dart @@ -0,0 +1,18 @@ +import 'package:drift/drift.dart'; + +part 'test_database.g.dart'; + +class TodoItems extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get title => text().withLength(min: 6, max: 32)(); + TextColumn get content => text().named('body')(); + IntColumn get category => integer().nullable()(); +} + +@DriftDatabase(tables: [TodoItems]) +class AppDatabase extends _$AppDatabase { + AppDatabase(super.e); + + @override + int get schemaVersion => 1; +} diff --git a/drift/test/test_database.g.dart b/drift/test/test_database.g.dart new file mode 100644 index 0000000000..65430026da --- /dev/null +++ b/drift/test/test_database.g.dart @@ -0,0 +1,269 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'test_database.dart'; + +// ignore_for_file: type=lint +class $TodoItemsTable extends TodoItems + with TableInfo<$TodoItemsTable, TodoItem> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TodoItemsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _titleMeta = const VerificationMeta('title'); + @override + late final GeneratedColumn title = GeneratedColumn( + 'title', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 32), + type: DriftSqlType.string, + requiredDuringInsert: true); + static const VerificationMeta _contentMeta = + const VerificationMeta('content'); + @override + late final GeneratedColumn content = GeneratedColumn( + 'body', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _categoryMeta = + const VerificationMeta('category'); + @override + late final GeneratedColumn category = GeneratedColumn( + 'category', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + @override + List get $columns => [id, title, content, category]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'todo_items'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('title')) { + context.handle( + _titleMeta, title.isAcceptableOrUnknown(data['title']!, _titleMeta)); + } else if (isInserting) { + context.missing(_titleMeta); + } + if (data.containsKey('body')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['body']!, _contentMeta)); + } else if (isInserting) { + context.missing(_contentMeta); + } + if (data.containsKey('category')) { + context.handle(_categoryMeta, + category.isAcceptableOrUnknown(data['category']!, _categoryMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TodoItem map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TodoItem( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + title: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}title'])!, + content: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}body'])!, + category: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}category']), + ); + } + + @override + $TodoItemsTable createAlias(String alias) { + return $TodoItemsTable(attachedDatabase, alias); + } +} + +class TodoItem extends DataClass implements Insertable { + final int id; + final String title; + final String content; + final int? category; + const TodoItem( + {required this.id, + required this.title, + required this.content, + this.category}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['title'] = Variable(title); + map['body'] = Variable(content); + if (!nullToAbsent || category != null) { + map['category'] = Variable(category); + } + return map; + } + + TodoItemsCompanion toCompanion(bool nullToAbsent) { + return TodoItemsCompanion( + id: Value(id), + title: Value(title), + content: Value(content), + category: category == null && nullToAbsent + ? const Value.absent() + : Value(category), + ); + } + + factory TodoItem.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TodoItem( + id: serializer.fromJson(json['id']), + title: serializer.fromJson(json['title']), + content: serializer.fromJson(json['content']), + category: serializer.fromJson(json['category']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'title': serializer.toJson(title), + 'content': serializer.toJson(content), + 'category': serializer.toJson(category), + }; + } + + TodoItem copyWith( + {int? id, + String? title, + String? content, + Value category = const Value.absent()}) => + TodoItem( + id: id ?? this.id, + title: title ?? this.title, + content: content ?? this.content, + category: category.present ? category.value : this.category, + ); + @override + String toString() { + return (StringBuffer('TodoItem(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('content: $content, ') + ..write('category: $category') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, title, content, category); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TodoItem && + other.id == this.id && + other.title == this.title && + other.content == this.content && + other.category == this.category); +} + +class TodoItemsCompanion extends UpdateCompanion { + final Value id; + final Value title; + final Value content; + final Value category; + const TodoItemsCompanion({ + this.id = const Value.absent(), + this.title = const Value.absent(), + this.content = const Value.absent(), + this.category = const Value.absent(), + }); + TodoItemsCompanion.insert({ + this.id = const Value.absent(), + required String title, + required String content, + this.category = const Value.absent(), + }) : title = Value(title), + content = Value(content); + static Insertable custom({ + Expression? id, + Expression? title, + Expression? content, + Expression? category, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (title != null) 'title': title, + if (content != null) 'body': content, + if (category != null) 'category': category, + }); + } + + TodoItemsCompanion copyWith( + {Value? id, + Value? title, + Value? content, + Value? category}) { + return TodoItemsCompanion( + id: id ?? this.id, + title: title ?? this.title, + content: content ?? this.content, + category: category ?? this.category, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (title.present) { + map['title'] = Variable(title.value); + } + if (content.present) { + map['body'] = Variable(content.value); + } + if (category.present) { + map['category'] = Variable(category.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TodoItemsCompanion(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('content: $content, ') + ..write('category: $category') + ..write(')')) + .toString(); + } +} + +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + late final $TodoItemsTable todoItems = $TodoItemsTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [todoItems]; +} diff --git a/drift/test/utils/windows_helper.dart b/drift/test/utils/windows_helper.dart new file mode 100644 index 0000000000..e77fb03bed --- /dev/null +++ b/drift/test/utils/windows_helper.dart @@ -0,0 +1,8 @@ +import 'dart:ffi'; +import 'dart:io'; + +DynamicLibrary openOnWindows() { + final scriptDir = File(Platform.script.toFilePath()).parent; + final libraryNextToScript = File('${scriptDir.path}/test/sqlite3.dll'); + return DynamicLibrary.open(libraryNextToScript.path); +} diff --git a/flutter/example/lib/drift/connection/connection.dart b/flutter/example/lib/drift/connection/connection.dart new file mode 100644 index 0000000000..7ef7ddc757 --- /dev/null +++ b/flutter/example/lib/drift/connection/connection.dart @@ -0,0 +1 @@ +export 'unsupported.dart' if (dart.library.ffi) 'native.dart'; diff --git a/flutter/example/lib/drift/connection/native.dart b/flutter/example/lib/drift/connection/native.dart new file mode 100644 index 0000000000..fb9987412e --- /dev/null +++ b/flutter/example/lib/drift/connection/native.dart @@ -0,0 +1,7 @@ +import 'package:drift/backends.dart'; +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; + +QueryExecutor inMemoryExecutor() { + return NativeDatabase.memory(); +} diff --git a/flutter/example/lib/drift/connection/unsupported.dart b/flutter/example/lib/drift/connection/unsupported.dart new file mode 100644 index 0000000000..87b9b45143 --- /dev/null +++ b/flutter/example/lib/drift/connection/unsupported.dart @@ -0,0 +1,17 @@ +import 'package:drift/drift.dart'; + +Never _unsupported() { + throw UnsupportedError( + 'No suitable database implementation was found on this platform.'); +} + +// Depending on the platform the app is compiled to, the following stubs will +// be replaced with the methods in native.dart or web.dart + +QueryExecutor inMemoryExecutor() { + return _unsupported(); +} + +Future validateDatabaseSchema(GeneratedDatabase database) async { + _unsupported(); +} diff --git a/flutter/example/lib/drift/database.dart b/flutter/example/lib/drift/database.dart new file mode 100644 index 0000000000..914a87ca1a --- /dev/null +++ b/flutter/example/lib/drift/database.dart @@ -0,0 +1,18 @@ +import 'package:drift/drift.dart'; + +part 'database.g.dart'; + +class TodoItems extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get title => text().withLength(min: 6, max: 32)(); + TextColumn get content => text().named('body')(); + IntColumn get category => integer().nullable()(); +} + +@DriftDatabase(tables: [TodoItems]) +class AppDatabase extends _$AppDatabase { + AppDatabase(super.e); + + @override + int get schemaVersion => 1; +} diff --git a/flutter/example/lib/drift/database.g.dart b/flutter/example/lib/drift/database.g.dart new file mode 100644 index 0000000000..1f9d234456 --- /dev/null +++ b/flutter/example/lib/drift/database.g.dart @@ -0,0 +1,269 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ignore_for_file: type=lint +class $TodoItemsTable extends TodoItems + with TableInfo<$TodoItemsTable, TodoItem> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TodoItemsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _titleMeta = const VerificationMeta('title'); + @override + late final GeneratedColumn title = GeneratedColumn( + 'title', aliasedName, false, + additionalChecks: + GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 32), + type: DriftSqlType.string, + requiredDuringInsert: true); + static const VerificationMeta _contentMeta = + const VerificationMeta('content'); + @override + late final GeneratedColumn content = GeneratedColumn( + 'body', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _categoryMeta = + const VerificationMeta('category'); + @override + late final GeneratedColumn category = GeneratedColumn( + 'category', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + @override + List get $columns => [id, title, content, category]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'todo_items'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('title')) { + context.handle( + _titleMeta, title.isAcceptableOrUnknown(data['title']!, _titleMeta)); + } else if (isInserting) { + context.missing(_titleMeta); + } + if (data.containsKey('body')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['body']!, _contentMeta)); + } else if (isInserting) { + context.missing(_contentMeta); + } + if (data.containsKey('category')) { + context.handle(_categoryMeta, + category.isAcceptableOrUnknown(data['category']!, _categoryMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TodoItem map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TodoItem( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + title: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}title'])!, + content: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}body'])!, + category: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}category']), + ); + } + + @override + $TodoItemsTable createAlias(String alias) { + return $TodoItemsTable(attachedDatabase, alias); + } +} + +class TodoItem extends DataClass implements Insertable { + final int id; + final String title; + final String content; + final int? category; + const TodoItem( + {required this.id, + required this.title, + required this.content, + this.category}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['title'] = Variable(title); + map['body'] = Variable(content); + if (!nullToAbsent || category != null) { + map['category'] = Variable(category); + } + return map; + } + + TodoItemsCompanion toCompanion(bool nullToAbsent) { + return TodoItemsCompanion( + id: Value(id), + title: Value(title), + content: Value(content), + category: category == null && nullToAbsent + ? const Value.absent() + : Value(category), + ); + } + + factory TodoItem.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TodoItem( + id: serializer.fromJson(json['id']), + title: serializer.fromJson(json['title']), + content: serializer.fromJson(json['content']), + category: serializer.fromJson(json['category']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'title': serializer.toJson(title), + 'content': serializer.toJson(content), + 'category': serializer.toJson(category), + }; + } + + TodoItem copyWith( + {int? id, + String? title, + String? content, + Value category = const Value.absent()}) => + TodoItem( + id: id ?? this.id, + title: title ?? this.title, + content: content ?? this.content, + category: category.present ? category.value : this.category, + ); + @override + String toString() { + return (StringBuffer('TodoItem(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('content: $content, ') + ..write('category: $category') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, title, content, category); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TodoItem && + other.id == this.id && + other.title == this.title && + other.content == this.content && + other.category == this.category); +} + +class TodoItemsCompanion extends UpdateCompanion { + final Value id; + final Value title; + final Value content; + final Value category; + const TodoItemsCompanion({ + this.id = const Value.absent(), + this.title = const Value.absent(), + this.content = const Value.absent(), + this.category = const Value.absent(), + }); + TodoItemsCompanion.insert({ + this.id = const Value.absent(), + required String title, + required String content, + this.category = const Value.absent(), + }) : title = Value(title), + content = Value(content); + static Insertable custom({ + Expression? id, + Expression? title, + Expression? content, + Expression? category, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (title != null) 'title': title, + if (content != null) 'body': content, + if (category != null) 'category': category, + }); + } + + TodoItemsCompanion copyWith( + {Value? id, + Value? title, + Value? content, + Value? category}) { + return TodoItemsCompanion( + id: id ?? this.id, + title: title ?? this.title, + content: content ?? this.content, + category: category ?? this.category, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (title.present) { + map['title'] = Variable(title.value); + } + if (content.present) { + map['body'] = Variable(content.value); + } + if (category.present) { + map['category'] = Variable(category.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TodoItemsCompanion(') + ..write('id: $id, ') + ..write('title: $title, ') + ..write('content: $content, ') + ..write('category: $category') + ..write(')')) + .toString(); + } +} + +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + late final $TodoItemsTable todoItems = $TodoItemsTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [todoItems]; +} diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 3d044c155d..2011342d2a 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -7,14 +7,18 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; +import 'package:sentry_drift/sentry_drift.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_sqflite/sentry_sqflite.dart'; import 'package:sqflite/sqflite.dart'; + // import 'package:sqflite_common_ffi/sqflite_ffi.dart'; // import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:feedback/feedback.dart' as feedback; import 'package:provider/provider.dart'; +import 'drift/database.dart'; +import 'drift/connection/connection.dart'; import 'user_feedback_dialog.dart'; import 'package:dio/dio.dart'; import 'package:sentry_dio/sentry_dio.dart'; @@ -149,6 +153,12 @@ class MainScaffold extends StatelessWidget { children: [ if (_isIntegrationTest) const IntegrationTestWidget(), const Center(child: Text('Trigger an action:\n')), + // For simplicity sake we skip the web set up for now. + if (!UniversalPlatform.isWeb) + ElevatedButton( + onPressed: () => driftTest(), + child: const Text('drift'), + ), ElevatedButton( onPressed: () => sqfliteTest(), child: const Text('sqflite'), @@ -467,6 +477,34 @@ class MainScaffold extends StatelessWidget { await tr.finish(status: const SpanStatus.ok()); } + + Future driftTest() async { + final tr = Sentry.startTransaction( + 'driftTest', + 'db', + bindToScope: true, + ); + + final executor = SentryQueryExecutor( + () async => inMemoryExecutor(), + databaseName: 'sentry_in_memory_db', + ); + + final db = AppDatabase(executor); + + await db.into(db.todoItems).insert( + TodoItemsCompanion.insert( + title: 'This is a test thing', + content: 'test', + ), + ); + + await db.select(db.todoItems).get(); + + await db.close(); + + await tr.finish(status: const SpanStatus.ok()); + } } extension BuildContextExtension on BuildContext { diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index b375a829d0..d7e5b0b244 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -17,17 +17,20 @@ dependencies: sentry_logging: sentry_sqflite: sentry_file: + sentry_drift: universal_platform: ^1.0.0 feedback: ^2.0.0 provider: ^6.0.0 dio: any # This gets constrained by `sentry_dio` sqflite: any # This gets constrained by `sentry_sqflite` logging: any # This gets constrained by `sentry_logging` + drift: any # This gets constrained by `sentry_drift` package_info_plus: ^4.0.0 path_provider: ^2.0.0 #sqflite_common_ffi: ^2.0.0 #sqflite_common_ffi_web: ^0.3.0 http: ^1.0.0 + sqlite3_flutter_libs: ^0.5.0 dev_dependencies: flutter_lints: ^2.0.0 diff --git a/flutter/example/pubspec_overrides.yaml b/flutter/example/pubspec_overrides.yaml index 7afb410d83..fd543af840 100644 --- a/flutter/example/pubspec_overrides.yaml +++ b/flutter/example/pubspec_overrides.yaml @@ -11,3 +11,5 @@ dependency_overrides: path: ../../sqflite sentry_file: path: ../../file + sentry_drift: + path: ../../drift diff --git a/flutter/test/mocks.mocks.dart b/flutter/test/mocks.mocks.dart index 45a91b1b0f..e2807ef405 100644 --- a/flutter/test/mocks.mocks.dart +++ b/flutter/test/mocks.mocks.dart @@ -80,9 +80,8 @@ class _FakeSentryTracer_4 extends _i1.SmartFake implements _i4.SentryTracer { ); } -class _FakeSentryTransaction_5 extends _i1.SmartFake - implements _i3.SentryTransaction { - _FakeSentryTransaction_5( +class _FakeSentryId_5 extends _i1.SmartFake implements _i3.SentryId { + _FakeSentryId_5( Object parent, Invocation parentInvocation, ) : super( @@ -91,8 +90,8 @@ class _FakeSentryTransaction_5 extends _i1.SmartFake ); } -class _FakeMethodCodec_6 extends _i1.SmartFake implements _i5.MethodCodec { - _FakeMethodCodec_6( +class _FakeContexts_6 extends _i1.SmartFake implements _i3.Contexts { + _FakeContexts_6( Object parent, Invocation parentInvocation, ) : super( @@ -101,9 +100,19 @@ class _FakeMethodCodec_6 extends _i1.SmartFake implements _i5.MethodCodec { ); } -class _FakeBinaryMessenger_7 extends _i1.SmartFake - implements _i6.BinaryMessenger { - _FakeBinaryMessenger_7( +class _FakeSentryTransaction_7 extends _i1.SmartFake + implements _i3.SentryTransaction { + _FakeSentryTransaction_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeMethodCodec_8 extends _i1.SmartFake implements _i5.MethodCodec { + _FakeMethodCodec_8( Object parent, Invocation parentInvocation, ) : super( @@ -112,8 +121,9 @@ class _FakeBinaryMessenger_7 extends _i1.SmartFake ); } -class _FakeSentryOptions_8 extends _i1.SmartFake implements _i2.SentryOptions { - _FakeSentryOptions_8( +class _FakeBinaryMessenger_9 extends _i1.SmartFake + implements _i6.BinaryMessenger { + _FakeBinaryMessenger_9( Object parent, Invocation parentInvocation, ) : super( @@ -122,8 +132,8 @@ class _FakeSentryOptions_8 extends _i1.SmartFake implements _i2.SentryOptions { ); } -class _FakeSentryId_9 extends _i1.SmartFake implements _i3.SentryId { - _FakeSentryId_9( +class _FakeSentryOptions_10 extends _i1.SmartFake implements _i2.SentryOptions { + _FakeSentryOptions_10( Object parent, Invocation parentInvocation, ) : super( @@ -132,8 +142,8 @@ class _FakeSentryId_9 extends _i1.SmartFake implements _i3.SentryId { ); } -class _FakeScope_10 extends _i1.SmartFake implements _i2.Scope { - _FakeScope_10( +class _FakeScope_11 extends _i1.SmartFake implements _i2.Scope { + _FakeScope_11( Object parent, Invocation parentInvocation, ) : super( @@ -142,8 +152,8 @@ class _FakeScope_10 extends _i1.SmartFake implements _i2.Scope { ); } -class _FakeHub_11 extends _i1.SmartFake implements _i2.Hub { - _FakeHub_11( +class _FakeHub_12 extends _i1.SmartFake implements _i2.Hub { + _FakeHub_12( Object parent, Invocation parentInvocation, ) : super( @@ -184,6 +194,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { Invocation.getter(#name), returnValue: '', ) as String); + @override set name(String? _name) => super.noSuchMethod( Invocation.setter( @@ -192,12 +203,14 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override _i3.SentryTransactionNameSource get transactionNameSource => (super.noSuchMethod( Invocation.getter(#transactionNameSource), returnValue: _i3.SentryTransactionNameSource.custom, ) as _i3.SentryTransactionNameSource); + @override set transactionNameSource( _i3.SentryTransactionNameSource? _transactionNameSource) => @@ -208,6 +221,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override set profiler(_i9.SentryProfiler? _profiler) => super.noSuchMethod( Invocation.setter( @@ -216,6 +230,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override set profileInfo(_i9.SentryProfileInfo? _profileInfo) => super.noSuchMethod( Invocation.setter( @@ -224,6 +239,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override _i2.SentrySpanContext get context => (super.noSuchMethod( Invocation.getter(#context), @@ -232,6 +248,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { Invocation.getter(#context), ), ) as _i2.SentrySpanContext); + @override set origin(String? origin) => super.noSuchMethod( Invocation.setter( @@ -240,6 +257,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override DateTime get startTimestamp => (super.noSuchMethod( Invocation.getter(#startTimestamp), @@ -248,21 +266,25 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { Invocation.getter(#startTimestamp), ), ) as DateTime); + @override Map get data => (super.noSuchMethod( Invocation.getter(#data), returnValue: {}, ) as Map); + @override bool get finished => (super.noSuchMethod( Invocation.getter(#finished), returnValue: false, ) as bool); + @override List<_i3.SentrySpan> get children => (super.noSuchMethod( Invocation.getter(#children), returnValue: <_i3.SentrySpan>[], ) as List<_i3.SentrySpan>); + @override set throwable(dynamic throwable) => super.noSuchMethod( Invocation.setter( @@ -271,27 +293,31 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override - set status(dynamic status) => super.noSuchMethod( + set status(_i3.SpanStatus? status) => super.noSuchMethod( Invocation.setter( #status, status, ), returnValueForMissingStub: null, ); + @override Map get tags => (super.noSuchMethod( Invocation.getter(#tags), returnValue: {}, ) as Map); + @override Map get measurements => (super.noSuchMethod( Invocation.getter(#measurements), returnValue: {}, ) as Map); + @override _i7.Future finish({ - dynamic status, + _i3.SpanStatus? status, DateTime? endTimestamp, }) => (super.noSuchMethod( @@ -306,6 +332,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override void removeData(String? key) => super.noSuchMethod( Invocation.method( @@ -314,6 +341,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override void removeTag(String? key) => super.noSuchMethod( Invocation.method( @@ -322,6 +350,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override void setData( String? key, @@ -337,6 +366,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override void setTag( String? key, @@ -352,6 +382,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override _i2.ISentrySpan startChild( String? operation, { @@ -379,9 +410,10 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), ), ) as _i2.ISentrySpan); + @override _i2.ISentrySpan startChildWithParentSpanId( - dynamic parentSpanId, + _i3.SpanId? parentSpanId, String? operation, { String? description, DateTime? startTimestamp, @@ -413,6 +445,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), ), ) as _i2.ISentrySpan); + @override _i3.SentryTraceHeader toSentryTrace() => (super.noSuchMethod( Invocation.method( @@ -427,6 +460,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), ), ) as _i3.SentryTraceHeader); + @override void setMeasurement( String? name, @@ -444,6 +478,7 @@ class MockSentryTracer extends _i1.Mock implements _i4.SentryTracer { ), returnValueForMissingStub: null, ); + @override void scheduleFinish() => super.noSuchMethod( Invocation.method( @@ -471,6 +506,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { Invocation.getter(#startTimestamp), ), ) as DateTime); + @override set startTimestamp(DateTime? _startTimestamp) => super.noSuchMethod( Invocation.setter( @@ -479,11 +515,13 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { ), returnValueForMissingStub: null, ); + @override List<_i3.SentrySpan> get spans => (super.noSuchMethod( Invocation.getter(#spans), returnValue: <_i3.SentrySpan>[], ) as List<_i3.SentrySpan>); + @override set spans(List<_i3.SentrySpan>? _spans) => super.noSuchMethod( Invocation.setter( @@ -492,6 +530,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { ), returnValueForMissingStub: null, ); + @override _i4.SentryTracer get tracer => (super.noSuchMethod( Invocation.getter(#tracer), @@ -500,11 +539,13 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { Invocation.getter(#tracer), ), ) as _i4.SentryTracer); + @override Map get measurements => (super.noSuchMethod( Invocation.getter(#measurements), returnValue: {}, ) as Map); + @override set measurements(Map? _measurements) => super.noSuchMethod( @@ -514,6 +555,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { ), returnValueForMissingStub: null, ); + @override set transactionInfo(_i3.SentryTransactionInfo? _transactionInfo) => super.noSuchMethod( @@ -523,16 +565,37 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { ), returnValueForMissingStub: null, ); + @override bool get finished => (super.noSuchMethod( Invocation.getter(#finished), returnValue: false, ) as bool); + @override bool get sampled => (super.noSuchMethod( Invocation.getter(#sampled), returnValue: false, ) as bool); + + @override + _i3.SentryId get eventId => (super.noSuchMethod( + Invocation.getter(#eventId), + returnValue: _FakeSentryId_5( + this, + Invocation.getter(#eventId), + ), + ) as _i3.SentryId); + + @override + _i3.Contexts get contexts => (super.noSuchMethod( + Invocation.getter(#contexts), + returnValue: _FakeContexts_6( + this, + Invocation.getter(#contexts), + ), + ) as _i3.Contexts); + @override Map toJson() => (super.noSuchMethod( Invocation.method( @@ -541,6 +604,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { ), returnValue: {}, ) as Map); + @override _i3.SentryTransaction copyWith({ _i3.SentryId? eventId, @@ -552,7 +616,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { String? dist, String? environment, Map? modules, - dynamic message, + _i3.SentryMessage? message, String? transaction, dynamic throwable, _i3.SentryLevel? level, @@ -561,13 +625,13 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { Map? extra, List? fingerprint, _i3.SentryUser? user, - dynamic contexts, + _i3.Contexts? contexts, List<_i3.Breadcrumb>? breadcrumbs, _i3.SdkVersion? sdk, - dynamic request, - dynamic debugMeta, + _i3.SentryRequest? request, + _i3.DebugMeta? debugMeta, List<_i3.SentryException>? exceptions, - List? threads, + List<_i3.SentryThread>? threads, String? type, Map? measurements, _i3.SentryTransactionInfo? transactionInfo, @@ -607,7 +671,7 @@ class MockSentryTransaction extends _i1.Mock implements _i3.SentryTransaction { #transactionInfo: transactionInfo, }, ), - returnValue: _FakeSentryTransaction_5( + returnValue: _FakeSentryTransaction_7( this, Invocation.method( #copyWith, @@ -660,22 +724,25 @@ class MockMethodChannel extends _i1.Mock implements _i10.MethodChannel { Invocation.getter(#name), returnValue: '', ) as String); + @override _i5.MethodCodec get codec => (super.noSuchMethod( Invocation.getter(#codec), - returnValue: _FakeMethodCodec_6( + returnValue: _FakeMethodCodec_8( this, Invocation.getter(#codec), ), ) as _i5.MethodCodec); + @override _i6.BinaryMessenger get binaryMessenger => (super.noSuchMethod( Invocation.getter(#binaryMessenger), - returnValue: _FakeBinaryMessenger_7( + returnValue: _FakeBinaryMessenger_9( this, Invocation.getter(#binaryMessenger), ), ) as _i6.BinaryMessenger); + @override _i7.Future invokeMethod( String? method, [ @@ -691,6 +758,7 @@ class MockMethodChannel extends _i1.Mock implements _i10.MethodChannel { ), returnValue: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future?> invokeListMethod( String? method, [ @@ -706,6 +774,7 @@ class MockMethodChannel extends _i1.Mock implements _i10.MethodChannel { ), returnValue: _i7.Future?>.value(), ) as _i7.Future?>); + @override _i7.Future?> invokeMapMethod( String? method, [ @@ -721,6 +790,7 @@ class MockMethodChannel extends _i1.Mock implements _i10.MethodChannel { ), returnValue: _i7.Future?>.value(), ) as _i7.Future?>); + @override void setMethodCallHandler( _i7.Future Function(_i5.MethodCall)? handler) => @@ -744,32 +814,36 @@ class MockHub extends _i1.Mock implements _i2.Hub { @override _i2.SentryOptions get options => (super.noSuchMethod( Invocation.getter(#options), - returnValue: _FakeSentryOptions_8( + returnValue: _FakeSentryOptions_10( this, Invocation.getter(#options), ), ) as _i2.SentryOptions); + @override bool get isEnabled => (super.noSuchMethod( Invocation.getter(#isEnabled), returnValue: false, ) as bool); + @override _i3.SentryId get lastEventId => (super.noSuchMethod( Invocation.getter(#lastEventId), - returnValue: _FakeSentryId_9( + returnValue: _FakeSentryId_5( this, Invocation.getter(#lastEventId), ), ) as _i3.SentryId); + @override _i2.Scope get scope => (super.noSuchMethod( Invocation.getter(#scope), - returnValue: _FakeScope_10( + returnValue: _FakeScope_11( this, Invocation.getter(#scope), ), ) as _i2.Scope); + @override set profilerFactory(_i9.SentryProfilerFactory? value) => super.noSuchMethod( Invocation.setter( @@ -778,9 +852,10 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), returnValueForMissingStub: null, ); + @override _i7.Future<_i3.SentryId> captureEvent( - dynamic event, { + _i3.SentryEvent? event, { dynamic stackTrace, _i2.Hint? hint, _i2.ScopeCallback? withScope, @@ -795,7 +870,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { #withScope: withScope, }, ), - returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_9( + returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_5( this, Invocation.method( #captureEvent, @@ -808,6 +883,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), )), ) as _i7.Future<_i3.SentryId>); + @override _i7.Future<_i3.SentryId> captureException( dynamic throwable, { @@ -825,7 +901,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { #withScope: withScope, }, ), - returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_9( + returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_5( this, Invocation.method( #captureException, @@ -838,6 +914,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), )), ) as _i7.Future<_i3.SentryId>); + @override _i7.Future<_i3.SentryId> captureMessage( String? message, { @@ -859,7 +936,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { #withScope: withScope, }, ), - returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_9( + returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_5( this, Invocation.method( #captureMessage, @@ -874,6 +951,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), )), ) as _i7.Future<_i3.SentryId>); + @override _i7.Future captureUserFeedback(_i2.SentryUserFeedback? userFeedback) => (super.noSuchMethod( @@ -884,6 +962,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override _i7.Future addBreadcrumb( _i3.Breadcrumb? crumb, { @@ -898,6 +977,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override void bindClient(_i2.SentryClient? client) => super.noSuchMethod( Invocation.method( @@ -906,13 +986,14 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), returnValueForMissingStub: null, ); + @override _i2.Hub clone() => (super.noSuchMethod( Invocation.method( #clone, [], ), - returnValue: _FakeHub_11( + returnValue: _FakeHub_12( this, Invocation.method( #clone, @@ -920,6 +1001,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), ), ) as _i2.Hub); + @override _i7.Future close() => (super.noSuchMethod( Invocation.method( @@ -929,12 +1011,14 @@ class MockHub extends _i1.Mock implements _i2.Hub { returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override _i7.FutureOr configureScope(_i2.ScopeCallback? callback) => (super.noSuchMethod(Invocation.method( #configureScope, [callback], )) as _i7.FutureOr); + @override _i2.ISentrySpan startTransaction( String? name, @@ -979,6 +1063,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { customSamplingContext: customSamplingContext, ), ) as _i2.ISentrySpan); + @override _i2.ISentrySpan startTransactionWithContext( _i2.SentryTransactionContext? transactionContext, { @@ -1021,6 +1106,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), ), ) as _i2.ISentrySpan); + @override _i7.Future<_i3.SentryId> captureTransaction( _i3.SentryTransaction? transaction, { @@ -1032,7 +1118,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { [transaction], {#traceContext: traceContext}, ), - returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_9( + returnValue: _i7.Future<_i3.SentryId>.value(_FakeSentryId_5( this, Invocation.method( #captureTransaction, @@ -1041,6 +1127,7 @@ class MockHub extends _i1.Mock implements _i2.Hub { ), )), ) as _i7.Future<_i3.SentryId>); + @override void setSpanContext( dynamic throwable,