Skip to content

Commit

Permalink
feat(storage): multi-bucket remove many api
Browse files Browse the repository at this point in the history
  • Loading branch information
Nika Hassani committed Nov 1, 2024
1 parent 14cfff4 commit 4ba83be
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 4 deletions.
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.remove_many_options}
/// Configurable options for `Amplify.Storage.removeMany`.
Expand All @@ -14,20 +14,28 @@ class StorageRemoveManyOptions
/// {@macro amplify_core.storage.remove_many_options}
const StorageRemoveManyOptions({
this.pluginOptions,
this.bucket,
});

/// {@macro amplify_core.storage.remove_many_plugin_options}
final StorageRemoveManyPluginOptions? 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 => 'StorageRemoveManyOptions';

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,127 @@ void main() {
});
});

group('Multi-bucket', () {
final mainBucket = StorageBucket.fromOutputs(
'Storage Integ Test main bucket',
);
final secondaryBucket = StorageBucket.fromOutputs(
'Storage Integ Test secondary bucket',
);
final path1 = 'public/multi-bucket-remove-many-${uuid()}';
final path2 = 'public/multi-bucket-remove-many-${uuid()}';
final storagePath1 = StoragePath.fromString(path1);
final storagePath2 = StoragePath.fromString(path2);
setUp(() async {
await Amplify.Storage.uploadData(
data: StorageDataPayload.bytes('data'.codeUnits),
path: storagePath1,
options: StorageUploadDataOptions(
bucket: mainBucket,
),
).result;
await Amplify.Storage.uploadData(
data: StorageDataPayload.bytes('data'.codeUnits),
path: storagePath2,
options: StorageUploadDataOptions(
bucket: mainBucket,
),
).result;
await Amplify.Storage.uploadData(
data: StorageDataPayload.bytes('data'.codeUnits),
path: storagePath1,
options: StorageUploadDataOptions(
bucket: secondaryBucket,
),
).result;
await Amplify.Storage.uploadData(
data: StorageDataPayload.bytes('data'.codeUnits),
path: storagePath2,
options: StorageUploadDataOptions(
bucket: secondaryBucket,
),
).result;
});

testWidgets('removes objects from main bucket', (_) async {
expect(
await objectExists(
storagePath1,
bucket: mainBucket,
),
true,
);
expect(
await objectExists(
storagePath2,
bucket: mainBucket,
),
true,
);
final result = await Amplify.Storage.removeMany(
paths: [storagePath1, storagePath2],
options: StorageRemoveManyOptions(
bucket: mainBucket,
),
).result;
expect(
await objectExists(
storagePath1,
bucket: mainBucket,
),
false,
);
expect(
await objectExists(
storagePath2,
bucket: mainBucket,
),
false,
);
final removedPaths = result.removedItems.map((i) => i.path).toList();
expect(removedPaths, unorderedEquals([path1, path2]));
});

testWidgets('removes objects from secondary bucket', (_) async {
expect(
await objectExists(
storagePath1,
bucket: secondaryBucket,
),
true,
);
expect(
await objectExists(
storagePath2,
bucket: secondaryBucket,
),
true,
);
final result = await Amplify.Storage.removeMany(
paths: [storagePath1, storagePath2],
options: StorageRemoveManyOptions(
bucket: secondaryBucket,
),
).result;
expect(
await objectExists(
storagePath1,
bucket: secondaryBucket,
),
false,
);
expect(
await objectExists(
storagePath2,
bucket: secondaryBucket,
),
false,
);
final removedPaths = result.removedItems.map((i) => i.path).toList();
expect(removedPaths, unorderedEquals([path1, path2]));
});
});

testWidgets('unauthorized path', (_) async {
final result = await Amplify.Storage.removeMany(
paths: [const StoragePath.fromString('unauthorized/path')],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ class AmplifyStorageS3Dart extends StoragePluginInterface

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

return S3RemoveManyOperation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ class StorageS3Service {
final objectIdentifiersToRemove =
resolvedPaths.map((path) => s3.ObjectIdentifier(key: path)).toList();

final s3ClientInfo = getS3ClientInfo(storageBucket: options.bucket);

final removedItems = <S3Item>[];
final removedErrors = <s3.Error>[];

Expand All @@ -502,15 +504,15 @@ class StorageS3Service {
);
final request = s3.DeleteObjectsRequest.build((builder) {
builder
..bucket = _storageOutputs.bucketName
..bucket = s3ClientInfo.bucketName
// force to use sha256 instead of md5
..checksumAlgorithm = s3.ChecksumAlgorithm.sha256
..delete = s3.Delete.build((builder) {
builder.objects.addAll(bachedObjectIdentifiers);
}).toBuilder();
});
try {
final output = await _defaultS3Client.deleteObjects(request).result;
final output = await s3ClientInfo.client.deleteObjects(request).result;
removedItems.addAll(
output.deleted?.toList().map(
(removedObject) => S3Item.fromS3Object(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,9 @@ void main() {
() async {
const testOptions = StorageRemoveManyOptions(
pluginOptions: S3RemoveManyPluginOptions(),
bucket: StorageBucket.fromBucketInfo(
BucketInfo(bucketName: 'unit-test-bucket', region: 'us-east-2'),
),
);

when(
Expand Down

0 comments on commit 4ba83be

Please sign in to comment.