From 0ca07c1331e31cec7788a85334768eca40041f12 Mon Sep 17 00:00:00 2001 From: Elijah Quartey Date: Fri, 8 Nov 2024 12:25:08 -0800 Subject: [PATCH] feat(storage): multi bucket get url --- .../src/types/storage/get_url_options.dart | 12 +++- .../integration_test/get_url_test.dart | 56 +++++++++++++++++++ .../lib/src/amplify_storage_s3_dart_impl.dart | 1 + .../service/storage_s3_service_impl.dart | 17 +++--- .../test/amplify_storage_s3_dart_test.dart | 23 ++++---- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/packages/amplify_core/lib/src/types/storage/get_url_options.dart b/packages/amplify_core/lib/src/types/storage/get_url_options.dart index 3f4078839e..62954e8281 100644 --- a/packages/amplify_core/lib/src/types/storage/get_url_options.dart +++ b/packages/amplify_core/lib/src/types/storage/get_url_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.get_url_options} /// Configurable options for `Amplify.Storage.getUrl`. @@ -14,13 +14,20 @@ class StorageGetUrlOptions /// {@macro amplify_core.storage.get_url_options} const StorageGetUrlOptions({ this.pluginOptions, + this.bucket, }); /// {@macro amplify_core.storage.get_url_plugin_options} final StorageGetUrlPluginOptions? pluginOptions; + /// Optionally specify which bucket to target + final StorageBucket? bucket; + @override - List get props => [pluginOptions]; + List get props => [ + pluginOptions, + bucket, + ]; @override String get runtimeTypeName => 'StorageGetUrlOptions'; @@ -28,6 +35,7 @@ class StorageGetUrlOptions @override Map toJson() => { 'pluginOptions': pluginOptions?.toJson(), + 'bucket': bucket, }; } diff --git a/packages/storage/amplify_storage_s3/example/integration_test/get_url_test.dart b/packages/storage/amplify_storage_s3/example/integration_test/get_url_test.dart index 2c26f5b610..4d49674900 100644 --- a/packages/storage/amplify_storage_s3/example/integration_test/get_url_test.dart +++ b/packages/storage/amplify_storage_s3/example/integration_test/get_url_test.dart @@ -172,6 +172,62 @@ void main() { expect(actualData, data); }); }); + + group('multi bucket', () { + final mainBucket = StorageBucket.fromOutputs( + 'Storage Integ Test main bucket', + ); + final secondaryBucket = StorageBucket.fromOutputs( + 'Storage Integ Test secondary bucket', + ); + final pathMain = 'public/multi-bucket-get-url-${uuid()}'; + final pathSecondary = 'public/multi-bucket-get-url-${uuid()}'; + final storagePathMain = StoragePath.fromString(pathMain); + final storagePathSecondary = StoragePath.fromString(pathSecondary); + + setUp(() async { + addTearDownPath(storagePathMain); + addTearDownPath(storagePathSecondary); + await Amplify.Storage.uploadData( + data: StorageDataPayload.bytes(data), + path: storagePathMain, + options: StorageUploadDataOptions( + bucket: mainBucket, + ), + ).result; + await Amplify.Storage.uploadData( + data: StorageDataPayload.bytes(data), + path: storagePathSecondary, + options: StorageUploadDataOptions( + bucket: secondaryBucket, + ), + ).result; + }); + + testWidgets('can get url from main bucket', (_) async { + final result = await Amplify.Storage.getUrl( + path: storagePathMain, + options: StorageGetUrlOptions( + bucket: mainBucket, + ), + ).result; + expect(result.url.path, '/$pathMain'); + final actualData = await readData(result.url); + expect(actualData, data); + }); + + testWidgets('can get url from secondary bucket', (_) async { + final result = await Amplify.Storage.getUrl( + path: storagePathSecondary, + options: StorageGetUrlOptions( + bucket: secondaryBucket, + ), + ).result; + expect(result.url.path, '/$pathSecondary'); + final actualData = await readData(result.url); + expect(actualData, data); + }); + }); }); group('config with dots in name', () { 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 346b08a2b9..3abc9c7490 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 @@ -191,6 +191,7 @@ class AmplifyStorageS3Dart extends StoragePluginInterface final s3Options = StorageGetUrlOptions( pluginOptions: s3PluginOptions, + bucket: options?.bucket, ); return S3GetUrlOperation( 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 4cadf2d644..05b71ea05f 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 @@ -222,9 +222,10 @@ class StorageS3Service { }) async { final s3PluginOptions = options.pluginOptions as S3GetUrlPluginOptions? ?? const S3GetUrlPluginOptions(); + final s3ClientInfo = getS3ClientInfo(storageBucket: options.bucket); if (s3PluginOptions.useAccelerateEndpoint && - _defaultS3ClientConfig.usePathStyle) { + s3ClientInfo.config.usePathStyle) { throw s3_exception.accelerateEndpointUnusable; } @@ -234,20 +235,20 @@ class StorageS3Service { // the `getProperties` API (i.e. HeadObject) await getProperties( path: path, - options: const StorageGetPropertiesOptions(), + options: StorageGetPropertiesOptions(bucket: options.bucket), ); } var resolvedPath = await _pathResolver.resolvePath(path: path); var host = - '${_storageOutputs.bucketName}.${_getS3EndpointHost(region: _storageOutputs.awsRegion)}'; - if (_defaultS3ClientConfig.usePathStyle) { - host = host.replaceFirst('${_storageOutputs.bucketName}.', ''); - resolvedPath = '${_storageOutputs.bucketName}/$resolvedPath'; + '${s3ClientInfo.bucketName}.${_getS3EndpointHost(region: s3ClientInfo.awsRegion)}'; + if (s3ClientInfo.config.usePathStyle) { + host = host.replaceFirst('${s3ClientInfo.bucketName}.', ''); + resolvedPath = '${s3ClientInfo.bucketName}/$resolvedPath'; } else if (s3PluginOptions.useAccelerateEndpoint) { // https: //docs.aws.amazon.com/AmazonS3/latest/userguide/transfer-acceleration-getting-started.html host = host - .replaceFirst(RegExp('${_storageOutputs.awsRegion}\\.'), '') + .replaceFirst(RegExp('${s3ClientInfo.awsRegion}\\.'), '') .replaceFirst(RegExp(r'\.s3\.'), '.s3-accelerate.'); } @@ -263,7 +264,7 @@ class StorageS3Service { credentialsProvider: _credentialsProvider, ); final signerScope = sigv4.AWSCredentialScope( - region: _storageOutputs.awsRegion, + region: s3ClientInfo.awsRegion, service: AWSService.s3, ); 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 f144a068c1..db95a371d2 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 @@ -348,6 +348,9 @@ void main() { expiresIn: Duration(minutes: 10), useAccelerateEndpoint: true, ), + bucket: StorageBucket.fromBucketInfo( + BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), + ), ); when( @@ -418,9 +421,6 @@ void main() { () async { const defaultOptions = StorageDownloadDataOptions( pluginOptions: S3DownloadDataPluginOptions(), - bucket: StorageBucket.fromBucketInfo( - BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), - ), ); when( @@ -438,7 +438,6 @@ void main() { downloadDataOperation = storageS3Plugin.downloadData( path: const StoragePath.fromString('public/$testKey'), - options: defaultOptions, ); final capturedOptions = verify( @@ -471,6 +470,9 @@ void main() { useAccelerateEndpoint: true, getProperties: true, ), + bucket: StorageBucket.fromBucketInfo( + BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), + ), ); when( @@ -737,9 +739,6 @@ void main() { () async { const defaultOptions = StorageUploadFileOptions( pluginOptions: S3UploadFilePluginOptions(), - bucket: StorageBucket.fromBucketInfo( - BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), - ), ); when( @@ -758,7 +757,6 @@ void main() { uploadFileOperation = storageS3Plugin.uploadFile( path: testPath, localFile: testLocalFile, - options: defaultOptions, ); final capturedParams = verify( @@ -798,6 +796,9 @@ void main() { getProperties: true, useAccelerateEndpoint: true, ), + bucket: StorageBucket.fromBucketInfo( + BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), + ), ); when( @@ -984,9 +985,6 @@ void main() { () async { const defaultOptions = StorageRemoveOptions( pluginOptions: S3RemovePluginOptions(), - bucket: StorageBucket.fromBucketInfo( - BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), - ), ); when( () => storageS3Service.remove( @@ -1024,6 +1022,9 @@ void main() { test('should forward options to StorageS3Service.remove() API', () async { const testOptions = StorageRemoveOptions( pluginOptions: S3RemovePluginOptions(), + bucket: StorageBucket.fromBucketInfo( + BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'), + ), ); when(