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 e647bed310f..218feec596c 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 @@ -19,6 +19,7 @@ void main() { '$uniquePrefix/file1.txt', '$uniquePrefix/file2.txt', '$uniquePrefix/subdir/file3.txt', + '$uniquePrefix/subdir2#file4.txt', ]; group('standard config', () { setUpAll(() async { @@ -70,20 +71,46 @@ void main() { }); group('list() with options', () { - testWidgets( - 'should list all files with unique prefix excluding subPaths', - (_) async { - final listResult = await Amplify.Storage.list( - path: StoragePath.fromString('$uniquePrefix/'), - options: const StorageListOptions( - pluginOptions: S3ListPluginOptions( - excludeSubPaths: true, + group('excluding sub paths', () { + testWidgets('default delimiter', (_) async { + final listResult = await Amplify.Storage.list( + path: StoragePath.fromString('$uniquePrefix/'), + options: const StorageListOptions( + pluginOptions: S3ListPluginOptions( + excludeSubPaths: true, + ), ), - ), - ).result; + ).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.metadata.delimiter, '/'); + }); + + testWidgets('custom delimiter', (_) async { + final listResult = await Amplify.Storage.list( + path: StoragePath.fromString('$uniquePrefix/'), + options: const StorageListOptions( + pluginOptions: S3ListPluginOptions( + excludeSubPaths: true, + delimiter: '#', + ), + ), + ).result as S3ListResult; - expect(listResult.items.length, 2); - expect(listResult.items.first.path, contains('file1.txt')); + 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/subdir2#', + ); + expect(listResult.metadata.delimiter, '#'); + }); }); testWidgets('should respect pageSize limitation', (_) async { @@ -130,7 +157,7 @@ void main() { ), ).result; - expect(listResult.items.length, 3); + expect(listResult.items.length, uploadedPaths.length); expect(listResult.nextToken, isNull); }); }); diff --git a/packages/storage/amplify_storage_s3/lib/src/amplify_storage_s3_impl.dart b/packages/storage/amplify_storage_s3/lib/src/amplify_storage_s3_impl.dart index d6202a58bd1..a3a693fcd7a 100644 --- a/packages/storage/amplify_storage_s3/lib/src/amplify_storage_s3_impl.dart +++ b/packages/storage/amplify_storage_s3/lib/src/amplify_storage_s3_impl.dart @@ -12,9 +12,7 @@ import 'package:amplify_storage_s3_dart/amplify_storage_s3_dart.dart'; /// {@endtemplate} class AmplifyStorageS3 extends AmplifyStorageS3Dart { /// {@macro amplify_storage_s3_.amplify_storage_s3_plugin} - AmplifyStorageS3({ - super.delimiter, - }); + AmplifyStorageS3(); /// A plugin key which can be used with `Amplify.Storage.getPlugin` to retrieve /// a S3-specific Storage category interface. 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 079dab34c72..ea432433f1f 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 @@ -30,10 +30,8 @@ class AmplifyStorageS3Dart extends StoragePluginInterface with AWSDebuggable, AWSLoggerMixin { /// {@macro amplify_storage_s3_dart.amplify_storage_s3_plugin_dart} AmplifyStorageS3Dart({ - String? delimiter, @visibleForTesting DependencyManager? dependencyManagerOverride, - }) : _delimiter = delimiter, - _dependencyManagerOverride = dependencyManagerOverride; + }) : _dependencyManagerOverride = dependencyManagerOverride; /// {@template amplify_storage_s3_dart.plugin_key} /// A plugin key which can be used with `Amplify.Storage.getPlugin` to retrieve @@ -42,8 +40,6 @@ class AmplifyStorageS3Dart extends StoragePluginInterface static const StoragePluginKey pluginKey = _AmplifyStorageS3DartPluginKey(); - final String? _delimiter; - final DependencyManager? _dependencyManagerOverride; @override @@ -112,7 +108,6 @@ class AmplifyStorageS3Dart extends StoragePluginInterface StorageS3Service( credentialsProvider: credentialsProvider, s3PluginConfig: s3PluginConfig, - delimiter: _delimiter, pathResolver: _pathResolver, logger: logger, dependencyManager: dependencies, 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 ebebdbbc411..17480ee70ef 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 @@ -13,12 +13,15 @@ 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.excludeSubPaths = false, + this.delimiter = '/', this.listAll = false, }); @@ -40,6 +43,10 @@ class S3ListPluginOptions extends StorageListPluginOptions { /// 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`. /// 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 ef58e53529d..66af0a459ff 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 @@ -9,10 +9,12 @@ part of 's3_list_plugin_options.dart'; S3ListPluginOptions _$S3ListPluginOptionsFromJson(Map json) => S3ListPluginOptions( excludeSubPaths: json['excludeSubPaths'] as bool? ?? false, + delimiter: json['delimiter'] as String? ?? '/', ); Map _$S3ListPluginOptionsToJson( S3ListPluginOptions instance) => { 'excludeSubPaths': instance.excludeSubPaths, + 'delimiter': instance.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 42dc30a682a..26e56cd1d70 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 @@ -36,7 +36,6 @@ class StorageS3Service { required AWSIamAmplifyAuthProvider credentialsProvider, required AWSLogger logger, required DependencyManager dependencyManager, - String? delimiter, }) { final usePathStyle = s3PluginConfig.bucket.contains('.'); @@ -61,7 +60,6 @@ class StorageS3Service { credentialsProvider: credentialsProvider, logger: logger, dependencyManager: dependencyManager, - delimiter: delimiter, ); } @@ -72,9 +70,7 @@ class StorageS3Service { required AWSIamAmplifyAuthProvider credentialsProvider, required AWSLogger logger, required DependencyManager dependencyManager, - String? delimiter, }) : _s3PluginConfig = s3PluginConfig, - _delimiter = delimiter ?? '/', _defaultS3ClientConfig = s3ClientConfig, // dependencyManager.get() => S3Client is used for unit tests _defaultS3Client = dependencyManager.get() ?? @@ -99,7 +95,6 @@ class StorageS3Service { sigv4.S3ServiceConfiguration(signPayload: false); final S3PluginConfig _s3PluginConfig; - final String _delimiter; final smithy_aws.S3ClientConfig _defaultS3ClientConfig; final s3.S3Client _defaultS3Client; final S3PathResolver _pathResolver; @@ -141,7 +136,9 @@ class StorageS3Service { ..prefix = resolvedPath ..maxKeys = options.pageSize ..continuationToken = options.nextToken - ..delimiter = s3PluginOptions.excludeSubPaths ? _delimiter : null; + ..delimiter = s3PluginOptions.excludeSubPaths + ? s3PluginOptions.delimiter + : null; }); try { @@ -164,7 +161,9 @@ class StorageS3Service { builder ..bucket = _s3PluginConfig.bucket ..prefix = resolvedPath - ..delimiter = s3PluginOptions.excludeSubPaths ? _delimiter : null; + ..delimiter = s3PluginOptions.excludeSubPaths + ? s3PluginOptions.delimiter + : null; }); listResult = await _defaultS3Client.listObjectsV2(request).result;