From c4657894b32a640d1eb1d024ee1366957ff6d450 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:39:11 -0700 Subject: [PATCH 01/10] refactor(kinesis): extract shared record cache into amplify_record_cache_dart Create amplify_record_cache_dart package with shared caching infrastructure: - RecordCacheException hierarchy (const constructors) - Record/RecordInput models (partitionKey optional, dataSize caller-computed) - RecordStorage base + SqliteRecordStorage, InMemoryRecordStorage, IndexedDbRecordStorage - RecordCacheDatabase (Drift, parameterized dbPrefix) - Sender interface + SendResult (replaces KDS-specific PutRecordsResult) - RecordClient, AutoFlushScheduler, FlushStrategy, FlushData, RecordData, ClearCacheData - Platform resolution (VM/web/stub conditional exports) Update amplify_kinesis_dart to depend on shared package: - KinesisSender implements Sender interface (sendBatch replaces putRecords) - Partition key validation moved from RecordStorage to AmplifyKinesisClient - createKinesisRecordInputNow computes dataSize with partition key - All test imports updated, zero behavioral change --- .github/dependabot.yaml | 796 ++++++++++++++++-- .github/workflows/amplify_firehose_dart.yaml | 83 ++ .github/workflows/amplify_kinesis.yaml | 4 + .github/workflows/amplify_kinesis_dart.yaml | 4 + .../workflows/amplify_kinesis_example.yaml | 4 + .../workflows/amplify_record_cache_dart.yaml | 75 ++ .../lib/amplify_kinesis_dart.dart | 16 +- .../lib/src/amplify_kinesis_client.dart | 82 +- .../src/amplify_kinesis_client_options.dart | 2 +- .../exception/amplify_kinesis_exception.dart | 2 +- .../lib/src/impl/kinesis_record.dart | 75 +- .../lib/src/impl/kinesis_sender.dart | 53 +- .../kinesis/amplify_kinesis_dart/pubspec.yaml | 10 +- .../test/amplify_kinesis_exception_test.dart | 6 +- .../test/auto_flush_scheduler_test.dart | 4 +- .../test/common/mocktail_mocks.dart | 16 +- .../test/helpers/test_database.dart | 6 +- .../test/in_memory_record_storage_test.dart | 28 +- .../test/kinesis_data_streams_test.dart | 11 +- .../test/kinesis_sender_test.dart | 6 +- .../record_client_concurrent_flush_test.dart | 29 +- .../test/record_client_test.dart | 146 ++-- .../test/record_validation_test.dart | 129 +-- ...te_record_storage_cache_accuracy_test.dart | 44 +- .../analysis_options.yaml | 5 + .../lib/amplify_record_cache_dart.dart | 29 + .../lib/src/client}/auto_flush_scheduler.dart | 6 +- .../lib/src/client}/record_client.dart | 31 +- .../lib/src/db/record_cache_database.dart} | 52 +- .../lib/src/db/record_cache_database.g.dart} | 155 ++-- .../src/exception/record_cache_exception.dart | 21 +- .../src/flush_strategy/flush_strategy.dart | 12 +- .../lib/src/model/clear_cache_data.dart | 4 +- .../lib/src/model/flush_data.dart | 7 +- .../lib/src/model/record.dart | 22 +- .../lib/src/model/record_data.dart | 4 +- .../lib/src/model/record_input.dart | 54 ++ .../lib/src/sender/sender.dart | 45 + .../platform/record_storage_platform.dart | 0 .../record_storage_platform_stub.dart | 7 +- .../platform/record_storage_platform_vm.dart | 17 +- .../platform/record_storage_platform_web.dart | 23 +- .../lib/src}/storage/record_storage.dart | 66 +- .../storage/record_storage_indexeddb.dart | 66 +- .../src}/storage/record_storage_memory.dart | 19 +- .../src}/storage/record_storage_sqlite.dart | 82 +- .../amplify_record_cache_dart/pubspec.yaml | 33 + 47 files changed, 1683 insertions(+), 708 deletions(-) create mode 100644 .github/workflows/amplify_firehose_dart.yaml create mode 100644 .github/workflows/amplify_record_cache_dart.yaml create mode 100644 packages/kinesis/amplify_record_cache_dart/analysis_options.yaml create mode 100644 packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src/client}/auto_flush_scheduler.dart (91%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src/client}/record_client.dart (83%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/db/kinesis_record_database.dart => amplify_record_cache_dart/lib/src/db/record_cache_database.dart} (52%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/db/kinesis_record_database.g.dart => amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart} (83%) rename packages/kinesis/{amplify_kinesis_dart => amplify_record_cache_dart}/lib/src/exception/record_cache_exception.dart (73%) rename packages/kinesis/{amplify_kinesis_dart => amplify_record_cache_dart}/lib/src/flush_strategy/flush_strategy.dart (71%) rename packages/kinesis/{amplify_kinesis_dart => amplify_record_cache_dart}/lib/src/model/clear_cache_data.dart (80%) rename packages/kinesis/{amplify_kinesis_dart => amplify_record_cache_dart}/lib/src/model/flush_data.dart (71%) rename packages/kinesis/{amplify_kinesis_dart => amplify_record_cache_dart}/lib/src/model/record.dart (68%) rename packages/kinesis/{amplify_kinesis_dart => amplify_record_cache_dart}/lib/src/model/record_data.dart (73%) create mode 100644 packages/kinesis/amplify_record_cache_dart/lib/src/model/record_input.dart create mode 100644 packages/kinesis/amplify_record_cache_dart/lib/src/sender/sender.dart rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/platform/record_storage_platform.dart (100%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/platform/record_storage_platform_stub.dart (73%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/platform/record_storage_platform_vm.dart (58%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/platform/record_storage_platform_web.dart (59%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/record_storage.dart (67%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/record_storage_indexeddb.dart (77%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/record_storage_memory.dart (84%) rename packages/kinesis/{amplify_kinesis_dart/lib/src/impl => amplify_record_cache_dart/lib/src}/storage/record_storage_sqlite.dart (60%) create mode 100644 packages/kinesis/amplify_record_cache_dart/pubspec.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index fe14b7e0da0..6a10a6f5671 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1985,6 +1985,40 @@ updates: test: patterns: - "test" + - package-ecosystem: "pub" + directory: "packages/amplify/amplify_flutter/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_api" + - dependency-name: "amplify_api_dart" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_datastore" + - dependency-name: "amplify_datastore_plugin_interface" + - dependency-name: "amplify_storage_s3" + - dependency-name: "amplify_storage_s3_dart" - package-ecosystem: "pub" directory: "packages/amplify_core/doc" schedule: @@ -2027,6 +2061,39 @@ updates: test: patterns: - "test" + - package-ecosystem: "pub" + directory: "packages/amplify_datastore/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_api" + - dependency-name: "amplify_api_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "amplify_datastore" + - dependency-name: "amplify_datastore_plugin_interface" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_authenticator" - package-ecosystem: "pub" directory: "packages/amplify_foundation/amplify_foundation_dart" schedule: @@ -2074,6 +2141,184 @@ updates: test: patterns: - "test" + - package-ecosystem: "pub" + directory: "packages/amplify_native_legacy_wrapper/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_native_legacy_wrapper" + - dependency-name: "amplify_lints" + - dependency-name: "aws_common" + - package-ecosystem: "pub" + directory: "packages/analytics/amplify_analytics_pinpoint/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_api" + - dependency-name: "amplify_api_dart" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + build_runner: + patterns: + - "build_runner" + built_value: + patterns: + - "built_value" + json_annotation: + patterns: + - "json_annotation" + json_serializable: + patterns: + - "json_serializable" + - package-ecosystem: "pub" + directory: "packages/api/amplify_api/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_api" + - dependency-name: "amplify_api_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_authenticator" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + async: + patterns: + - "async" + - package-ecosystem: "pub" + directory: "packages/auth/amplify_auth_cognito/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_api" + - dependency-name: "amplify_api_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_native_legacy_wrapper" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + async: + patterns: + - "async" + http: + patterns: + - "http" + stack_trace: + patterns: + - "stack_trace" + test: + patterns: + - "test" + - package-ecosystem: "pub" + directory: "packages/auth/amplify_auth_cognito_dart/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "smithy_codegen" + - dependency-name: "example_common" + - dependency-name: "amplify_api_dart" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" + mime: + patterns: + - "mime" + test: + patterns: + - "test" - package-ecosystem: "pub" directory: "packages/auth/amplify_auth_cognito_test" schedule: @@ -2084,41 +2329,309 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + async: + patterns: + - "async" + built_value: + patterns: + - "built_value" + http: + patterns: + - "http" + test: + patterns: + - "test" + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" + - package-ecosystem: "pub" + directory: "packages/authenticator/amplify_authenticator/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_api" + - dependency-name: "amplify_api_dart" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + uuid: + patterns: + - "uuid" + - package-ecosystem: "pub" + directory: "packages/authenticator/amplify_authenticator_test" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_flutter" + - package-ecosystem: "pub" + directory: "packages/authenticator/amplify_authenticator_test/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_flutter" + - package-ecosystem: "pub" + directory: "packages/aws_signature_v4/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" + - package-ecosystem: "pub" + directory: "packages/common/amplify_db_common/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + drift: + patterns: + - "drift" + drift_dev: + patterns: + - "drift_dev" + build_runner: + patterns: + - "build_runner" + - package-ecosystem: "pub" + directory: "packages/common/amplify_db_common_dart/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "example_common" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + drift: + patterns: + - "drift" + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" + drift_dev: + patterns: + - "drift_dev" + - package-ecosystem: "pub" + directory: "packages/example_common" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_lints" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + test: + patterns: + - "test" + - package-ecosystem: "pub" + directory: "packages/example_common/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "example_common" + - dependency-name: "amplify_lints" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" + - package-ecosystem: "pub" + directory: "packages/kinesis/amplify_firehose_dart" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + built_value: + patterns: + - "built_value" + drift: + patterns: + - "drift" + build_runner: + patterns: + - "build_runner" + built_value_generator: + patterns: + - "built_value_generator" + drift_dev: + patterns: + - "drift_dev" + test: + patterns: + - "test" + - package-ecosystem: "pub" + directory: "packages/kinesis/amplify_kinesis" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_lints" + - dependency-name: "amplify_kinesis_dart" - dependency-name: "amplify_core" - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "amplify_record_cache_dart" - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - dependency-name: "smithy" - dependency-name: "smithy_aws" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - async: - patterns: - - "async" - built_value: - patterns: - - "built_value" - http: - patterns: - - "http" - test: - patterns: - - "test" - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - package-ecosystem: "pub" - directory: "packages/authenticator/amplify_authenticator_test" + directory: "packages/kinesis/amplify_kinesis/example" schedule: interval: "daily" ignore: @@ -2127,7 +2640,6 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_authenticator" - dependency-name: "amplify_auth_cognito" - dependency-name: "amplify_analytics_pinpoint" - dependency-name: "amplify_analytics_pinpoint_dart" @@ -2146,8 +2658,14 @@ updates: - dependency-name: "amplify_auth_cognito_dart" - dependency-name: "smithy_codegen" - dependency-name: "amplify_flutter" + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "amplify_kinesis" + - dependency-name: "amplify_kinesis_dart" + - dependency-name: "amplify_record_cache_dart" - package-ecosystem: "pub" - directory: "packages/authenticator/amplify_authenticator_test/example" + directory: "packages/kinesis/amplify_kinesis/example" schedule: interval: "daily" ignore: @@ -2156,7 +2674,6 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_authenticator" - dependency-name: "amplify_auth_cognito" - dependency-name: "amplify_analytics_pinpoint" - dependency-name: "amplify_analytics_pinpoint_dart" @@ -2175,8 +2692,14 @@ updates: - dependency-name: "amplify_auth_cognito_dart" - dependency-name: "smithy_codegen" - dependency-name: "amplify_flutter" + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "amplify_kinesis" + - dependency-name: "amplify_kinesis_dart" + - dependency-name: "amplify_record_cache_dart" - package-ecosystem: "pub" - directory: "packages/example_common" + directory: "packages/kinesis/amplify_kinesis_dart" schedule: interval: "daily" ignore: @@ -2185,14 +2708,29 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages + - dependency-name: "amplify_core" + - dependency-name: "aws_common" - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "amplify_record_cache_dart" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" # Group dependencies which have a constraint set in the global "pubspec.yaml" groups: + built_value: + patterns: + - "built_value" + drift: + patterns: + - "drift" test: patterns: - "test" - package-ecosystem: "pub" - directory: "packages/example_common/example" + directory: "packages/kinesis/amplify_record_cache_dart" schedule: interval: "daily" ignore: @@ -2201,18 +2739,29 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "example_common" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "smithy" # Group dependencies which have a constraint set in the global "pubspec.yaml" groups: + drift: + patterns: + - "drift" build_runner: patterns: - "build_runner" - build_web_compilers: + drift_dev: patterns: - - "build_web_compilers" + - "drift_dev" + test: + patterns: + - "test" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis" + directory: "packages/notifications/push/amplify_push_notifications/example" schedule: interval: "daily" ignore: @@ -2221,18 +2770,17 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_lints" - - dependency-name: "amplify_kinesis_dart" + - dependency-name: "amplify_push_notifications" - dependency-name: "amplify_core" - dependency-name: "aws_common" + - dependency-name: "amplify_lints" - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis/example" + directory: "packages/notifications/push/amplify_push_notifications_pinpoint/example" schedule: interval: "daily" ignore: @@ -2259,13 +2807,10 @@ updates: - dependency-name: "amplify_auth_cognito_dart" - dependency-name: "smithy_codegen" - dependency-name: "amplify_flutter" - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "amplify_kinesis" - - dependency-name: "amplify_kinesis_dart" + - dependency-name: "amplify_push_notifications_pinpoint" + - dependency-name: "amplify_push_notifications" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis_dart" + directory: "packages/secure_storage/amplify_secure_storage/example" schedule: interval: "daily" ignore: @@ -2274,35 +2819,36 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_core" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_secure_storage_dart" - dependency-name: "aws_common" - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - package-ecosystem: "pub" + directory: "packages/secure_storage/amplify_secure_storage_dart/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "example_common" # Group dependencies which have a constraint set in the global "pubspec.yaml" groups: - built_value: - patterns: - - "built_value" - drift: - patterns: - - "drift" build_runner: patterns: - "build_runner" - built_value_generator: - patterns: - - "built_value_generator" - drift_dev: - patterns: - - "drift_dev" - test: + build_web_compilers: patterns: - - "test" + - "build_web_compilers" - package-ecosystem: "pub" directory: "packages/secure_storage/amplify_secure_storage_test" schedule: @@ -2879,6 +3425,84 @@ updates: xml: patterns: - "xml" + - package-ecosystem: "pub" + directory: "packages/storage/amplify_storage_s3/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_auth_cognito" + - dependency-name: "amplify_analytics_pinpoint" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "amplify_db_common" + - dependency-name: "amplify_secure_storage" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_flutter" + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_storage_s3" + - dependency-name: "amplify_storage_s3_dart" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + drift: + patterns: + - "drift" + http: + patterns: + - "http" + stack_trace: + patterns: + - "stack_trace" + test: + patterns: + - "test" + - package-ecosystem: "pub" + directory: "packages/storage/amplify_storage_s3_dart/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "amplify_analytics_pinpoint_dart" + - dependency-name: "amplify_core" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" + - dependency-name: "smithy_codegen" + - dependency-name: "amplify_storage_s3_dart" + - dependency-name: "example_common" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" - package-ecosystem: "pub" directory: "packages/test/amplify_auth_integration_test" schedule: @@ -3057,6 +3681,34 @@ updates: test: patterns: - "test" + - package-ecosystem: "pub" + directory: "packages/worker_bee/worker_bee/example" + schedule: + interval: "daily" + ignore: + # Ignore patch version bumps + - dependency-name: "*" + update-types: + - "version-update:semver-patch" + # Ignore all repo packages + - dependency-name: "worker_bee" + - dependency-name: "aws_common" + - dependency-name: "amplify_lints" + - dependency-name: "worker_bee_builder" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + built_value: + patterns: + - "built_value" + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" + built_value_generator: + patterns: + - "built_value_generator" - package-ecosystem: "pub" directory: "templates/dart-package/hooks" schedule: diff --git a/.github/workflows/amplify_firehose_dart.yaml b/.github/workflows/amplify_firehose_dart.yaml new file mode 100644 index 00000000000..74614db994a --- /dev/null +++ b/.github/workflows/amplify_firehose_dart.yaml @@ -0,0 +1,83 @@ +# Generated with aft. To update, run: `aft generate workflows` +name: amplify_firehose_dart +on: + push: + branches: + - main + - stable + paths: + - '.github/workflows/amplify_firehose_dart.yaml' + - '.github/workflows/dart_vm.yaml' + - 'packages/amplify_core/lib/**/*.dart' + - 'packages/amplify_core/pubspec.yaml' + - 'packages/amplify_foundation/amplify_foundation_dart/lib/**/*.dart' + - 'packages/amplify_foundation/amplify_foundation_dart/pubspec.yaml' + - 'packages/amplify_foundation/amplify_foundation_dart_bridge/lib/**/*.dart' + - 'packages/amplify_foundation/amplify_foundation_dart_bridge/pubspec.yaml' + - 'packages/amplify_lints/lib/**/*.yaml' + - 'packages/amplify_lints/pubspec.yaml' + - 'packages/aws_common/lib/**/*.dart' + - 'packages/aws_common/pubspec.yaml' + - 'packages/aws_signature_v4/lib/**/*.dart' + - 'packages/aws_signature_v4/pubspec.yaml' + - 'packages/common/amplify_db_common_dart/lib/**/*.dart' + - 'packages/common/amplify_db_common_dart/pubspec.yaml' + - 'packages/kinesis/amplify_firehose_dart/**/*.dart' + - 'packages/kinesis/amplify_firehose_dart/**/*.yaml' + - 'packages/kinesis/amplify_firehose_dart/lib/**/*' + - 'packages/kinesis/amplify_firehose_dart/test/**/*' + - 'packages/smithy/smithy/lib/**/*.dart' + - 'packages/smithy/smithy/pubspec.yaml' + - 'packages/smithy/smithy_aws/lib/**/*.dart' + - 'packages/smithy/smithy_aws/pubspec.yaml' + pull_request: + paths: + - '.github/workflows/amplify_firehose_dart.yaml' + - '.github/workflows/dart_vm.yaml' + - 'packages/amplify_core/lib/**/*.dart' + - 'packages/amplify_core/pubspec.yaml' + - 'packages/amplify_foundation/amplify_foundation_dart/lib/**/*.dart' + - 'packages/amplify_foundation/amplify_foundation_dart/pubspec.yaml' + - 'packages/amplify_foundation/amplify_foundation_dart_bridge/lib/**/*.dart' + - 'packages/amplify_foundation/amplify_foundation_dart_bridge/pubspec.yaml' + - 'packages/amplify_lints/lib/**/*.yaml' + - 'packages/amplify_lints/pubspec.yaml' + - 'packages/aws_common/lib/**/*.dart' + - 'packages/aws_common/pubspec.yaml' + - 'packages/aws_signature_v4/lib/**/*.dart' + - 'packages/aws_signature_v4/pubspec.yaml' + - 'packages/common/amplify_db_common_dart/lib/**/*.dart' + - 'packages/common/amplify_db_common_dart/pubspec.yaml' + - 'packages/kinesis/amplify_firehose_dart/**/*.dart' + - 'packages/kinesis/amplify_firehose_dart/**/*.yaml' + - 'packages/kinesis/amplify_firehose_dart/lib/**/*' + - 'packages/kinesis/amplify_firehose_dart/test/**/*' + - 'packages/smithy/smithy/lib/**/*.dart' + - 'packages/smithy/smithy/pubspec.yaml' + - 'packages/smithy/smithy_aws/lib/**/*.dart' + - 'packages/smithy/smithy_aws/pubspec.yaml' + schedule: + - cron: "0 13 * * 1" # Every Monday at 06:00 PST + workflow_dispatch: +defaults: + run: + shell: bash + +# These permissions are needed to interact with GitHub's OIDC Token endpoint. +permissions: + id-token: write + contents: read + +# Cancels in-progress job when there is another push to same ref. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-only-cancel-in-progress-jobs-or-runs-for-the-current-workflow +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + uses: ./.github/workflows/dart_vm.yaml + secrets: inherit + with: + package-name: amplify_firehose_dart + working-directory: packages/kinesis/amplify_firehose_dart diff --git a/.github/workflows/amplify_kinesis.yaml b/.github/workflows/amplify_kinesis.yaml index 3c71abc57cb..d27eb6e2b14 100644 --- a/.github/workflows/amplify_kinesis.yaml +++ b/.github/workflows/amplify_kinesis.yaml @@ -28,6 +28,8 @@ on: - 'packages/kinesis/amplify_kinesis/test/**/*' - 'packages/kinesis/amplify_kinesis_dart/lib/**/*.dart' - 'packages/kinesis/amplify_kinesis_dart/pubspec.yaml' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/pubspec.yaml' - 'packages/smithy/smithy/lib/**/*.dart' - 'packages/smithy/smithy/pubspec.yaml' - 'packages/smithy/smithy_aws/lib/**/*.dart' @@ -56,6 +58,8 @@ on: - 'packages/kinesis/amplify_kinesis/test/**/*' - 'packages/kinesis/amplify_kinesis_dart/lib/**/*.dart' - 'packages/kinesis/amplify_kinesis_dart/pubspec.yaml' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/pubspec.yaml' - 'packages/smithy/smithy/lib/**/*.dart' - 'packages/smithy/smithy/pubspec.yaml' - 'packages/smithy/smithy_aws/lib/**/*.dart' diff --git a/.github/workflows/amplify_kinesis_dart.yaml b/.github/workflows/amplify_kinesis_dart.yaml index baaa6e452e2..bb65205e7ad 100644 --- a/.github/workflows/amplify_kinesis_dart.yaml +++ b/.github/workflows/amplify_kinesis_dart.yaml @@ -27,6 +27,8 @@ on: - 'packages/kinesis/amplify_kinesis_dart/**/*.yaml' - 'packages/kinesis/amplify_kinesis_dart/lib/**/*' - 'packages/kinesis/amplify_kinesis_dart/test/**/*' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/pubspec.yaml' - 'packages/smithy/smithy/lib/**/*.dart' - 'packages/smithy/smithy/pubspec.yaml' - 'packages/smithy/smithy_aws/lib/**/*.dart' @@ -54,6 +56,8 @@ on: - 'packages/kinesis/amplify_kinesis_dart/**/*.yaml' - 'packages/kinesis/amplify_kinesis_dart/lib/**/*' - 'packages/kinesis/amplify_kinesis_dart/test/**/*' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/pubspec.yaml' - 'packages/smithy/smithy/lib/**/*.dart' - 'packages/smithy/smithy/pubspec.yaml' - 'packages/smithy/smithy_aws/lib/**/*.dart' diff --git a/.github/workflows/amplify_kinesis_example.yaml b/.github/workflows/amplify_kinesis_example.yaml index 160ac0b1731..54da2af128c 100644 --- a/.github/workflows/amplify_kinesis_example.yaml +++ b/.github/workflows/amplify_kinesis_example.yaml @@ -57,6 +57,8 @@ on: - 'packages/kinesis/amplify_kinesis/pubspec.yaml' - 'packages/kinesis/amplify_kinesis_dart/lib/**/*.dart' - 'packages/kinesis/amplify_kinesis_dart/pubspec.yaml' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/pubspec.yaml' - 'packages/secure_storage/amplify_secure_storage/android/**/*' - 'packages/secure_storage/amplify_secure_storage/ios/**/*' - 'packages/secure_storage/amplify_secure_storage/lib/**/*.dart' @@ -127,6 +129,8 @@ on: - 'packages/kinesis/amplify_kinesis/pubspec.yaml' - 'packages/kinesis/amplify_kinesis_dart/lib/**/*.dart' - 'packages/kinesis/amplify_kinesis_dart/pubspec.yaml' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/pubspec.yaml' - 'packages/secure_storage/amplify_secure_storage/android/**/*' - 'packages/secure_storage/amplify_secure_storage/ios/**/*' - 'packages/secure_storage/amplify_secure_storage/lib/**/*.dart' diff --git a/.github/workflows/amplify_record_cache_dart.yaml b/.github/workflows/amplify_record_cache_dart.yaml new file mode 100644 index 00000000000..b60dcf6b9a8 --- /dev/null +++ b/.github/workflows/amplify_record_cache_dart.yaml @@ -0,0 +1,75 @@ +# Generated with aft. To update, run: `aft generate workflows` +name: amplify_record_cache_dart +on: + push: + branches: + - main + - stable + paths: + - '.github/workflows/amplify_record_cache_dart.yaml' + - '.github/workflows/dart_vm.yaml' + - 'packages/amplify_core/lib/**/*.dart' + - 'packages/amplify_core/pubspec.yaml' + - 'packages/amplify_foundation/amplify_foundation_dart/lib/**/*.dart' + - 'packages/amplify_foundation/amplify_foundation_dart/pubspec.yaml' + - 'packages/amplify_lints/lib/**/*.yaml' + - 'packages/amplify_lints/pubspec.yaml' + - 'packages/aws_common/lib/**/*.dart' + - 'packages/aws_common/pubspec.yaml' + - 'packages/aws_signature_v4/lib/**/*.dart' + - 'packages/aws_signature_v4/pubspec.yaml' + - 'packages/common/amplify_db_common_dart/lib/**/*.dart' + - 'packages/common/amplify_db_common_dart/pubspec.yaml' + - 'packages/kinesis/amplify_record_cache_dart/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/**/*.yaml' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*' + - 'packages/kinesis/amplify_record_cache_dart/test/**/*' + - 'packages/smithy/smithy/lib/**/*.dart' + - 'packages/smithy/smithy/pubspec.yaml' + pull_request: + paths: + - '.github/workflows/amplify_record_cache_dart.yaml' + - '.github/workflows/dart_vm.yaml' + - 'packages/amplify_core/lib/**/*.dart' + - 'packages/amplify_core/pubspec.yaml' + - 'packages/amplify_foundation/amplify_foundation_dart/lib/**/*.dart' + - 'packages/amplify_foundation/amplify_foundation_dart/pubspec.yaml' + - 'packages/amplify_lints/lib/**/*.yaml' + - 'packages/amplify_lints/pubspec.yaml' + - 'packages/aws_common/lib/**/*.dart' + - 'packages/aws_common/pubspec.yaml' + - 'packages/aws_signature_v4/lib/**/*.dart' + - 'packages/aws_signature_v4/pubspec.yaml' + - 'packages/common/amplify_db_common_dart/lib/**/*.dart' + - 'packages/common/amplify_db_common_dart/pubspec.yaml' + - 'packages/kinesis/amplify_record_cache_dart/**/*.dart' + - 'packages/kinesis/amplify_record_cache_dart/**/*.yaml' + - 'packages/kinesis/amplify_record_cache_dart/lib/**/*' + - 'packages/kinesis/amplify_record_cache_dart/test/**/*' + - 'packages/smithy/smithy/lib/**/*.dart' + - 'packages/smithy/smithy/pubspec.yaml' + schedule: + - cron: "0 13 * * 1" # Every Monday at 06:00 PST + workflow_dispatch: +defaults: + run: + shell: bash + +# These permissions are needed to interact with GitHub's OIDC Token endpoint. +permissions: + id-token: write + contents: read + +# Cancels in-progress job when there is another push to same ref. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-only-cancel-in-progress-jobs-or-runs-for-the-current-workflow +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + uses: ./.github/workflows/dart_vm.yaml + secrets: inherit + with: + package-name: amplify_record_cache_dart + working-directory: packages/kinesis/amplify_record_cache_dart diff --git a/packages/kinesis/amplify_kinesis_dart/lib/amplify_kinesis_dart.dart b/packages/kinesis/amplify_kinesis_dart/lib/amplify_kinesis_dart.dart index 761db258e44..ced24275aa0 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/amplify_kinesis_dart.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/amplify_kinesis_dart.dart @@ -4,18 +4,22 @@ /// Amplify Kinesis Data Streams client for Dart. library; +// Re-export shared types used in the public API +export 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart' + show + FlushStrategy, + FlushInterval, + FlushNone, + FlushData, + RecordData, + ClearCacheData; + // Main client export 'src/amplify_kinesis_client.dart'; // Options export 'src/amplify_kinesis_client_options.dart'; // Exceptions export 'src/exception/amplify_kinesis_exception.dart'; -// Flush strategies -export 'src/flush_strategy/flush_strategy.dart'; -// Return types -export 'src/model/clear_cache_data.dart'; -export 'src/model/flush_data.dart'; -export 'src/model/record_data.dart'; // SDK client (for escape hatch) export 'src/sdk/kinesis.dart' show diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart index 638edab1483..d3fca0268f5 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart @@ -12,17 +12,12 @@ import 'package:amplify_foundation_dart/amplify_foundation_dart.dart' import 'package:amplify_foundation_dart_bridge/amplify_foundation_dart_bridge.dart'; import 'package:amplify_kinesis_dart/src/amplify_kinesis_client_options.dart'; import 'package:amplify_kinesis_dart/src/exception/amplify_kinesis_exception.dart'; -import 'package:amplify_kinesis_dart/src/flush_strategy/flush_strategy.dart'; -import 'package:amplify_kinesis_dart/src/impl/auto_flush_scheduler.dart'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/platform/record_storage_platform.dart'; -import 'package:amplify_kinesis_dart/src/model/clear_cache_data.dart'; -import 'package:amplify_kinesis_dart/src/model/flush_data.dart'; -import 'package:amplify_kinesis_dart/src/model/record_data.dart'; +import 'package:amplify_kinesis_dart/src/kinesis_limits.dart' as limits; import 'package:amplify_kinesis_dart/src/sdk/kinesis.dart'; import 'package:amplify_kinesis_dart/src/version.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:smithy/smithy.dart' show WithUserAgent; /// User agent component identifying this library. @@ -96,12 +91,6 @@ class AmplifyKinesisClient { _logger = AmplifyLogging.logger('AmplifyKinesisClient'); /// {@macro amplify_kinesis.amplify_kinesis_client} - /// - /// [storagePath] is the directory path for the database file on IO - /// platforms. On web, pass `null` (the path is unused; IndexedDB storage - /// is used instead, with an in-memory fallback). - /// The [region] is used as the database identifier to namespace - /// the database file (e.g. `kinesis_records_us-east-1`). static Future create({ required String region, required AWSCredentialsProvider credentialsProvider, @@ -114,6 +103,11 @@ class AmplifyKinesisClient { identifier: region, storagePath: storagePath, maxCacheBytes: opts.cacheMaxBytes, + maxRecordsPerBatch: limits.maxRecordsPerStream, + maxBytesPerBatch: limits.maxPutRecordsSizeBytes, + maxRecordSizeBytes: limits.maxRecordSizeBytes, + dbPrefix: 'kinesis_records', + storeName: 'kinesis_records', ); final kinesisClient = KinesisClient( @@ -170,11 +164,6 @@ class AmplifyKinesisClient { bool get isClosed => _closed; /// Direct access to the underlying Kinesis SDK client. - /// - /// Use this for advanced operations not covered by this client's API. - /// - /// Note: This getter is only available when the client was created with - /// [create] (not [AmplifyKinesisClient.withRecordClient]). KinesisClient get kinesisClient { final client = _kinesisClient; if (client == null) { @@ -188,16 +177,10 @@ class AmplifyKinesisClient { /// Records data to be sent to a Kinesis Data Stream. /// - /// The record is persisted to local storage and will be sent during - /// the next flush operation (automatic or manual). - /// /// Returns [Result.ok] with [RecordData] on success, or [Result.error] with: - /// - [KinesisValidationException] for invalid input (e.g. oversized record, - /// empty or too-long partition key) + /// - [KinesisValidationException] for invalid input /// - [KinesisLimitExceededException] if the cache is full /// - [KinesisStorageException] for database errors - /// - /// Returns [Result.ok] silently if the client is disabled. Future> record({ required Uint8List data, required String partitionKey, @@ -208,8 +191,21 @@ class AmplifyKinesisClient { _logger.debug('Record collection is disabled, dropping record'); return const Result.ok(RecordData()); } + // KDS-specific partition key validation + final codePoints = partitionKey.runes.length; + if (codePoints == 0 || codePoints > limits.maxPartitionKeyLength) { + return Result.error( + KinesisValidationException( + 'Partition key length ($codePoints) is outside the allowed ' + 'range of 1-${limits.maxPartitionKeyLength} characters.', + recoverySuggestion: + 'Use a partition key between 1 and ' + '${limits.maxPartitionKeyLength} characters.', + ), + ); + } _logger.verbose('Recording to stream: $streamName'); - final kinesisRecord = RecordInput.now( + final kinesisRecord = createKinesisRecordInputNow( data: data, partitionKey: partitionKey, streamName: streamName, @@ -218,27 +214,6 @@ class AmplifyKinesisClient { } /// Flushes cached records to their respective Kinesis streams. - /// - /// Each invocation sends at most one batch per stream, limited by the Kinesis - /// `PutRecords` constraints (up to 500 records or 5 MB per stream). If the - /// cache contains more records than a single batch can hold, the remaining - /// records are sent on subsequent flush invocations — either manually or via - /// the auto-flush scheduler. - /// - /// Records that fail within a batch are marked for retry on the next flush. - /// Records that exceed [AmplifyKinesisClientOptions.maxRetries] are removed - /// from the cache. - /// - /// SDK Kinesis errors (throttling, invalid stream, etc.) are logged and - /// skipped so other streams can still flush. Non-SDK errors (e.g. network, - /// storage) abort the flush and are returned as [Result.error]. - /// - /// If a flush is already in progress, the call returns immediately with - /// `FlushData(recordsFlushed: 0, flushInProgress: true)`. - /// - /// Manual flushes are allowed even when the client is disabled, so that - /// callers can drain cached records without re-enabling collection. - /// Only the automatic flush scheduler is paused when disabled. Future> flush() async { if (_closed) return const Result.error(ClientClosedException()); _logger.verbose('Starting flush'); @@ -246,10 +221,6 @@ class AmplifyKinesisClient { } /// Clears all cached records from local storage. - /// - /// Returns [Result.ok] with [ClearCacheData] containing the count of - /// records cleared, or [Result.error] with: - /// - [KinesisStorageException] for database errors Future> clearCache() async { if (_closed) return const Result.error(ClientClosedException()); _logger.verbose('Clearing cache'); @@ -264,10 +235,6 @@ class AmplifyKinesisClient { } /// Disables record collection and automatic flushing. - /// - /// Records submitted while disabled are silently dropped. Already-cached - /// records remain in storage and will be sent on the next flush after - /// re-enabling. void disable() { _logger.info('Disabling record collection and automatic flushing'); _enabled = false; @@ -275,17 +242,12 @@ class AmplifyKinesisClient { } /// Closes the client and releases all resources. - /// - /// The client cannot be reused after closing. Future close() async { _closed = true; _scheduler?.stop(); await _recordClient.close(); } - /// Wraps an async operation, catching any exceptions and returning them - /// as [Result.error] with the appropriate [AmplifyKinesisException] - /// subtype. Future> _wrapError(Future Function() operation) async { try { final value = await operation(); diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client_options.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client_options.dart index c2b896be47f..82b66d15045 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client_options.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client_options.dart @@ -3,7 +3,7 @@ import 'package:amplify_kinesis_dart/src/amplify_kinesis_client.dart' show AmplifyKinesisClient; -import 'package:amplify_kinesis_dart/src/flush_strategy/flush_strategy.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; /// {@template amplify_kinesis.amplify_kinesis_client_options} /// Configuration options for [AmplifyKinesisClient]. diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart index 815649bca50..5cbea1a8613 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:amplify_core/amplify_core.dart'; -import 'package:amplify_kinesis_dart/src/exception/record_cache_exception.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; /// Default recovery suggestion for errors. const String defaultRecoverySuggestion = diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_record.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_record.dart index e1bb025c2fe..91a560ec50e 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_record.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_record.dart @@ -4,46 +4,39 @@ import 'dart:convert'; import 'dart:typed_data'; -/// Internal representation of a record to be sent to Kinesis. -final class RecordInput { - /// Creates a new Kinesis record. - RecordInput({ - required this.data, - required this.partitionKey, - required this.streamName, - required this.createdAt, - }) : dataSize = data.length + utf8.encode(partitionKey).length; - - /// Creates a Kinesis record with the current timestamp. - factory RecordInput.now({ - required Uint8List data, - required String partitionKey, - required String streamName, - }) { - return RecordInput( - data: data, - partitionKey: partitionKey, - streamName: streamName, - createdAt: DateTime.now(), - ); - } - - /// The data blob to send to Kinesis. - final Uint8List data; - - /// The partition key for the record. - final String partitionKey; - - /// The name of the Kinesis Data Stream. - final String streamName; - - /// The size of the record in bytes (data blob + partition key). - /// - /// Per AWS docs, the record size limit applies to the total size of the - /// partition key and data blob combined. Computed once at construction - /// to avoid repeated UTF-8 encoding of the partition key. - final int dataSize; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; + +/// Creates a [RecordInput] for Kinesis Data Streams. +/// +/// Unlike the generic `RecordInput`, this factory computes `dataSize` +/// as `data.length + utf8.encode(partitionKey).length` per the KDS +/// PutRecords API spec. +RecordInput createKinesisRecordInput({ + required Uint8List data, + required String partitionKey, + required String streamName, + required DateTime createdAt, +}) { + return RecordInput( + data: data, + streamName: streamName, + partitionKey: partitionKey, + dataSize: data.length + utf8.encode(partitionKey).length, + createdAt: createdAt, + ); +} - /// Timestamp of when the record was created. - final DateTime createdAt; +/// Creates a [RecordInput] for Kinesis Data Streams with the current +/// timestamp. +RecordInput createKinesisRecordInputNow({ + required Uint8List data, + required String partitionKey, + required String streamName, +}) { + return createKinesisRecordInput( + data: data, + partitionKey: partitionKey, + streamName: streamName, + createdAt: DateTime.now(), + ); } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart index a33eefb3fa1..580be391149 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart @@ -1,34 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'package:amplify_kinesis_dart/src/model/record.dart'; import 'package:amplify_kinesis_dart/src/sdk/kinesis.dart'; - -/// Result of a PutRecords operation. -/// -/// Records are categorized into three buckets: -/// - [successfulIds]: records that were accepted by Kinesis. -/// - [retryableIds]: records that failed with any error code but have not -/// yet exceeded the retry limit. These will be retried in the next flush. -/// - [failedIds]: records that have exceeded the retry limit and should be -/// deleted from the cache. -final class PutRecordsResult { - /// Creates a new [PutRecordsResult]. - const PutRecordsResult({ - required this.successfulIds, - required this.retryableIds, - required this.failedIds, - }); - - /// IDs of records that were successfully sent. - final List successfulIds; - - /// IDs of records that failed but can be retried (retry count < max). - final List retryableIds; - - /// IDs of records that exceeded the retry limit and should be deleted. - final List failedIds; -} +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; /// {@template amplify_kinesis.kinesis_sender} /// Handles communication with AWS Kinesis Data Streams. @@ -37,7 +11,7 @@ final class PutRecordsResult { /// categorization so that all error codes are treated as retryable /// until the record exceeds `maxRetries`. /// {@endtemplate} -class KinesisSender { +class KinesisSender implements Sender { /// {@macro amplify_kinesis.kinesis_sender} KinesisSender({required KinesisClient kinesisClient, required int maxRetries}) : _kinesisClient = kinesisClient, @@ -46,18 +20,13 @@ class KinesisSender { final KinesisClient _kinesisClient; final int _maxRetries; - /// Sends records to a Kinesis stream and categorizes the response. - /// - /// Each record in the response is categorized as: - /// - successful: no error code - /// - failed: has an error code AND retry count >= [_maxRetries] - /// - retryable: has an error code AND retry count < [_maxRetries] - Future putRecords({ + @override + Future sendBatch({ required String streamName, required List records, }) async { if (records.isEmpty) { - return const PutRecordsResult( + return const SendResult( successfulIds: [], retryableIds: [], failedIds: [], @@ -68,7 +37,7 @@ class KinesisSender { .map( (record) => PutRecordsRequestEntry( data: record.data, - partitionKey: record.partitionKey, + partitionKey: record.partitionKey ?? '', ), ) .toList(); @@ -84,10 +53,7 @@ class KinesisSender { /// Splits the PutRecords response into successful, retryable, and failed /// record IDs based on error codes and retry counts. - PutRecordsResult _splitResponse( - PutRecordsResponse response, - List records, - ) { + SendResult _splitResponse(PutRecordsResponse response, List records) { final successfulIds = []; final retryableIds = []; final failedIds = []; @@ -104,14 +70,11 @@ class KinesisSender { } else if (retryCount >= _maxRetries) { failedIds.add(recordId); } else { - // Error codes can be: ProvisionedThroughputExceededException or - // InternalFailure. All are treated as retryable until the retry - // limit is reached. retryableIds.add(recordId); } } - return PutRecordsResult( + return SendResult( successfulIds: successfulIds, retryableIds: retryableIds, failedIds: failedIds, diff --git a/packages/kinesis/amplify_kinesis_dart/pubspec.yaml b/packages/kinesis/amplify_kinesis_dart/pubspec.yaml index 69e6378bc7e..bb095c4b9de 100644 --- a/packages/kinesis/amplify_kinesis_dart/pubspec.yaml +++ b/packages/kinesis/amplify_kinesis_dart/pubspec.yaml @@ -17,26 +17,20 @@ environment: dependencies: amplify_core: ">=2.10.0 <2.11.0" - amplify_db_common_dart: ">=0.4.17 <0.5.0" amplify_foundation_dart: ">=2.11.0 <2.12.0" amplify_foundation_dart_bridge: ">=2.11.0 <2.12.0" + amplify_record_cache_dart: ">=0.1.0 <0.2.0" aws_common: ">=0.7.12 <0.8.0" aws_signature_v4: ">=0.6.10 <0.7.0" built_collection: ^5.1.1 built_value: ^8.10.1 - drift: ^2.25.0 meta: ^1.16.0 smithy: ">=0.7.10 <0.8.0" smithy_aws: ">=0.7.10 <0.8.0" - synchronized: ^3.3.0 - web: ^1.1.1 dev_dependencies: amplify_lints: ">=3.1.4 <3.2.0" - build_runner: ^2.4.15 - build_version: ^2.1.1 - built_value_generator: ^8.10.1 - drift_dev: ^2.25.1 + drift: ^2.25.0 fake_async: ^1.3.0 mocktail: ^1.0.0 test: ^1.22.1 diff --git a/packages/kinesis/amplify_kinesis_dart/test/amplify_kinesis_exception_test.dart b/packages/kinesis/amplify_kinesis_dart/test/amplify_kinesis_exception_test.dart index 61f9de630b1..5a7106f8436 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/amplify_kinesis_exception_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/amplify_kinesis_exception_test.dart @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:amplify_kinesis_dart/src/exception/amplify_kinesis_exception.dart'; -import 'package:amplify_kinesis_dart/src/exception/record_cache_exception.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:test/test.dart'; void main() { @@ -16,7 +16,7 @@ void main() { test( 'converts RecordCacheValidationException to KinesisValidationException', () { - final cause = RecordCacheValidationException('bad input', 'fix it'); + const cause = RecordCacheValidationException('bad input', 'fix it'); final result = AmplifyKinesisException.from(cause); expect(result, isA()); expect(result.message, 'bad input'); @@ -44,7 +44,7 @@ void main() { test( 'converts RecordCacheLimitExceededException to KinesisLimitExceededException', () { - final cause = RecordCacheLimitExceededException( + const cause = RecordCacheLimitExceededException( 'cache full', 'flush first', ); diff --git a/packages/kinesis/amplify_kinesis_dart/test/auto_flush_scheduler_test.dart b/packages/kinesis/amplify_kinesis_dart/test/auto_flush_scheduler_test.dart index bf574220d71..c8e1591dfa6 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/auto_flush_scheduler_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/auto_flush_scheduler_test.dart @@ -1,9 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'package:amplify_kinesis_dart/src/impl/auto_flush_scheduler.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; -import 'package:amplify_kinesis_dart/src/model/flush_data.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:fake_async/fake_async.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; diff --git a/packages/kinesis/amplify_kinesis_dart/test/common/mocktail_mocks.dart b/packages/kinesis/amplify_kinesis_dart/test/common/mocktail_mocks.dart index f77e0098ecf..58a4526d99e 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/common/mocktail_mocks.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/common/mocktail_mocks.dart @@ -9,8 +9,8 @@ import 'dart:async'; import 'package:amplify_foundation_dart/amplify_foundation_dart.dart' as foundation; import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; import 'package:amplify_kinesis_dart/src/sdk/src/kinesis/kinesis_client.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:aws_common/aws_common.dart'; import 'package:mocktail/mocktail.dart'; import 'package:smithy/smithy.dart'; @@ -20,13 +20,6 @@ import 'package:smithy/smithy.dart'; // ============================================================================= /// Creates a mock [SmithyOperation] that returns the result of [fn]. -/// -/// Use this helper to mock SDK client method returns: -/// ```dart -/// when(() => mockClient.putRecords(any())).thenReturn( -/// mockSmithyOperation(() => PutRecordsResponse(...)), -/// ); -/// ``` SmithyOperation mockSmithyOperation(FutureOr Function() fn) => SmithyOperation( CancelableOperation.fromFuture(Future.value(fn())), @@ -39,13 +32,6 @@ SmithyOperation mockSmithyOperation(FutureOr Function() fn) => class MockKinesisClient extends Mock implements KinesisClient {} /// Mock implementation of [SmithyOperation]. -/// -/// Use when you need to throw exceptions from SDK operations: -/// ```dart -/// final mockOperation = MockSmithyOperation(); -/// when(() => mockOperation.result).thenThrow(SomeException()); -/// when(() => mockClient.putRecords(any())).thenReturn(mockOperation); -/// ``` class MockSmithyOperation extends Mock implements SmithyOperation {} /// Mock implementation of [AWSHttpException]. diff --git a/packages/kinesis/amplify_kinesis_dart/test/helpers/test_database.dart b/packages/kinesis/amplify_kinesis_dart/test/helpers/test_database.dart index ebde2530ee1..a334ecb2a4c 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/helpers/test_database.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/helpers/test_database.dart @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'package:amplify_kinesis_dart/src/db/kinesis_record_database.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:drift/native.dart'; /// Creates an in-memory database for testing. -KinesisRecordDatabase createTestDatabase() { - return KinesisRecordDatabase.forTesting(NativeDatabase.memory()); +RecordCacheDatabase createTestDatabase() { + return RecordCacheDatabase.forTesting(NativeDatabase.memory()); } diff --git a/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart b/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart index 16acbc9b1a2..ccbf01938f6 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart @@ -4,8 +4,7 @@ import 'dart:typed_data'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_memory.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:test/test.dart'; void main() { @@ -13,7 +12,12 @@ void main() { late InMemoryRecordStorage storage; setUp(() { - storage = InMemoryRecordStorage(maxCacheBytes: 10 * 1024 * 1024); + storage = InMemoryRecordStorage( + maxCacheBytes: 10 * 1024 * 1024, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, + ); }); tearDown(() async { @@ -29,7 +33,7 @@ void main() { group('addRecord', () { test('saves and retrieves a record', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3, 4, 5]), partitionKey: 'test-partition', streamName: 'test-stream', @@ -52,7 +56,7 @@ void main() { test('removes correct records by ID', () async { for (var i = 0; i < 5; i++) { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'pk-$i', streamName: 'stream', @@ -74,7 +78,7 @@ void main() { test('handles empty ID list gracefully', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1]), partitionKey: 'pk', streamName: 'stream', @@ -89,7 +93,7 @@ void main() { group('incrementRetryCount', () { test('increments retry count correctly', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1]), partitionKey: 'pk', streamName: 'stream', @@ -112,21 +116,21 @@ void main() { group('getRecordsByStream', () { test('returns records grouped by stream name', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1]), partitionKey: 'pk', streamName: 'stream-a', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([2]), partitionKey: 'pk', streamName: 'stream-b', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([3]), partitionKey: 'pk', streamName: 'stream-a', @@ -149,7 +153,7 @@ void main() { test('removes all records', () async { for (var i = 0; i < 5; i++) { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'pk-$i', streamName: 'stream', @@ -169,7 +173,7 @@ void main() { expect(await storage.getRecordCount(), equals(0)); for (var i = 0; i < 3; i++) { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'pk-$i', streamName: 'stream', diff --git a/packages/kinesis/amplify_kinesis_dart/test/kinesis_data_streams_test.dart b/packages/kinesis/amplify_kinesis_dart/test/kinesis_data_streams_test.dart index 13f107cce5c..522986197d5 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/kinesis_data_streams_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/kinesis_data_streams_test.dart @@ -8,11 +8,8 @@ import 'package:amplify_foundation_dart/amplify_foundation_dart.dart' import 'package:amplify_kinesis_dart/src/amplify_kinesis_client.dart'; import 'package:amplify_kinesis_dart/src/amplify_kinesis_client_options.dart'; import 'package:amplify_kinesis_dart/src/exception/amplify_kinesis_exception.dart'; -import 'package:amplify_kinesis_dart/src/flush_strategy/flush_strategy.dart'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/model/clear_cache_data.dart'; -import 'package:amplify_kinesis_dart/src/model/flush_data.dart'; -import 'package:amplify_kinesis_dart/src/model/record_data.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; @@ -24,7 +21,11 @@ void main() { setUpAll(() { registerFallbackValue( - RecordInput.now(data: Uint8List(0), partitionKey: '', streamName: ''), + createKinesisRecordInputNow( + data: Uint8List(0), + partitionKey: '', + streamName: '', + ), ); }); diff --git a/packages/kinesis/amplify_kinesis_dart/test/kinesis_sender_test.dart b/packages/kinesis/amplify_kinesis_dart/test/kinesis_sender_test.dart index c3a95a760c6..485cdc54ee2 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/kinesis_sender_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/kinesis_sender_test.dart @@ -10,8 +10,8 @@ library; import 'dart:typed_data'; import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/model/record.dart'; import 'package:amplify_kinesis_dart/src/sdk/kinesis.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; @@ -79,7 +79,7 @@ void main() { final sender = _DirectMockSender(mockClient, maxRetries: maxRetries); - await sender.putRecords( + await sender.sendBatch( streamName: 'my-stream', records: [ _testRecord( @@ -132,7 +132,7 @@ void main() { final sender = _DirectMockSender(mockClient, maxRetries: maxRetries); - final result = await sender.putRecords( + final result = await sender.sendBatch( streamName: 'test-stream', records: [ _testRecord( diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart index ec6fc52ad76..566973908fc 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart @@ -11,10 +11,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_sqlite.dart'; -import 'package:amplify_kinesis_dart/src/model/record.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:test/test.dart'; import 'helpers/test_database.dart'; @@ -28,6 +25,9 @@ void main() { final storage = SqliteRecordStorage( database: db, maxCacheBytes: 1024 * 1024, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, ); final sender = _GatedSender(); final client = RecordClient( @@ -39,7 +39,7 @@ void main() { // Seed records for (var i = 0; i < 5; i++) { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'key$i', streamName: 'test-stream', @@ -47,7 +47,7 @@ void main() { ); } - // Launch flush1 — it will block inside putRecords until we complete + // Launch flush1 — it will block inside sendBatch until we complete // the gate completer. final flush1 = client.flush(); @@ -78,31 +78,28 @@ void main() { }); } -/// A sender that blocks inside [putRecords] until [gate] is completed, +/// A sender that blocks inside [sendBatch] until [gate] is completed, /// giving the test deterministic control over when the flush finishes. -class _GatedSender implements KinesisSender { - /// Completes when [putRecords] is entered, signaling that the flush +class _GatedSender implements Sender { + /// Completes when [sendBatch] is entered, signaling that the flush /// is in progress and holding the `_flushing` flag. final Completer entered = Completer(); - /// The test completes this to unblock [putRecords]. + /// The test completes this to unblock [sendBatch]. final Completer gate = Completer(); @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); - - @override - Future putRecords({ + Future sendBatch({ required String streamName, required List records, }) async { - // Signal that we're inside putRecords (flush is in progress). + // Signal that we're inside sendBatch (flush is in progress). if (!entered.isCompleted) entered.complete(); // Block until the test says go. await gate.future; - return PutRecordsResult( + return SendResult( successfulIds: records.map((r) => r.id).toList(), failedIds: const [], retryableIds: const [], diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart index 19942d358d2..9004df5df55 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart @@ -3,7 +3,7 @@ /// Tests for RecordClient. /// -/// Uses mocktail mocks for KinesisSender with pre-built PutRecordsResult +/// Uses mocktail mocks for KinesisSender with pre-built SendResult /// values and explicit IDs, rather than behavioral test doubles with /// callback logic. library; @@ -11,13 +11,8 @@ library; import 'dart:typed_data'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_sqlite.dart'; -import 'package:amplify_kinesis_dart/src/model/clear_cache_data.dart'; -import 'package:amplify_kinesis_dart/src/model/flush_data.dart'; import 'package:amplify_kinesis_dart/src/sdk/kinesis.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; @@ -34,7 +29,10 @@ void main() { final db = createTestDatabase(); storage = SqliteRecordStorage( database: db, - maxCacheBytes: 1024, // 1KB for testing + maxCacheBytes: 1024, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, ); mockSender = MockKinesisSender(); client = RecordClient( @@ -59,7 +57,7 @@ void main() { group('record()', () { test('accepts records when enabled', () async { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'pk', streamName: 'stream', @@ -75,7 +73,7 @@ void main() { test('sends all cached records and returns FlushData', () async { for (var i = 0; i < 3; i++) { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'pk-$i', streamName: 'stream', @@ -85,14 +83,13 @@ void main() { final allRecords = await getAllRecords(); - // Mock: all records succeed when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), ).thenAnswer( - (_) async => PutRecordsResult( + (_) async => SendResult( successfulIds: allRecords.map((r) => r.id).toList(), retryableIds: [], failedIds: [], @@ -104,7 +101,7 @@ void main() { expect(result, isA()); expect(result.recordsFlushed, equals(3)); verify( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), @@ -113,36 +110,35 @@ void main() { test('separates records by stream', () async { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1]), partitionKey: 'pk', streamName: 'stream-a', ), ); await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([2]), partitionKey: 'pk', streamName: 'stream-b', ), ); await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([3]), partitionKey: 'pk', streamName: 'stream-a', ), ); - // Mock: all records succeed for any stream when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), ).thenAnswer((invocation) async { final records = invocation.namedArguments[#records] as List; - return PutRecordsResult( + return SendResult( successfulIds: records.map((r) => r.id).toList(), retryableIds: [], failedIds: [], @@ -153,7 +149,7 @@ void main() { expect(result.recordsFlushed, equals(3)); verify( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), @@ -162,7 +158,7 @@ void main() { test('deletes successful records after send', () async { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1]), partitionKey: 'pk', streamName: 'stream', @@ -171,12 +167,12 @@ void main() { final allRecords = await getAllRecords(); when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), ).thenAnswer( - (_) async => PutRecordsResult( + (_) async => SendResult( successfulIds: allRecords.map((r) => r.id).toList(), retryableIds: [], failedIds: [], @@ -191,7 +187,7 @@ void main() { test('handles mixed success, retryable, and failed', () async { for (var i = 0; i < 3; i++) { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'pk-$i', streamName: 'stream', @@ -206,14 +202,13 @@ void main() { await storage.incrementRetryCount([allRecords[2].id]); } - // Mock: record 1 succeeds, record 2 retryable, record 3 failed when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), ).thenAnswer( - (_) async => PutRecordsResult( + (_) async => SendResult( successfulIds: [allRecords[0].id], retryableIds: [allRecords[1].id], failedIds: [allRecords[2].id], @@ -235,7 +230,7 @@ void main() { () async { for (var i = 0; i < 3; i++) { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'key$i', streamName: 'stream', @@ -244,7 +239,7 @@ void main() { } when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), @@ -265,59 +260,62 @@ void main() { }, ); - test('deletes records at max retries when non-SDK error occurs', () async { - for (var i = 0; i < 3; i++) { - await client.record( - RecordInput.now( - data: Uint8List.fromList([i]), - partitionKey: 'key$i', - streamName: 'stream', - ), - ); - } + test( + 'deletes records at max retries when non-SDK error occurs', + () async { + for (var i = 0; i < 3; i++) { + await client.record( + createKinesisRecordInputNow( + data: Uint8List.fromList([i]), + partitionKey: 'key$i', + streamName: 'stream', + ), + ); + } - // Set records 2 and 3 to max retries (3) - final allRecords = await getAllRecords(); - for (var i = 0; i < 3; i++) { - await storage.incrementRetryCount([ - allRecords[1].id, - allRecords[2].id, - ]); - } + // Set records 2 and 3 to max retries (3) + final allRecords = await getAllRecords(); + for (var i = 0; i < 3; i++) { + await storage.incrementRetryCount([ + allRecords[1].id, + allRecords[2].id, + ]); + } - when( - () => mockSender.putRecords( - streamName: any(named: 'streamName'), - records: any(named: 'records'), - ), - ).thenThrow(Exception('Network error')); + when( + () => mockSender.sendBatch( + streamName: any(named: 'streamName'), + records: any(named: 'records'), + ), + ).thenThrow(Exception('Network error')); - try { - await client.flush(); - fail('Expected flush to throw'); - } on Exception { - // Expected — non-SDK errors are rethrown - } + try { + await client.flush(); + fail('Expected flush to throw'); + } on Exception { + // Expected — non-SDK errors are rethrown + } - // Only record 1 should remain (records 2 and 3 deleted at max retries) - final remaining = await getAllRecords(); - expect(remaining, hasLength(1)); - expect(remaining[0].id, equals(allRecords[0].id)); - expect(remaining[0].retryCount, equals(1)); - }); + // Only record 1 should remain (records 2 and 3 at max retries) + final remaining = await getAllRecords(); + expect(remaining, hasLength(1)); + expect(remaining[0].id, equals(allRecords[0].id)); + expect(remaining[0].retryCount, equals(1)); + }, + ); test( 'invalid stream records do not block valid stream flushes', () async { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'pk', streamName: 'invalid-stream', ), ); await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([4, 5, 6]), partitionKey: 'pk', streamName: 'valid-stream', @@ -330,19 +328,19 @@ void main() { ); when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: 'invalid-stream', records: any(named: 'records'), ), ).thenThrow(ResourceNotFoundException(message: 'Stream not found')); when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: 'valid-stream', records: any(named: 'records'), ), ).thenAnswer( - (_) async => PutRecordsResult( + (_) async => SendResult( successfulIds: [validRecord.id], retryableIds: [], failedIds: [], @@ -356,7 +354,7 @@ void main() { test('non-SDK errors abort the flush', () async { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'pk', streamName: 'stream', @@ -364,7 +362,7 @@ void main() { ); when( - () => mockSender.putRecords( + () => mockSender.sendBatch( streamName: any(named: 'streamName'), records: any(named: 'records'), ), @@ -378,7 +376,7 @@ void main() { test('removes all cached records and returns ClearCacheData', () async { for (var i = 0; i < 5; i++) { await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([i]), partitionKey: 'pk-$i', streamName: 'stream', diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart index 46604c31e58..115c26b7900 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart @@ -18,13 +18,13 @@ library; import 'dart:convert'; import 'dart:typed_data'; -import 'package:amplify_kinesis_dart/src/exception/record_cache_exception.dart'; +import 'package:amplify_foundation_dart/amplify_foundation_dart.dart' + show Error, Ok; +import 'package:amplify_kinesis_dart/src/amplify_kinesis_client.dart'; +import 'package:amplify_kinesis_dart/src/exception/amplify_kinesis_exception.dart'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_sqlite.dart'; import 'package:amplify_kinesis_dart/src/kinesis_limits.dart' as limits; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:test/test.dart'; import 'helpers/test_database.dart'; @@ -48,7 +48,13 @@ void main() { setUp(() { final db = createTestDatabase(); - storage = SqliteRecordStorage(database: db, maxCacheBytes: 10000); + storage = SqliteRecordStorage( + database: db, + maxCacheBytes: 10000, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, + ); client = createClient(storage: storage); }); @@ -68,6 +74,9 @@ void main() { final largeStorage = SqliteRecordStorage( database: largeDb, maxCacheBytes: 20 * 1024 * 1024, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, ); final largeClient = createClient(storage: largeStorage); @@ -78,7 +87,7 @@ void main() { ); await largeClient.record( - RecordInput.now( + createKinesisRecordInputNow( data: exactLimitData, partitionKey: partitionKey, streamName: 'stream', @@ -102,7 +111,7 @@ void main() { expect( () => client.record( - RecordInput.now( + createKinesisRecordInputNow( data: oversizedData, partitionKey: partitionKey, streamName: 'stream', @@ -121,7 +130,7 @@ void main() { test('dataSize accounts for partition key bytes', () { final partitionKey = 'k' * 10; // 10 bytes UTF-8 final data = Uint8List(50); - final record = RecordInput.now( + final record = createKinesisRecordInputNow( data: data, partitionKey: partitionKey, streamName: 'stream', @@ -134,7 +143,7 @@ void main() { // Each emoji is 4 bytes in UTF-8, 2 emojis = 8 bytes const partitionKey = '😀😀'; final data = Uint8List(10); - final record = RecordInput.now( + final record = createKinesisRecordInputNow( data: data, partitionKey: partitionKey, streamName: 'stream', @@ -156,6 +165,9 @@ void main() { final tightStorage = SqliteRecordStorage( database: tightDb, maxCacheBytes: 80, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, ); final tightClient = createClient(storage: tightStorage); @@ -165,7 +177,7 @@ void main() { // First record: 40 bytes — fits in 80-byte cache await tightClient.record( - RecordInput.now( + createKinesisRecordInputNow( data: data, partitionKey: partitionKey, streamName: 'stream', @@ -174,7 +186,7 @@ void main() { // Second record: 40 more → total 80 — still fits await tightClient.record( - RecordInput.now( + createKinesisRecordInputNow( data: data, partitionKey: partitionKey, streamName: 'stream', @@ -184,7 +196,7 @@ void main() { // Third record: 40 more → total 120 > 80 limit expect( () => tightClient.record( - RecordInput.now( + createKinesisRecordInputNow( data: data, partitionKey: partitionKey, streamName: 'stream', @@ -203,27 +215,34 @@ void main() { // --------------------------------------------------------------- group('partition key validation', () { + late AmplifyKinesisClient kinesisClient; + + setUp(() { + kinesisClient = AmplifyKinesisClient.withRecordClient( + recordClient: client, + ); + }); + test('empty partition key is rejected', () async { + final result = await kinesisClient.record( + data: Uint8List.fromList([1, 2, 3]), + partitionKey: '', + streamName: 'stream', + ); + expect(result, isA>()); expect( - () => client.record( - RecordInput.now( - data: Uint8List.fromList([1, 2, 3]), - partitionKey: '', - streamName: 'stream', - ), - ), - throwsA(isA()), + (result as Error).error, + isA(), ); }); test('partition key at max length 256 code points is accepted', () async { - await client.record( - RecordInput.now( - data: Uint8List.fromList([1]), - partitionKey: 'k' * 256, - streamName: 'stream', - ), + final result = await kinesisClient.record( + data: Uint8List.fromList([1]), + partitionKey: 'k' * 256, + streamName: 'stream', ); + expect(result, isA>()); final records = (await storage.getRecordsByStream()).values .expand((r) => r) @@ -232,15 +251,15 @@ void main() { }); test('partition key exceeding 256 code points is rejected', () async { + final result = await kinesisClient.record( + data: Uint8List.fromList([1]), + partitionKey: 'k' * 257, + streamName: 'stream', + ); + expect(result, isA>()); expect( - () => client.record( - RecordInput.now( - data: Uint8List.fromList([1]), - partitionKey: 'k' * 257, - streamName: 'stream', - ), - ), - throwsA(isA()), + (result as Error).error, + isA(), ); }); @@ -248,13 +267,12 @@ void main() { // Each emoji (😀) is 1 code point but 4 bytes in UTF-8. // 10 emoji = 10 code points (within 256 limit). final partitionKey = '😀' * 10; - await client.record( - RecordInput.now( - data: Uint8List.fromList([1]), - partitionKey: partitionKey, - streamName: 'stream', - ), + final result = await kinesisClient.record( + data: Uint8List.fromList([1]), + partitionKey: partitionKey, + streamName: 'stream', ); + expect(result, isA>()); final records = (await storage.getRecordsByStream()).values .expand((r) => r) @@ -267,15 +285,15 @@ void main() { () async { // 257 emoji = 257 code points > 256 limit final partitionKey = '😀' * 257; + final result = await kinesisClient.record( + data: Uint8List.fromList([1]), + partitionKey: partitionKey, + streamName: 'stream', + ); + expect(result, isA>()); expect( - () => client.record( - RecordInput.now( - data: Uint8List.fromList([1]), - partitionKey: partitionKey, - streamName: 'stream', - ), - ), - throwsA(isA()), + (result as Error).error, + isA(), ); }, ); @@ -292,7 +310,7 @@ void main() { // Oversized record should be rejected expect( () => client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List(limits.maxRecordSizeBytes), partitionKey: 'k' * 20, streamName: 'stream', @@ -303,7 +321,7 @@ void main() { // Valid record should still work await client.record( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'a', streamName: 'stream', @@ -320,16 +338,13 @@ void main() { } /// No-op sender for validation tests that don't need to send records. -class _NoOpSender implements KinesisSender { - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); - +class _NoOpSender implements Sender { @override - Future putRecords({ + Future sendBatch({ required String streamName, required List records, }) async { - return PutRecordsResult( + return SendResult( successfulIds: records.map((r) => r.id).toList(), failedIds: const [], retryableIds: const [], diff --git a/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart b/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart index 1a13a96aae7..ecf3e7d9900 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart @@ -11,7 +11,7 @@ library; import 'dart:typed_data'; import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_sqlite.dart'; +import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; import 'package:test/test.dart'; import 'helpers/test_database.dart'; @@ -22,7 +22,13 @@ void main() { setUp(() { final db = createTestDatabase(); - storage = SqliteRecordStorage(database: db, maxCacheBytes: 1024 * 1024); + storage = SqliteRecordStorage( + database: db, + maxCacheBytes: 1024 * 1024, + maxRecordsPerBatch: 500, + maxBytesPerBatch: 5 * 1024 * 1024, + maxRecordSizeBytes: 10 * 1024 * 1024, + ); }); tearDown(() async { @@ -35,14 +41,14 @@ void main() { test('cached size matches database after add operations', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'a', streamName: 'stream1', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([4, 5, 6, 7]), partitionKey: 'b', streamName: 'stream1', @@ -56,21 +62,21 @@ void main() { test('cached size matches database after delete operations', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'a', streamName: 'stream1', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([4, 5, 6, 7]), partitionKey: 'b', streamName: 'stream1', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([8, 9]), partitionKey: 'c', streamName: 'stream2', @@ -90,14 +96,14 @@ void main() { test('cached size matches database after clear operations', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3]), partitionKey: 'a', streamName: 'stream1', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([4, 5]), partitionKey: 'b', streamName: 'stream2', @@ -113,7 +119,7 @@ void main() { test('cached size remains accurate through mixed operations', () async { // "a"(1) + data(5) = 6 await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1, 2, 3, 4, 5]), partitionKey: 'a', streamName: 'stream1', @@ -121,7 +127,7 @@ void main() { ); // "b"(1) + data(3) = 4 await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([6, 7, 8]), partitionKey: 'b', streamName: 'stream2', @@ -142,7 +148,7 @@ void main() { // Add another record: "c"(1) + data(2) = 3 await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([9, 10]), partitionKey: 'c', streamName: 'stream3', @@ -174,7 +180,7 @@ void main() { for (var i = 0; i < recordsPerProducer; i++) { final key = 'producer${p}_record$i'; await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List(recordSize), partitionKey: key, streamName: 'stream$p', @@ -194,7 +200,7 @@ void main() { if (records.isNotEmpty) { final toDelete = records.first; await storage.deleteRecords([toDelete.id]); - deleted.add(toDelete.partitionKey); + deleted.add(toDelete.partitionKey ?? ''); } } deletedKeys.add(deleted); @@ -215,7 +221,9 @@ void main() { expect(finalCacheSize, equals(expectedCacheSize)); // Verify every created key is either in DB or was deleted - final remainingKeys = finalRecords.map((r) => r.partitionKey).toSet(); + final remainingKeys = finalRecords + .map((r) => r.partitionKey ?? '') + .toSet(); final allCreatedKeys = createdKeys.values .expand((keys) => keys) .toSet(); @@ -253,21 +261,21 @@ void main() { 'getRecordsByStream with empty excludingIds returns all records', () async { await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([1]), partitionKey: 'key1', streamName: 'stream1', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([2]), partitionKey: 'key2', streamName: 'stream1', ), ); await storage.addRecord( - RecordInput.now( + createKinesisRecordInputNow( data: Uint8List.fromList([3]), partitionKey: 'key3', streamName: 'stream2', diff --git a/packages/kinesis/amplify_record_cache_dart/analysis_options.yaml b/packages/kinesis/amplify_record_cache_dart/analysis_options.yaml new file mode 100644 index 00000000000..01538d576cf --- /dev/null +++ b/packages/kinesis/amplify_record_cache_dart/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:amplify_lints/library.yaml + +analyzer: + exclude: + - '**/*.g.dart' diff --git a/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart b/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart new file mode 100644 index 00000000000..9abb024c6c8 --- /dev/null +++ b/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart @@ -0,0 +1,29 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/// Shared record caching infrastructure for Amplify streaming clients. +library; + +// Client +export 'src/client/auto_flush_scheduler.dart'; +export 'src/client/record_client.dart'; +// Database +export 'src/db/record_cache_database.dart' show RecordCacheDatabase; +// Exceptions +export 'src/exception/record_cache_exception.dart'; +// Flush strategy +export 'src/flush_strategy/flush_strategy.dart'; +// Models +export 'src/model/clear_cache_data.dart'; +export 'src/model/flush_data.dart'; +export 'src/model/record.dart'; +export 'src/model/record_data.dart'; +export 'src/model/record_input.dart'; +// Sender +export 'src/sender/sender.dart'; +// Storage +export 'src/storage/platform/record_storage_platform.dart'; +export 'src/storage/record_storage.dart' hide defaultRecoverySuggestion; +export 'src/storage/record_storage_indexeddb.dart'; +export 'src/storage/record_storage_memory.dart'; +export 'src/storage/record_storage_sqlite.dart'; diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/auto_flush_scheduler.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/client/auto_flush_scheduler.dart similarity index 91% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/auto_flush_scheduler.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/client/auto_flush_scheduler.dart index 3332abfa806..3220eb30fa6 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/auto_flush_scheduler.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/client/auto_flush_scheduler.dart @@ -4,9 +4,9 @@ import 'dart:async'; import 'package:amplify_foundation_dart/amplify_foundation_dart.dart'; -import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; +import 'package:amplify_record_cache_dart/src/client/record_client.dart'; -/// {@template amplify_kinesis.auto_flush_scheduler} +/// {@template amplify_record_cache.auto_flush_scheduler} /// Manages automatic flush scheduling at a fixed interval. /// /// Takes a [Duration] interval and a [RecordClient]. @@ -17,7 +17,7 @@ import 'package:amplify_kinesis_dart/src/impl/record_client.dart'; /// a new one, preventing duplicate concurrent loops. /// {@endtemplate} final class AutoFlushScheduler { - /// {@macro amplify_kinesis.auto_flush_scheduler} + /// {@macro amplify_record_cache.auto_flush_scheduler} AutoFlushScheduler({required Duration interval, required RecordClient client}) : _interval = interval, _client = client; diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/record_client.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/client/record_client.dart similarity index 83% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/record_client.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/client/record_client.dart index 873ce07d116..9d564418ca2 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/record_client.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/client/record_client.dart @@ -2,17 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import 'package:amplify_foundation_dart/amplify_foundation_dart.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_sender.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; -import 'package:amplify_kinesis_dart/src/model/clear_cache_data.dart'; -import 'package:amplify_kinesis_dart/src/model/flush_data.dart'; -import 'package:amplify_kinesis_dart/src/model/record_data.dart' - show RecordData; +import 'package:amplify_record_cache_dart/src/model/clear_cache_data.dart'; +import 'package:amplify_record_cache_dart/src/model/flush_data.dart'; +import 'package:amplify_record_cache_dart/src/model/record_data.dart'; +import 'package:amplify_record_cache_dart/src/model/record_input.dart'; +import 'package:amplify_record_cache_dart/src/sender/sender.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; import 'package:smithy/smithy.dart' show SmithyHttpException, UnknownSmithyHttpException; -/// {@template amplify_kinesis.record_client} +/// {@template amplify_record_cache.record_client} /// Orchestrates record operations: storage, sending, and retry logic. /// /// - `record()` delegates directly to `storage.addRecord()` (validation @@ -21,22 +20,25 @@ import 'package:smithy/smithy.dart' /// stream and sends it. /// {@endtemplate} class RecordClient { - /// {@macro amplify_kinesis.record_client} + /// {@macro amplify_record_cache.record_client} RecordClient({ required RecordStorage storage, - required KinesisSender sender, + required Sender sender, required int maxRetries, }) : _storage = storage, _sender = sender, _maxRetries = maxRetries; final RecordStorage _storage; - final KinesisSender _sender; + final Sender _sender; final int _maxRetries; final Logger _logger = AmplifyLogging.logger('RecordClient'); bool _flushing = false; + /// Provides access to the underlying storage (for testing). + RecordStorage get storage => _storage; + /// Records data to the local cache. /// /// Delegates to [RecordStorage.addRecord] which handles validation @@ -48,7 +50,7 @@ class RecordClient { return const RecordData(); } - /// Flushes cached records to Kinesis. + /// Flushes cached records to the streaming service. /// /// Single-pass: retrieves one batch of records per stream, sends each /// batch, and returns. Records beyond the per-stream limit are picked @@ -79,8 +81,7 @@ class RecordClient { ? 'HTTP ${e.statusCode}: ${e.body}' : e.message; _logger.warn( - 'Kinesis SDK error flushing stream $streamName: $details. ' - 'Skipping', + 'SDK error flushing stream $streamName: $details. Skipping', ); await _handleFailedRequest(records); } catch (e) { @@ -97,7 +98,7 @@ class RecordClient { } Future _sendStreamBatch(String streamName, List records) async { - final result = await _sender.putRecords( + final result = await _sender.sendBatch( streamName: streamName, records: records, ); diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/db/kinesis_record_database.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart similarity index 52% rename from packages/kinesis/amplify_kinesis_dart/lib/src/db/kinesis_record_database.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart index 3f72a6da0b2..42e8b98074d 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/db/kinesis_record_database.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart @@ -6,26 +6,30 @@ import 'dart:async'; import 'package:amplify_db_common_dart/amplify_db_common_dart.dart'; import 'package:drift/drift.dart'; -part 'kinesis_record_database.g.dart'; +part 'record_cache_database.g.dart'; -/// Schema of the KinesisRecords table in SQLite. +/// Schema of the cached records table in SQLite. /// -/// When updating this schema, please bump [KinesisRecordDatabase.schemaVersion]. +/// The `partitionKey` column is present for Kinesis Data Streams +/// compatibility. Firehose clients write an empty string. +/// +/// When updating this schema, please bump +/// [RecordCacheDatabase.schemaVersion]. @DataClassName('DriftStoredRecord') -class KinesisRecords extends Table { +class CachedRecords extends Table { /// Auto-incrementing primary key. IntColumn get id => integer().autoIncrement()(); - /// The name of the Kinesis Data Stream. + /// The name of the target stream. TextColumn get streamName => text()(); - /// The partition key for the record. - TextColumn get partitionKey => text()(); + /// The partition key (empty string for services that don't use it). + TextColumn get partitionKey => text().withDefault(const Constant(''))(); - /// The data blob to send to Kinesis. + /// The data blob to send. BlobColumn get data => blob()(); - /// The size of the data blob in bytes. + /// The size of the record in bytes. IntColumn get dataSize => integer()(); /// The number of times this record has been retried. @@ -35,30 +39,33 @@ class KinesisRecords extends Table { IntColumn get createdAt => integer()(); } -/// {@template amplify_kinesis.kinesis_record_database} -/// Drift database for managing stored Kinesis records. +/// {@template amplify_record_cache.record_cache_database} +/// Drift database for managing cached records. /// {@endtemplate} -@DriftDatabase(tables: [KinesisRecords]) -class KinesisRecordDatabase extends _$KinesisRecordDatabase { - /// {@macro amplify_kinesis.kinesis_record_database} +@DriftDatabase(tables: [CachedRecords]) +class RecordCacheDatabase extends _$RecordCacheDatabase { + /// {@macro amplify_record_cache.record_cache_database} /// + /// [dbPrefix] is the database name prefix (e.g. `kinesis_records`, + /// `firehose_records`). /// [identifier] is used to namespace the database (typically the AWS region). - /// [storagePath] is the directory path for the database file - factory KinesisRecordDatabase({ + /// [storagePath] is the directory path for the database file. + factory RecordCacheDatabase({ + required String dbPrefix, required String identifier, required FutureOr? storagePath, }) { final driftQueryExecutor = connect( - name: 'kinesis_records_$identifier', + name: '${dbPrefix}_$identifier', path: storagePath, ); - return KinesisRecordDatabase._(driftQueryExecutor); + return RecordCacheDatabase._(driftQueryExecutor); } /// Creates a database with a custom query executor (for testing). - KinesisRecordDatabase.forTesting(super.executor); + RecordCacheDatabase.forTesting(super.executor); - KinesisRecordDatabase._(super.driftQueryExecutor); + RecordCacheDatabase._(super.driftQueryExecutor); // Bump this number whenever you change or add a table definition. @override @@ -69,14 +76,13 @@ class KinesisRecordDatabase extends _$KinesisRecordDatabase { return MigrationStrategy( onCreate: (Migrator m) async { await m.createAll(); - // Indices matching the Android schema. await customStatement( 'CREATE INDEX IF NOT EXISTS idx_stream_id ' - 'ON kinesis_records(stream_name, id)', + 'ON cached_records(stream_name, id)', ); await customStatement( 'CREATE INDEX IF NOT EXISTS idx_data_size ' - 'ON kinesis_records(data_size)', + 'ON cached_records(data_size)', ); }, ); diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/db/kinesis_record_database.g.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart similarity index 83% rename from packages/kinesis/amplify_kinesis_dart/lib/src/db/kinesis_record_database.g.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart index e76bbb21b87..51023659802 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/db/kinesis_record_database.g.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart @@ -1,14 +1,14 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'kinesis_record_database.dart'; +part of 'record_cache_database.dart'; // ignore_for_file: type=lint -class $KinesisRecordsTable extends KinesisRecords - with TableInfo<$KinesisRecordsTable, DriftStoredRecord> { +class $CachedRecordsTable extends CachedRecords + with TableInfo<$CachedRecordsTable, DriftStoredRecord> { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $KinesisRecordsTable(this.attachedDatabase, [this._alias]); + $CachedRecordsTable(this.attachedDatabase, [this._alias]); static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -42,7 +42,8 @@ class $KinesisRecordsTable extends KinesisRecords aliasedName, false, type: DriftSqlType.string, - requiredDuringInsert: true, + requiredDuringInsert: false, + defaultValue: const Constant(''), ); static const VerificationMeta _dataMeta = const VerificationMeta('data'); @override @@ -101,7 +102,7 @@ class $KinesisRecordsTable extends KinesisRecords String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'kinesis_records'; + static const String $name = 'cached_records'; @override VerificationContext validateIntegrity( Insertable instance, { @@ -128,8 +129,6 @@ class $KinesisRecordsTable extends KinesisRecords _partitionKeyMeta, ), ); - } else if (isInserting) { - context.missing(_partitionKeyMeta); } if (data.containsKey('data')) { context.handle( @@ -202,8 +201,8 @@ class $KinesisRecordsTable extends KinesisRecords } @override - $KinesisRecordsTable createAlias(String alias) { - return $KinesisRecordsTable(attachedDatabase, alias); + $CachedRecordsTable createAlias(String alias) { + return $CachedRecordsTable(attachedDatabase, alias); } } @@ -212,16 +211,16 @@ class DriftStoredRecord extends DataClass /// Auto-incrementing primary key. final int id; - /// The name of the Kinesis Data Stream. + /// The name of the target stream. final String streamName; - /// The partition key for the record. + /// The partition key (empty string for services that don't use it). final String partitionKey; - /// The data blob to send to Kinesis. + /// The data blob to send. final Uint8List data; - /// The size of the data blob in bytes. + /// The size of the record in bytes. final int dataSize; /// The number of times this record has been retried. @@ -251,8 +250,8 @@ class DriftStoredRecord extends DataClass return map; } - KinesisRecordsCompanion toCompanion(bool nullToAbsent) { - return KinesisRecordsCompanion( + CachedRecordsCompanion toCompanion(bool nullToAbsent) { + return CachedRecordsCompanion( id: Value(id), streamName: Value(streamName), partitionKey: Value(partitionKey), @@ -309,7 +308,7 @@ class DriftStoredRecord extends DataClass retryCount: retryCount ?? this.retryCount, createdAt: createdAt ?? this.createdAt, ); - DriftStoredRecord copyWithCompanion(KinesisRecordsCompanion data) { + DriftStoredRecord copyWithCompanion(CachedRecordsCompanion data) { return DriftStoredRecord( id: data.id.present ? data.id.value : this.id, streamName: data.streamName.present @@ -364,7 +363,7 @@ class DriftStoredRecord extends DataClass other.createdAt == this.createdAt); } -class KinesisRecordsCompanion extends UpdateCompanion { +class CachedRecordsCompanion extends UpdateCompanion { final Value id; final Value streamName; final Value partitionKey; @@ -372,7 +371,7 @@ class KinesisRecordsCompanion extends UpdateCompanion { final Value dataSize; final Value retryCount; final Value createdAt; - const KinesisRecordsCompanion({ + const CachedRecordsCompanion({ this.id = const Value.absent(), this.streamName = const Value.absent(), this.partitionKey = const Value.absent(), @@ -381,16 +380,15 @@ class KinesisRecordsCompanion extends UpdateCompanion { this.retryCount = const Value.absent(), this.createdAt = const Value.absent(), }); - KinesisRecordsCompanion.insert({ + CachedRecordsCompanion.insert({ this.id = const Value.absent(), required String streamName, - required String partitionKey, + this.partitionKey = const Value.absent(), required Uint8List data, required int dataSize, this.retryCount = const Value.absent(), required int createdAt, }) : streamName = Value(streamName), - partitionKey = Value(partitionKey), data = Value(data), dataSize = Value(dataSize), createdAt = Value(createdAt); @@ -414,7 +412,7 @@ class KinesisRecordsCompanion extends UpdateCompanion { }); } - KinesisRecordsCompanion copyWith({ + CachedRecordsCompanion copyWith({ Value? id, Value? streamName, Value? partitionKey, @@ -423,7 +421,7 @@ class KinesisRecordsCompanion extends UpdateCompanion { Value? retryCount, Value? createdAt, }) { - return KinesisRecordsCompanion( + return CachedRecordsCompanion( id: id ?? this.id, streamName: streamName ?? this.streamName, partitionKey: partitionKey ?? this.partitionKey, @@ -463,7 +461,7 @@ class KinesisRecordsCompanion extends UpdateCompanion { @override String toString() { - return (StringBuffer('KinesisRecordsCompanion(') + return (StringBuffer('CachedRecordsCompanion(') ..write('id: $id, ') ..write('streamName: $streamName, ') ..write('partitionKey: $partitionKey, ') @@ -476,30 +474,29 @@ class KinesisRecordsCompanion extends UpdateCompanion { } } -abstract class _$KinesisRecordDatabase extends GeneratedDatabase { - _$KinesisRecordDatabase(QueryExecutor e) : super(e); - $KinesisRecordDatabaseManager get managers => - $KinesisRecordDatabaseManager(this); - late final $KinesisRecordsTable kinesisRecords = $KinesisRecordsTable(this); +abstract class _$RecordCacheDatabase extends GeneratedDatabase { + _$RecordCacheDatabase(QueryExecutor e) : super(e); + $RecordCacheDatabaseManager get managers => $RecordCacheDatabaseManager(this); + late final $CachedRecordsTable cachedRecords = $CachedRecordsTable(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override - List get allSchemaEntities => [kinesisRecords]; + List get allSchemaEntities => [cachedRecords]; } -typedef $$KinesisRecordsTableCreateCompanionBuilder = - KinesisRecordsCompanion Function({ +typedef $$CachedRecordsTableCreateCompanionBuilder = + CachedRecordsCompanion Function({ Value id, required String streamName, - required String partitionKey, + Value partitionKey, required Uint8List data, required int dataSize, Value retryCount, required int createdAt, }); -typedef $$KinesisRecordsTableUpdateCompanionBuilder = - KinesisRecordsCompanion Function({ +typedef $$CachedRecordsTableUpdateCompanionBuilder = + CachedRecordsCompanion Function({ Value id, Value streamName, Value partitionKey, @@ -509,9 +506,9 @@ typedef $$KinesisRecordsTableUpdateCompanionBuilder = Value createdAt, }); -class $$KinesisRecordsTableFilterComposer - extends Composer<_$KinesisRecordDatabase, $KinesisRecordsTable> { - $$KinesisRecordsTableFilterComposer({ +class $$CachedRecordsTableFilterComposer + extends Composer<_$RecordCacheDatabase, $CachedRecordsTable> { + $$CachedRecordsTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -554,9 +551,9 @@ class $$KinesisRecordsTableFilterComposer ); } -class $$KinesisRecordsTableOrderingComposer - extends Composer<_$KinesisRecordDatabase, $KinesisRecordsTable> { - $$KinesisRecordsTableOrderingComposer({ +class $$CachedRecordsTableOrderingComposer + extends Composer<_$RecordCacheDatabase, $CachedRecordsTable> { + $$CachedRecordsTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -599,9 +596,9 @@ class $$KinesisRecordsTableOrderingComposer ); } -class $$KinesisRecordsTableAnnotationComposer - extends Composer<_$KinesisRecordDatabase, $KinesisRecordsTable> { - $$KinesisRecordsTableAnnotationComposer({ +class $$CachedRecordsTableAnnotationComposer + extends Composer<_$RecordCacheDatabase, $CachedRecordsTable> { + $$CachedRecordsTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -636,41 +633,41 @@ class $$KinesisRecordsTableAnnotationComposer $composableBuilder(column: $table.createdAt, builder: (column) => column); } -class $$KinesisRecordsTableTableManager +class $$CachedRecordsTableTableManager extends RootTableManager< - _$KinesisRecordDatabase, - $KinesisRecordsTable, + _$RecordCacheDatabase, + $CachedRecordsTable, DriftStoredRecord, - $$KinesisRecordsTableFilterComposer, - $$KinesisRecordsTableOrderingComposer, - $$KinesisRecordsTableAnnotationComposer, - $$KinesisRecordsTableCreateCompanionBuilder, - $$KinesisRecordsTableUpdateCompanionBuilder, + $$CachedRecordsTableFilterComposer, + $$CachedRecordsTableOrderingComposer, + $$CachedRecordsTableAnnotationComposer, + $$CachedRecordsTableCreateCompanionBuilder, + $$CachedRecordsTableUpdateCompanionBuilder, ( DriftStoredRecord, BaseReferences< - _$KinesisRecordDatabase, - $KinesisRecordsTable, + _$RecordCacheDatabase, + $CachedRecordsTable, DriftStoredRecord >, ), DriftStoredRecord, PrefetchHooks Function() > { - $$KinesisRecordsTableTableManager( - _$KinesisRecordDatabase db, - $KinesisRecordsTable table, + $$CachedRecordsTableTableManager( + _$RecordCacheDatabase db, + $CachedRecordsTable table, ) : super( TableManagerState( db: db, table: table, createFilteringComposer: () => - $$KinesisRecordsTableFilterComposer($db: db, $table: table), + $$CachedRecordsTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$KinesisRecordsTableOrderingComposer($db: db, $table: table), + $$CachedRecordsTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$KinesisRecordsTableAnnotationComposer($db: db, $table: table), + $$CachedRecordsTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value id = const Value.absent(), @@ -680,7 +677,7 @@ class $$KinesisRecordsTableTableManager Value dataSize = const Value.absent(), Value retryCount = const Value.absent(), Value createdAt = const Value.absent(), - }) => KinesisRecordsCompanion( + }) => CachedRecordsCompanion( id: id, streamName: streamName, partitionKey: partitionKey, @@ -693,12 +690,12 @@ class $$KinesisRecordsTableTableManager ({ Value id = const Value.absent(), required String streamName, - required String partitionKey, + Value partitionKey = const Value.absent(), required Uint8List data, required int dataSize, Value retryCount = const Value.absent(), required int createdAt, - }) => KinesisRecordsCompanion.insert( + }) => CachedRecordsCompanion.insert( id: id, streamName: streamName, partitionKey: partitionKey, @@ -715,21 +712,21 @@ class $$KinesisRecordsTableTableManager ); } -typedef $$KinesisRecordsTableProcessedTableManager = +typedef $$CachedRecordsTableProcessedTableManager = ProcessedTableManager< - _$KinesisRecordDatabase, - $KinesisRecordsTable, + _$RecordCacheDatabase, + $CachedRecordsTable, DriftStoredRecord, - $$KinesisRecordsTableFilterComposer, - $$KinesisRecordsTableOrderingComposer, - $$KinesisRecordsTableAnnotationComposer, - $$KinesisRecordsTableCreateCompanionBuilder, - $$KinesisRecordsTableUpdateCompanionBuilder, + $$CachedRecordsTableFilterComposer, + $$CachedRecordsTableOrderingComposer, + $$CachedRecordsTableAnnotationComposer, + $$CachedRecordsTableCreateCompanionBuilder, + $$CachedRecordsTableUpdateCompanionBuilder, ( DriftStoredRecord, BaseReferences< - _$KinesisRecordDatabase, - $KinesisRecordsTable, + _$RecordCacheDatabase, + $CachedRecordsTable, DriftStoredRecord >, ), @@ -737,9 +734,9 @@ typedef $$KinesisRecordsTableProcessedTableManager = PrefetchHooks Function() >; -class $KinesisRecordDatabaseManager { - final _$KinesisRecordDatabase _db; - $KinesisRecordDatabaseManager(this._db); - $$KinesisRecordsTableTableManager get kinesisRecords => - $$KinesisRecordsTableTableManager(_db, _db.kinesisRecords); +class $RecordCacheDatabaseManager { + final _$RecordCacheDatabase _db; + $RecordCacheDatabaseManager(this._db); + $$CachedRecordsTableTableManager get cachedRecords => + $$CachedRecordsTableTableManager(_db, _db.cachedRecords); } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/exception/record_cache_exception.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/exception/record_cache_exception.dart similarity index 73% rename from packages/kinesis/amplify_kinesis_dart/lib/src/exception/record_cache_exception.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/exception/record_cache_exception.dart index 969cd892d2d..a5152118c22 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/exception/record_cache_exception.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/exception/record_cache_exception.dart @@ -1,13 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +/// {@template amplify_record_cache.record_cache_exception} /// Internal error type used by RecordClient / RecordStorage. /// -/// Mapped to the public AmplifyKinesisException hierarchy at the -/// AmplifyKinesisClient boundary via `AmplifyKinesisException.from`. +/// Mapped to the public exception hierarchy at the client boundary +/// (e.g. `AmplifyKinesisException.from` or `AmplifyFirehoseException.from`). +/// {@endtemplate} sealed class RecordCacheException implements Exception { /// Creates a [RecordCacheException]. - RecordCacheException(this.message, this.recoverySuggestion, [this.cause]); + const RecordCacheException( + this.message, + this.recoverySuggestion, [ + this.cause, + ]); /// A message describing the error. final String message; @@ -29,7 +35,7 @@ sealed class RecordCacheException implements Exception { /// Database operation failed. final class RecordCacheDatabaseException extends RecordCacheException { /// Creates a [RecordCacheDatabaseException]. - RecordCacheDatabaseException( + const RecordCacheDatabaseException( super.message, super.recoverySuggestion, [ super.cause, @@ -39,18 +45,17 @@ final class RecordCacheDatabaseException extends RecordCacheException { /// Cache limit exceeded — no space for new records. final class RecordCacheLimitExceededException extends RecordCacheException { /// Creates a [RecordCacheLimitExceededException]. - RecordCacheLimitExceededException( + const RecordCacheLimitExceededException( super.message, super.recoverySuggestion, [ super.cause, ]); } -/// Record input validation failed (e.g. oversized record, invalid partition -/// key). +/// Record input validation failed (e.g. oversized record). final class RecordCacheValidationException extends RecordCacheException { /// Creates a [RecordCacheValidationException]. - RecordCacheValidationException( + const RecordCacheValidationException( super.message, super.recoverySuggestion, [ super.cause, diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/flush_strategy/flush_strategy.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/flush_strategy/flush_strategy.dart similarity index 71% rename from packages/kinesis/amplify_kinesis_dart/lib/src/flush_strategy/flush_strategy.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/flush_strategy/flush_strategy.dart index 63b0c62135d..106d803be95 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/flush_strategy/flush_strategy.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/flush_strategy/flush_strategy.dart @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/// {@template amplify_kinesis.flush_strategy} +/// {@template amplify_record_cache.flush_strategy} /// Determines when automatic flushing of cached records occurs. /// /// Available strategies: @@ -9,25 +9,25 @@ /// - [FlushNone]: Disable automatic flushing entirely /// {@endtemplate} sealed class FlushStrategy { - /// {@macro amplify_kinesis.flush_strategy} + /// {@macro amplify_record_cache.flush_strategy} const FlushStrategy(); } -/// {@template amplify_kinesis.interval_flush_strategy} +/// {@template amplify_record_cache.interval_flush_strategy} /// A flush strategy that triggers automatic flushes at a fixed interval. /// {@endtemplate} final class FlushInterval extends FlushStrategy { - /// {@macro amplify_kinesis.interval_flush_strategy} + /// {@macro amplify_record_cache.interval_flush_strategy} const FlushInterval({this.interval = const Duration(seconds: 30)}); /// The interval between automatic flush operations. final Duration interval; } -/// {@template amplify_kinesis.none_flush_strategy} +/// {@template amplify_record_cache.none_flush_strategy} /// A flush strategy that disables automatic flushing. /// {@endtemplate} final class FlushNone extends FlushStrategy { - /// {@macro amplify_kinesis.none_flush_strategy} + /// {@macro amplify_record_cache.none_flush_strategy} const FlushNone(); } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/model/clear_cache_data.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/model/clear_cache_data.dart similarity index 80% rename from packages/kinesis/amplify_kinesis_dart/lib/src/model/clear_cache_data.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/model/clear_cache_data.dart index e0016ac18ef..b8d8a0e38da 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/model/clear_cache_data.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/model/clear_cache_data.dart @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/// {@template amplify_kinesis.clear_cache_data} +/// {@template amplify_record_cache.clear_cache_data} /// Data returned from a clearCache operation. /// {@endtemplate} final class ClearCacheData { - /// {@macro amplify_kinesis.clear_cache_data} + /// {@macro amplify_record_cache.clear_cache_data} const ClearCacheData({this.recordsCleared = 0}); /// The number of records that were cleared from the cache. diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/model/flush_data.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/model/flush_data.dart similarity index 71% rename from packages/kinesis/amplify_kinesis_dart/lib/src/model/flush_data.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/model/flush_data.dart index b908b88be8a..b69f968554b 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/model/flush_data.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/model/flush_data.dart @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/// {@template amplify_kinesis.flush_data} +/// {@template amplify_record_cache.flush_data} /// Data returned from a flush operation. /// {@endtemplate} final class FlushData { - /// {@macro amplify_kinesis.flush_data} + /// {@macro amplify_record_cache.flush_data} const FlushData({this.recordsFlushed = 0, this.flushInProgress = false}); /// The number of records successfully flushed. @@ -16,5 +16,6 @@ final class FlushData { @override String toString() => - 'FlushData(recordsFlushed: $recordsFlushed, flushInProgress: $flushInProgress)'; + 'FlushData(recordsFlushed: $recordsFlushed, ' + 'flushInProgress: $flushInProgress)'; } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/model/record.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/model/record.dart similarity index 68% rename from packages/kinesis/amplify_kinesis_dart/lib/src/model/record.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/model/record.dart index 77e89a89d11..3ee0f9f0fb1 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/model/record.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/model/record.dart @@ -3,37 +3,38 @@ import 'dart:typed_data'; -/// {@template amplify_kinesis.record} -/// A record persisted in local storage, ready to be flushed to Kinesis. +/// {@template amplify_record_cache.record} +/// A record persisted in local storage, ready to be flushed to a streaming +/// service (Kinesis Data Streams, Firehose, etc.). /// /// This is a plain Dart class with no ORM coupling, shared across all /// storage backends (SQLite, IndexedDB, In-Memory). /// {@endtemplate} final class Record { - /// {@macro amplify_kinesis.record} + /// {@macro amplify_record_cache.record} const Record({ required this.id, required this.streamName, - required this.partitionKey, required this.data, required this.dataSize, required this.retryCount, required this.createdAt, + this.partitionKey, }); /// Auto-incrementing primary key. final int id; - /// The name of the Kinesis Data Stream. + /// The name of the target stream. final String streamName; - /// The partition key for the record. - final String partitionKey; + /// Optional partition key (used by Kinesis Data Streams, null for Firehose). + final String? partitionKey; - /// The data blob to send to Kinesis. + /// The data blob to send. final Uint8List data; - /// The size of the data blob in bytes. + /// The size of the record in bytes. final int dataSize; /// The number of times this record has been retried. @@ -45,6 +46,5 @@ final class Record { @override String toString() => 'Record(id: $id, streamName: $streamName, ' - 'partitionKey: $partitionKey, dataSize: $dataSize, ' - 'retryCount: $retryCount)'; + 'dataSize: $dataSize, retryCount: $retryCount)'; } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/model/record_data.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/model/record_data.dart similarity index 73% rename from packages/kinesis/amplify_kinesis_dart/lib/src/model/record_data.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/model/record_data.dart index 149c2302406..5421fbd4ab4 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/model/record_data.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/model/record_data.dart @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/// {@template amplify_kinesis.record_data} +/// {@template amplify_record_cache.record_data} /// Data returned from a record operation. /// {@endtemplate} final class RecordData { - /// {@macro amplify_kinesis.record_data} + /// {@macro amplify_record_cache.record_data} const RecordData(); @override diff --git a/packages/kinesis/amplify_record_cache_dart/lib/src/model/record_input.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/model/record_input.dart new file mode 100644 index 00000000000..f622c604219 --- /dev/null +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/model/record_input.dart @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:typed_data'; + +/// {@template amplify_record_cache.record_input} +/// A record to be persisted in the cache before sending. +/// +/// The [dataSize] is provided by the caller so that each service can +/// compute it according to its own rules: +/// - Kinesis Data Streams: `data.length + utf8.encode(partitionKey).length` +/// - Firehose: `data.length` +/// {@endtemplate} +final class RecordInput { + /// Creates a new record input. + RecordInput({ + required this.data, + required this.streamName, + required this.dataSize, + required this.createdAt, + this.partitionKey, + }); + + /// Creates a record input with the current timestamp. + factory RecordInput.now({ + required Uint8List data, + required String streamName, + required int dataSize, + String? partitionKey, + }) { + return RecordInput( + data: data, + streamName: streamName, + dataSize: dataSize, + createdAt: DateTime.now(), + partitionKey: partitionKey, + ); + } + + /// The data blob. + final Uint8List data; + + /// The name of the target stream. + final String streamName; + + /// Optional partition key (used by KDS, null for Firehose). + final String? partitionKey; + + /// The size of the record in bytes (caller-computed). + final int dataSize; + + /// Timestamp of when the record was created. + final DateTime createdAt; +} diff --git a/packages/kinesis/amplify_record_cache_dart/lib/src/sender/sender.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/sender/sender.dart new file mode 100644 index 00000000000..0fe6cde261d --- /dev/null +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/sender/sender.dart @@ -0,0 +1,45 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import 'package:amplify_record_cache_dart/src/model/record.dart'; + +/// Result of a batch send operation. +/// +/// Records are categorized into three buckets: +/// - [successfulIds]: records accepted by the service. +/// - [retryableIds]: records that failed but can be retried. +/// - [failedIds]: records that exceeded the retry limit and should be deleted. +final class SendResult { + /// Creates a new [SendResult]. + const SendResult({ + required this.successfulIds, + required this.retryableIds, + required this.failedIds, + }); + + /// IDs of records that were successfully sent. + final List successfulIds; + + /// IDs of records that failed but can be retried (retry count < max). + final List retryableIds; + + /// IDs of records that exceeded the retry limit and should be deleted. + final List failedIds; +} + +/// {@template amplify_record_cache.sender} +/// Abstract interface for sending a batch of records to a streaming service. +/// +/// Implementations handle the service-specific API call (e.g. Kinesis +/// `PutRecords`, Firehose `PutRecordBatch`) and categorize the response +/// into successful, retryable, and failed records. +/// {@endtemplate} +// ignore: one_member_abstracts +abstract interface class Sender { + /// Sends a batch of records to the specified stream and returns the + /// categorized result. + Future sendBatch({ + required String streamName, + required List records, + }); +} diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform.dart similarity index 100% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform.dart diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_stub.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_stub.dart similarity index 73% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_stub.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_stub.dart index 2cbf1693390..eda24335ef2 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_stub.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_stub.dart @@ -3,7 +3,7 @@ import 'dart:async'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; /// Creates a platform-specific [RecordStorage] instance. /// @@ -13,6 +13,11 @@ Future createPlatformRecordStorage({ required String identifier, required FutureOr? storagePath, required int maxCacheBytes, + required int maxRecordsPerBatch, + required int maxBytesPerBatch, + required int maxRecordSizeBytes, + required String dbPrefix, + required String storeName, }) { throw UnsupportedError( 'Cannot create RecordStorage: no platform implementation available.', diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_vm.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_vm.dart similarity index 58% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_vm.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_vm.dart index 273caba33af..9ee9d72b91a 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_vm.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_vm.dart @@ -4,26 +4,35 @@ import 'dart:async'; import 'package:amplify_foundation_dart/amplify_foundation_dart.dart'; -import 'package:amplify_kinesis_dart/src/db/kinesis_record_database.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_sqlite.dart'; +import 'package:amplify_record_cache_dart/src/db/record_cache_database.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage_sqlite.dart'; /// Creates a [SqliteRecordStorage] for VM platforms. Future createPlatformRecordStorage({ required String identifier, required FutureOr? storagePath, required int maxCacheBytes, + required int maxRecordsPerBatch, + required int maxBytesPerBatch, + required int maxRecordSizeBytes, + required String dbPrefix, + required String storeName, }) async { assert(storagePath != null, 'storagePath is required on VM platforms.'); AmplifyLogging.logger( 'RecordStorage', ).info('Using SQLite storage (path: $storagePath)'); - final database = KinesisRecordDatabase( + final database = RecordCacheDatabase( + dbPrefix: dbPrefix, identifier: identifier, storagePath: storagePath, ); return SqliteRecordStorage.create( database: database, maxCacheBytes: maxCacheBytes, + maxRecordsPerBatch: maxRecordsPerBatch, + maxBytesPerBatch: maxBytesPerBatch, + maxRecordSizeBytes: maxRecordSizeBytes, ); } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_web.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_web.dart similarity index 59% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_web.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_web.dart index 8b96184e92c..f1b37aeb35f 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/platform/record_storage_platform_web.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/platform/record_storage_platform_web.dart @@ -4,9 +4,9 @@ import 'dart:async'; import 'package:amplify_foundation_dart/amplify_foundation_dart.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_indexeddb.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage_memory.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage_indexeddb.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage_memory.dart'; /// Creates a web [RecordStorage] instance. /// @@ -16,6 +16,11 @@ Future createPlatformRecordStorage({ required String identifier, required FutureOr? storagePath, required int maxCacheBytes, + required int maxRecordsPerBatch, + required int maxBytesPerBatch, + required int maxRecordSizeBytes, + required String dbPrefix, + required String storeName, }) async { final logger = AmplifyLogging.logger('RecordStorage'); // storagePath is ignored on web. @@ -24,11 +29,21 @@ Future createPlatformRecordStorage({ return IndexedDbRecordStorage.create( identifier: identifier, maxCacheBytes: maxCacheBytes, + maxRecordsPerBatch: maxRecordsPerBatch, + maxBytesPerBatch: maxBytesPerBatch, + maxRecordSizeBytes: maxRecordSizeBytes, + dbPrefix: dbPrefix, + storeName: storeName, ); } logger.warn( 'IndexedDB is not available. Falling back to in-memory storage. ' 'Records will be lost when the page is closed.', ); - return InMemoryRecordStorage(maxCacheBytes: maxCacheBytes); + return InMemoryRecordStorage( + maxCacheBytes: maxCacheBytes, + maxRecordsPerBatch: maxRecordsPerBatch, + maxBytesPerBatch: maxBytesPerBatch, + maxRecordSizeBytes: maxRecordSizeBytes, + ); } diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage.dart similarity index 67% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage.dart index 5833c12d7d0..91cf6784e27 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage.dart @@ -1,23 +1,27 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'package:amplify_kinesis_dart/src/exception/amplify_kinesis_exception.dart' - show defaultRecoverySuggestion; -import 'package:amplify_kinesis_dart/src/exception/record_cache_exception.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/kinesis_limits.dart' as limits; -import 'package:amplify_kinesis_dart/src/model/record.dart'; +import 'package:amplify_record_cache_dart/src/exception/record_cache_exception.dart'; +import 'package:amplify_record_cache_dart/src/model/record.dart'; +import 'package:amplify_record_cache_dart/src/model/record_input.dart'; import 'package:meta/meta.dart'; -export 'package:amplify_kinesis_dart/src/model/record.dart'; +export 'package:amplify_record_cache_dart/src/model/record.dart'; -/// {@template amplify_kinesis.record_storage} +/// Default recovery suggestion for wrapped database errors. +const defaultRecoverySuggestion = + 'This is an internal error. Please report it as a bug.'; + +/// {@template amplify_record_cache.record_storage} /// Abstract base class for record persistence. /// /// Implementations provide platform-specific storage (SQLite on VM, -/// IndexedDB on web, in-memory fallback). Validation of partition key -/// length, record size, and cache limits is handled here in [addRecord]; -/// subclasses implement [writeRecord] for the actual write. +/// IndexedDB on web, in-memory fallback). Validation of record size +/// and cache limits is handled here in [addRecord]; subclasses +/// implement [writeRecord] for the actual write. +/// +/// Service-specific validation (e.g. partition key length for KDS) +/// should be performed by the client before calling [addRecord]. /// /// All public methods wrap unexpected errors as /// [RecordCacheDatabaseException]. Subclasses throw @@ -25,26 +29,23 @@ export 'package:amplify_kinesis_dart/src/model/record.dart'; /// caught and wrapped automatically. /// {@endtemplate} abstract class RecordStorage { - /// {@macro amplify_kinesis.record_storage} + /// {@macro amplify_record_cache.record_storage} RecordStorage({ required int maxCacheBytes, - int maxRecordsPerStream = limits.maxRecordsPerStream, - int maxBytesPerStream = limits.maxPutRecordsSizeBytes, - int maxRecordSizeBytes = limits.maxRecordSizeBytes, - int maxPartitionKeyLength = limits.maxPartitionKeyLength, + required int maxRecordsPerBatch, + required int maxBytesPerBatch, + required int maxRecordSizeBytes, int initialCachedSize = 0, }) : _maxCacheBytes = maxCacheBytes, - _maxRecordsPerStream = maxRecordsPerStream, - _maxBytesPerStream = maxBytesPerStream, + _maxRecordsPerBatch = maxRecordsPerBatch, + _maxBytesPerBatch = maxBytesPerBatch, _maxRecordSizeBytes = maxRecordSizeBytes, - _maxPartitionKeyLength = maxPartitionKeyLength, cachedSize = initialCachedSize; final int _maxCacheBytes; - final int _maxRecordsPerStream; - final int _maxBytesPerStream; + final int _maxRecordsPerBatch; + final int _maxBytesPerBatch; final int _maxRecordSizeBytes; - final int _maxPartitionKeyLength; /// The current total cached size in bytes. @protected @@ -53,11 +54,11 @@ abstract class RecordStorage { /// The maximum cache size in bytes. int get maxCacheBytes => _maxCacheBytes; - /// Maximum number of records per stream in a single batch. - int get maxRecordsPerStream => _maxRecordsPerStream; + /// Maximum number of records per batch. + int get maxRecordsPerBatch => _maxRecordsPerBatch; - /// Maximum total bytes per stream in a single batch. - int get maxBytesPerStream => _maxBytesPerStream; + /// Maximum total bytes per batch. + int get maxBytesPerBatch => _maxBytesPerBatch; /// Validates and saves a record to storage. /// Throws [RecordCacheValidationException] on invalid input. @@ -65,20 +66,11 @@ abstract class RecordStorage { /// Throws [RecordCacheDatabaseException] on storage errors. Future addRecord(RecordInput record) => _wrap('Failed to add record to cache', () async { - final codePoints = record.partitionKey.runes.length; - if (codePoints == 0 || codePoints > _maxPartitionKeyLength) { - throw RecordCacheValidationException( - 'Partition key length ($codePoints) is outside the allowed ' - 'range of 1-$_maxPartitionKeyLength characters.', - 'Use a partition key between 1 and ' - '$_maxPartitionKeyLength characters.', - ); - } if (record.dataSize > _maxRecordSizeBytes) { throw RecordCacheValidationException( 'Record size (${record.dataSize} bytes) exceeds the maximum ' - 'of $_maxRecordSizeBytes bytes (partition key + data blob).', - 'Reduce the record payload size or use a shorter partition key.', + 'of $_maxRecordSizeBytes bytes.', + 'Reduce the record payload size.', ); } if (cachedSize + record.dataSize > _maxCacheBytes) { diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_indexeddb.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_indexeddb.dart similarity index 77% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_indexeddb.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_indexeddb.dart index 3b6a82fbe11..edcf9ca4742 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_indexeddb.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_indexeddb.dart @@ -5,13 +5,13 @@ import 'dart:async'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; +import 'package:amplify_record_cache_dart/src/model/record_input.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; // ignore: implementation_imports import 'package:aws_common/src/js/indexed_db.dart'; import 'package:web/web.dart'; -/// {@template amplify_kinesis.indexeddb_record_storage} +/// {@template amplify_record_cache.indexeddb_record_storage} /// IndexedDB-backed [RecordStorage] implementation for web. /// /// Use [create] to open the database and eagerly compute the initial @@ -20,29 +20,48 @@ import 'package:web/web.dart'; final class IndexedDbRecordStorage extends RecordStorage { IndexedDbRecordStorage._({ required super.maxCacheBytes, + required super.maxRecordsPerBatch, + required super.maxBytesPerBatch, + required super.maxRecordSizeBytes, required super.initialCachedSize, required IDBDatabase database, - }) : _database = database; + required String storeName, + }) : _database = database, + _storeName = storeName; - /// {@macro amplify_kinesis.indexeddb_record_storage} + /// {@macro amplify_record_cache.indexeddb_record_storage} /// /// Opens the IndexedDB database and eagerly computes the initial /// cache size. + /// + /// [dbPrefix] is used to namespace the database (e.g. `amplify_kinesis_`, + /// `amplify_firehose_`). + /// [storeName] is the object store name (e.g. `kinesis_records`, + /// `firehose_records`). static Future create({ required int maxCacheBytes, + required int maxRecordsPerBatch, + required int maxBytesPerBatch, + required int maxRecordSizeBytes, required String identifier, + required String dbPrefix, + required String storeName, }) async { - final database = await _openDatabase('amplify_kinesis_$identifier'); - final initialSize = await _computeCacheSize(database); + final database = await _openDatabase('$dbPrefix$identifier', storeName); + final initialSize = await _computeCacheSize(database, storeName); return IndexedDbRecordStorage._( maxCacheBytes: maxCacheBytes, + maxRecordsPerBatch: maxRecordsPerBatch, + maxBytesPerBatch: maxBytesPerBatch, + maxRecordSizeBytes: maxRecordSizeBytes, initialCachedSize: initialSize, database: database, + storeName: storeName, ); } final IDBDatabase _database; - static const _storeName = 'kinesis_records'; + final String _storeName; /// Returns an object store handle within a new transaction. IDBObjectStore _getStore([String mode = 'readwrite']) { @@ -54,7 +73,7 @@ final class IndexedDbRecordStorage extends RecordStorage { Future writeRecord(RecordInput record) async { final obj = JSObject() ..setProperty('stream_name'.toJS, record.streamName.toJS) - ..setProperty('partition_key'.toJS, record.partitionKey.toJS) + ..setProperty('partition_key'.toJS, (record.partitionKey ?? '').toJS) ..setProperty('data'.toJS, record.data.toJS) ..setProperty('data_size'.toJS, record.dataSize.toJS) ..setProperty('retry_count'.toJS, 0.toJS) @@ -82,8 +101,8 @@ final class IndexedDbRecordStorage extends RecordStorage { final stream = record.streamName; final count = streamCounts[stream] ?? 0; final size = streamSizes[stream] ?? 0; - if (count >= maxRecordsPerStream) continue; - if (size + record.dataSize > maxBytesPerStream) continue; + if (count >= maxRecordsPerBatch) continue; + if (size + record.dataSize > maxBytesPerBatch) continue; result.putIfAbsent(stream, () => []).add(record); streamCounts[stream] = count + 1; @@ -104,7 +123,7 @@ final class IndexedDbRecordStorage extends RecordStorage { } @override - Future doQueryCacheSize() => _computeCacheSize(_database); + Future doQueryCacheSize() => _computeCacheSize(_database, _storeName); @override Future doIncrementRetryCount(Iterable ids) async { @@ -161,10 +180,11 @@ final class IndexedDbRecordStorage extends RecordStorage { } static Record _jsToRecord(JSObject obj) { + final pk = obj.getProperty('partition_key'.toJS).toDart; return Record( id: obj.getProperty('id'.toJS).toDartInt, streamName: obj.getProperty('stream_name'.toJS).toDart, - partitionKey: obj.getProperty('partition_key'.toJS).toDart, + partitionKey: pk.isEmpty ? null : pk, data: (obj.getProperty('data'.toJS)).toDart, dataSize: obj.getProperty('data_size'.toJS).toDartInt, retryCount: obj.getProperty('retry_count'.toJS).toDartInt, @@ -173,7 +193,10 @@ final class IndexedDbRecordStorage extends RecordStorage { } /// Opens an IndexedDB database, creating the object store if needed. - static Future _openDatabase(String dbName) async { + static Future _openDatabase( + String dbName, + String storeName, + ) async { final db = indexedDB; if (db == null) { throw StateError('IndexedDB is not available'); @@ -182,10 +205,10 @@ final class IndexedDbRecordStorage extends RecordStorage { void onUpgradeNeeded(IDBVersionChangeEvent event) { final database = event.target?.getProperty('result'.toJS); final names = database?.objectStoreNames; - if (!(names?.contains(_storeName) ?? false)) { + if (!(names?.contains(storeName) ?? false)) { database! .createObjectStore( - _storeName, + storeName, IDBObjectStoreParameters(keyPath: 'id'.toJS, autoIncrement: true), ) .createIndex( @@ -205,9 +228,12 @@ final class IndexedDbRecordStorage extends RecordStorage { } /// Computes cache size from DB using a cursor. - static Future _computeCacheSize(IDBDatabase database) async { - final tx = database.transaction(_storeName.toJS, 'readonly'); - final store = tx.objectStore(_storeName); + static Future _computeCacheSize( + IDBDatabase database, + String storeName, + ) async { + final tx = database.transaction(storeName.toJS, 'readonly'); + final store = tx.objectStore(storeName); final request = store.openCursor(); final completer = Completer(); var total = 0; @@ -241,7 +267,7 @@ final class IndexedDbRecordStorage extends RecordStorage { static Future checkIsSupported() async { if (indexedDB == null) return false; try { - final request = indexedDB!.open('kinesis_idb_test', 1); + final request = indexedDB!.open('record_cache_idb_test', 1); await request.future; return true; } on Object { diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_memory.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_memory.dart similarity index 84% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_memory.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_memory.dart index 7131c6ae9a0..24708ffa5b2 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_memory.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_memory.dart @@ -3,21 +3,20 @@ import 'dart:collection'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; +import 'package:amplify_record_cache_dart/src/model/record_input.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; -/// {@template amplify_kinesis.in_memory_record_storage} +/// {@template amplify_record_cache.in_memory_record_storage} /// In-memory [RecordStorage] fallback for web when IndexedDB is unavailable. /// Records are not persisted. /// {@endtemplate} final class InMemoryRecordStorage extends RecordStorage { - /// {@macro amplify_kinesis.in_memory_record_storage} + /// {@macro amplify_record_cache.in_memory_record_storage} InMemoryRecordStorage({ required super.maxCacheBytes, - super.maxRecordsPerStream, - super.maxBytesPerStream, - super.maxRecordSizeBytes, - super.maxPartitionKeyLength, + required super.maxRecordsPerBatch, + required super.maxBytesPerBatch, + required super.maxRecordSizeBytes, }); int _nextId = 1; @@ -54,8 +53,8 @@ final class InMemoryRecordStorage extends RecordStorage { final stream = record.streamName; final count = streamCounts[stream] ?? 0; final size = streamSizes[stream] ?? 0; - if (count >= maxRecordsPerStream) continue; - if (size + record.dataSize > maxBytesPerStream) continue; + if (count >= maxRecordsPerBatch) continue; + if (size + record.dataSize > maxBytesPerBatch) continue; result.putIfAbsent(stream, () => []).add(record); streamCounts[stream] = count + 1; diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_sqlite.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart similarity index 60% rename from packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_sqlite.dart rename to packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart index d3b5a88c9e3..c3de48e4d86 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/storage/record_storage_sqlite.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart @@ -3,60 +3,65 @@ import 'dart:async'; -import 'package:amplify_kinesis_dart/src/db/kinesis_record_database.dart'; -import 'package:amplify_kinesis_dart/src/impl/kinesis_record.dart'; -import 'package:amplify_kinesis_dart/src/impl/storage/record_storage.dart'; +import 'package:amplify_record_cache_dart/src/db/record_cache_database.dart'; +import 'package:amplify_record_cache_dart/src/model/record_input.dart'; +import 'package:amplify_record_cache_dart/src/storage/record_storage.dart'; import 'package:drift/drift.dart'; import 'package:meta/meta.dart'; -/// {@template amplify_kinesis.sqlite_record_storage} +/// {@template amplify_record_cache.sqlite_record_storage} /// SQLite-backed [RecordStorage] implementation using Drift. /// /// Used on VM (iOS, macOS, Linux, Windows, Android) platforms. /// {@endtemplate} final class SqliteRecordStorage extends RecordStorage { - /// {@macro amplify_kinesis.sqlite_record_storage} + /// {@macro amplify_record_cache.sqlite_record_storage} /// /// Prefer [create] for production use — it eagerly queries the cache /// size from the database. This constructor is available for tests /// where the database starts empty. SqliteRecordStorage({ - required KinesisRecordDatabase database, + required RecordCacheDatabase database, required super.maxCacheBytes, - super.maxRecordsPerStream, - super.maxBytesPerStream, - super.maxRecordSizeBytes, - super.maxPartitionKeyLength, + required super.maxRecordsPerBatch, + required super.maxBytesPerBatch, + required super.maxRecordSizeBytes, super.initialCachedSize, }) : _db = database; - /// {@macro amplify_kinesis.sqlite_record_storage} + /// {@macro amplify_record_cache.sqlite_record_storage} /// - /// Opens the database. + /// Opens the database and eagerly queries the cache size. static Future create({ - required KinesisRecordDatabase database, + required RecordCacheDatabase database, required int maxCacheBytes, + required int maxRecordsPerBatch, + required int maxBytesPerBatch, + required int maxRecordSizeBytes, }) async { final initialSize = await _queryCacheSize(database); return SqliteRecordStorage( database: database, maxCacheBytes: maxCacheBytes, + maxRecordsPerBatch: maxRecordsPerBatch, + maxBytesPerBatch: maxBytesPerBatch, + maxRecordSizeBytes: maxRecordSizeBytes, initialCachedSize: initialSize, ); } - final KinesisRecordDatabase _db; + final RecordCacheDatabase _db; /// Provides access to the underlying database (for testing). - KinesisRecordDatabase get database => _db; + RecordCacheDatabase get database => _db; /// Queries the current cache size from the database. - static Future _queryCacheSize(KinesisRecordDatabase db) async { - final query = db.selectOnly(db.kinesisRecords) - ..addColumns([db.kinesisRecords.dataSize.sum()]); + static Future _queryCacheSize(RecordCacheDatabase db) async { + final query = db.selectOnly(db.cachedRecords) + ..addColumns([db.cachedRecords.dataSize.sum()]); final result = await query.getSingleOrNull(); if (result == null) return 0; - return result.read(db.kinesisRecords.dataSize.sum()) ?? 0; + return result.read(db.cachedRecords.dataSize.sum()) ?? 0; } /// Returns the in-memory cached size directly (O(1), no DB query). @@ -66,11 +71,11 @@ final class SqliteRecordStorage extends RecordStorage { @override Future writeRecord(RecordInput record) async { await _db - .into(_db.kinesisRecords) + .into(_db.cachedRecords) .insert( - KinesisRecordsCompanion.insert( + CachedRecordsCompanion.insert( streamName: record.streamName, - partitionKey: record.partitionKey, + partitionKey: Value(record.partitionKey ?? ''), data: record.data, dataSize: record.dataSize, createdAt: record.createdAt.millisecondsSinceEpoch, @@ -83,21 +88,23 @@ final class SqliteRecordStorage extends RecordStorage { final results = await _db .customSelect( ''' - SELECT id, stream_name, partition_key, data, data_size, retry_count, created_at + SELECT id, stream_name, partition_key, data, data_size, retry_count, + created_at FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY stream_name ORDER BY id) as rn, - SUM(data_size) OVER (PARTITION BY stream_name ORDER BY id) as running_size - FROM kinesis_records + SUM(data_size) OVER (PARTITION BY stream_name ORDER BY id) + as running_size + FROM cached_records ) WHERE rn <= ?1 AND running_size <= ?2 ORDER BY stream_name, id ''', variables: [ - Variable.withInt(maxRecordsPerStream), - Variable.withInt(maxBytesPerStream), + Variable.withInt(maxRecordsPerBatch), + Variable.withInt(maxBytesPerBatch), ], - readsFrom: {_db.kinesisRecords}, + readsFrom: {_db.cachedRecords}, ) .get(); @@ -112,7 +119,7 @@ final class SqliteRecordStorage extends RecordStorage { @override Future doDeleteRecords(Iterable ids) async { if (ids.isEmpty) return; - await (_db.delete(_db.kinesisRecords)..where((t) => t.id.isIn(ids))).go(); + await (_db.delete(_db.cachedRecords)..where((t) => t.id.isIn(ids))).go(); } @override @@ -121,25 +128,25 @@ final class SqliteRecordStorage extends RecordStorage { @override Future doIncrementRetryCount(Iterable ids) async { if (ids.isEmpty) return; - await (_db.update(_db.kinesisRecords)..where((t) => t.id.isIn(ids))).write( - KinesisRecordsCompanion.custom( - retryCount: _db.kinesisRecords.retryCount + const Constant(1), + await (_db.update(_db.cachedRecords)..where((t) => t.id.isIn(ids))).write( + CachedRecordsCompanion.custom( + retryCount: _db.cachedRecords.retryCount + const Constant(1), ), ); } @override Future doGetRecordCount() async { - final query = _db.selectOnly(_db.kinesisRecords) - ..addColumns([_db.kinesisRecords.id.count()]); + final query = _db.selectOnly(_db.cachedRecords) + ..addColumns([_db.cachedRecords.id.count()]); final result = await query.getSingleOrNull(); if (result == null) return 0; - return result.read(_db.kinesisRecords.id.count()) ?? 0; + return result.read(_db.cachedRecords.id.count()) ?? 0; } @override Future doClearRecords() async { - await _db.delete(_db.kinesisRecords).go(); + await _db.delete(_db.cachedRecords).go(); } @override @@ -148,10 +155,11 @@ final class SqliteRecordStorage extends RecordStorage { } Record _rowToRecord(QueryRow row) { + final pk = row.read('partition_key'); return Record( id: row.read('id'), streamName: row.read('stream_name'), - partitionKey: row.read('partition_key'), + partitionKey: pk.isEmpty ? null : pk, data: row.read('data'), dataSize: row.read('data_size'), retryCount: row.read('retry_count'), diff --git a/packages/kinesis/amplify_record_cache_dart/pubspec.yaml b/packages/kinesis/amplify_record_cache_dart/pubspec.yaml new file mode 100644 index 00000000000..9ba99f60029 --- /dev/null +++ b/packages/kinesis/amplify_record_cache_dart/pubspec.yaml @@ -0,0 +1,33 @@ +name: amplify_record_cache_dart +description: Shared record caching infrastructure for Amplify streaming clients (Kinesis Data Streams, Firehose). Provides offline storage, batching, retry logic, and auto-flush scheduling. +version: 0.1.0 +homepage: https://docs.amplify.aws/lib/q/platform/flutter/ +repository: https://github.com/aws-amplify/amplify-flutter/tree/main/packages/kinesis/amplify_record_cache_dart +issue_tracker: https://github.com/aws-amplify/amplify-flutter/issues + +topics: + - aws + - kinesis + - streaming + - caching + - aws-amplify + +environment: + sdk: ^3.9.0 + +dependencies: + amplify_db_common_dart: ">=0.4.17 <0.5.0" + amplify_foundation_dart: ">=2.11.0 <2.12.0" + aws_common: ">=0.7.12 <0.8.0" + drift: ^2.25.0 + meta: ^1.16.0 + smithy: ">=0.7.10 <0.8.0" + web: ^1.1.1 + +dev_dependencies: + amplify_lints: ">=3.1.4 <3.2.0" + build_runner: ^2.4.15 + drift_dev: ^2.25.1 + fake_async: ^1.3.0 + mocktail: ^1.0.0 + test: ^1.22.1 From 40d379121b5c41567b5ab17a1bd7a66e26905250 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:36:28 -0700 Subject: [PATCH 02/10] chore: revert dependabot.yaml changes from aft generate The aft generate workflows command regenerated the entire dependabot.yaml with entries for many unrelated packages. Reverting to keep this PR scoped to the record cache extraction only. --- .github/dependabot.yaml | 796 ++++------------------------------------ 1 file changed, 72 insertions(+), 724 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 6a10a6f5671..fe14b7e0da0 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1985,40 +1985,6 @@ updates: test: patterns: - "test" - - package-ecosystem: "pub" - directory: "packages/amplify/amplify_flutter/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_api" - - dependency-name: "amplify_api_dart" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_datastore" - - dependency-name: "amplify_datastore_plugin_interface" - - dependency-name: "amplify_storage_s3" - - dependency-name: "amplify_storage_s3_dart" - package-ecosystem: "pub" directory: "packages/amplify_core/doc" schedule: @@ -2061,39 +2027,6 @@ updates: test: patterns: - "test" - - package-ecosystem: "pub" - directory: "packages/amplify_datastore/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_api" - - dependency-name: "amplify_api_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "amplify_datastore" - - dependency-name: "amplify_datastore_plugin_interface" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_authenticator" - package-ecosystem: "pub" directory: "packages/amplify_foundation/amplify_foundation_dart" schedule: @@ -2141,184 +2074,6 @@ updates: test: patterns: - "test" - - package-ecosystem: "pub" - directory: "packages/amplify_native_legacy_wrapper/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_native_legacy_wrapper" - - dependency-name: "amplify_lints" - - dependency-name: "aws_common" - - package-ecosystem: "pub" - directory: "packages/analytics/amplify_analytics_pinpoint/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_api" - - dependency-name: "amplify_api_dart" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - build_runner: - patterns: - - "build_runner" - built_value: - patterns: - - "built_value" - json_annotation: - patterns: - - "json_annotation" - json_serializable: - patterns: - - "json_serializable" - - package-ecosystem: "pub" - directory: "packages/api/amplify_api/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_api" - - dependency-name: "amplify_api_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_authenticator" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - async: - patterns: - - "async" - - package-ecosystem: "pub" - directory: "packages/auth/amplify_auth_cognito/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_api" - - dependency-name: "amplify_api_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_native_legacy_wrapper" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - async: - patterns: - - "async" - http: - patterns: - - "http" - stack_trace: - patterns: - - "stack_trace" - test: - patterns: - - "test" - - package-ecosystem: "pub" - directory: "packages/auth/amplify_auth_cognito_dart/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "smithy_codegen" - - dependency-name: "example_common" - - dependency-name: "amplify_api_dart" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - mime: - patterns: - - "mime" - test: - patterns: - - "test" - package-ecosystem: "pub" directory: "packages/auth/amplify_auth_cognito_test" schedule: @@ -2329,309 +2084,41 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - async: - patterns: - - "async" - built_value: - patterns: - - "built_value" - http: - patterns: - - "http" - test: - patterns: - - "test" - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - - package-ecosystem: "pub" - directory: "packages/authenticator/amplify_authenticator/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_api" - - dependency-name: "amplify_api_dart" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - uuid: - patterns: - - "uuid" - - package-ecosystem: "pub" - directory: "packages/authenticator/amplify_authenticator_test" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_flutter" - - package-ecosystem: "pub" - directory: "packages/authenticator/amplify_authenticator_test/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_flutter" - - package-ecosystem: "pub" - directory: "packages/aws_signature_v4/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - - package-ecosystem: "pub" - directory: "packages/common/amplify_db_common/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - drift: - patterns: - - "drift" - drift_dev: - patterns: - - "drift_dev" - build_runner: - patterns: - - "build_runner" - - package-ecosystem: "pub" - directory: "packages/common/amplify_db_common_dart/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "example_common" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - drift: - patterns: - - "drift" - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - drift_dev: - patterns: - - "drift_dev" - - package-ecosystem: "pub" - directory: "packages/example_common" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_lints" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - test: - patterns: - - "test" - - package-ecosystem: "pub" - directory: "packages/example_common/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "example_common" - - dependency-name: "amplify_lints" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_firehose_dart" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - built_value: - patterns: - - "built_value" - drift: - patterns: - - "drift" - build_runner: - patterns: - - "build_runner" - built_value_generator: - patterns: - - "built_value_generator" - drift_dev: - patterns: - - "drift_dev" - test: - patterns: - - "test" - - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_lints" - - dependency-name: "amplify_kinesis_dart" + - dependency-name: "amplify_analytics_pinpoint_dart" - dependency-name: "amplify_core" - dependency-name: "aws_common" + - dependency-name: "amplify_lints" - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "amplify_record_cache_dart" - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "worker_bee" + - dependency-name: "worker_bee_builder" - dependency-name: "smithy" - dependency-name: "smithy_aws" + - dependency-name: "amplify_auth_cognito_dart" + - dependency-name: "smithy_codegen" + # Group dependencies which have a constraint set in the global "pubspec.yaml" + groups: + async: + patterns: + - "async" + built_value: + patterns: + - "built_value" + http: + patterns: + - "http" + test: + patterns: + - "test" + build_runner: + patterns: + - "build_runner" + build_web_compilers: + patterns: + - "build_web_compilers" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis/example" + directory: "packages/authenticator/amplify_authenticator_test" schedule: interval: "daily" ignore: @@ -2640,6 +2127,7 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages + - dependency-name: "amplify_authenticator" - dependency-name: "amplify_auth_cognito" - dependency-name: "amplify_analytics_pinpoint" - dependency-name: "amplify_analytics_pinpoint_dart" @@ -2658,14 +2146,8 @@ updates: - dependency-name: "amplify_auth_cognito_dart" - dependency-name: "smithy_codegen" - dependency-name: "amplify_flutter" - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "amplify_kinesis" - - dependency-name: "amplify_kinesis_dart" - - dependency-name: "amplify_record_cache_dart" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis/example" + directory: "packages/authenticator/amplify_authenticator_test/example" schedule: interval: "daily" ignore: @@ -2674,6 +2156,7 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages + - dependency-name: "amplify_authenticator" - dependency-name: "amplify_auth_cognito" - dependency-name: "amplify_analytics_pinpoint" - dependency-name: "amplify_analytics_pinpoint_dart" @@ -2692,14 +2175,8 @@ updates: - dependency-name: "amplify_auth_cognito_dart" - dependency-name: "smithy_codegen" - dependency-name: "amplify_flutter" - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "amplify_kinesis" - - dependency-name: "amplify_kinesis_dart" - - dependency-name: "amplify_record_cache_dart" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_kinesis_dart" + directory: "packages/example_common" schedule: interval: "daily" ignore: @@ -2708,29 +2185,14 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "amplify_foundation_dart_bridge" - - dependency-name: "amplify_record_cache_dart" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" # Group dependencies which have a constraint set in the global "pubspec.yaml" groups: - built_value: - patterns: - - "built_value" - drift: - patterns: - - "drift" test: patterns: - "test" - package-ecosystem: "pub" - directory: "packages/kinesis/amplify_record_cache_dart" + directory: "packages/example_common/example" schedule: interval: "daily" ignore: @@ -2739,29 +2201,18 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" + - dependency-name: "example_common" - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_foundation_dart" - - dependency-name: "smithy" # Group dependencies which have a constraint set in the global "pubspec.yaml" groups: - drift: - patterns: - - "drift" build_runner: patterns: - "build_runner" - drift_dev: - patterns: - - "drift_dev" - test: + build_web_compilers: patterns: - - "test" + - "build_web_compilers" - package-ecosystem: "pub" - directory: "packages/notifications/push/amplify_push_notifications/example" + directory: "packages/kinesis/amplify_kinesis" schedule: interval: "daily" ignore: @@ -2770,17 +2221,18 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_push_notifications" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_lints" + - dependency-name: "amplify_kinesis_dart" - dependency-name: "amplify_core" - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" - package-ecosystem: "pub" - directory: "packages/notifications/push/amplify_push_notifications_pinpoint/example" + directory: "packages/kinesis/amplify_kinesis/example" schedule: interval: "daily" ignore: @@ -2807,26 +2259,13 @@ updates: - dependency-name: "amplify_auth_cognito_dart" - dependency-name: "smithy_codegen" - dependency-name: "amplify_flutter" - - dependency-name: "amplify_push_notifications_pinpoint" - - dependency-name: "amplify_push_notifications" - - package-ecosystem: "pub" - directory: "packages/secure_storage/amplify_secure_storage/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" + - dependency-name: "amplify_authenticator" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "amplify_kinesis" + - dependency-name: "amplify_kinesis_dart" - package-ecosystem: "pub" - directory: "packages/secure_storage/amplify_secure_storage_dart/example" + directory: "packages/kinesis/amplify_kinesis_dart" schedule: interval: "daily" ignore: @@ -2835,20 +2274,35 @@ updates: update-types: - "version-update:semver-patch" # Ignore all repo packages - - dependency-name: "amplify_secure_storage_dart" + - dependency-name: "amplify_core" - dependency-name: "aws_common" - dependency-name: "amplify_lints" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "example_common" + - dependency-name: "aws_signature_v4" + - dependency-name: "amplify_db_common_dart" + - dependency-name: "amplify_foundation_dart" + - dependency-name: "amplify_foundation_dart_bridge" + - dependency-name: "smithy" + - dependency-name: "smithy_aws" # Group dependencies which have a constraint set in the global "pubspec.yaml" groups: + built_value: + patterns: + - "built_value" + drift: + patterns: + - "drift" build_runner: patterns: - "build_runner" - build_web_compilers: + built_value_generator: patterns: - - "build_web_compilers" + - "built_value_generator" + drift_dev: + patterns: + - "drift_dev" + test: + patterns: + - "test" - package-ecosystem: "pub" directory: "packages/secure_storage/amplify_secure_storage_test" schedule: @@ -3425,84 +2879,6 @@ updates: xml: patterns: - "xml" - - package-ecosystem: "pub" - directory: "packages/storage/amplify_storage_s3/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_auth_cognito" - - dependency-name: "amplify_analytics_pinpoint" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "amplify_db_common" - - dependency-name: "amplify_secure_storage" - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_flutter" - - dependency-name: "amplify_authenticator" - - dependency-name: "amplify_storage_s3" - - dependency-name: "amplify_storage_s3_dart" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - drift: - patterns: - - "drift" - http: - patterns: - - "http" - stack_trace: - patterns: - - "stack_trace" - test: - patterns: - - "test" - - package-ecosystem: "pub" - directory: "packages/storage/amplify_storage_s3_dart/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "amplify_auth_cognito_dart" - - dependency-name: "amplify_analytics_pinpoint_dart" - - dependency-name: "amplify_core" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "aws_signature_v4" - - dependency-name: "amplify_db_common_dart" - - dependency-name: "amplify_secure_storage_dart" - - dependency-name: "worker_bee" - - dependency-name: "worker_bee_builder" - - dependency-name: "smithy" - - dependency-name: "smithy_aws" - - dependency-name: "smithy_codegen" - - dependency-name: "amplify_storage_s3_dart" - - dependency-name: "example_common" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - package-ecosystem: "pub" directory: "packages/test/amplify_auth_integration_test" schedule: @@ -3681,34 +3057,6 @@ updates: test: patterns: - "test" - - package-ecosystem: "pub" - directory: "packages/worker_bee/worker_bee/example" - schedule: - interval: "daily" - ignore: - # Ignore patch version bumps - - dependency-name: "*" - update-types: - - "version-update:semver-patch" - # Ignore all repo packages - - dependency-name: "worker_bee" - - dependency-name: "aws_common" - - dependency-name: "amplify_lints" - - dependency-name: "worker_bee_builder" - # Group dependencies which have a constraint set in the global "pubspec.yaml" - groups: - built_value: - patterns: - - "built_value" - build_runner: - patterns: - - "build_runner" - build_web_compilers: - patterns: - - "build_web_compilers" - built_value_generator: - patterns: - - "built_value_generator" - package-ecosystem: "pub" directory: "templates/dart-package/hooks" schedule: From f6a3b2a076d0e1bc47a578050170fcb7dd27e214 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:47:27 -0700 Subject: [PATCH 03/10] fix: remove IndexedDB storage from barrel export to fix VM test compilation The barrel file was unconditionally exporting record_storage_indexeddb.dart which imports dart:js_interop. This caused VM tests to fail with "Dart library 'dart:js_interop' is not available on this platform". IndexedDB storage is only reachable through the platform conditional export (record_storage_platform_web.dart), not the barrel. --- .../amplify_record_cache_dart/lib/amplify_record_cache_dart.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart b/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart index 9abb024c6c8..49c0ed289dd 100644 --- a/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart @@ -24,6 +24,5 @@ export 'src/sender/sender.dart'; // Storage export 'src/storage/platform/record_storage_platform.dart'; export 'src/storage/record_storage.dart' hide defaultRecoverySuggestion; -export 'src/storage/record_storage_indexeddb.dart'; export 'src/storage/record_storage_memory.dart'; export 'src/storage/record_storage_sqlite.dart'; From 89411f26617136f8b7b150e0967a813d4b9cf4b3 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:37:53 -0700 Subject: [PATCH 04/10] fix(test): close default client before creating large-cache client in validation test Drift warns when multiple RecordCacheDatabase instances exist simultaneously. Close the default client first, then reassign so tearDown handles cleanup. --- .../amplify_kinesis_dart/test/record_validation_test.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart index 115c26b7900..fab9a055b65 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart @@ -68,8 +68,9 @@ void main() { group('per-record size limit', () { test('record exactly at max size is accepted', () async { - // RecordClient validates against limits.maxRecordSizeBytes (10 MiB). - // Use a large-cache client so the cache limit doesn't interfere. + // Close the default client so we can create one with a larger cache. + await client.close(); + final largeDb = createTestDatabase(); final largeStorage = SqliteRecordStorage( database: largeDb, @@ -99,7 +100,8 @@ void main() { .toList(); expect(records, hasLength(1)); - await largeClient.close(); + // Reassign so tearDown closes this client instead. + client = largeClient; }); test('record exceeding max size by one byte is rejected', () async { From 2496a74a9d30c34a6575794637415de0967ce5ee Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:47:22 -0700 Subject: [PATCH 05/10] fix(test): increase maxBytesPerBatch in large-cache validation test The 10 MiB test record exceeded the 5 MiB batch limit, causing getRecordsByStream to filter it out. Bumped to 20 MiB to match the cache size. --- .../amplify_kinesis_dart/test/record_validation_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart index fab9a055b65..cb7e126efb3 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart @@ -76,7 +76,7 @@ void main() { database: largeDb, maxCacheBytes: 20 * 1024 * 1024, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 20 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); final largeClient = createClient(storage: largeStorage); From c6453f1a86dca605845f1623517f43a26bbf197e Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:51:31 -0700 Subject: [PATCH 06/10] fix(test): use correct KDS maxBytesPerBatch (10 MiB) in all test files All test files were using 5 MiB for maxBytesPerBatch, but the KDS default was 10 MiB (matching maxRecordSizeBytes). This mismatch caused getRecordsByStream to filter out records at the max size limit. --- .../test/in_memory_record_storage_test.dart | 2 +- .../test/record_client_concurrent_flush_test.dart | 2 +- .../kinesis/amplify_kinesis_dart/test/record_client_test.dart | 2 +- .../amplify_kinesis_dart/test/record_validation_test.dart | 4 ++-- .../test/sqlite_record_storage_cache_accuracy_test.dart | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart b/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart index ccbf01938f6..bc494cf011e 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/in_memory_record_storage_test.dart @@ -15,7 +15,7 @@ void main() { storage = InMemoryRecordStorage( maxCacheBytes: 10 * 1024 * 1024, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 10 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); }); diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart index 566973908fc..75028ed81d3 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_client_concurrent_flush_test.dart @@ -26,7 +26,7 @@ void main() { database: db, maxCacheBytes: 1024 * 1024, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 10 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); final sender = _GatedSender(); diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart index 9004df5df55..b66eafc8624 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_client_test.dart @@ -31,7 +31,7 @@ void main() { database: db, maxCacheBytes: 1024, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 10 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); mockSender = MockKinesisSender(); diff --git a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart index cb7e126efb3..a0160c8c265 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/record_validation_test.dart @@ -52,7 +52,7 @@ void main() { database: db, maxCacheBytes: 10000, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 10 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); client = createClient(storage: storage); @@ -168,7 +168,7 @@ void main() { database: tightDb, maxCacheBytes: 80, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 10 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); final tightClient = createClient(storage: tightStorage); diff --git a/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart b/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart index ecf3e7d9900..f8328f82d62 100644 --- a/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart +++ b/packages/kinesis/amplify_kinesis_dart/test/sqlite_record_storage_cache_accuracy_test.dart @@ -26,7 +26,7 @@ void main() { database: db, maxCacheBytes: 1024 * 1024, maxRecordsPerBatch: 500, - maxBytesPerBatch: 5 * 1024 * 1024, + maxBytesPerBatch: 10 * 1024 * 1024, maxRecordSizeBytes: 10 * 1024 * 1024, ); }); From 20e115a76569e4c0e21c62c1e2d0331fd10540fb Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Wed, 8 Apr 2026 08:51:32 -0700 Subject: [PATCH 07/10] docs: restore stripped doc comments and error code comment in KDS client Restore detailed dartdoc comments on AmplifyKinesisClient methods (create, kinesisClient, record, flush, clearCache, disable, close, _wrapError) and the error code comment in KinesisSender that were accidentally removed during the extraction rewrite. --- .../lib/src/amplify_kinesis_client.dart | 53 ++++++++++++++++++- .../lib/src/impl/kinesis_sender.dart | 3 ++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart index d3fca0268f5..b9ddd5ae924 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/amplify_kinesis_client.dart @@ -91,6 +91,12 @@ class AmplifyKinesisClient { _logger = AmplifyLogging.logger('AmplifyKinesisClient'); /// {@macro amplify_kinesis.amplify_kinesis_client} + /// + /// [storagePath] is the directory path for the database file on IO + /// platforms. On web, pass `null` (the path is unused; IndexedDB storage + /// is used instead, with an in-memory fallback). + /// The [region] is used as the database identifier to namespace + /// the database file (e.g. `kinesis_records_us-east-1`). static Future create({ required String region, required AWSCredentialsProvider credentialsProvider, @@ -164,6 +170,11 @@ class AmplifyKinesisClient { bool get isClosed => _closed; /// Direct access to the underlying Kinesis SDK client. + /// + /// Use this for advanced operations not covered by this client's API. + /// + /// Note: This getter is only available when the client was created with + /// [create] (not [AmplifyKinesisClient.withRecordClient]). KinesisClient get kinesisClient { final client = _kinesisClient; if (client == null) { @@ -177,10 +188,16 @@ class AmplifyKinesisClient { /// Records data to be sent to a Kinesis Data Stream. /// + /// The record is persisted to local storage and will be sent during + /// the next flush operation (automatic or manual). + /// /// Returns [Result.ok] with [RecordData] on success, or [Result.error] with: - /// - [KinesisValidationException] for invalid input + /// - [KinesisValidationException] for invalid input (e.g. oversized record, + /// empty or too-long partition key) /// - [KinesisLimitExceededException] if the cache is full /// - [KinesisStorageException] for database errors + /// + /// Returns [Result.ok] silently if the client is disabled. Future> record({ required Uint8List data, required String partitionKey, @@ -214,6 +231,27 @@ class AmplifyKinesisClient { } /// Flushes cached records to their respective Kinesis streams. + /// + /// Each invocation sends at most one batch per stream, limited by the Kinesis + /// `PutRecords` constraints (up to 500 records or 5 MB per stream). If the + /// cache contains more records than a single batch can hold, the remaining + /// records are sent on subsequent flush invocations — either manually or via + /// the auto-flush scheduler. + /// + /// Records that fail within a batch are marked for retry on the next flush. + /// Records that exceed [AmplifyKinesisClientOptions.maxRetries] are removed + /// from the cache. + /// + /// SDK Kinesis errors (throttling, invalid stream, etc.) are logged and + /// skipped so other streams can still flush. Non-SDK errors (e.g. network, + /// storage) abort the flush and are returned as [Result.error]. + /// + /// If a flush is already in progress, the call returns immediately with + /// `FlushData(recordsFlushed: 0, flushInProgress: true)`. + /// + /// Manual flushes are allowed even when the client is disabled, so that + /// callers can drain cached records without re-enabling collection. + /// Only the automatic flush scheduler is paused when disabled. Future> flush() async { if (_closed) return const Result.error(ClientClosedException()); _logger.verbose('Starting flush'); @@ -221,6 +259,10 @@ class AmplifyKinesisClient { } /// Clears all cached records from local storage. + /// + /// Returns [Result.ok] with [ClearCacheData] containing the count of + /// records cleared, or [Result.error] with: + /// - [KinesisStorageException] for database errors Future> clearCache() async { if (_closed) return const Result.error(ClientClosedException()); _logger.verbose('Clearing cache'); @@ -235,6 +277,10 @@ class AmplifyKinesisClient { } /// Disables record collection and automatic flushing. + /// + /// Records submitted while disabled are silently dropped. Already-cached + /// records remain in storage and will be sent on the next flush after + /// re-enabling. void disable() { _logger.info('Disabling record collection and automatic flushing'); _enabled = false; @@ -242,12 +288,17 @@ class AmplifyKinesisClient { } /// Closes the client and releases all resources. + /// + /// The client cannot be reused after closing. Future close() async { _closed = true; _scheduler?.stop(); await _recordClient.close(); } + /// Wraps an async operation, catching any exceptions and returning them + /// as [Result.error] with the appropriate [AmplifyKinesisException] + /// subtype. Future> _wrapError(Future Function() operation) async { try { final value = await operation(); diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart index 580be391149..c200929e2af 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart @@ -70,6 +70,9 @@ class KinesisSender implements Sender { } else if (retryCount >= _maxRetries) { failedIds.add(recordId); } else { + // Error codes can be: ProvisionedThroughputExceededException or + // InternalFailure. All are treated as retryable until the retry + // limit is reached. retryableIds.add(recordId); } } From f340909375f79eba4f4a67fbe6260e956b81038c Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Wed, 8 Apr 2026 08:51:55 -0700 Subject: [PATCH 08/10] fix: assert non-null partitionKey in KinesisSender instead of defaulting to empty string KDS always provides a partition key. Using ?? '' silently hides bugs. Use ! to assert non-null since KDS records always have partitionKey set. --- .../amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart index c200929e2af..7d77636e9d6 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/impl/kinesis_sender.dart @@ -37,7 +37,7 @@ class KinesisSender implements Sender { .map( (record) => PutRecordsRequestEntry( data: record.data, - partitionKey: record.partitionKey ?? '', + partitionKey: record.partitionKey!, ), ) .toList(); From 86fa70e5d6254555f79db33e52e78086d9354d49 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Wed, 8 Apr 2026 08:53:34 -0700 Subject: [PATCH 09/10] chore: export defaultRecoverySuggestion from shared package, mark as internal, document drift dev dep - Export defaultRecoverySuggestion from shared barrel (remove hide clause) - KDS exception file now uses the shared constant instead of its own - Mark amplify_record_cache_dart as internal (publish_to: none) - Add comment explaining why drift is a dev dependency in KDS --- .../lib/src/exception/amplify_kinesis_exception.dart | 4 ---- packages/kinesis/amplify_kinesis_dart/pubspec.yaml | 3 +++ .../lib/amplify_record_cache_dart.dart | 2 +- .../kinesis/amplify_record_cache_dart/pubspec.yaml | 11 +---------- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart b/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart index 5cbea1a8613..43192b0019d 100644 --- a/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart +++ b/packages/kinesis/amplify_kinesis_dart/lib/src/exception/amplify_kinesis_exception.dart @@ -4,10 +4,6 @@ import 'package:amplify_core/amplify_core.dart'; import 'package:amplify_record_cache_dart/amplify_record_cache_dart.dart'; -/// Default recovery suggestion for errors. -const String defaultRecoverySuggestion = - 'Inspect the underlying error for more details.'; - /// {@template amplify_kinesis.amplify_kinesis_exception} /// Base exception for Amplify Kinesis Data Streams errors. /// {@endtemplate} diff --git a/packages/kinesis/amplify_kinesis_dart/pubspec.yaml b/packages/kinesis/amplify_kinesis_dart/pubspec.yaml index bb095c4b9de..550419bd08c 100644 --- a/packages/kinesis/amplify_kinesis_dart/pubspec.yaml +++ b/packages/kinesis/amplify_kinesis_dart/pubspec.yaml @@ -30,6 +30,9 @@ dependencies: dev_dependencies: amplify_lints: ">=3.1.4 <3.2.0" + # drift is a dev dependency because it's only needed for tests + # (NativeDatabase.memory()). Runtime drift comes transitively via + # amplify_record_cache_dart. drift: ^2.25.0 fake_async: ^1.3.0 mocktail: ^1.0.0 diff --git a/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart b/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart index 49c0ed289dd..d6b3883c4af 100644 --- a/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/amplify_record_cache_dart.dart @@ -23,6 +23,6 @@ export 'src/model/record_input.dart'; export 'src/sender/sender.dart'; // Storage export 'src/storage/platform/record_storage_platform.dart'; -export 'src/storage/record_storage.dart' hide defaultRecoverySuggestion; +export 'src/storage/record_storage.dart'; export 'src/storage/record_storage_memory.dart'; export 'src/storage/record_storage_sqlite.dart'; diff --git a/packages/kinesis/amplify_record_cache_dart/pubspec.yaml b/packages/kinesis/amplify_record_cache_dart/pubspec.yaml index 9ba99f60029..60d61f47791 100644 --- a/packages/kinesis/amplify_record_cache_dart/pubspec.yaml +++ b/packages/kinesis/amplify_record_cache_dart/pubspec.yaml @@ -1,16 +1,7 @@ name: amplify_record_cache_dart description: Shared record caching infrastructure for Amplify streaming clients (Kinesis Data Streams, Firehose). Provides offline storage, batching, retry logic, and auto-flush scheduling. version: 0.1.0 -homepage: https://docs.amplify.aws/lib/q/platform/flutter/ -repository: https://github.com/aws-amplify/amplify-flutter/tree/main/packages/kinesis/amplify_record_cache_dart -issue_tracker: https://github.com/aws-amplify/amplify-flutter/issues - -topics: - - aws - - kinesis - - streaming - - caching - - aws-amplify +publish_to: none environment: sdk: ^3.9.0 From 4bbaa4f247b8be5db3db2348aeb37a8656d3e1b0 Mon Sep 17 00:00:00 2001 From: Ekjot <43255916+ekjotmultani@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:18:07 -0700 Subject: [PATCH 10/10] fix: rename Drift table class back to KinesisRecords to avoid breaking change The table name in SQLite is derived from the Drift class name. Renaming from KinesisRecords to CachedRecords would change the table from 'kinesis_records' to 'cached_records', breaking existing users. Firehose is in the Kinesis family so the name is semantically fine. Regenerated .g.dart and updated all references. --- .../lib/src/db/record_cache_database.dart | 8 +- .../lib/src/db/record_cache_database.g.dart | 110 +++++++++--------- .../src/storage/record_storage_sqlite.dart | 30 ++--- 3 files changed, 74 insertions(+), 74 deletions(-) diff --git a/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart index 42e8b98074d..c03289937c6 100644 --- a/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.dart @@ -16,7 +16,7 @@ part 'record_cache_database.g.dart'; /// When updating this schema, please bump /// [RecordCacheDatabase.schemaVersion]. @DataClassName('DriftStoredRecord') -class CachedRecords extends Table { +class KinesisRecords extends Table { /// Auto-incrementing primary key. IntColumn get id => integer().autoIncrement()(); @@ -42,7 +42,7 @@ class CachedRecords extends Table { /// {@template amplify_record_cache.record_cache_database} /// Drift database for managing cached records. /// {@endtemplate} -@DriftDatabase(tables: [CachedRecords]) +@DriftDatabase(tables: [KinesisRecords]) class RecordCacheDatabase extends _$RecordCacheDatabase { /// {@macro amplify_record_cache.record_cache_database} /// @@ -78,11 +78,11 @@ class RecordCacheDatabase extends _$RecordCacheDatabase { await m.createAll(); await customStatement( 'CREATE INDEX IF NOT EXISTS idx_stream_id ' - 'ON cached_records(stream_name, id)', + 'ON kinesis_records(stream_name, id)', ); await customStatement( 'CREATE INDEX IF NOT EXISTS idx_data_size ' - 'ON cached_records(data_size)', + 'ON kinesis_records(data_size)', ); }, ); diff --git a/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart index 51023659802..20545c89084 100644 --- a/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/db/record_cache_database.g.dart @@ -3,12 +3,12 @@ part of 'record_cache_database.dart'; // ignore_for_file: type=lint -class $CachedRecordsTable extends CachedRecords - with TableInfo<$CachedRecordsTable, DriftStoredRecord> { +class $KinesisRecordsTable extends KinesisRecords + with TableInfo<$KinesisRecordsTable, DriftStoredRecord> { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $CachedRecordsTable(this.attachedDatabase, [this._alias]); + $KinesisRecordsTable(this.attachedDatabase, [this._alias]); static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -102,7 +102,7 @@ class $CachedRecordsTable extends CachedRecords String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'cached_records'; + static const String $name = 'kinesis_records'; @override VerificationContext validateIntegrity( Insertable instance, { @@ -201,8 +201,8 @@ class $CachedRecordsTable extends CachedRecords } @override - $CachedRecordsTable createAlias(String alias) { - return $CachedRecordsTable(attachedDatabase, alias); + $KinesisRecordsTable createAlias(String alias) { + return $KinesisRecordsTable(attachedDatabase, alias); } } @@ -250,8 +250,8 @@ class DriftStoredRecord extends DataClass return map; } - CachedRecordsCompanion toCompanion(bool nullToAbsent) { - return CachedRecordsCompanion( + KinesisRecordsCompanion toCompanion(bool nullToAbsent) { + return KinesisRecordsCompanion( id: Value(id), streamName: Value(streamName), partitionKey: Value(partitionKey), @@ -308,7 +308,7 @@ class DriftStoredRecord extends DataClass retryCount: retryCount ?? this.retryCount, createdAt: createdAt ?? this.createdAt, ); - DriftStoredRecord copyWithCompanion(CachedRecordsCompanion data) { + DriftStoredRecord copyWithCompanion(KinesisRecordsCompanion data) { return DriftStoredRecord( id: data.id.present ? data.id.value : this.id, streamName: data.streamName.present @@ -363,7 +363,7 @@ class DriftStoredRecord extends DataClass other.createdAt == this.createdAt); } -class CachedRecordsCompanion extends UpdateCompanion { +class KinesisRecordsCompanion extends UpdateCompanion { final Value id; final Value streamName; final Value partitionKey; @@ -371,7 +371,7 @@ class CachedRecordsCompanion extends UpdateCompanion { final Value dataSize; final Value retryCount; final Value createdAt; - const CachedRecordsCompanion({ + const KinesisRecordsCompanion({ this.id = const Value.absent(), this.streamName = const Value.absent(), this.partitionKey = const Value.absent(), @@ -380,7 +380,7 @@ class CachedRecordsCompanion extends UpdateCompanion { this.retryCount = const Value.absent(), this.createdAt = const Value.absent(), }); - CachedRecordsCompanion.insert({ + KinesisRecordsCompanion.insert({ this.id = const Value.absent(), required String streamName, this.partitionKey = const Value.absent(), @@ -412,7 +412,7 @@ class CachedRecordsCompanion extends UpdateCompanion { }); } - CachedRecordsCompanion copyWith({ + KinesisRecordsCompanion copyWith({ Value? id, Value? streamName, Value? partitionKey, @@ -421,7 +421,7 @@ class CachedRecordsCompanion extends UpdateCompanion { Value? retryCount, Value? createdAt, }) { - return CachedRecordsCompanion( + return KinesisRecordsCompanion( id: id ?? this.id, streamName: streamName ?? this.streamName, partitionKey: partitionKey ?? this.partitionKey, @@ -461,7 +461,7 @@ class CachedRecordsCompanion extends UpdateCompanion { @override String toString() { - return (StringBuffer('CachedRecordsCompanion(') + return (StringBuffer('KinesisRecordsCompanion(') ..write('id: $id, ') ..write('streamName: $streamName, ') ..write('partitionKey: $partitionKey, ') @@ -477,16 +477,16 @@ class CachedRecordsCompanion extends UpdateCompanion { abstract class _$RecordCacheDatabase extends GeneratedDatabase { _$RecordCacheDatabase(QueryExecutor e) : super(e); $RecordCacheDatabaseManager get managers => $RecordCacheDatabaseManager(this); - late final $CachedRecordsTable cachedRecords = $CachedRecordsTable(this); + late final $KinesisRecordsTable kinesisRecords = $KinesisRecordsTable(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override - List get allSchemaEntities => [cachedRecords]; + List get allSchemaEntities => [kinesisRecords]; } -typedef $$CachedRecordsTableCreateCompanionBuilder = - CachedRecordsCompanion Function({ +typedef $$KinesisRecordsTableCreateCompanionBuilder = + KinesisRecordsCompanion Function({ Value id, required String streamName, Value partitionKey, @@ -495,8 +495,8 @@ typedef $$CachedRecordsTableCreateCompanionBuilder = Value retryCount, required int createdAt, }); -typedef $$CachedRecordsTableUpdateCompanionBuilder = - CachedRecordsCompanion Function({ +typedef $$KinesisRecordsTableUpdateCompanionBuilder = + KinesisRecordsCompanion Function({ Value id, Value streamName, Value partitionKey, @@ -506,9 +506,9 @@ typedef $$CachedRecordsTableUpdateCompanionBuilder = Value createdAt, }); -class $$CachedRecordsTableFilterComposer - extends Composer<_$RecordCacheDatabase, $CachedRecordsTable> { - $$CachedRecordsTableFilterComposer({ +class $$KinesisRecordsTableFilterComposer + extends Composer<_$RecordCacheDatabase, $KinesisRecordsTable> { + $$KinesisRecordsTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -551,9 +551,9 @@ class $$CachedRecordsTableFilterComposer ); } -class $$CachedRecordsTableOrderingComposer - extends Composer<_$RecordCacheDatabase, $CachedRecordsTable> { - $$CachedRecordsTableOrderingComposer({ +class $$KinesisRecordsTableOrderingComposer + extends Composer<_$RecordCacheDatabase, $KinesisRecordsTable> { + $$KinesisRecordsTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -596,9 +596,9 @@ class $$CachedRecordsTableOrderingComposer ); } -class $$CachedRecordsTableAnnotationComposer - extends Composer<_$RecordCacheDatabase, $CachedRecordsTable> { - $$CachedRecordsTableAnnotationComposer({ +class $$KinesisRecordsTableAnnotationComposer + extends Composer<_$RecordCacheDatabase, $KinesisRecordsTable> { + $$KinesisRecordsTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, @@ -633,41 +633,41 @@ class $$CachedRecordsTableAnnotationComposer $composableBuilder(column: $table.createdAt, builder: (column) => column); } -class $$CachedRecordsTableTableManager +class $$KinesisRecordsTableTableManager extends RootTableManager< _$RecordCacheDatabase, - $CachedRecordsTable, + $KinesisRecordsTable, DriftStoredRecord, - $$CachedRecordsTableFilterComposer, - $$CachedRecordsTableOrderingComposer, - $$CachedRecordsTableAnnotationComposer, - $$CachedRecordsTableCreateCompanionBuilder, - $$CachedRecordsTableUpdateCompanionBuilder, + $$KinesisRecordsTableFilterComposer, + $$KinesisRecordsTableOrderingComposer, + $$KinesisRecordsTableAnnotationComposer, + $$KinesisRecordsTableCreateCompanionBuilder, + $$KinesisRecordsTableUpdateCompanionBuilder, ( DriftStoredRecord, BaseReferences< _$RecordCacheDatabase, - $CachedRecordsTable, + $KinesisRecordsTable, DriftStoredRecord >, ), DriftStoredRecord, PrefetchHooks Function() > { - $$CachedRecordsTableTableManager( + $$KinesisRecordsTableTableManager( _$RecordCacheDatabase db, - $CachedRecordsTable table, + $KinesisRecordsTable table, ) : super( TableManagerState( db: db, table: table, createFilteringComposer: () => - $$CachedRecordsTableFilterComposer($db: db, $table: table), + $$KinesisRecordsTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$CachedRecordsTableOrderingComposer($db: db, $table: table), + $$KinesisRecordsTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$CachedRecordsTableAnnotationComposer($db: db, $table: table), + $$KinesisRecordsTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value id = const Value.absent(), @@ -677,7 +677,7 @@ class $$CachedRecordsTableTableManager Value dataSize = const Value.absent(), Value retryCount = const Value.absent(), Value createdAt = const Value.absent(), - }) => CachedRecordsCompanion( + }) => KinesisRecordsCompanion( id: id, streamName: streamName, partitionKey: partitionKey, @@ -695,7 +695,7 @@ class $$CachedRecordsTableTableManager required int dataSize, Value retryCount = const Value.absent(), required int createdAt, - }) => CachedRecordsCompanion.insert( + }) => KinesisRecordsCompanion.insert( id: id, streamName: streamName, partitionKey: partitionKey, @@ -712,21 +712,21 @@ class $$CachedRecordsTableTableManager ); } -typedef $$CachedRecordsTableProcessedTableManager = +typedef $$KinesisRecordsTableProcessedTableManager = ProcessedTableManager< _$RecordCacheDatabase, - $CachedRecordsTable, + $KinesisRecordsTable, DriftStoredRecord, - $$CachedRecordsTableFilterComposer, - $$CachedRecordsTableOrderingComposer, - $$CachedRecordsTableAnnotationComposer, - $$CachedRecordsTableCreateCompanionBuilder, - $$CachedRecordsTableUpdateCompanionBuilder, + $$KinesisRecordsTableFilterComposer, + $$KinesisRecordsTableOrderingComposer, + $$KinesisRecordsTableAnnotationComposer, + $$KinesisRecordsTableCreateCompanionBuilder, + $$KinesisRecordsTableUpdateCompanionBuilder, ( DriftStoredRecord, BaseReferences< _$RecordCacheDatabase, - $CachedRecordsTable, + $KinesisRecordsTable, DriftStoredRecord >, ), @@ -737,6 +737,6 @@ typedef $$CachedRecordsTableProcessedTableManager = class $RecordCacheDatabaseManager { final _$RecordCacheDatabase _db; $RecordCacheDatabaseManager(this._db); - $$CachedRecordsTableTableManager get cachedRecords => - $$CachedRecordsTableTableManager(_db, _db.cachedRecords); + $$KinesisRecordsTableTableManager get kinesisRecords => + $$KinesisRecordsTableTableManager(_db, _db.kinesisRecords); } diff --git a/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart index c3de48e4d86..a5f1c6677d5 100644 --- a/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart +++ b/packages/kinesis/amplify_record_cache_dart/lib/src/storage/record_storage_sqlite.dart @@ -57,11 +57,11 @@ final class SqliteRecordStorage extends RecordStorage { /// Queries the current cache size from the database. static Future _queryCacheSize(RecordCacheDatabase db) async { - final query = db.selectOnly(db.cachedRecords) - ..addColumns([db.cachedRecords.dataSize.sum()]); + final query = db.selectOnly(db.kinesisRecords) + ..addColumns([db.kinesisRecords.dataSize.sum()]); final result = await query.getSingleOrNull(); if (result == null) return 0; - return result.read(db.cachedRecords.dataSize.sum()) ?? 0; + return result.read(db.kinesisRecords.dataSize.sum()) ?? 0; } /// Returns the in-memory cached size directly (O(1), no DB query). @@ -71,9 +71,9 @@ final class SqliteRecordStorage extends RecordStorage { @override Future writeRecord(RecordInput record) async { await _db - .into(_db.cachedRecords) + .into(_db.kinesisRecords) .insert( - CachedRecordsCompanion.insert( + KinesisRecordsCompanion.insert( streamName: record.streamName, partitionKey: Value(record.partitionKey ?? ''), data: record.data, @@ -95,7 +95,7 @@ final class SqliteRecordStorage extends RecordStorage { ROW_NUMBER() OVER (PARTITION BY stream_name ORDER BY id) as rn, SUM(data_size) OVER (PARTITION BY stream_name ORDER BY id) as running_size - FROM cached_records + FROM kinesis_records ) WHERE rn <= ?1 AND running_size <= ?2 ORDER BY stream_name, id @@ -104,7 +104,7 @@ final class SqliteRecordStorage extends RecordStorage { Variable.withInt(maxRecordsPerBatch), Variable.withInt(maxBytesPerBatch), ], - readsFrom: {_db.cachedRecords}, + readsFrom: {_db.kinesisRecords}, ) .get(); @@ -119,7 +119,7 @@ final class SqliteRecordStorage extends RecordStorage { @override Future doDeleteRecords(Iterable ids) async { if (ids.isEmpty) return; - await (_db.delete(_db.cachedRecords)..where((t) => t.id.isIn(ids))).go(); + await (_db.delete(_db.kinesisRecords)..where((t) => t.id.isIn(ids))).go(); } @override @@ -128,25 +128,25 @@ final class SqliteRecordStorage extends RecordStorage { @override Future doIncrementRetryCount(Iterable ids) async { if (ids.isEmpty) return; - await (_db.update(_db.cachedRecords)..where((t) => t.id.isIn(ids))).write( - CachedRecordsCompanion.custom( - retryCount: _db.cachedRecords.retryCount + const Constant(1), + await (_db.update(_db.kinesisRecords)..where((t) => t.id.isIn(ids))).write( + KinesisRecordsCompanion.custom( + retryCount: _db.kinesisRecords.retryCount + const Constant(1), ), ); } @override Future doGetRecordCount() async { - final query = _db.selectOnly(_db.cachedRecords) - ..addColumns([_db.cachedRecords.id.count()]); + final query = _db.selectOnly(_db.kinesisRecords) + ..addColumns([_db.kinesisRecords.id.count()]); final result = await query.getSingleOrNull(); if (result == null) return 0; - return result.read(_db.cachedRecords.id.count()) ?? 0; + return result.read(_db.kinesisRecords.id.count()) ?? 0; } @override Future doClearRecords() async { - await _db.delete(_db.cachedRecords).go(); + await _db.delete(_db.kinesisRecords).go(); } @override