Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(storage): multi bucket get url #5659

Merged
merged 1 commit into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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`.
Expand All @@ -14,20 +14,28 @@ 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<Object?> get props => [pluginOptions];
List<Object?> get props => [
pluginOptions,
bucket,
];

@override
String get runtimeTypeName => 'StorageGetUrlOptions';

@override
Map<String, Object?> toJson() => {
'pluginOptions': pluginOptions?.toJson(),
'bucket': bucket,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ class AmplifyStorageS3Dart extends StoragePluginInterface

final s3Options = StorageGetUrlOptions(
pluginOptions: s3PluginOptions,
bucket: options?.bucket,
);

return S3GetUrlOperation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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.');
}

Expand All @@ -263,7 +264,7 @@ class StorageS3Service {
credentialsProvider: _credentialsProvider,
);
final signerScope = sigv4.AWSCredentialScope(
region: _storageOutputs.awsRegion,
region: s3ClientInfo.awsRegion,
service: AWSService.s3,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -438,7 +438,6 @@ void main() {

downloadDataOperation = storageS3Plugin.downloadData(
path: const StoragePath.fromString('public/$testKey'),
options: defaultOptions,
);

final capturedOptions = verify(
Expand Down Expand Up @@ -471,6 +470,9 @@ void main() {
useAccelerateEndpoint: true,
getProperties: true,
),
bucket: StorageBucket.fromBucketInfo(
BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'),
),
);

when(
Expand Down Expand Up @@ -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(
Expand All @@ -758,7 +757,6 @@ void main() {
uploadFileOperation = storageS3Plugin.uploadFile(
path: testPath,
localFile: testLocalFile,
options: defaultOptions,
);

final capturedParams = verify(
Expand Down Expand Up @@ -798,6 +796,9 @@ void main() {
getProperties: true,
useAccelerateEndpoint: true,
),
bucket: StorageBucket.fromBucketInfo(
BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'),
),
);

when(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
Loading