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): copy and move APIs #4569

Merged
merged 4 commits into from
Mar 21, 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
Expand Up @@ -194,8 +194,8 @@ class StorageCategory extends AmplifyCategory<StoragePluginInterface> {
/// corresponding [StorageAccessLevel].
/// {@endtemplate}
StorageCopyOperation copy({
required StorageItemWithAccessLevel<StorageItem> source,
required StorageItemWithAccessLevel<StorageItem> destination,
required StoragePath source,
required StoragePath destination,
StorageCopyOptions? options,
}) {
return identifyCall(
Expand All @@ -208,31 +208,6 @@ class StorageCategory extends AmplifyCategory<StoragePluginInterface> {
);
}

/// {@template amplify_core.amplify_storage_category.move}
/// Moves [source] to [destination] with optional [StorageMoveOptions],
/// and returns a [StorageMoveOperation].
///
/// This API performs two consecutive S3 service calls:
/// 1. copy the source object to destination objection
/// 2. delete the source object
///
/// {@macro amplify_core.amplify_storage_category.copy_source}
/// {@endtemplate}
StorageMoveOperation move({
required StorageItemWithAccessLevel<StorageItem> source,
required StorageItemWithAccessLevel<StorageItem> destination,
StorageMoveOptions? options,
}) {
return identifyCall(
StorageCategoryMethod.move,
() => defaultPlugin.move(
source: source,
destination: destination,
options: options,
),
);
}

/// {@template amplify_core.amplify_storage_category.remove}
/// Removes an object specified by [key] with optional [StorageRemoveOptions],
/// and returns a [StorageRemoveOperation].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,13 @@ abstract class StoragePluginInterface extends AmplifyPluginInterface {

/// {@macro amplify_core.amplify_storage_category.copy}
StorageCopyOperation copy({
required StorageItemWithAccessLevel<StorageItem> source,
required StorageItemWithAccessLevel<StorageItem> destination,
required StoragePath source,
required StoragePath destination,
StorageCopyOptions? options,
}) {
throw UnimplementedError('copy() has not been implemented.');
}

/// {@macro amplify_core.amplify_storage_category.move}
StorageMoveOperation move({
required StorageItemWithAccessLevel<StorageItem> source,
required StorageItemWithAccessLevel<StorageItem> destination,
StorageMoveOptions? options,
}) {
throw UnimplementedError('move() has not been implemented.');
}

/// {@macro amplify_core.amplify_storage_category.remove}
StorageRemoveOperation remove({
required String key,
Expand Down
4 changes: 2 additions & 2 deletions packages/amplify_core/lib/src/types/storage/copy_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class StorageCopyRequest<Item extends StorageItem> {
});

/// Copy source.
final StorageItemWithAccessLevel<Item> source;
final StoragePath source;

/// Copy destination.
final StorageItemWithAccessLevel<Item> destination;
final StoragePath destination;

/// Configurable options of the [StorageCopyRequest].
final StorageCopyOptions? options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ void main() {
testLargeFileBytes[0] = 101;
testLargeFileBytes[5 * 1024] = 102;
final testObjectKey1 = 'user1-guest-object-${uuid()}';
final testObject1MoveKey = 'user1-guest-object-move-${uuid()}';
final testObjectKey2 = 'user1-protected-object-${uuid()}';
final testObject2MoveKey = 'user1-protected-object-move-${uuid()}';
final testObjectKey3 = 'user1-private-object-${uuid()}';
final testObject3CopyKey = 'user1-private-object-copy-${uuid()}';
final testObject3CopyMoveKey = 'user1-private-object-copy-move-${uuid()}';
Expand Down Expand Up @@ -407,13 +405,11 @@ void main() {
'copy object with access level private for the currently signed in user',
(WidgetTester tester) async {
final result = await Amplify.Storage.copy(
source: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObjectKey3),
accessLevel: StorageAccessLevel.private,
source: StoragePath.withIdentityId(
(identityId) => '/private/$identityId/$testObjectKey3',
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObject3CopyKey),
accessLevel: StorageAccessLevel.private,
destination: StoragePath.withIdentityId(
(identityId) => '/private/$identityId/$testObject3CopyKey',
),
options: const StorageCopyOptions(
pluginOptions: S3CopyPluginOptions(
Expand All @@ -426,41 +422,6 @@ void main() {
expect(result.copiedItem.eTag, isNotEmpty);
});

testWidgets(
skip: true,
'move object with access level private for the currently signed in user',
(WidgetTester tester) async {
final result = await Amplify.Storage.move(
source: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObject3CopyKey),
accessLevel: StorageAccessLevel.private,
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObject3CopyMoveKey),
accessLevel: StorageAccessLevel.private,
),
options: const StorageMoveOptions(
pluginOptions: S3MovePluginOptions(getProperties: true),
),
).result;

expect(result.movedItem.key, testObject3CopyMoveKey);
expect(result.movedItem.eTag, isNotEmpty);

final listedObjects = await Amplify.Storage.list(
options: const StorageListOptions(
accessLevel: StorageAccessLevel.private,
),
).result;

expect(
listedObjects.items.map((item) => item.key),
isNot(
contains(testObject3CopyKey),
),
);
});

testWidgets(
'delete object with access level private for the currently signed in user',
(WidgetTester tester) async {
Expand Down Expand Up @@ -696,41 +657,16 @@ void main() {
);
});

testWidgets(
skip: true,
'move object with access level guest for the currently signed in user',
(WidgetTester tester) async {
final result = await Amplify.Storage.move(
source: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObjectKey1),
accessLevel: StorageAccessLevel.guest,
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObject1MoveKey),
accessLevel: StorageAccessLevel.private,
),
options: const StorageMoveOptions(
pluginOptions: S3MovePluginOptions(
getProperties: true,
),
),
).result;

expect(result.movedItem.eTag, isNotEmpty);
});

testWidgets(
skip: true,
'copy object (belongs to other user) with access level protected'
' for the currently signed in user', (WidgetTester tester) async {
final result = await Amplify.Storage.copy(
source: S3ItemWithAccessLevel.forIdentity(
user1IdentityId,
storageItem: S3Item(key: testObjectKey2),
source: StoragePath.fromString(
'/protected/$user1IdentityId/$testObjectKey2',
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObject2MoveKey),
accessLevel: StorageAccessLevel.private,
destination: StoragePath.withIdentityId(
(identityId) => '/private/$identityId/$testObjectKey2',
),
options: const StorageCopyOptions(
pluginOptions: S3CopyPluginOptions(
Expand Down Expand Up @@ -876,27 +812,6 @@ void main() {
await Amplify.Auth.signOut();
});

testWidgets(
skip: true,
'move object with access level protected as object owner',
(WidgetTester tester) async {
final result = await Amplify.Storage.move(
source: S3ItemWithAccessLevel.forIdentity(
user1IdentityId,
storageItem: S3Item(key: testObjectKey2),
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: testObject2MoveKey),
accessLevel: StorageAccessLevel.private,
),
options: const StorageMoveOptions(
pluginOptions: S3MovePluginOptions(getProperties: true),
),
).result;

expect(result.movedItem.eTag, isNotEmpty);
});

testWidgets(
skip: true,
'remove many objects belongs to the currently signed user',
Expand Down
62 changes: 3 additions & 59 deletions packages/storage/amplify_storage_s3_dart/example/bin/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ Future<void> main() async {
3. getUrl 4. download data
5. download file 6. upload data url
7. upload file 8. copy
9. move 10. remove
0. exit
9. remove 0. exit
''');
final operationNum = int.tryParse(operation);

Expand All @@ -64,8 +63,6 @@ Future<void> main() async {
case 8:
await copyOperation();
case 9:
await moveOperation();
case 10:
await removeOperation();
case null:
break;
Expand Down Expand Up @@ -369,24 +366,12 @@ Future<void> uploadFileOperation() async {

Future<void> copyOperation() async {
final sourceKey = prompt('Enter the key of the source object: ');
final sourceStorageAccessLevel = promptStorageAccessLevel(
'Choose the storage access level associated with the source object: ',
);
final destinationKey = prompt('Enter the key of the destination object: ');
final destinationStorageAccessLevel = promptStorageAccessLevel(
'Choose the storage access level associated with the destination object: ',
);

final s3Plugin = Amplify.Storage.getPlugin(AmplifyStorageS3Dart.pluginKey);
final copyOperation = s3Plugin.copy(
source: S3ItemWithAccessLevel(
storageItem: S3Item(key: sourceKey),
accessLevel: sourceStorageAccessLevel,
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: destinationKey),
accessLevel: destinationStorageAccessLevel,
),
source: StoragePath.fromString(sourceKey),
destination: StoragePath.fromString(destinationKey),
options: const StorageCopyOptions(
pluginOptions: S3CopyPluginOptions(getProperties: true),
),
Expand All @@ -408,47 +393,6 @@ Future<void> copyOperation() async {
}
}

Future<void> moveOperation() async {
final sourceKey = prompt('Enter the key of the source object: ');
final sourceStorageAccessLevel = promptStorageAccessLevel(
'Choose the storage access level associated with the source object: ',
);
final destinationKey = prompt('Enter the key of the destination object: ');
final destinationStorageAccessLevel = promptStorageAccessLevel(
'Choose the storage access level associated with the destination object: ',
);

final s3Plugin = Amplify.Storage.getPlugin(AmplifyStorageS3Dart.pluginKey);
final moveOperation = s3Plugin.move(
source: S3ItemWithAccessLevel(
storageItem: S3Item(key: sourceKey),
accessLevel: sourceStorageAccessLevel,
),
destination: S3ItemWithAccessLevel(
storageItem: S3Item(key: destinationKey),
accessLevel: destinationStorageAccessLevel,
),
options: const StorageMoveOptions(
pluginOptions: S3MovePluginOptions(getProperties: true),
),
);

try {
final result = await moveOperation.result;
stdout
..writeln('Copied object: ')
..writeln('key: ${result.movedItem.key}')
..writeln('size: ${result.movedItem.size}')
..writeln('lastModified: ${result.movedItem.lastModified}')
..writeln('eTag: ${result.movedItem.eTag}')
..writeln('metadata: ${result.movedItem.metadata}');
} on Exception catch (error) {
stderr
..writeln('Something went wrong...')
..writeln(error);
}
}

Future<void> removeOperation() async {
final key = prompt('Enter the object key to remove: ');
final accessLevel = promptStorageAccessLevel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,10 @@ class AmplifyStorageS3Dart extends StoragePluginInterface

@override
S3CopyOperation copy({
required StorageItemWithAccessLevel<StorageItem> source,
required StorageItemWithAccessLevel<StorageItem> destination,
required StoragePath source,
required StoragePath destination,
StorageCopyOptions? options,
}) {
final s3Source = S3ItemWithAccessLevel.from(source);
final s3Destination = S3ItemWithAccessLevel.from(destination);

final s3PluginOptions = reifyPluginOptions(
pluginOptions: options?.pluginOptions,
defaultPluginOptions: const S3CopyPluginOptions(),
Expand All @@ -381,45 +378,13 @@ class AmplifyStorageS3Dart extends StoragePluginInterface

return S3CopyOperation(
request: StorageCopyRequest(
source: s3Source,
destination: s3Destination,
source: source,
destination: destination,
options: options,
),
result: storageS3Service.copy(
source: s3Source,
destination: s3Destination,
options: s3Options,
),
);
}

@override
S3MoveOperation move({
required StorageItemWithAccessLevel<StorageItem> source,
required StorageItemWithAccessLevel<StorageItem> destination,
StorageMoveOptions? options,
}) {
final s3PluginOptions = reifyPluginOptions(
pluginOptions: options?.pluginOptions,
defaultPluginOptions: const S3MovePluginOptions(),
);

final s3Options = StorageMoveOptions(
pluginOptions: s3PluginOptions,
);

final s3Source = S3ItemWithAccessLevel.from(source);
final s3Destination = S3ItemWithAccessLevel.from(destination);

return S3MoveOperation(
request: StorageMoveRequest(
source: s3Source,
destination: s3Destination,
options: options,
),
result: storageS3Service.move(
source: s3Source,
destination: s3Destination,
source: source,
destination: destination,
options: s3Options,
),
);
Expand Down
Loading
Loading