Skip to content

Commit

Permalink
feat(storage): copy and move APIs (#4569)
Browse files Browse the repository at this point in the history
* chore: update copy api

* chore: update integ tests for copy

* chore: removed unused code

* chore: add back test
  • Loading branch information
Jordan-Nelson committed Mar 22, 2024
1 parent 0460d84 commit d817f03
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 747 deletions.
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

0 comments on commit d817f03

Please sign in to comment.