From 7748b7cce475ff9e094eafe66ba7e3850ef59f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Andra=C5=A1ec?= Date: Wed, 15 Nov 2023 11:34:37 +0000 Subject: [PATCH] APM for hive (#1672) * setup sentry hive library * Add SentryBox with first test * test added spans * Add sentry hive impl * introduce base box to avoid duplication * implement lazy box test * expose sentry hive and updata documentation * add hive to flutter example app * test throwing in base_box, cleanup * test lazy box error cases * test sentry hive impl failures * run fix & format * fix test failure race condition * add hive workflow, update other workflows * add changelog entry * remove path+provider from dev_dependencies * add coverage dev dependency * fix deprecation warnings * fix changelog * move span wrapping on own class to avoid duplication * add sentry box collection * test added spans, fix imports * test throwing code * fix changelog * change dbSystem value * change db system * remove unneccessary async & await * Fix analyze * Formatting * Add changelog symlink * Ignore hive coverage * Create symlink for dartdoc_options --------- Co-authored-by: Giancarlo Buenaflor --- .craft.yml | 4 +- .github/workflows/dart.yml | 1 + .github/workflows/diagrams.yml | 4 + .github/workflows/dio.yml | 1 + .github/workflows/e2e_dart.yml | 1 + .github/workflows/file.yml | 1 + .github/workflows/flutter.yml | 1 + .github/workflows/hive.yml | 71 + .github/workflows/logging.yml | 1 + .github/workflows/min_version_test.yml | 1 + .github/workflows/sqflite.yml | 1 + .gitignore | 1 + CHANGELOG.md | 1 + dart/lib/src/sentry_trace_origins.dart | 4 + drift/CHANGELOG.md | 1597 +---------------- drift/dartdoc_options.yaml | 4 +- flutter/example/lib/main.dart | 31 + flutter/example/pubspec.yaml | 2 + flutter/example/pubspec_overrides.yaml | 2 + hive/.gitignore | 14 + hive/CHANGELOG.md | 1 + hive/LICENSE | 21 + hive/README.md | 85 + hive/analysis_options.yaml | 34 + hive/boxes_bad_keys.hive | 0 hive/dartdoc_options.yaml | 1 + hive/example/example.dart | 54 + hive/lib/sentry_hive.dart | 15 + hive/lib/src/default_compaction_strategy.dart | 9 + hive/lib/src/default_key_comparator.dart | 20 + hive/lib/src/sentry_box.dart | 37 + hive/lib/src/sentry_box_base.dart | 203 +++ hive/lib/src/sentry_box_collection.dart | 125 ++ hive/lib/src/sentry_hive_impl.dart | 203 +++ hive/lib/src/sentry_hive_interface.dart | 11 + hive/lib/src/sentry_lazy_box.dart | 45 + hive/lib/src/sentry_span_helper.dart | 58 + hive/people-box-collection_bad_keys.hive | 0 hive/pubspec.yaml | 22 + hive/pubspec_overrides.yaml | 3 + hive/test/mocks/mocks.dart | 18 + hive/test/mocks/mocks.mocks.dart | 1307 ++++++++++++++ hive/test/person.dart | 24 + hive/test/sentry_box_base_test.dart | 297 +++ hive/test/sentry_box_collection_test.dart | 225 +++ hive/test/sentry_hive_impl_test.dart | 310 ++++ hive/test/sentry_lazy_box_test.dart | 170 ++ 47 files changed, 3441 insertions(+), 1600 deletions(-) create mode 100644 .github/workflows/hive.yml mode change 100644 => 120000 drift/CHANGELOG.md mode change 100644 => 120000 drift/dartdoc_options.yaml create mode 100644 hive/.gitignore create mode 120000 hive/CHANGELOG.md create mode 100644 hive/LICENSE create mode 100644 hive/README.md create mode 100644 hive/analysis_options.yaml create mode 100644 hive/boxes_bad_keys.hive create mode 120000 hive/dartdoc_options.yaml create mode 100644 hive/example/example.dart create mode 100644 hive/lib/sentry_hive.dart create mode 100644 hive/lib/src/default_compaction_strategy.dart create mode 100644 hive/lib/src/default_key_comparator.dart create mode 100644 hive/lib/src/sentry_box.dart create mode 100644 hive/lib/src/sentry_box_base.dart create mode 100644 hive/lib/src/sentry_box_collection.dart create mode 100644 hive/lib/src/sentry_hive_impl.dart create mode 100644 hive/lib/src/sentry_hive_interface.dart create mode 100644 hive/lib/src/sentry_lazy_box.dart create mode 100644 hive/lib/src/sentry_span_helper.dart create mode 100644 hive/people-box-collection_bad_keys.hive create mode 100644 hive/pubspec.yaml create mode 100644 hive/pubspec_overrides.yaml create mode 100644 hive/test/mocks/mocks.dart create mode 100644 hive/test/mocks/mocks.mocks.dart create mode 100644 hive/test/person.dart create mode 100644 hive/test/sentry_box_base_test.dart create mode 100644 hive/test/sentry_box_collection_test.dart create mode 100644 hive/test/sentry_hive_impl_test.dart create mode 100644 hive/test/sentry_lazy_box_test.dart diff --git a/.craft.yml b/.craft.yml index b456b09aab..73e213edb7 100644 --- a/.craft.yml +++ b/.craft.yml @@ -11,6 +11,7 @@ targets: dio: file: sqflite: + hive: drift: - name: github - name: registry @@ -21,5 +22,6 @@ targets: pub:sentry_dio: pub:sentry_file: pub:sentry_sqflite: - # This needs to be published on pub.dev first before uncommenting + # Initial release on pub.dev needed first before uncommenting # pub:sentry_drift: + # pub:sentry_hive: diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 20d028f0d9..32b805a524 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -11,6 +11,7 @@ on: - "dio/**" - "file/**" - "sqflite/**" + - "hive/**" - "drift/**" jobs: diff --git a/.github/workflows/diagrams.yml b/.github/workflows/diagrams.yml index ac026f4cfc..b4f403b78c 100644 --- a/.github/workflows/diagrams.yml +++ b/.github/workflows/diagrams.yml @@ -43,6 +43,10 @@ jobs: working-directory: ./logging run: lakos . -i "{test/**,example/**}" | dot -Tsvg -o class-diagram.svg + - name: hive + working-directory: ./hive + run: lakos . -i "{test/**,example/**}" | dot -Tsvg -o class-diagram.svg + # Source: https://stackoverflow.com/a/58035262 - name: Extract branch name shell: bash diff --git a/.github/workflows/dio.yml b/.github/workflows/dio.yml index 801138c407..6b011213e0 100644 --- a/.github/workflows/dio.yml +++ b/.github/workflows/dio.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "file/**" - "sqflite/**" + - "hive/**" - "drift/**" jobs: diff --git a/.github/workflows/e2e_dart.yml b/.github/workflows/e2e_dart.yml index 62827e69c3..9deb9a3eaf 100644 --- a/.github/workflows/e2e_dart.yml +++ b/.github/workflows/e2e_dart.yml @@ -12,6 +12,7 @@ on: - "flutter/**" - "file/**" - "sqflite/**" + - "hive/**" - "drift/**" env: diff --git a/.github/workflows/file.yml b/.github/workflows/file.yml index a4e17af787..f9f03aa5cf 100644 --- a/.github/workflows/file.yml +++ b/.github/workflows/file.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "dio/**" - "sqflite/**" + - "hive/**" - "drift/**" jobs: diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index d81896825e..6c48bf4747 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -11,6 +11,7 @@ on: - "dio/**" - "file/**" - "sqflite/**" + - "hive/**" - "drift/**" jobs: diff --git a/.github/workflows/hive.yml b/.github/workflows/hive.yml new file mode 100644 index 0000000000..42b434d30e --- /dev/null +++ b/.github/workflows/hive.yml @@ -0,0 +1,71 @@ +name: sentry-hive +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@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # pin@0.11.0 + with: + access_token: ${{ github.token }} + + build: + name: Build ${{matrix.sdk}} on ${{matrix.os}} + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + defaults: + run: + shell: bash + working-directory: ./hive + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + sdk: [stable, beta] + exclude: + - os: windows-latest + sdk: beta + - os: macos-latest + sdk: beta + steps: + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d # pin@v1 + with: + sdk: ${{ matrix.sdk }} + - uses: actions/checkout@v3 + + - name: Test VM + run: | + dart pub get + dart test -p vm --coverage=coverage --test-randomize-ordering-seed=random --chain-stack-traces + dart pub run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib + + - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # pin@v3 + if: runner.os == 'Linux' && matrix.sdk == 'stable' + with: + name: sentry_hive + files: ./hive/coverage/lcov.info + + - uses: VeryGoodOpenSource/very_good_coverage@e5c91bc7ce9843e87c800b3bcafdfb86fbe28491 # pin@v2.1.0 + if: runner.os == 'Linux' && matrix.sdk == 'stable' + with: + path: "./hive/coverage/lcov.info" + min_coverage: 55 + + analyze: + uses: ./.github/workflows/analyze.yml + with: + package: hive + panaThreshold: 90 diff --git a/.github/workflows/logging.yml b/.github/workflows/logging.yml index 3696428cd8..8529b2db12 100644 --- a/.github/workflows/logging.yml +++ b/.github/workflows/logging.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "file/**" - "sqflite/**" + - "hive/**" - "drift/**" jobs: diff --git a/.github/workflows/min_version_test.yml b/.github/workflows/min_version_test.yml index 8499838d41..b9116cdc52 100644 --- a/.github/workflows/min_version_test.yml +++ b/.github/workflows/min_version_test.yml @@ -9,6 +9,7 @@ on: - "**/*.md" - "file/**" - "sqflite/**" + - "hive/**" - "drift/**" jobs: diff --git a/.github/workflows/sqflite.yml b/.github/workflows/sqflite.yml index b1a1858756..5da5b7e557 100644 --- a/.github/workflows/sqflite.yml +++ b/.github/workflows/sqflite.yml @@ -11,6 +11,7 @@ on: - "flutter/**" - "dio/**" - "file/**" + - "hive/**" - "drift/**" jobs: diff --git a/.gitignore b/.gitignore index 55f9d96c39..60bf635a0c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ file/coverage/* flutter/coverage/* sqflite/coverage/* drift/coverage/* +hive/coverage/* pubspec.lock Podfile.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e84e2f71d..cec50f9143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - 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)) +- APM for hive ([#1672](https://github.com/getsentry/sentry-dart/pull/1672)) - Add `attachScreenshotOnlyWhenResumed` to options ([#1700](https://github.com/getsentry/sentry-dart/pull/1700)) ### Dependencies diff --git a/dart/lib/src/sentry_trace_origins.dart b/dart/lib/src/sentry_trace_origins.dart index 8910292b74..903348aa19 100644 --- a/dart/lib/src/sentry_trace_origins.dart +++ b/dart/lib/src/sentry_trace_origins.dart @@ -18,6 +18,10 @@ class SentryTraceOrigins { 'auto.db.sqflite.database_executor'; static const autoDbSqfliteDatabaseFactory = 'auto.db.sqflite.database_factory'; + static const autoDbHive = 'auto.db.hive'; + static const autoDbHiveBoxBase = 'auto.db.hive.box_base'; + static const autoDbHiveLazyBox = 'auto.db.hive.lazy_box'; + static const autoDbHiveBoxCollection = 'auto.db.hive.box_collection'; static const autoDbDriftQueryExecutor = 'auto.db.drift.query.executor'; static const autoDbDriftTransactionExecutor = 'auto.db.drift.transaction.executor'; diff --git a/drift/CHANGELOG.md b/drift/CHANGELOG.md deleted file mode 100644 index c640f46f6b..0000000000 --- a/drift/CHANGELOG.md +++ /dev/null @@ -1,1596 +0,0 @@ -# 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/CHANGELOG.md b/drift/CHANGELOG.md new file mode 120000 index 0000000000..04c99a55ca --- /dev/null +++ b/drift/CHANGELOG.md @@ -0,0 +1 @@ +../CHANGELOG.md \ No newline at end of file diff --git a/drift/dartdoc_options.yaml b/drift/dartdoc_options.yaml deleted file mode 100644 index 20edf7f942..0000000000 --- a/drift/dartdoc_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -dartdoc: - errors: - - unresolved-doc-reference diff --git a/drift/dartdoc_options.yaml b/drift/dartdoc_options.yaml new file mode 120000 index 0000000000..7cbb8c0d74 --- /dev/null +++ b/drift/dartdoc_options.yaml @@ -0,0 +1 @@ +../dart/dartdoc_options.yaml \ No newline at end of file diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 2011342d2a..8ff567b8cb 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:sentry_drift/sentry_drift.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_sqflite/sentry_sqflite.dart'; @@ -23,6 +24,7 @@ import 'user_feedback_dialog.dart'; import 'package:dio/dio.dart'; import 'package:sentry_dio/sentry_dio.dart'; import 'package:sentry_logging/sentry_logging.dart'; +import 'package:sentry_hive/sentry_hive.dart'; // ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io const String exampleDsn = @@ -159,6 +161,10 @@ class MainScaffold extends StatelessWidget { onPressed: () => driftTest(), child: const Text('drift'), ), + ElevatedButton( + onPressed: () => hiveTest(), + child: const Text('hive'), + ), ElevatedButton( onPressed: () => sqfliteTest(), child: const Text('sqflite'), @@ -432,6 +438,31 @@ class MainScaffold extends StatelessWidget { ); } + Future hiveTest() async { + if (kIsWeb) { + return; + } + + final tr = Sentry.startTransaction( + 'hiveTest', + 'db', + bindToScope: true, + ); + + final appDir = await getApplicationDocumentsDirectory(); + SentryHive.init(appDir.path); + + final catsBox = await SentryHive.openBox('cats'); + await catsBox.put('fluffy', {'name': 'Fluffy', 'age': 4}); + await catsBox.put('loki', {'name': 'Loki', 'age': 2}); + await catsBox.clear(); + await catsBox.close(); + + SentryHive.close(); + + await tr.finish(status: const SpanStatus.ok()); + } + Future sqfliteTest() async { final tr = Sentry.startTransaction( 'sqfliteTest', diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index d7e5b0b244..ba0c742a7b 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: sentry_logging: sentry_sqflite: sentry_file: + sentry_hive: sentry_drift: universal_platform: ^1.0.0 feedback: ^2.0.0 @@ -30,6 +31,7 @@ dependencies: #sqflite_common_ffi: ^2.0.0 #sqflite_common_ffi_web: ^0.3.0 http: ^1.0.0 + hive: any # This gets constrained by `sentry_hive` sqlite3_flutter_libs: ^0.5.0 dev_dependencies: diff --git a/flutter/example/pubspec_overrides.yaml b/flutter/example/pubspec_overrides.yaml index fd543af840..4a3bb3c8cc 100644 --- a/flutter/example/pubspec_overrides.yaml +++ b/flutter/example/pubspec_overrides.yaml @@ -11,5 +11,7 @@ dependency_overrides: path: ../../sqflite sentry_file: path: ../../file + sentry_hive: + path: ../../hive sentry_drift: path: ../../drift diff --git a/hive/.gitignore b/hive/.gitignore new file mode 100644 index 0000000000..ba521d5a39 --- /dev/null +++ b/hive/.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/hive/CHANGELOG.md b/hive/CHANGELOG.md new file mode 120000 index 0000000000..04c99a55ca --- /dev/null +++ b/hive/CHANGELOG.md @@ -0,0 +1 @@ +../CHANGELOG.md \ No newline at end of file diff --git a/hive/LICENSE b/hive/LICENSE new file mode 100644 index 0000000000..2a6964d84d --- /dev/null +++ b/hive/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/hive/README.md b/hive/README.md new file mode 100644 index 0000000000..593d367100 --- /dev/null +++ b/hive/README.md @@ -0,0 +1,85 @@ +

+ + + +
+

+ +Sentry integration for `hive` package +=========== + +| package | build | pub | likes | popularity | pub points | +|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------| ------- | +| sentry_hive | [![build](https://github.com/getsentry/sentry-dart/workflows/sentry-hive/badge.svg?branch=main)](https://github.com/getsentry/sentry-dart/actions?query=workflow%3Asentry-hive) | [![pub package](https://img.shields.io/pub/v/sentry_hive.svg)](https://pub.dev/packages/sentry_hive) | [![likes](https://img.shields.io/pub/likes/sentry_hive)](https://pub.dev/packages/sentry_hive/score) | [![popularity](https://img.shields.io/pub/popularity/sentry_hive)](https://pub.dev/packages/sentry_hive/score) | [![pub points](https://img.shields.io/pub/points/sentry_hive)](https://pub.dev/packages/sentry_hive/score) + +Integration for the [`hive`](https://pub.dev/packages/hive) package. + +#### Usage + +- 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:sentry/sentry.dart'; +import 'package:hive/hive.dart'; +import 'package:sentry_hive/sentry_hive.dart'; +import 'package:path_provider/path_provider.dart'; + +Future main() async { + await SentryFlutter.init( + (options) { + options.dsn = 'https://example@sentry.io/add-your-dsn-here'; + options.tracesSampleRate = 1.0; + }, + appRunner: () => runApp(YourApp()), + ); +} + +Future insertUser() async { + // Use [SentryHive] where you would use [Hive] + final appDir = await getApplicationDocumentsDirectory(); + SentryHive + ..init(appDir.path) + ..registerAdapter(PersonAdapter()); + + var box = await SentryHive.openBox('testBox'); + + var person = Person( + name: 'Dave', + age: 22, + ); + + await box.put('dave', person); + + print(box.get('dave')); // Dave: 22 +} + +@HiveType(typeId: 1) +class Person { + Person({required this.name, required this.age}); + + @HiveField(0) + String name; + + @HiveField(1) + int age; + + @override + String toString() { + return '$name: $age'; + } +} +``` + +#### 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/hive/analysis_options.yaml b/hive/analysis_options.yaml new file mode 100644 index 0000000000..28ddbe2636 --- /dev/null +++ b/hive/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/hive/boxes_bad_keys.hive b/hive/boxes_bad_keys.hive new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hive/dartdoc_options.yaml b/hive/dartdoc_options.yaml new file mode 120000 index 0000000000..7cbb8c0d74 --- /dev/null +++ b/hive/dartdoc_options.yaml @@ -0,0 +1 @@ +../dart/dartdoc_options.yaml \ No newline at end of file diff --git a/hive/example/example.dart b/hive/example/example.dart new file mode 100644 index 0000000000..b6d824e576 --- /dev/null +++ b/hive/example/example.dart @@ -0,0 +1,54 @@ +import 'package:sentry/sentry.dart'; +import 'package:hive/hive.dart'; +import 'package:sentry_hive/sentry_hive.dart'; + +part 'main.g.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 { + // Use [SentryHive] where you would use [Hive] + SentryHive + ..init(Directory.current.path) + ..registerAdapter(PersonAdapter()); + + var box = await SentryHive.openBox('testBox'); + + var person = Person( + name: 'Dave', + age: 22, + ); + + await box.put('dave', person); + + print(box.get('dave')); // Dave: 22 +} + +@HiveType(typeId: 1) +class Person { + Person({required this.name, required this.age}); + + @HiveField(0) + String name; + + @HiveField(1) + int age; + + @override + String toString() { + return '$name: $age'; + } +} diff --git a/hive/lib/sentry_hive.dart b/hive/lib/sentry_hive.dart new file mode 100644 index 0000000000..03be720ca1 --- /dev/null +++ b/hive/lib/sentry_hive.dart @@ -0,0 +1,15 @@ +library sentry_hive; + +import 'package:meta/meta.dart'; +import 'package:hive/hive.dart'; +import 'src/sentry_hive_impl.dart'; +import 'src/sentry_hive_interface.dart'; + +export 'src/sentry_hive_interface.dart'; +export 'src/sentry_box_collection.dart'; + +/// Use [SentryHive] constant instead of [Hive] to get automatic performance +/// monitoring. +@experimental +// ignore: non_constant_identifier_names +SentryHiveInterface SentryHive = SentryHiveImpl(Hive); diff --git a/hive/lib/src/default_compaction_strategy.dart b/hive/lib/src/default_compaction_strategy.dart new file mode 100644 index 0000000000..0bcd0ecdb7 --- /dev/null +++ b/hive/lib/src/default_compaction_strategy.dart @@ -0,0 +1,9 @@ +const _deletedRatio = 0.15; +const _deletedThreshold = 60; + +/// Default compaction strategy compacts if 15% of total values and at least 60 +/// values have been deleted +bool defaultCompactionStrategy(int entries, int deletedEntries) { + return deletedEntries > _deletedThreshold && + deletedEntries / entries > _deletedRatio; +} diff --git a/hive/lib/src/default_key_comparator.dart b/hive/lib/src/default_key_comparator.dart new file mode 100644 index 0000000000..d7833ff536 --- /dev/null +++ b/hive/lib/src/default_key_comparator.dart @@ -0,0 +1,20 @@ +/// Efficient default implementation to compare keys +int defaultKeyComparator(dynamic k1, dynamic k2) { + if (k1 is int) { + if (k2 is int) { + if (k1 > k2) { + return 1; + } else if (k1 < k2) { + return -1; + } else { + return 0; + } + } else { + return -1; + } + } else if (k2 is String) { + return (k1 as String).compareTo(k2); + } else { + return 1; + } +} diff --git a/hive/lib/src/sentry_box.dart b/hive/lib/src/sentry_box.dart new file mode 100644 index 0000000000..f405307d43 --- /dev/null +++ b/hive/lib/src/sentry_box.dart @@ -0,0 +1,37 @@ +import 'package:meta/meta.dart'; +import 'package:hive/hive.dart'; +import 'package:sentry/sentry.dart'; + +import 'sentry_box_base.dart'; + +/// @nodoc +@internal +class SentryBox extends SentryBoxBase implements Box { + final Box _box; + + /// @nodoc + SentryBox(this._box, @internal Hub hub) : super(_box, hub); + + @override + E? get(key, {E? defaultValue}) { + return _box.get(key, defaultValue: defaultValue); + } + + @override + E? getAt(int index) { + return _box.getAt(index); + } + + @override + Map toMap() { + return _box.toMap(); + } + + @override + Iterable get values => _box.values; + + @override + Iterable valuesBetween({startKey, endKey}) { + return _box.valuesBetween(startKey: startKey, endKey: endKey); + } +} diff --git a/hive/lib/src/sentry_box_base.dart b/hive/lib/src/sentry_box_base.dart new file mode 100644 index 0000000000..6623fed5ad --- /dev/null +++ b/hive/lib/src/sentry_box_base.dart @@ -0,0 +1,203 @@ +import 'package:hive/hive.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; +import 'sentry_span_helper.dart'; + +/// @nodoc +@internal +class SentryBoxBase implements BoxBase { + final BoxBase _boxBase; + final Hub _hub; + + final _spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbHiveBoxBase, + ); + + /// @nodoc + SentryBoxBase(this._boxBase, this._hub) { + _spanHelper.setHub(this._hub); + } + + @override + Future add(E value) async { + return _spanHelper.asyncWrapInSpan( + 'add', + () { + return _boxBase.add(value); + }, + dbName: name, + ); + } + + @override + Future> addAll(Iterable values) { + return _spanHelper.asyncWrapInSpan( + 'addAll', + () { + return _boxBase.addAll(values); + }, + dbName: name, + ); + } + + @override + Future clear() { + return _spanHelper.asyncWrapInSpan( + 'clear', + () { + return _boxBase.clear(); + }, + dbName: name, + ); + } + + @override + Future close() { + return _spanHelper.asyncWrapInSpan( + 'close', + () { + return _boxBase.close(); + }, + dbName: name, + ); + } + + @override + Future compact() { + return _spanHelper.asyncWrapInSpan( + 'compact', + () { + return _boxBase.compact(); + }, + dbName: name, + ); + } + + @override + bool containsKey(key) { + return _boxBase.containsKey(key); + } + + @override + Future delete(key) { + return _spanHelper.asyncWrapInSpan( + 'delete', + () { + return _boxBase.delete(key); + }, + dbName: name, + ); + } + + @override + Future deleteAll(Iterable keys) { + return _spanHelper.asyncWrapInSpan( + 'deleteAll', + () { + return _boxBase.deleteAll(keys); + }, + dbName: name, + ); + } + + @override + Future deleteAt(int index) { + return _spanHelper.asyncWrapInSpan( + 'deleteAt', + () { + return _boxBase.deleteAt(index); + }, + dbName: name, + ); + } + + @override + Future deleteFromDisk() { + return _spanHelper.asyncWrapInSpan( + 'deleteFromDisk', + () { + return _boxBase.deleteFromDisk(); + }, + dbName: name, + ); + } + + @override + Future flush() { + return _spanHelper.asyncWrapInSpan( + 'flush', + () { + return _boxBase.flush(); + }, + dbName: name, + ); + } + + @override + bool get isEmpty => _boxBase.isEmpty; + + @override + bool get isNotEmpty => _boxBase.isNotEmpty; + + @override + bool get isOpen => _boxBase.isOpen; + + @override + keyAt(int index) { + return _boxBase.keyAt(index); + } + + @override + Iterable get keys => _boxBase.keys; + + @override + bool get lazy => _boxBase.lazy; + + @override + int get length => _boxBase.length; + + @override + String get name => _boxBase.name; + + @override + String? get path => _boxBase.path; + + @override + Future put(key, value) { + return _spanHelper.asyncWrapInSpan( + 'put', + () { + return _boxBase.put(key, value); + }, + dbName: name, + ); + } + + @override + Future putAll(Map entries) { + return _spanHelper.asyncWrapInSpan( + 'putAll', + () { + return _boxBase.putAll(entries); + }, + dbName: name, + ); + } + + @override + Future putAt(int index, value) { + return _spanHelper.asyncWrapInSpan( + 'putAt', + () { + return _boxBase.putAt(index, value); + }, + dbName: name, + ); + } + + @override + Stream watch({key}) { + return _boxBase.watch(key: key); + } +} diff --git a/hive/lib/src/sentry_box_collection.dart b/hive/lib/src/sentry_box_collection.dart new file mode 100644 index 0000000000..d4c605efe2 --- /dev/null +++ b/hive/lib/src/sentry_box_collection.dart @@ -0,0 +1,125 @@ +import 'package:hive/hive.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; + +import 'sentry_span_helper.dart'; + +// ignore: implementation_imports +import 'package:hive/src/box_collection/box_collection_stub.dart' as stub; + +// ignore: implementation_imports +import 'package:hive/src/box_collection/box_collection_stub.dart' + if (dart.library.html) 'package:hive/src/box_collection/box_collection_indexed_db.dart' + if (dart.library.io) 'package:hive/src/box_collection/box_collection.dart' + as impl; + +/// Use instead of [BoxCollection] to add automatic tracing. +class SentryBoxCollection implements stub.BoxCollection { + final impl.BoxCollection _boxCollection; + + final _spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbHiveBoxCollection, + ); + + /// Init with [impl.BoxCollection] + SentryBoxCollection(this._boxCollection); + + @override + Set get boxNames => _boxCollection.boxNames; + + @override + void close() { + _boxCollection.close(); + } + + /// @nodoc + @internal + void setHub(Hub hub) { + _spanHelper.setHub(hub); + } + + @override + Future deleteFromDisk() { + return _spanHelper.asyncWrapInSpan( + 'deleteFromDisk', + () { + return _boxCollection.deleteFromDisk(); + }, + dbName: name, + ); + } + + @override + String get name => _boxCollection.name; + + // ignore: public_member_api_docs + static Future open( + String name, + Set boxNames, { + String? path, + HiveCipher? key, + Hub? hub, + }) async { + final spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbHiveBoxCollection, + ); + spanHelper.setHub(hub ?? HubAdapter()); + + return await spanHelper.asyncWrapInSpan( + 'open', + () async { + final boxCollection = await impl.BoxCollection.open( + name, + boxNames, + path: path, + key: key, + ); + final sbc = SentryBoxCollection(boxCollection); + sbc.setHub(hub ?? HubAdapter()); + return sbc; + }, + dbName: name, + ); + } + + @override + Future> openBox( + String name, { + bool preload = false, + stub.CollectionBox Function(String p1, stub.BoxCollection p2)? + boxCreator, + }) { + return _spanHelper.asyncWrapInSpan( + 'openBox', + () { + return _boxCollection.openBox( + name, + preload: preload, + boxCreator: boxCreator, + ); + }, + dbName: this.name, + ); + } + + @override + Future transaction( + Future Function() action, { + List? boxNames, + bool readOnly = false, + }) async { + return await _spanHelper.asyncWrapInSpan( + 'transaction', + () { + return _boxCollection.transaction( + action, + boxNames: boxNames, + readOnly: readOnly, + ); + }, + dbName: name, + ); + } +} diff --git a/hive/lib/src/sentry_hive_impl.dart b/hive/lib/src/sentry_hive_impl.dart new file mode 100644 index 0000000000..4eccc87506 --- /dev/null +++ b/hive/lib/src/sentry_hive_impl.dart @@ -0,0 +1,203 @@ +import 'dart:typed_data'; + +import 'package:hive/hive.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; +import 'sentry_box.dart'; +import 'sentry_lazy_box.dart'; +import 'sentry_hive_interface.dart'; + +import 'default_compaction_strategy.dart'; +import 'default_key_comparator.dart'; +import 'sentry_span_helper.dart'; + +/// @nodoc +@internal +class SentryHiveImpl implements SentryHiveInterface { + @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 = 'flutter_hive'; + + @internal + // ignore: public_member_api_docs + static const dbNameKey = 'db.name'; + + final HiveInterface _hive; + Hub _hub = HubAdapter(); + + /// @nodoc + SentryHiveImpl(this._hive); + + final _spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbHive, + ); + + // SentryHiveInterface + + @override + void setHub(Hub hub) { + _hub = hub; + _spanHelper.setHub(hub); + } + + // HiveInterface + + @override + void init( + String? path, { + HiveStorageBackendPreference backendPreference = + HiveStorageBackendPreference.native, + }) { + return Hive.init(path, backendPreference: backendPreference); + } + + @override + Box box(String name) { + return _hive.box(name); + } + + @override + Future boxExists(String name, {String? path}) { + return _spanHelper.asyncWrapInSpan('boxExists', () { + return _hive.boxExists(name, path: path); + }); + } + + @override + Future close() { + return _spanHelper.asyncWrapInSpan('close', () { + return _hive.close(); + }); + } + + @override + Future deleteBoxFromDisk(String name, {String? path}) { + return _spanHelper.asyncWrapInSpan('deleteBoxFromDisk', () { + return _hive.deleteBoxFromDisk(name, path: path); + }); + } + + @override + Future deleteFromDisk() { + return _spanHelper.asyncWrapInSpan('deleteFromDisk', () { + return _hive.deleteFromDisk(); + }); + } + + @override + List generateSecureKey() { + return _hive.generateSecureKey(); + } + + @override + void ignoreTypeId(int typeId) { + return _hive.ignoreTypeId(typeId); + } + + @override + bool isAdapterRegistered(int typeId) { + return _hive.isAdapterRegistered(typeId); + } + + @override + bool isBoxOpen(String name) { + return _hive.isBoxOpen(name); + } + + @override + LazyBox lazyBox(String name) { + return _hive.lazyBox(name); + } + + @override + Future> openBox( + String name, { + HiveCipher? encryptionCipher, + KeyComparator keyComparator = defaultKeyComparator, + CompactionStrategy compactionStrategy = defaultCompactionStrategy, + bool crashRecovery = true, + String? path, + Uint8List? bytes, + String? collection, + @Deprecated('Use encryptionCipher instead') List? encryptionKey, + }) { + return _spanHelper.asyncWrapInSpan( + 'openBox', + () async { + final Box box = await _hive.openBox( + name, + encryptionCipher: encryptionCipher, + keyComparator: keyComparator, + compactionStrategy: compactionStrategy, + crashRecovery: crashRecovery, + path: path, + bytes: bytes, + collection: collection, + // ignore: deprecated_member_use + encryptionKey: encryptionKey, + ); + return SentryBox(box, _hub); + }, + dbName: name, + ); + } + + @override + Future> openLazyBox( + String name, { + HiveCipher? encryptionCipher, + KeyComparator keyComparator = defaultKeyComparator, + CompactionStrategy compactionStrategy = defaultCompactionStrategy, + bool crashRecovery = true, + String? path, + String? collection, + @Deprecated('Use encryptionCipher instead') List? encryptionKey, + }) { + return _spanHelper.asyncWrapInSpan( + 'openLazyBox', + () async { + final LazyBox lazyBox = await _hive.openLazyBox( + name, + encryptionCipher: encryptionCipher, + keyComparator: keyComparator, + compactionStrategy: compactionStrategy, + crashRecovery: crashRecovery, + path: path, + collection: collection, + // ignore: deprecated_member_use + encryptionKey: encryptionKey, + ); + return SentryLazyBox(lazyBox, _hub); + }, + dbName: name, + ); + } + + @override + void registerAdapter( + TypeAdapter adapter, { + bool internal = false, + bool override = false, + }) { + return _hive.registerAdapter( + adapter, + internal: internal, + override: override, + ); + } + + @visibleForTesting + @override + void resetAdapters() { + // ignore: invalid_use_of_visible_for_testing_member + return _hive.resetAdapters(); + } +} diff --git a/hive/lib/src/sentry_hive_interface.dart b/hive/lib/src/sentry_hive_interface.dart new file mode 100644 index 0000000000..18c76611e8 --- /dev/null +++ b/hive/lib/src/sentry_hive_interface.dart @@ -0,0 +1,11 @@ +import 'package:hive/hive.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; + +/// The main API interface of SentryHive. Available through the `SentryHive` +/// constant. +@experimental +abstract class SentryHiveInterface implements HiveInterface { + /// Set the Sentry [Hub] + void setHub(Hub hub); +} diff --git a/hive/lib/src/sentry_lazy_box.dart b/hive/lib/src/sentry_lazy_box.dart new file mode 100644 index 0000000000..86eabb9fdd --- /dev/null +++ b/hive/lib/src/sentry_lazy_box.dart @@ -0,0 +1,45 @@ +import 'package:hive/hive.dart'; +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; + +import 'sentry_box_base.dart'; +import 'sentry_span_helper.dart'; + +/// @nodoc +@internal +class SentryLazyBox extends SentryBoxBase implements LazyBox { + final LazyBox _lazyBox; + final Hub _hub; + + final _spanHelper = SentrySpanHelper( + // ignore: invalid_use_of_internal_member + SentryTraceOrigins.autoDbHiveLazyBox, + ); + + /// @nodoc + SentryLazyBox(this._lazyBox, @internal this._hub) : super(_lazyBox, _hub) { + _spanHelper.setHub(_hub); + } + + @override + Future get(key, {E? defaultValue}) { + return _spanHelper.asyncWrapInSpan( + 'get', + () { + return _lazyBox.get(key, defaultValue: defaultValue); + }, + dbName: name, + ); + } + + @override + Future getAt(int index) { + return _spanHelper.asyncWrapInSpan( + 'getAt', + () { + return _lazyBox.getAt(index); + }, + dbName: name, + ); + } +} diff --git a/hive/lib/src/sentry_span_helper.dart b/hive/lib/src/sentry_span_helper.dart new file mode 100644 index 0000000000..a1c780ef08 --- /dev/null +++ b/hive/lib/src/sentry_span_helper.dart @@ -0,0 +1,58 @@ +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; +import 'sentry_hive_impl.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, + }) async { + final currentSpan = _hub.getSpan(); + final span = currentSpan?.startChild( + SentryHiveImpl.dbOp, + description: description, + ); + + // ignore: invalid_use_of_internal_member + span?.origin = _origin; + + span?.setData(SentryHiveImpl.dbSystemKey, SentryHiveImpl.dbSystem); + + if (dbName != null) { + span?.setData(SentryHiveImpl.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(); + } + } +} diff --git a/hive/people-box-collection_bad_keys.hive b/hive/people-box-collection_bad_keys.hive new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hive/pubspec.yaml b/hive/pubspec.yaml new file mode 100644 index 0000000000..9e1e79f2b5 --- /dev/null +++ b/hive/pubspec.yaml @@ -0,0 +1,22 @@ +name: sentry_hive +description: An integration which adds support for performance tracing for the hive package. +version: 7.10.1 +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' + +dependencies: + sentry: 7.10.1 + hive: ^2.2.3 + meta: ^1.3.0 + +dev_dependencies: + lints: ^2.0.0 + test: ^1.21.0 + yaml: ^3.1.0 # needed for version match (code and pubspec) + mockito: ^5.1.0 + build_runner: ^2.4.2 + coverage: ^1.3.0 diff --git a/hive/pubspec_overrides.yaml b/hive/pubspec_overrides.yaml new file mode 100644 index 0000000000..16e71d16f0 --- /dev/null +++ b/hive/pubspec_overrides.yaml @@ -0,0 +1,3 @@ +dependency_overrides: + sentry: + path: ../dart diff --git a/hive/test/mocks/mocks.dart b/hive/test/mocks/mocks.dart new file mode 100644 index 0000000000..9b2db0e30d --- /dev/null +++ b/hive/test/mocks/mocks.dart @@ -0,0 +1,18 @@ +import 'package:hive/hive.dart'; +import 'package:mockito/annotations.dart'; +import 'package:sentry/sentry.dart'; + +import 'package:hive/src/box_collection/box_collection_stub.dart' + if (dart.library.html) 'package:hive/src/box_collection/box_collection_indexed_db.dart' + if (dart.library.io) 'package:hive/src/box_collection/box_collection.dart' + as impl; + +@GenerateMocks([ + Hub, + Box, + LazyBox, + HiveInterface, + // Edit generated code to make sure correct impl/stub class is used + impl.BoxCollection, +]) +void main() {} diff --git a/hive/test/mocks/mocks.mocks.dart b/hive/test/mocks/mocks.mocks.dart new file mode 100644 index 0000000000..6d1bb907d7 --- /dev/null +++ b/hive/test/mocks/mocks.mocks.dart @@ -0,0 +1,1307 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in sentry_hive/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 'dart:typed_data' as _i8; + +import 'package:hive/hive.dart' as _i3; +import 'package:hive/src/box/default_compaction_strategy.dart' as _i7; +import 'package:hive/src/box/default_key_comparator.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; + +import 'package:hive/src/box_collection/box_collection_stub.dart' as stub; + +// ignore: implementation_imports +import 'package:hive/src/box_collection/box_collection_stub.dart' + if (dart.library.html) 'package:hive/src/box_collection/box_collection_indexed_db.dart' + if (dart.library.io) 'package:hive/src/box_collection/box_collection.dart' + as impl; + +// 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 _FakeBox_5 extends _i1.SmartFake implements _i3.Box { + _FakeBox_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeLazyBox_6 extends _i1.SmartFake implements _i3.LazyBox { + _FakeLazyBox_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeCollectionBox_7 extends _i1.SmartFake + implements _i3.CollectionBox { + _FakeCollectionBox_7( + 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( + dynamic 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 [Box]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockBox extends _i1.Mock implements _i3.Box { + MockBox() { + _i1.throwOnMissingStub(this); + } + + @override + Iterable get values => (super.noSuchMethod( + Invocation.getter(#values), + returnValue: [], + ) as Iterable); + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: '', + ) as String); + + @override + bool get isOpen => (super.noSuchMethod( + Invocation.getter(#isOpen), + returnValue: false, + ) as bool); + + @override + bool get lazy => (super.noSuchMethod( + Invocation.getter(#lazy), + returnValue: false, + ) as bool); + + @override + Iterable get keys => (super.noSuchMethod( + Invocation.getter(#keys), + returnValue: [], + ) as Iterable); + + @override + int get length => (super.noSuchMethod( + Invocation.getter(#length), + returnValue: 0, + ) as int); + + @override + bool get isEmpty => (super.noSuchMethod( + Invocation.getter(#isEmpty), + returnValue: false, + ) as bool); + + @override + bool get isNotEmpty => (super.noSuchMethod( + Invocation.getter(#isNotEmpty), + returnValue: false, + ) as bool); + + @override + Iterable valuesBetween({ + dynamic startKey, + dynamic endKey, + }) => + (super.noSuchMethod( + Invocation.method( + #valuesBetween, + [], + { + #startKey: startKey, + #endKey: endKey, + }, + ), + returnValue: [], + ) as Iterable); + + @override + E? getAt(int? index) => (super.noSuchMethod(Invocation.method( + #getAt, + [index], + )) as E?); + + @override + Map toMap() => (super.noSuchMethod( + Invocation.method( + #toMap, + [], + ), + returnValue: {}, + ) as Map); + + @override + dynamic keyAt(int? index) => super.noSuchMethod(Invocation.method( + #keyAt, + [index], + )); + + @override + _i5.Stream<_i3.BoxEvent> watch({dynamic key}) => (super.noSuchMethod( + Invocation.method( + #watch, + [], + {#key: key}, + ), + returnValue: _i5.Stream<_i3.BoxEvent>.empty(), + ) as _i5.Stream<_i3.BoxEvent>); + + @override + bool containsKey(dynamic key) => (super.noSuchMethod( + Invocation.method( + #containsKey, + [key], + ), + returnValue: false, + ) as bool); + + @override + _i5.Future put( + dynamic key, + E? value, + ) => + (super.noSuchMethod( + Invocation.method( + #put, + [ + key, + value, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future putAt( + int? index, + E? value, + ) => + (super.noSuchMethod( + Invocation.method( + #putAt, + [ + index, + value, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future putAll(Map? entries) => (super.noSuchMethod( + Invocation.method( + #putAll, + [entries], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future add(E? value) => (super.noSuchMethod( + Invocation.method( + #add, + [value], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future> addAll(Iterable? values) => (super.noSuchMethod( + Invocation.method( + #addAll, + [values], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); + + @override + _i5.Future delete(dynamic key) => (super.noSuchMethod( + Invocation.method( + #delete, + [key], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future deleteAt(int? index) => (super.noSuchMethod( + Invocation.method( + #deleteAt, + [index], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future deleteAll(Iterable? keys) => (super.noSuchMethod( + Invocation.method( + #deleteAll, + [keys], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future compact() => (super.noSuchMethod( + Invocation.method( + #compact, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future clear() => (super.noSuchMethod( + Invocation.method( + #clear, + [], + ), + 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); + + @override + _i5.Future deleteFromDisk() => (super.noSuchMethod( + Invocation.method( + #deleteFromDisk, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future flush() => (super.noSuchMethod( + Invocation.method( + #flush, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} + +/// A class which mocks [LazyBox]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockLazyBox extends _i1.Mock implements _i3.LazyBox { + MockLazyBox() { + _i1.throwOnMissingStub(this); + } + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: '', + ) as String); + + @override + bool get isOpen => (super.noSuchMethod( + Invocation.getter(#isOpen), + returnValue: false, + ) as bool); + + @override + bool get lazy => (super.noSuchMethod( + Invocation.getter(#lazy), + returnValue: false, + ) as bool); + + @override + Iterable get keys => (super.noSuchMethod( + Invocation.getter(#keys), + returnValue: [], + ) as Iterable); + + @override + int get length => (super.noSuchMethod( + Invocation.getter(#length), + returnValue: 0, + ) as int); + + @override + bool get isEmpty => (super.noSuchMethod( + Invocation.getter(#isEmpty), + returnValue: false, + ) as bool); + + @override + bool get isNotEmpty => (super.noSuchMethod( + Invocation.getter(#isNotEmpty), + returnValue: false, + ) as bool); + + @override + _i5.Future get( + dynamic key, { + E? defaultValue, + }) => + (super.noSuchMethod( + Invocation.method( + #get, + [key], + {#defaultValue: defaultValue}, + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future getAt(int? index) => (super.noSuchMethod( + Invocation.method( + #getAt, + [index], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + + @override + dynamic keyAt(int? index) => super.noSuchMethod(Invocation.method( + #keyAt, + [index], + )); + + @override + _i5.Stream<_i3.BoxEvent> watch({dynamic key}) => (super.noSuchMethod( + Invocation.method( + #watch, + [], + {#key: key}, + ), + returnValue: _i5.Stream<_i3.BoxEvent>.empty(), + ) as _i5.Stream<_i3.BoxEvent>); + + @override + bool containsKey(dynamic key) => (super.noSuchMethod( + Invocation.method( + #containsKey, + [key], + ), + returnValue: false, + ) as bool); + + @override + _i5.Future put( + dynamic key, + E? value, + ) => + (super.noSuchMethod( + Invocation.method( + #put, + [ + key, + value, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future putAt( + int? index, + E? value, + ) => + (super.noSuchMethod( + Invocation.method( + #putAt, + [ + index, + value, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future putAll(Map? entries) => (super.noSuchMethod( + Invocation.method( + #putAll, + [entries], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future add(E? value) => (super.noSuchMethod( + Invocation.method( + #add, + [value], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + + @override + _i5.Future> addAll(Iterable? values) => (super.noSuchMethod( + Invocation.method( + #addAll, + [values], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); + + @override + _i5.Future delete(dynamic key) => (super.noSuchMethod( + Invocation.method( + #delete, + [key], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future deleteAt(int? index) => (super.noSuchMethod( + Invocation.method( + #deleteAt, + [index], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future deleteAll(Iterable? keys) => (super.noSuchMethod( + Invocation.method( + #deleteAll, + [keys], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future compact() => (super.noSuchMethod( + Invocation.method( + #compact, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future clear() => (super.noSuchMethod( + Invocation.method( + #clear, + [], + ), + 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); + + @override + _i5.Future deleteFromDisk() => (super.noSuchMethod( + Invocation.method( + #deleteFromDisk, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future flush() => (super.noSuchMethod( + Invocation.method( + #flush, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} + +/// A class which mocks [HiveInterface]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockHiveInterface extends _i1.Mock implements _i3.HiveInterface { + MockHiveInterface() { + _i1.throwOnMissingStub(this); + } + + @override + void init( + String? path, { + _i3.HiveStorageBackendPreference? backendPreference = + _i3.HiveStorageBackendPreference.native, + }) => + super.noSuchMethod( + Invocation.method( + #init, + [path], + {#backendPreference: backendPreference}, + ), + returnValueForMissingStub: null, + ); + + @override + _i5.Future<_i3.Box> openBox( + String? name, { + _i3.HiveCipher? encryptionCipher, + _i3.KeyComparator? keyComparator = _i6.defaultKeyComparator, + _i3.CompactionStrategy? compactionStrategy = _i7.defaultCompactionStrategy, + bool? crashRecovery = true, + String? path, + _i8.Uint8List? bytes, + String? collection, + List? encryptionKey, + }) => + (super.noSuchMethod( + Invocation.method( + #openBox, + [name], + { + #encryptionCipher: encryptionCipher, + #keyComparator: keyComparator, + #compactionStrategy: compactionStrategy, + #crashRecovery: crashRecovery, + #path: path, + #bytes: bytes, + #collection: collection, + #encryptionKey: encryptionKey, + }, + ), + returnValue: _i5.Future<_i3.Box>.value(_FakeBox_5( + this, + Invocation.method( + #openBox, + [name], + { + #encryptionCipher: encryptionCipher, + #keyComparator: keyComparator, + #compactionStrategy: compactionStrategy, + #crashRecovery: crashRecovery, + #path: path, + #bytes: bytes, + #collection: collection, + #encryptionKey: encryptionKey, + }, + ), + )), + ) as _i5.Future<_i3.Box>); + + @override + _i5.Future<_i3.LazyBox> openLazyBox( + String? name, { + _i3.HiveCipher? encryptionCipher, + _i3.KeyComparator? keyComparator = _i6.defaultKeyComparator, + _i3.CompactionStrategy? compactionStrategy = _i7.defaultCompactionStrategy, + bool? crashRecovery = true, + String? path, + String? collection, + List? encryptionKey, + }) => + (super.noSuchMethod( + Invocation.method( + #openLazyBox, + [name], + { + #encryptionCipher: encryptionCipher, + #keyComparator: keyComparator, + #compactionStrategy: compactionStrategy, + #crashRecovery: crashRecovery, + #path: path, + #collection: collection, + #encryptionKey: encryptionKey, + }, + ), + returnValue: _i5.Future<_i3.LazyBox>.value(_FakeLazyBox_6( + this, + Invocation.method( + #openLazyBox, + [name], + { + #encryptionCipher: encryptionCipher, + #keyComparator: keyComparator, + #compactionStrategy: compactionStrategy, + #crashRecovery: crashRecovery, + #path: path, + #collection: collection, + #encryptionKey: encryptionKey, + }, + ), + )), + ) as _i5.Future<_i3.LazyBox>); + + @override + _i3.Box box(String? name) => (super.noSuchMethod( + Invocation.method( + #box, + [name], + ), + returnValue: _FakeBox_5( + this, + Invocation.method( + #box, + [name], + ), + ), + ) as _i3.Box); + + @override + _i3.LazyBox lazyBox(String? name) => (super.noSuchMethod( + Invocation.method( + #lazyBox, + [name], + ), + returnValue: _FakeLazyBox_6( + this, + Invocation.method( + #lazyBox, + [name], + ), + ), + ) as _i3.LazyBox); + + @override + bool isBoxOpen(String? name) => (super.noSuchMethod( + Invocation.method( + #isBoxOpen, + [name], + ), + returnValue: false, + ) as bool); + + @override + _i5.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future deleteBoxFromDisk( + String? name, { + String? path, + }) => + (super.noSuchMethod( + Invocation.method( + #deleteBoxFromDisk, + [name], + {#path: path}, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future deleteFromDisk() => (super.noSuchMethod( + Invocation.method( + #deleteFromDisk, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + List generateSecureKey() => (super.noSuchMethod( + Invocation.method( + #generateSecureKey, + [], + ), + returnValue: [], + ) as List); + + @override + _i5.Future boxExists( + String? name, { + String? path, + }) => + (super.noSuchMethod( + Invocation.method( + #boxExists, + [name], + {#path: path}, + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + + @override + void resetAdapters() => super.noSuchMethod( + Invocation.method( + #resetAdapters, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void registerAdapter( + _i3.TypeAdapter? adapter, { + bool? internal = false, + bool? override = false, + }) => + super.noSuchMethod( + Invocation.method( + #registerAdapter, + [adapter], + { + #internal: internal, + #override: override, + }, + ), + returnValueForMissingStub: null, + ); + + @override + bool isAdapterRegistered(int? typeId) => (super.noSuchMethod( + Invocation.method( + #isAdapterRegistered, + [typeId], + ), + returnValue: false, + ) as bool); + + @override + void ignoreTypeId(int? typeId) => super.noSuchMethod( + Invocation.method( + #ignoreTypeId, + [typeId], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [BoxCollection]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockBoxCollection extends _i1.Mock implements impl.BoxCollection { + MockBoxCollection() { + _i1.throwOnMissingStub(this); + } + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: '', + ) as String); + + @override + Set get boxNames => (super.noSuchMethod( + Invocation.getter(#boxNames), + returnValue: {}, + ) as Set); + + @override + _i5.Future> openBox( + String? name, { + bool? preload = false, + stub.CollectionBox Function( + String, + impl.BoxCollection, + )? boxCreator, + }) => + (super.noSuchMethod( + Invocation.method( + #openBox, + [name], + { + #preload: preload, + #boxCreator: boxCreator, + }, + ), + returnValue: + _i5.Future>.value(_FakeCollectionBox_7( + this, + Invocation.method( + #openBox, + [name], + { + #preload: preload, + #boxCreator: boxCreator, + }, + ), + )), + ) as _i5.Future>); + + @override + _i5.Future transaction( + _i5.Future Function()? action, { + List? boxNames, + bool? readOnly = false, + }) => + (super.noSuchMethod( + Invocation.method( + #transaction, + [action], + { + #boxNames: boxNames, + #readOnly: readOnly, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + void close() => super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValueForMissingStub: null, + ); + + @override + _i5.Future deleteFromDisk() => (super.noSuchMethod( + Invocation.method( + #deleteFromDisk, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} diff --git a/hive/test/person.dart b/hive/test/person.dart new file mode 100644 index 0000000000..9e9e06e13e --- /dev/null +++ b/hive/test/person.dart @@ -0,0 +1,24 @@ +import 'package:hive/hive.dart'; + +@HiveType(typeId: 0) +class Person extends HiveObject { + @HiveField(0) + final String name; + + Person(this.name); +} + +class PersonAdapter extends TypeAdapter { + @override + final typeId = 0; + + @override + Person read(BinaryReader reader) { + return Person(reader.read() as String); + } + + @override + void write(BinaryWriter writer, Person obj) { + writer.write(obj.name); + } +} diff --git a/hive/test/sentry_box_base_test.dart b/hive/test/sentry_box_base_test.dart new file mode 100644 index 0000000000..3caf5e06e5 --- /dev/null +++ b/hive/test/sentry_box_base_test.dart @@ -0,0 +1,297 @@ +@TestOn('vm') + +import 'dart:io'; + +import 'package:hive/hive.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry_hive/src/sentry_box_base.dart'; +import 'package:sentry_hive/src/sentry_hive_impl.dart'; +import 'package:test/test.dart'; +import 'package:sentry/src/sentry_tracer.dart'; + +import 'mocks/mocks.mocks.dart'; +import 'person.dart'; + +void main() { + void verifySpan(String description, SentrySpan? span) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.ok()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHiveBoxBase); + expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + } + + void verifyErrorSpan( + String description, + Exception exception, + SentrySpan? span, + ) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.internalError()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHiveBoxBase); + expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + + expect(span?.throwable, exception); + } + + 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('add adds span', () async { + final sut = fixture.getSut(); + + await sut.add(Person('Joe Dirt')); + + verifySpan('add', fixture.getCreatedSpan()); + }); + + test('addAll adds span', () async { + final sut = fixture.getSut(); + + await sut.addAll([Person('Joe Dirt')]); + + verifySpan('addAll', fixture.getCreatedSpan()); + }); + + test('clear adds span', () async { + final sut = fixture.getSut(); + + await sut.clear(); + + verifySpan('clear', fixture.getCreatedSpan()); + }); + + test('close adds span', () async { + final sut = fixture.getSut(); + + await sut.close(); + + verifySpan('close', fixture.getCreatedSpan()); + }); + + test('compact adds span', () async { + final sut = fixture.getSut(); + + await sut.compact(); + + verifySpan('compact', fixture.getCreatedSpan()); + }); + + test('delete adds span', () async { + final sut = fixture.getSut(); + + await sut.delete('fixture-key'); + + verifySpan('delete', fixture.getCreatedSpan()); + }); + + test('deleteAll adds span', () async { + final sut = fixture.getSut(); + + await sut.deleteAll(['fixture-key']); + + verifySpan('deleteAll', fixture.getCreatedSpan()); + }); + + test('deleteAt adds span', () async { + final sut = fixture.getSut(); + + await sut.add(Person('Joe Dirt')); + await sut.deleteAt(0); + + verifySpan('deleteAt', fixture.getCreatedSpan()); + }); + }); + + group('adds error 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); + when(fixture.mockBox.name).thenReturn(Fixture.dbName); + }); + + tearDown(() async { + await fixture.tearDown(); + }); + + test('throwing add adds error span', () async { + when(fixture.mockBox.add(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.add(Person('Joe Dirt')); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('add', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing addAll adds error span', () async { + when(fixture.mockBox.addAll(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.addAll([Person('Joe Dirt')]); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('addAll', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing clear adds error span', () async { + when(fixture.mockBox.clear()).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.clear(); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('clear', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing close adds error span', () async { + when(fixture.mockBox.close()).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.close(); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('close', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing compact adds error span', () async { + when(fixture.mockBox.compact()).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.compact(); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('compact', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing delete adds error span', () async { + when(fixture.mockBox.delete(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.delete('fixture-key'); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('delete', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing deleteAll adds error span', () async { + when(fixture.mockBox.deleteAll(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + try { + await sut.deleteAll(['fixture-key']); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('deleteAll', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing deleteAt adds error span', () async { + when(fixture.mockBox.add(any)).thenAnswer((_) async { + return 1; + }); + when(fixture.mockBox.deleteAt(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + await sut.add(Person('Joe Dirt')); + try { + await sut.deleteAt(0); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('deleteAt', fixture.exception, fixture.getCreatedSpan()); + }); + }); +} + +class Fixture { + late final Box box; + late final mockBox = MockBox(); + final options = SentryOptions(); + final hub = MockHub(); + final exception = Exception('fixture-exception'); + + static final dbName = 'people-box-base'; + + final _context = SentryTransactionContext('name', 'operation'); + late final tracer = SentryTracer(_context, hub); + + Future setUp() async { + Hive.init(Directory.systemTemp.path); + if (!Hive.isAdapterRegistered(0)) { + Hive.registerAdapter(PersonAdapter()); + } + box = await Hive.openBox(dbName); + } + + Future tearDown() async { + if (box.isOpen) { + await box.deleteFromDisk(); + await box.close(); + } + await Hive.close(); + } + + SentryBoxBase getSut({bool injectMockBox = false}) { + if (injectMockBox) { + return SentryBoxBase(mockBox, hub); + } else { + return SentryBoxBase(box, hub); + } + } + + SentrySpan? getCreatedSpan() { + return tracer.children.last; + } +} diff --git a/hive/test/sentry_box_collection_test.dart b/hive/test/sentry_box_collection_test.dart new file mode 100644 index 0000000000..4965896611 --- /dev/null +++ b/hive/test/sentry_box_collection_test.dart @@ -0,0 +1,225 @@ +@TestOn('vm') + +import 'dart:io'; + +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry_hive/sentry_hive.dart'; +import 'package:sentry_hive/src/sentry_box_collection.dart'; +import 'package:sentry_hive/src/sentry_hive_impl.dart'; +import 'package:test/test.dart'; +import 'package:sentry/src/sentry_tracer.dart'; + +import 'mocks/mocks.mocks.dart'; +import 'person.dart'; + +import 'package:hive/src/box_collection/box_collection_stub.dart' as stub; + +void main() { + void verifySpan(String description, SentrySpan? span) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.ok()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHiveBoxCollection); + expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + } + + void verifyErrorSpan( + String description, + Exception exception, + SentrySpan? span, + ) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.internalError()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHiveBoxCollection); + expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + + expect(span?.throwable, exception); + } + + group('adds span when calling', () { + 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', () async { + await SentryBoxCollection.open( + Fixture.dbName, + {'people'}, + hub: fixture.hub, + ); + + final span = fixture.getCreatedSpan(); + verifySpan('open', span); + }); + + test('openBox', () async { + final sut = await fixture.getSut(); + + await sut.openBox('people'); + + final span = fixture.getCreatedSpan(); + verifySpan('openBox', span); + }); + + test('transaction', () async { + final sut = await fixture.getSut(); + + final people = await sut.openBox('people'); + await sut.transaction( + () async { + print(people.name); + }, + boxNames: ['people'], + ); + final span = fixture.getCreatedSpan(); + verifySpan('transaction', span); + }); + + test('deleteFromDisk', () async { + final sut = await fixture.getSut(); + + await sut.deleteFromDisk(); + + final span = fixture.getCreatedSpan(); + verifySpan('deleteFromDisk', span); + }); + }); + + group('adds error span when calling', () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + await fixture.setUp(); + + when(fixture.hub.options).thenReturn(fixture.options); + when(fixture.hub.getSpan()).thenReturn(fixture.tracer); + when(fixture.mockBoxCollection.name).thenReturn(Fixture.dbName); + }); + + tearDown(() async { + await fixture.tearDown(); + }); + + // open is static and cannot be mocked + + test('throwing openBox', () async { + when( + // ignore: inference_failure_on_function_invocation + fixture.mockBoxCollection.openBox( + any, + preload: anyNamed('preload'), + boxCreator: anyNamed('boxCreator'), + ), + ).thenThrow(fixture.exception); + + final sut = await fixture.getSut(injectMock: true); + + try { + // ignore: inference_failure_on_function_invocation + await sut.openBox('people'); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('openBox', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing transaction', () async { + when( + fixture.mockBoxCollection.transaction( + any, + boxNames: anyNamed('boxNames'), + readOnly: anyNamed('readOnly'), + ), + ).thenThrow(fixture.exception); + + final sut = await fixture.getSut(injectMock: true); + + try { + await sut.transaction(() async {}); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan( + 'transaction', + fixture.exception, + fixture.getCreatedSpan(), + ); + }); + + test('throwing deleteFromDisk', () async { + when(fixture.mockBoxCollection.deleteFromDisk()) + .thenThrow(fixture.exception); + + final sut = await fixture.getSut(injectMock: true); + + try { + await sut.deleteFromDisk(); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan( + 'deleteFromDisk', + fixture.exception, + fixture.getCreatedSpan(), + ); + }); + }); +} + +class Fixture { + final options = SentryOptions(); + final hub = MockHub(); + final exception = Exception('fixture-exception'); + + late final mockBoxCollection = MockBoxCollection(); + + static final dbName = 'people-box-collection'; + + final _context = SentryTransactionContext('name', 'operation'); + late final tracer = SentryTracer(_context, hub); + + Future setUp() async { + SentryHive.init(Directory.systemTemp.path); + if (!SentryHive.isAdapterRegistered(0)) { + SentryHive.registerAdapter(PersonAdapter()); + } + } + + Future tearDown() async { + await SentryHive.close(); + } + + Future getSut({bool injectMock = false}) async { + if (injectMock) { + final sbc = SentryBoxCollection(mockBoxCollection); + sbc.setHub(hub); + return sbc; + } else { + return await SentryBoxCollection.open(dbName, {'people'}, hub: hub); + } + } + + SentrySpan? getCreatedSpan() { + return tracer.children.last; + } +} diff --git a/hive/test/sentry_hive_impl_test.dart b/hive/test/sentry_hive_impl_test.dart new file mode 100644 index 0000000000..5bca3b75db --- /dev/null +++ b/hive/test/sentry_hive_impl_test.dart @@ -0,0 +1,310 @@ +@TestOn('vm') + +import 'dart:io'; + +import 'package:hive/hive.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry/src/sentry_tracer.dart'; +import 'package:sentry_hive/src/sentry_box.dart'; +import 'package:sentry_hive/src/sentry_hive_impl.dart'; +import 'package:sentry_hive/src/sentry_lazy_box.dart'; +import 'package:test/test.dart'; + +import 'mocks/mocks.mocks.dart'; +import 'person.dart'; + +void main() { + void verifySpan( + String description, + SentrySpan? span, { + bool checkName = false, + }) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.ok()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHive); + // expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + if (checkName) { + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + } + } + + void verifyErrorSpan(String description, SentrySpan? span, Exception error) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.internalError()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHive); + expect(span?.throwable, error); + } + + 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('boxExists adds span', () async { + final sut = fixture.getSut(); + + await sut.openBox(Fixture.dbName); + await sut.boxExists(Fixture.dbName); + + verifySpan('boxExists', fixture.getCreatedSpan()); + }); + + test('close adds span', () async { + final sut = fixture.getSut(); + + await sut.close(); + + verifySpan('close', fixture.getCreatedSpan()); + }); + + test('deleteBoxFromDisk adds span', () async { + final sut = fixture.getSut(); + + await sut.openBox(Fixture.dbName); + await sut.deleteBoxFromDisk(Fixture.dbName); + + verifySpan('deleteBoxFromDisk', fixture.getCreatedSpan()); + }); + + test('deleteFromDisk adds span', () async { + final sut = fixture.getSut(); + + await sut.deleteFromDisk(); + + verifySpan('deleteFromDisk', fixture.getCreatedSpan()); + }); + + test('openBox adds span', () async { + final sut = fixture.getSut(); + + final box = await sut.openBox(Fixture.dbName); + + expect(box is SentryBox, true); + verifySpan('openBox', fixture.getCreatedSpan(), checkName: true); + }); + + test('openLazyBox adds span', () async { + final sut = fixture.getSut(); + + final box = await sut.openLazyBox(Fixture.dbName); + + expect(box is SentryLazyBox, true); + verifySpan('openLazyBox', fixture.getCreatedSpan(), checkName: true); + }); + }); + + group('adds error span', () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + await fixture.setUp(injectMockHive: true); + + when(fixture.hub.options).thenReturn(fixture.options); + when(fixture.hub.getSpan()).thenReturn(fixture.tracer); + when(fixture.mockHive.close()).thenAnswer((_) async => {}); + }); + + test('throwing boxExists adds error span', () async { + final Box box = MockBox(); + when( + fixture.mockHive.openBox( + any, + encryptionCipher: anyNamed('encryptionCipher'), + keyComparator: anyNamed('keyComparator'), + compactionStrategy: anyNamed('compactionStrategy'), + crashRecovery: anyNamed('crashRecovery'), + path: anyNamed('path'), + bytes: anyNamed('bytes'), + collection: anyNamed('collection'), + encryptionKey: anyNamed('encryptionKey'), + ), + ).thenAnswer((_) => Future(() => box)); + when(fixture.mockHive.boxExists(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(); + + await sut.openBox(Fixture.dbName); + try { + await sut.boxExists(Fixture.dbName); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('boxExists', fixture.getCreatedSpan(), fixture.exception); + }); + + test('throwing close adds error span', () async { + when(fixture.mockHive.close()).thenThrow(fixture.exception); + + final sut = fixture.getSut(); + + try { + await sut.close(); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('close', fixture.getCreatedSpan(), fixture.exception); + }); + + test('throwing deleteBoxFromDisk adds error span', () async { + final Box box = MockBox(); + when( + fixture.mockHive.openBox( + any, + encryptionCipher: anyNamed('encryptionCipher'), + keyComparator: anyNamed('keyComparator'), + compactionStrategy: anyNamed('compactionStrategy'), + crashRecovery: anyNamed('crashRecovery'), + path: anyNamed('path'), + bytes: anyNamed('bytes'), + collection: anyNamed('collection'), + encryptionKey: anyNamed('encryptionKey'), + ), + ).thenAnswer((_) => Future(() => box)); + when(fixture.mockHive.deleteBoxFromDisk(any)) + .thenThrow(fixture.exception); + + final sut = fixture.getSut(); + + await sut.openBox(Fixture.dbName); + try { + await sut.deleteBoxFromDisk(Fixture.dbName); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan( + 'deleteBoxFromDisk', + fixture.getCreatedSpan(), + fixture.exception, + ); + }); + + test('throwing deleteFromDisk adds error span', () async { + when(fixture.mockHive.deleteFromDisk()).thenThrow(fixture.exception); + + final sut = fixture.getSut(); + + try { + await sut.deleteFromDisk(); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan( + 'deleteFromDisk', + fixture.getCreatedSpan(), + fixture.exception, + ); + }); + + test('throwing openBox adds error span', () async { + when( + fixture.mockHive.openBox( + any, + encryptionCipher: anyNamed('encryptionCipher'), + keyComparator: anyNamed('keyComparator'), + compactionStrategy: anyNamed('compactionStrategy'), + crashRecovery: anyNamed('crashRecovery'), + path: anyNamed('path'), + bytes: anyNamed('bytes'), + collection: anyNamed('collection'), + encryptionKey: anyNamed('encryptionKey'), + ), + ).thenThrow(fixture.exception); + + final sut = fixture.getSut(); + + try { + await sut.openBox(Fixture.dbName); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('openBox', fixture.getCreatedSpan(), fixture.exception); + }); + + test('throwing openLazyBox adds error span', () async { + when( + fixture.mockHive.openLazyBox( + any, + encryptionCipher: anyNamed('encryptionCipher'), + keyComparator: anyNamed('keyComparator'), + compactionStrategy: anyNamed('compactionStrategy'), + crashRecovery: anyNamed('crashRecovery'), + path: anyNamed('path'), + collection: anyNamed('collection'), + encryptionKey: anyNamed('encryptionKey'), + ), + ).thenThrow(fixture.exception); + + final sut = fixture.getSut(); + + try { + await sut.openLazyBox(Fixture.dbName); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan( + 'openLazyBox', + fixture.getCreatedSpan(), + fixture.exception, + ); + }); + }); +} + +class Fixture { + final options = SentryOptions(); + late final mockHive = MockHiveInterface(); + final hub = MockHub(); + static final dbName = 'people-hive-impl'; + final exception = Exception('fixture-exception'); + + final _context = SentryTransactionContext('name', 'operation'); + late final tracer = SentryTracer(_context, hub); + late SentryHiveImpl sut; + + Future setUp({bool injectMockHive = false}) async { + if (injectMockHive) { + sut = SentryHiveImpl(mockHive); + } else { + sut = SentryHiveImpl(Hive); + sut.init(Directory.systemTemp.path); + if (!sut.isAdapterRegistered(0)) { + sut.registerAdapter(PersonAdapter()); + } + } + sut.setHub(hub); + } + + Future tearDown() async { + await sut.close(); + } + + SentryHiveImpl getSut() { + return sut; + } + + SentrySpan? getCreatedSpan() { + return tracer.children.last; + } +} diff --git a/hive/test/sentry_lazy_box_test.dart b/hive/test/sentry_lazy_box_test.dart new file mode 100644 index 0000000000..263658af8d --- /dev/null +++ b/hive/test/sentry_lazy_box_test.dart @@ -0,0 +1,170 @@ +@TestOn('vm') + +import 'dart:io'; + +import 'package:hive/hive.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry/src/sentry_tracer.dart'; +import 'package:sentry_hive/src/sentry_hive_impl.dart'; +import 'package:sentry_hive/src/sentry_lazy_box.dart'; +import 'package:test/test.dart'; + +import 'mocks/mocks.mocks.dart'; +import 'person.dart'; + +void main() { + void verifySpan(String description, SentrySpan? span) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.ok()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHiveLazyBox); + expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + } + + void verifyErrorSpan( + String description, + Exception exception, + SentrySpan? span, + ) { + expect(span?.context.operation, SentryHiveImpl.dbOp); + expect(span?.context.description, description); + expect(span?.status, SpanStatus.internalError()); + // ignore: invalid_use_of_internal_member + expect(span?.origin, SentryTraceOrigins.autoDbHiveLazyBox); + expect(span?.data[SentryHiveImpl.dbSystemKey], SentryHiveImpl.dbSystem); + expect(span?.data[SentryHiveImpl.dbNameKey], Fixture.dbName); + + expect(span?.throwable, exception); + } + + 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('get adds span', () async { + final sut = fixture.getSut(); + + await sut.put('fixture-key', Person('John Malkovich')); + await sut.get('fixture-key'); + + verifySpan('get', fixture.getCreatedSpan()); + }); + + test('getAt adds span', () async { + final sut = fixture.getSut(); + + await sut.add(Person('John Malkovich')); + await sut.getAt(0); + + verifySpan('getAt', fixture.getCreatedSpan()); + }); + }); + + group('adds error 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); + when(fixture.mockBox.name).thenReturn(Fixture.dbName); + }); + + tearDown(() async { + await fixture.tearDown(); + }); + + test('throwing get adds error span', () async { + when(fixture.mockBox.add(any)).thenAnswer((_) async { + return 1; + }); + when(fixture.mockBox.get(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + await sut.put('fixture-key', Person('John Malkovich')); + try { + await sut.get('fixture-key'); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('get', fixture.exception, fixture.getCreatedSpan()); + }); + + test('throwing getAt adds error span', () async { + when(fixture.mockBox.add(any)).thenAnswer((_) async { + return 1; + }); + when(fixture.mockBox.getAt(any)).thenThrow(fixture.exception); + + final sut = fixture.getSut(injectMockBox: true); + + await sut.add(Person('John Malkovich')); + try { + await sut.getAt(0); + } catch (error) { + expect(error, fixture.exception); + } + + verifyErrorSpan('getAt', fixture.exception, fixture.getCreatedSpan()); + }); + }); +} + +class Fixture { + late final LazyBox box; + late final mockBox = MockLazyBox(); + final options = SentryOptions(); + final hub = MockHub(); + final exception = Exception('fixture-exception'); + + static final dbName = 'people-lazy-box'; + + final _context = SentryTransactionContext('name', 'operation'); + late final tracer = SentryTracer(_context, hub); + + Future setUp() async { + Hive.init(Directory.systemTemp.path); + if (!Hive.isAdapterRegistered(0)) { + Hive.registerAdapter(PersonAdapter()); + } + box = await Hive.openLazyBox(dbName); + } + + Future tearDown() async { + if (box.isOpen) { + await box.deleteFromDisk(); + await box.close(); + } + await Hive.close(); + } + + SentryLazyBox getSut({bool injectMockBox = false}) { + if (injectMockBox) { + return SentryLazyBox(mockBox, hub); + } else { + return SentryLazyBox(box, hub); + } + } + + SentrySpan? getCreatedSpan() { + return tracer.children.last; + } +}