From 83a5380cc6f798d19f35538184941e08308aed14 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Sun, 21 Jul 2024 11:31:29 -0700 Subject: [PATCH 01/11] feat(storage): storage delimiter implementation --- .../category/amplify_storage_category.dart | 35 +++++++++++++++ .../lib/src/types/storage/list_options.dart | 10 ++++- .../lib/src/types/storage/list_result.dart | 4 ++ .../lib/src/pinpoint_provider.dart | 9 ---- .../test/pinpoint_provider_test.dart | 42 ------------------ .../example/integration_test/list_test.dart | 19 ++++---- .../example/bin/example.dart | 2 +- .../lib/src/amplify_storage_s3_dart_impl.dart | 3 ++ .../lib/src/model/s3_list_plugin_options.dart | 26 ++--------- .../src/model/s3_list_plugin_options.g.dart | 11 +---- .../lib/src/model/s3_list_result.dart | 44 ++++++++----------- .../service/storage_s3_service_impl.dart | 12 ++--- .../test/amplify_storage_s3_dart_test.dart | 14 +++--- .../storage_s3_service_test.dart | 5 ++- 14 files changed, 101 insertions(+), 135 deletions(-) diff --git a/packages/amplify_core/lib/src/category/amplify_storage_category.dart b/packages/amplify_core/lib/src/category/amplify_storage_category.dart index 416446d12e..8f84b1e107 100644 --- a/packages/amplify_core/lib/src/category/amplify_storage_category.dart +++ b/packages/amplify_core/lib/src/category/amplify_storage_category.dart @@ -235,3 +235,38 @@ class StorageCategory extends AmplifyCategory { ); } } + +/// {@template amplify_core.amplify_storage_category.subpath_strategy} +/// Configurable options for `Amplify.Storage.list`. +/// {@endtemplate} +class SubpathStrategy with AWSSerializable> { + /// {@macro amplify_core.amplify_storage_category.subpath_strategy} + const SubpathStrategy.include() : this._(); + + const SubpathStrategy._({ + this.excludeSubPaths = false, + this.delimiter = '/', + }); + + /// Constructor for SubpathStrategy for excluding subpaths, option to specify the delimiter + const SubpathStrategy.exclude({String? delimiter = '/'}) + : this._( + excludeSubPaths: true, + delimiter: delimiter ?? '/', + ); + + /// Whether to exclude objects under the sub paths of the path to list. + final bool excludeSubPaths; + + /// The delimiter to use when evaluating sub paths. If [excludeSubPaths] is + /// false, this value has no impact on behavior. + final String delimiter; + + @override + Map toJson() { + return { + 'excludeSubPaths': excludeSubPaths, + 'delimiter': delimiter, + }; + } +} diff --git a/packages/amplify_core/lib/src/types/storage/list_options.dart b/packages/amplify_core/lib/src/types/storage/list_options.dart index c046f90d55..619756ad6e 100644 --- a/packages/amplify_core/lib/src/types/storage/list_options.dart +++ b/packages/amplify_core/lib/src/types/storage/list_options.dart @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import 'package:aws_common/aws_common.dart'; +import 'package:amplify_core/amplify_core.dart'; /// {@template amplify_core.storage.list_options} /// Configurable options for `Amplify.Storage.list`. @@ -16,6 +16,7 @@ class StorageListOptions this.pageSize = 1000, this.nextToken, this.pluginOptions, + this.subpathStrategy = const SubpathStrategy.include(), }); /// The number of object to be listed in each page. @@ -27,8 +28,12 @@ class StorageListOptions /// {@macro amplify_core.storage.list_plugin_options} final StorageListPluginOptions? pluginOptions; + /// Subpathstrategy for specifying storage subpath behavior + final SubpathStrategy subpathStrategy; + @override - List get props => [pageSize, nextToken, pluginOptions]; + List get props => + [pageSize, nextToken, pluginOptions, subpathStrategy]; @override String get runtimeTypeName => 'StorageListOptions'; @@ -38,6 +43,7 @@ class StorageListOptions 'pageSize': pageSize, 'nextToken': nextToken, 'pluginOptions': pluginOptions?.toJson(), + 'subpathStrategy': subpathStrategy.toJson(), }; } diff --git a/packages/amplify_core/lib/src/types/storage/list_result.dart b/packages/amplify_core/lib/src/types/storage/list_result.dart index aa65cd8d37..1c3bb55e18 100644 --- a/packages/amplify_core/lib/src/types/storage/list_result.dart +++ b/packages/amplify_core/lib/src/types/storage/list_result.dart @@ -9,11 +9,15 @@ import 'package:amplify_core/amplify_core.dart'; class StorageListResult { /// {@macro amplify_core.storage.list_result} const StorageListResult( + this.excludedSubpaths, this.items, { required this.hasNextPage, this.nextToken, }); + /// The subpaths that have been excluded + final List excludedSubpaths; + /// The objects listed in the current page. final List items; diff --git a/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart b/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart index 6cf94d6b6f..9ed9639d94 100644 --- a/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart +++ b/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart @@ -190,15 +190,6 @@ class PinpointProvider implements ServiceProviderClient { // `AnalyticsException` converts `AWSHttpException` to `NetworkException`. } on NetworkException catch (e) { _logger.error('Network problem when registering device: ', e); - // This is to allow configuration if `EndpointClient.updateEndpoint()` - // throws UnknownException due to expired token. - // the underlying exception is `NotAuthorizedException` with - // `Invalid login token. Token expired` message. - } on UnknownException catch (e) { - _logger.error( - 'Could not update Pinpoint endpoint to register the device: ', - e, - ); } } diff --git a/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart b/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart index ff72d427ee..1baaa9ccc7 100644 --- a/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart +++ b/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart @@ -319,48 +319,6 @@ void main() { verify(mockEndpointClient.updateEndpoint); }); - test('registerDevice should run successfully when token is expired', - () async { - when( - () => mockAmplifyAuthProviderRepository.getAuthProvider( - APIAuthorizationType.iam.authProviderToken, - ), - ).thenReturn(awsIamAmplifyAuthProvider); - when( - () => mockAnalyticsClient.init( - pinpointAppId: any(named: 'pinpointAppId'), - region: any(named: 'region'), - authProvider: any(named: 'authProvider'), - ), - ).thenAnswer((realInvocation) async {}); - - final mockEndpointClient = MockEndpointClient(); - - when(mockEndpointClient.updateEndpoint) - .thenThrow(const UnknownException('message')); - - when( - () => mockAnalyticsClient.endpointClient, - ).thenReturn(mockEndpointClient); - - await expectLater( - pinpointProvider.init( - config: notificationsPinpointConfig, - authProviderRepo: mockAmplifyAuthProviderRepository, - analyticsClient: mockAnalyticsClient, - ), - completes, - ); - - expect( - pinpointProvider.registerDevice( - '', - ), - completes, - ); - verify(mockEndpointClient.updateEndpoint); - }); - test('recordEvent should run successfully', () async { when( () => mockAmplifyAuthProviderRepository.getAuthProvider( diff --git a/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart b/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart index 22e2a2766b..4443479fea 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart @@ -76,17 +76,16 @@ void main() { final listResult = await Amplify.Storage.list( path: StoragePath.fromString('$uniquePrefix/'), options: const StorageListOptions( - pluginOptions: S3ListPluginOptions( - excludeSubPaths: true, - ), + pluginOptions: S3ListPluginOptions(), + subpathStrategy: SubpathStrategy.exclude(), ), ).result as S3ListResult; expect(listResult.items.length, 3); expect(listResult.items.first.path, contains('file1.txt')); - expect(listResult.metadata.subPaths.length, 1); - expect(listResult.metadata.subPaths.first, '$uniquePrefix/subdir/'); + expect(listResult.excludedSubpaths.length, 1); + expect(listResult.excludedSubpaths.first, '$uniquePrefix/subdir/'); expect(listResult.metadata.delimiter, '/'); }); @@ -94,19 +93,17 @@ void main() { final listResult = await Amplify.Storage.list( path: StoragePath.fromString('$uniquePrefix/'), options: const StorageListOptions( - pluginOptions: S3ListPluginOptions( - excludeSubPaths: true, - delimiter: '#', - ), + pluginOptions: S3ListPluginOptions(), + subpathStrategy: SubpathStrategy.exclude(delimiter: '#'), ), ).result as S3ListResult; expect(listResult.items.length, 3); expect(listResult.items.first.path, contains('file1.txt')); - expect(listResult.metadata.subPaths.length, 1); + expect(listResult.excludedSubpaths.length, 1); expect( - listResult.metadata.subPaths.first, + listResult.excludedSubpaths.first, '$uniquePrefix/subdir2#', ); expect(listResult.metadata.delimiter, '#'); diff --git a/packages/storage/amplify_storage_s3_dart/example/bin/example.dart b/packages/storage/amplify_storage_s3_dart/example/bin/example.dart index f51a73d84b..7a04209136 100644 --- a/packages/storage/amplify_storage_s3_dart/example/bin/example.dart +++ b/packages/storage/amplify_storage_s3_dart/example/bin/example.dart @@ -108,7 +108,7 @@ Future listOperation() async { while (true) { stdout.writeln('Listed ${result.items.length} objects.'); - stdout.writeln('Sub directories: ${result.metadata.subPaths}'); + stdout.writeln('Sub directories: ${result.excludedSubpaths}'); result.items.asMap().forEach((index, item) { stdout.writeln('$index. path: ${item.path} | size: ${item.size}'); }); diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/amplify_storage_s3_dart_impl.dart b/packages/storage/amplify_storage_s3_dart/lib/src/amplify_storage_s3_dart_impl.dart index cc709c5802..64ba9ed387 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/amplify_storage_s3_dart_impl.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/amplify_storage_s3_dart_impl.dart @@ -133,7 +133,10 @@ class AmplifyStorageS3Dart extends StoragePluginInterface pluginOptions: options?.pluginOptions, defaultPluginOptions: const S3ListPluginOptions(), ); + final s3Options = StorageListOptions( + subpathStrategy: + options?.subpathStrategy ?? const SubpathStrategy.include(), pluginOptions: s3PluginOptions, nextToken: options?.nextToken, pageSize: options?.pageSize ?? 1000, diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.dart index 17480ee70e..b2f8a925f2 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.dart @@ -11,27 +11,18 @@ part 's3_list_plugin_options.g.dart'; @zAmplifySerializable class S3ListPluginOptions extends StorageListPluginOptions { /// {@macro storage.amplify_storage_s3.list_plugin_options} - const S3ListPluginOptions({ - bool excludeSubPaths = false, - String delimiter = '/', - }) : this._( - excludeSubPaths: excludeSubPaths, - delimiter: delimiter, - ); + + const S3ListPluginOptions() : this._(); const S3ListPluginOptions._({ - this.excludeSubPaths = false, - this.delimiter = '/', this.listAll = false, }); /// {@macro storage.amplify_storage_s3.list_plugin_options} /// /// Use this to list all objects without pagination. - const S3ListPluginOptions.listAll({ - bool excludeSubPaths = false, - }) : this._( - excludeSubPaths: excludeSubPaths, + const S3ListPluginOptions.listAll() + : this._( listAll: true, ); @@ -39,14 +30,6 @@ class S3ListPluginOptions extends StorageListPluginOptions { factory S3ListPluginOptions.fromJson(Map json) => _$S3ListPluginOptionsFromJson(json); - /// Whether to exclude objects under the sub paths of the path to list. The - /// default value is `false`. - final bool excludeSubPaths; - - /// The delimiter to use when evaluating sub paths. If [excludeSubPaths] is - /// false, this value has no impact on behavior. - final String delimiter; - /// Whether to list all objects under a given path without pagination. The /// default value is `false`. /// @@ -57,7 +40,6 @@ class S3ListPluginOptions extends StorageListPluginOptions { @override List get props => [ - excludeSubPaths, listAll, ]; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.g.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.g.dart index 05d787e2a7..7c97df3231 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.g.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_plugin_options.g.dart @@ -11,18 +11,11 @@ S3ListPluginOptions _$S3ListPluginOptionsFromJson(Map json) => 'S3ListPluginOptions', json, ($checkedConvert) { - final val = S3ListPluginOptions( - excludeSubPaths: - $checkedConvert('excludeSubPaths', (v) => v as bool? ?? false), - delimiter: $checkedConvert('delimiter', (v) => v as String? ?? '/'), - ); + final val = S3ListPluginOptions(); return val; }, ); Map _$S3ListPluginOptionsToJson( S3ListPluginOptions instance) => - { - 'excludeSubPaths': instance.excludeSubPaths, - 'delimiter': instance.delimiter, - }; + {}; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart index a9ba5ca0fe..8af11eaf1c 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart @@ -13,6 +13,7 @@ import 'package:smithy/smithy.dart'; class S3ListResult extends StorageListResult { /// {@macro storage.amplify_storage_s3.list_result} S3ListResult( + super.excludedSubpaths, super.items, { required super.hasNextPage, required this.metadata, @@ -26,13 +27,19 @@ class S3ListResult extends StorageListResult { PaginatedResult paginatedResult, ) { final output = paginatedResult.items; - final metadata = S3ListMetadata.fromS3CommonPrefixes( - commonPrefixes: output.commonPrefixes?.toList(), + final metadata = S3ListMetadata.fromDelimiter( delimiter: output.delimiter, ); + + final subPaths = output.commonPrefixes + ?.map((commonPrefix) => commonPrefix.prefix) + .whereType() + .toList(); + final items = output.contents?.map(S3Item.fromS3Object).toList(); return S3ListResult( + subPaths ?? [], items ?? const [], hasNextPage: paginatedResult.hasNext, nextToken: paginatedResult.nextContinuationToken, @@ -43,8 +50,14 @@ class S3ListResult extends StorageListResult { /// Merges two instances of [S3ListResult] into one. S3ListResult merge(S3ListResult other) { final items = [...this.items, ...other.items]; - final metadata = this.metadata.merge(other.metadata); + + final mergedSubPaths = [ + ...excludedSubpaths, + ...other.excludedSubpaths, + ]; + return S3ListResult( + mergedSubPaths, items, hasNextPage: other.hasNextPage, nextToken: other.nextToken, @@ -58,38 +71,19 @@ class S3ListResult extends StorageListResult { /// The metadata returned from the Storage S3 plugin `list` API. class S3ListMetadata { - /// Creates a S3ListMetadata from the `commonPrefix` and `delimiter` + /// Creates a S3ListMetadata from the `delimiter` /// properties of the [s3.ListObjectsV2Output]. - factory S3ListMetadata.fromS3CommonPrefixes({ - List? commonPrefixes, + factory S3ListMetadata.fromDelimiter({ String? delimiter, }) { - final subPaths = commonPrefixes - ?.map((commonPrefix) => commonPrefix.prefix) - .whereType() - .toList(); - return S3ListMetadata._( - subPaths: subPaths, delimiter: delimiter, ); } S3ListMetadata._({ - List? subPaths, this.delimiter, - }) : subPaths = subPaths ?? const []; - - /// Merges two instances of [S3ListMetadata] into one. - S3ListMetadata merge(S3ListMetadata other) { - final subPaths = [...this.subPaths, ...other.subPaths]; - return S3ListMetadata._(subPaths: subPaths, delimiter: other.delimiter); - } - - /// Sub paths under the `path` parameter calling the `list` API. - /// - /// This list can be empty. - final List subPaths; + }); /// The delimiter used in S3 prefix if any. final String? delimiter; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart index f749de67f3..cf68c6cb0d 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart @@ -129,6 +129,8 @@ class StorageS3Service { final s3PluginOptions = options.pluginOptions as S3ListPluginOptions? ?? const S3ListPluginOptions(); + final s3Category = options.subpathStrategy; + final resolvedPath = await _pathResolver.resolvePath(path: path); if (!s3PluginOptions.listAll) { @@ -138,9 +140,8 @@ class StorageS3Service { ..prefix = resolvedPath ..maxKeys = options.pageSize ..continuationToken = options.nextToken - ..delimiter = s3PluginOptions.excludeSubPaths - ? s3PluginOptions.delimiter - : null; + ..delimiter = + s3Category.excludeSubPaths ? s3Category.delimiter : null; }); try { @@ -163,9 +164,8 @@ class StorageS3Service { builder ..bucket = _storageOutputs.bucketName ..prefix = resolvedPath - ..delimiter = s3PluginOptions.excludeSubPaths - ? s3PluginOptions.delimiter - : null; + ..delimiter = + s3Category.excludeSubPaths ? s3Category.delimiter : null; }); listResult = await _defaultS3Client.listObjectsV2(request).result; diff --git a/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart b/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart index 17015eb5f7..2ed27902c1 100644 --- a/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart +++ b/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart @@ -86,11 +86,10 @@ void main() { group('list()', () { const testPath = StoragePath.fromString('some/path'); final testResult = S3ListResult( + [], [], hasNextPage: false, - metadata: S3ListMetadata.fromS3CommonPrefixes( - commonPrefixes: [], - ), + metadata: S3ListMetadata.fromDelimiter(), ); setUpAll(() { @@ -101,8 +100,10 @@ void main() { test('should forward default options to StorageS3Service.list() API', () async { - const defaultOptions = - StorageListOptions(pluginOptions: S3ListPluginOptions()); + const defaultOptions = StorageListOptions( + pluginOptions: S3ListPluginOptions(), + subpathStrategy: SubpathStrategy.include(), + ); when( () => storageS3Service.list( @@ -138,7 +139,8 @@ void main() { test('should forward options to StorageS3Service.list() API', () async { const testOptions = StorageListOptions( - pluginOptions: S3ListPluginOptions(excludeSubPaths: true), + pluginOptions: S3ListPluginOptions(), + subpathStrategy: SubpathStrategy.exclude(), nextToken: 'next-token-123', pageSize: 2, ); diff --git a/packages/storage/amplify_storage_s3_dart/test/storage_s3_service/storage_s3_service_test.dart b/packages/storage/amplify_storage_s3_dart/test/storage_s3_service/storage_s3_service_test.dart index aec65db336..17e0f052be 100644 --- a/packages/storage/amplify_storage_s3_dart/test/storage_s3_service/storage_s3_service_test.dart +++ b/packages/storage/amplify_storage_s3_dart/test/storage_s3_service/storage_s3_service_test.dart @@ -167,7 +167,8 @@ void main() { const testPath = StoragePath.fromString('album'); const testOptions = StorageListOptions( pageSize: testPageSize, - pluginOptions: S3ListPluginOptions(excludeSubPaths: true), + pluginOptions: S3ListPluginOptions(), + subpathStrategy: SubpathStrategy.exclude(), ); const testSubPaths = [ 'album#folder1', @@ -210,7 +211,7 @@ void main() { options: testOptions, ); - expect(result.metadata.subPaths, equals(testSubPaths)); + expect(result.excludedSubpaths, equals(testSubPaths)); }); test( From 4a9fdcceb8bcc681c4b3f601e292d100f8f8094c Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Sun, 21 Jul 2024 11:42:08 -0700 Subject: [PATCH 02/11] test: revert irrelevant files --- .../lib/src/pinpoint_provider.dart | 9 ++++ .../test/pinpoint_provider_test.dart | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart b/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart index 9ed9639d94..6cf94d6b6f 100644 --- a/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart +++ b/packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart @@ -190,6 +190,15 @@ class PinpointProvider implements ServiceProviderClient { // `AnalyticsException` converts `AWSHttpException` to `NetworkException`. } on NetworkException catch (e) { _logger.error('Network problem when registering device: ', e); + // This is to allow configuration if `EndpointClient.updateEndpoint()` + // throws UnknownException due to expired token. + // the underlying exception is `NotAuthorizedException` with + // `Invalid login token. Token expired` message. + } on UnknownException catch (e) { + _logger.error( + 'Could not update Pinpoint endpoint to register the device: ', + e, + ); } } diff --git a/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart b/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart index 1baaa9ccc7..ff72d427ee 100644 --- a/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart +++ b/packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart @@ -319,6 +319,48 @@ void main() { verify(mockEndpointClient.updateEndpoint); }); + test('registerDevice should run successfully when token is expired', + () async { + when( + () => mockAmplifyAuthProviderRepository.getAuthProvider( + APIAuthorizationType.iam.authProviderToken, + ), + ).thenReturn(awsIamAmplifyAuthProvider); + when( + () => mockAnalyticsClient.init( + pinpointAppId: any(named: 'pinpointAppId'), + region: any(named: 'region'), + authProvider: any(named: 'authProvider'), + ), + ).thenAnswer((realInvocation) async {}); + + final mockEndpointClient = MockEndpointClient(); + + when(mockEndpointClient.updateEndpoint) + .thenThrow(const UnknownException('message')); + + when( + () => mockAnalyticsClient.endpointClient, + ).thenReturn(mockEndpointClient); + + await expectLater( + pinpointProvider.init( + config: notificationsPinpointConfig, + authProviderRepo: mockAmplifyAuthProviderRepository, + analyticsClient: mockAnalyticsClient, + ), + completes, + ); + + expect( + pinpointProvider.registerDevice( + '', + ), + completes, + ); + verify(mockEndpointClient.updateEndpoint); + }); + test('recordEvent should run successfully', () async { when( () => mockAmplifyAuthProviderRepository.getAuthProvider( From b171f728f7333f16a3dbc08981a5ebff219fd5b2 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Mon, 5 Aug 2024 10:09:50 -0700 Subject: [PATCH 03/11] test: excludedsubpaths named paramter in s3listresult --- .../amplify_storage_s3_dart/lib/src/model/s3_list_result.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart index 8af11eaf1c..c89c14e0f1 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart @@ -13,8 +13,8 @@ import 'package:smithy/smithy.dart'; class S3ListResult extends StorageListResult { /// {@macro storage.amplify_storage_s3.list_result} S3ListResult( - super.excludedSubpaths, super.items, { + super.excludedSubpaths, required super.hasNextPage, required this.metadata, super.nextToken, From 924eea11f89e558621c8195fce933afa4e290f63 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Mon, 5 Aug 2024 10:30:18 -0700 Subject: [PATCH 04/11] test: add excluded subpath s3listresult as named parameter --- .../amplify_core/lib/src/types/storage/list_result.dart | 4 ++-- .../example/integration_test/list_test.dart | 8 ++++---- .../lib/src/model/s3_list_result.dart | 8 ++++---- .../test/amplify_storage_s3_dart_test.dart | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/amplify_core/lib/src/types/storage/list_result.dart b/packages/amplify_core/lib/src/types/storage/list_result.dart index 1c3bb55e18..ac5288b5ff 100644 --- a/packages/amplify_core/lib/src/types/storage/list_result.dart +++ b/packages/amplify_core/lib/src/types/storage/list_result.dart @@ -9,14 +9,14 @@ import 'package:amplify_core/amplify_core.dart'; class StorageListResult { /// {@macro amplify_core.storage.list_result} const StorageListResult( - this.excludedSubpaths, this.items, { + this.excludedSubpaths, required this.hasNextPage, this.nextToken, }); /// The subpaths that have been excluded - final List excludedSubpaths; + final List? excludedSubpaths; /// The objects listed in the current page. final List items; diff --git a/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart b/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart index 4443479fea..d6321b8794 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart @@ -84,8 +84,8 @@ void main() { expect(listResult.items.length, 3); expect(listResult.items.first.path, contains('file1.txt')); - expect(listResult.excludedSubpaths.length, 1); - expect(listResult.excludedSubpaths.first, '$uniquePrefix/subdir/'); + expect(listResult.excludedSubpaths?.length, 1); + expect(listResult.excludedSubpaths?.first, '$uniquePrefix/subdir/'); expect(listResult.metadata.delimiter, '/'); }); @@ -101,9 +101,9 @@ void main() { expect(listResult.items.length, 3); expect(listResult.items.first.path, contains('file1.txt')); - expect(listResult.excludedSubpaths.length, 1); + expect(listResult.excludedSubpaths?.length, 1); expect( - listResult.excludedSubpaths.first, + listResult.excludedSubpaths?.first, '$uniquePrefix/subdir2#', ); expect(listResult.metadata.delimiter, '#'); diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart index c89c14e0f1..66536e8ac8 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart @@ -39,8 +39,8 @@ class S3ListResult extends StorageListResult { final items = output.contents?.map(S3Item.fromS3Object).toList(); return S3ListResult( - subPaths ?? [], items ?? const [], + excludedSubpaths: subPaths ?? [], hasNextPage: paginatedResult.hasNext, nextToken: paginatedResult.nextContinuationToken, metadata: metadata, @@ -52,13 +52,13 @@ class S3ListResult extends StorageListResult { final items = [...this.items, ...other.items]; final mergedSubPaths = [ - ...excludedSubpaths, - ...other.excludedSubpaths, + ...excludedSubpaths ?? [], + ...other.excludedSubpaths ?? [], ]; return S3ListResult( - mergedSubPaths, items, + excludedSubpaths: mergedSubPaths, hasNextPage: other.hasNextPage, nextToken: other.nextToken, metadata: metadata, diff --git a/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart b/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart index 2ed27902c1..e619191c95 100644 --- a/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart +++ b/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart @@ -86,8 +86,8 @@ void main() { group('list()', () { const testPath = StoragePath.fromString('some/path'); final testResult = S3ListResult( - [], [], + excludedSubpaths: [], hasNextPage: false, metadata: S3ListMetadata.fromDelimiter(), ); From 8cd20a9f5eca43a3325320ed002b8925301cf441 Mon Sep 17 00:00:00 2001 From: Andrew Hahn <58017052+hahnandrew@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:52:14 -0700 Subject: [PATCH 05/11] Apply suggestions from code review Better naming conventions, docstrings, chaning methods to use defaults instead of nullability Co-authored-by: NikaHsn --- .../lib/src/category/amplify_storage_category.dart | 10 +++++----- .../lib/src/types/storage/list_options.dart | 2 +- .../lib/src/types/storage/list_result.dart | 2 +- .../lib/src/model/s3_list_result.dart | 6 +++--- .../service/storage_s3_service_impl.dart | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/amplify_core/lib/src/category/amplify_storage_category.dart b/packages/amplify_core/lib/src/category/amplify_storage_category.dart index 8f84b1e107..37586167a4 100644 --- a/packages/amplify_core/lib/src/category/amplify_storage_category.dart +++ b/packages/amplify_core/lib/src/category/amplify_storage_category.dart @@ -249,14 +249,14 @@ class SubpathStrategy with AWSSerializable> { }); /// Constructor for SubpathStrategy for excluding subpaths, option to specify the delimiter - const SubpathStrategy.exclude({String? delimiter = '/'}) + const SubpathStrategy.exclude({String delimiter = '/'}) : this._( - excludeSubPaths: true, - delimiter: delimiter ?? '/', + excludeSubpaths: true, + delimiter: delimiter , ); /// Whether to exclude objects under the sub paths of the path to list. - final bool excludeSubPaths; + final bool excludeSubpaths; /// The delimiter to use when evaluating sub paths. If [excludeSubPaths] is /// false, this value has no impact on behavior. @@ -265,7 +265,7 @@ class SubpathStrategy with AWSSerializable> { @override Map toJson() { return { - 'excludeSubPaths': excludeSubPaths, + 'excludeSubpaths': excludeSubpaths, 'delimiter': delimiter, }; } diff --git a/packages/amplify_core/lib/src/types/storage/list_options.dart b/packages/amplify_core/lib/src/types/storage/list_options.dart index 619756ad6e..437efb9255 100644 --- a/packages/amplify_core/lib/src/types/storage/list_options.dart +++ b/packages/amplify_core/lib/src/types/storage/list_options.dart @@ -28,7 +28,7 @@ class StorageListOptions /// {@macro amplify_core.storage.list_plugin_options} final StorageListPluginOptions? pluginOptions; - /// Subpathstrategy for specifying storage subpath behavior + /// Subpath strategy for specifying storage subpath behavior final SubpathStrategy subpathStrategy; @override diff --git a/packages/amplify_core/lib/src/types/storage/list_result.dart b/packages/amplify_core/lib/src/types/storage/list_result.dart index ac5288b5ff..b83a373c5f 100644 --- a/packages/amplify_core/lib/src/types/storage/list_result.dart +++ b/packages/amplify_core/lib/src/types/storage/list_result.dart @@ -16,7 +16,7 @@ class StorageListResult { }); /// The subpaths that have been excluded - final List? excludedSubpaths; + final List excludedSubpaths; /// The objects listed in the current page. final List items; diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart index 66536e8ac8..c76fd24380 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart @@ -51,9 +51,9 @@ class S3ListResult extends StorageListResult { S3ListResult merge(S3ListResult other) { final items = [...this.items, ...other.items]; - final mergedSubPaths = [ - ...excludedSubpaths ?? [], - ...other.excludedSubpaths ?? [], + final mergedSubpaths = [ + ...excludedSubpaths , + ...other.excludedSubpaths, ]; return S3ListResult( diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart index cf68c6cb0d..f460c6f001 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart @@ -129,7 +129,7 @@ class StorageS3Service { final s3PluginOptions = options.pluginOptions as S3ListPluginOptions? ?? const S3ListPluginOptions(); - final s3Category = options.subpathStrategy; + final subpathStrategy = options.subpathStrategy; final resolvedPath = await _pathResolver.resolvePath(path: path); From 2585754176595a552310dbda5270d7e3246683b3 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 14 Aug 2024 09:59:04 -0700 Subject: [PATCH 06/11] chore: add subpath strategy class to its own file --- .../category/amplify_storage_category.dart | 35 ----------------- .../src/types/storage/subpath_strategy.dart | 39 +++++++++++++++++++ 2 files changed, 39 insertions(+), 35 deletions(-) create mode 100644 packages/amplify_core/lib/src/types/storage/subpath_strategy.dart diff --git a/packages/amplify_core/lib/src/category/amplify_storage_category.dart b/packages/amplify_core/lib/src/category/amplify_storage_category.dart index 37586167a4..416446d12e 100644 --- a/packages/amplify_core/lib/src/category/amplify_storage_category.dart +++ b/packages/amplify_core/lib/src/category/amplify_storage_category.dart @@ -235,38 +235,3 @@ class StorageCategory extends AmplifyCategory { ); } } - -/// {@template amplify_core.amplify_storage_category.subpath_strategy} -/// Configurable options for `Amplify.Storage.list`. -/// {@endtemplate} -class SubpathStrategy with AWSSerializable> { - /// {@macro amplify_core.amplify_storage_category.subpath_strategy} - const SubpathStrategy.include() : this._(); - - const SubpathStrategy._({ - this.excludeSubPaths = false, - this.delimiter = '/', - }); - - /// Constructor for SubpathStrategy for excluding subpaths, option to specify the delimiter - const SubpathStrategy.exclude({String delimiter = '/'}) - : this._( - excludeSubpaths: true, - delimiter: delimiter , - ); - - /// Whether to exclude objects under the sub paths of the path to list. - final bool excludeSubpaths; - - /// The delimiter to use when evaluating sub paths. If [excludeSubPaths] is - /// false, this value has no impact on behavior. - final String delimiter; - - @override - Map toJson() { - return { - 'excludeSubpaths': excludeSubpaths, - 'delimiter': delimiter, - }; - } -} diff --git a/packages/amplify_core/lib/src/types/storage/subpath_strategy.dart b/packages/amplify_core/lib/src/types/storage/subpath_strategy.dart new file mode 100644 index 0000000000..a4f4b13ab6 --- /dev/null +++ b/packages/amplify_core/lib/src/types/storage/subpath_strategy.dart @@ -0,0 +1,39 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import 'package:amplify_core/amplify_core.dart'; + +/// {@template amplify_core.amplify_storage_category.subpath_strategy} +/// Configurable options for `Amplify.Storage.list`. +/// {@endtemplate} +class SubpathStrategy with AWSSerializable> { + /// {@macro amplify_core.amplify_storage_category.subpath_strategy} + const SubpathStrategy.include() : this._(); + + const SubpathStrategy._({ + this.excludeSubpaths = false, + this.delimiter = '/', + }); + + /// Constructor for SubpathStrategy for excluding subpaths, option to specify the delimiter + const SubpathStrategy.exclude({String delimiter = '/'}) + : this._( + excludeSubpaths: true, + delimiter: delimiter, + ); + + /// Whether to exclude objects under the sub paths of the path to list. + final bool excludeSubpaths; + + /// The delimiter to use when evaluating sub paths. If [excludeSubpaths] is + /// false, this value has no impact on behavior. + final String delimiter; + + @override + Map toJson() { + return { + 'excludeSubpaths': excludeSubpaths, + 'delimiter': delimiter, + }; + } +} From a604cf96453ce911ee21a03e6cc6f6893f84eedf Mon Sep 17 00:00:00 2001 From: Andrew Hahn <58017052+hahnandrew@users.noreply.github.com> Date: Wed, 14 Aug 2024 10:02:08 -0700 Subject: [PATCH 07/11] chore: add default constructor to excludedSubpaths Co-authored-by: NikaHsn --- packages/amplify_core/lib/src/types/storage/list_result.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amplify_core/lib/src/types/storage/list_result.dart b/packages/amplify_core/lib/src/types/storage/list_result.dart index b83a373c5f..f24853a2b1 100644 --- a/packages/amplify_core/lib/src/types/storage/list_result.dart +++ b/packages/amplify_core/lib/src/types/storage/list_result.dart @@ -10,7 +10,7 @@ class StorageListResult { /// {@macro amplify_core.storage.list_result} const StorageListResult( this.items, { - this.excludedSubpaths, + this.excludedSubpaths = const [], required this.hasNextPage, this.nextToken, }); From 6c709cacaac8676b2b9bd518feb46bcec0840ef7 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 14 Aug 2024 10:04:19 -0700 Subject: [PATCH 08/11] chore: cleanup S3ListMetadata class --- .../lib/src/model/s3_list_result.dart | 16 ++++------------ .../test/amplify_storage_s3_dart_test.dart | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart index c76fd24380..0dc99ecdec 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/model/s3_list_result.dart @@ -27,7 +27,7 @@ class S3ListResult extends StorageListResult { PaginatedResult paginatedResult, ) { final output = paginatedResult.items; - final metadata = S3ListMetadata.fromDelimiter( + final metadata = S3ListMetadata( delimiter: output.delimiter, ); @@ -52,13 +52,13 @@ class S3ListResult extends StorageListResult { final items = [...this.items, ...other.items]; final mergedSubpaths = [ - ...excludedSubpaths , + ...excludedSubpaths, ...other.excludedSubpaths, ]; return S3ListResult( items, - excludedSubpaths: mergedSubPaths, + excludedSubpaths: mergedSubpaths, hasNextPage: other.hasNextPage, nextToken: other.nextToken, metadata: metadata, @@ -73,15 +73,7 @@ class S3ListResult extends StorageListResult { class S3ListMetadata { /// Creates a S3ListMetadata from the `delimiter` /// properties of the [s3.ListObjectsV2Output]. - factory S3ListMetadata.fromDelimiter({ - String? delimiter, - }) { - return S3ListMetadata._( - delimiter: delimiter, - ); - } - - S3ListMetadata._({ + const S3ListMetadata({ this.delimiter, }); diff --git a/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart b/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart index e619191c95..23bc4c1fd8 100644 --- a/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart +++ b/packages/storage/amplify_storage_s3_dart/test/amplify_storage_s3_dart_test.dart @@ -89,7 +89,7 @@ void main() { [], excludedSubpaths: [], hasNextPage: false, - metadata: S3ListMetadata.fromDelimiter(), + metadata: const S3ListMetadata(), ); setUpAll(() { From 3789b440175c299a28fa6176e7928fad14b9a419 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 14 Aug 2024 10:09:51 -0700 Subject: [PATCH 09/11] chore: add subpathstrategy in storage types file --- packages/amplify_core/lib/src/types/storage/storage_types.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/amplify_core/lib/src/types/storage/storage_types.dart b/packages/amplify_core/lib/src/types/storage/storage_types.dart index 9324a72683..e5e2d235b1 100644 --- a/packages/amplify_core/lib/src/types/storage/storage_types.dart +++ b/packages/amplify_core/lib/src/types/storage/storage_types.dart @@ -46,6 +46,7 @@ export 'remove_request.dart'; export 'remove_result.dart'; export 'storage_item.dart'; export 'storage_path.dart'; +export 'subpath_strategy.dart'; export 'transfer_progress.dart'; export 'upload_data_operation.dart'; export 'upload_data_options.dart'; From 094dea8d7435903cb4fc8a46aee121f1227b1e05 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 14 Aug 2024 10:25:47 -0700 Subject: [PATCH 10/11] chore: rm null operator on excludedSubpaths --- .../example/integration_test/list_test.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart b/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart index d6321b8794..4443479fea 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/list_test.dart @@ -84,8 +84,8 @@ void main() { expect(listResult.items.length, 3); expect(listResult.items.first.path, contains('file1.txt')); - expect(listResult.excludedSubpaths?.length, 1); - expect(listResult.excludedSubpaths?.first, '$uniquePrefix/subdir/'); + expect(listResult.excludedSubpaths.length, 1); + expect(listResult.excludedSubpaths.first, '$uniquePrefix/subdir/'); expect(listResult.metadata.delimiter, '/'); }); @@ -101,9 +101,9 @@ void main() { expect(listResult.items.length, 3); expect(listResult.items.first.path, contains('file1.txt')); - expect(listResult.excludedSubpaths?.length, 1); + expect(listResult.excludedSubpaths.length, 1); expect( - listResult.excludedSubpaths?.first, + listResult.excludedSubpaths.first, '$uniquePrefix/subdir2#', ); expect(listResult.metadata.delimiter, '#'); From 347d607c8ac77a94d44da64d22a2d1aa47beeb83 Mon Sep 17 00:00:00 2001 From: Andrew Hahn Date: Wed, 14 Aug 2024 10:33:49 -0700 Subject: [PATCH 11/11] test: fixed renaming --- .../service/storage_s3_service_impl.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart index f460c6f001..cd1ec91ac8 100644 --- a/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart +++ b/packages/storage/amplify_storage_s3_dart/lib/src/storage_s3_service/service/storage_s3_service_impl.dart @@ -140,8 +140,9 @@ class StorageS3Service { ..prefix = resolvedPath ..maxKeys = options.pageSize ..continuationToken = options.nextToken - ..delimiter = - s3Category.excludeSubPaths ? s3Category.delimiter : null; + ..delimiter = subpathStrategy.excludeSubpaths + ? subpathStrategy.delimiter + : null; }); try { @@ -164,8 +165,9 @@ class StorageS3Service { builder ..bucket = _storageOutputs.bucketName ..prefix = resolvedPath - ..delimiter = - s3Category.excludeSubPaths ? s3Category.delimiter : null; + ..delimiter = subpathStrategy.excludeSubpaths + ? subpathStrategy.delimiter + : null; }); listResult = await _defaultS3Client.listObjectsV2(request).result;