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): update AWSS3StoragePlugin to support multiple buckets #2895

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 @@ -18,6 +18,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.VisibleForTesting;

Expand All @@ -28,7 +29,12 @@
import com.amplifyframework.core.Consumer;
import com.amplifyframework.core.NoOpConsumer;
import com.amplifyframework.core.configuration.AmplifyOutputsData;
import com.amplifyframework.storage.BucketInfo;
import com.amplifyframework.storage.InvalidStorageBucketException;
import com.amplifyframework.storage.OutputsStorageBucket;
import com.amplifyframework.storage.ResolvedStorageBucket;
import com.amplifyframework.storage.StorageAccessLevel;
import com.amplifyframework.storage.StorageBucket;
import com.amplifyframework.storage.StorageException;
import com.amplifyframework.storage.StoragePath;
import com.amplifyframework.storage.StoragePlugin;
Expand Down Expand Up @@ -95,6 +101,9 @@

import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -121,11 +130,16 @@ public final class AWSS3StoragePlugin extends StoragePlugin<S3Client> {
private final ExecutorService executorService;
private final AuthCredentialsProvider authCredentialsProvider;
private final AWSS3StoragePluginConfiguration awsS3StoragePluginConfiguration;
private AWSS3StorageService storageService;
private AWSS3StorageService defaultStorageService;
@SuppressWarnings("deprecation")
private StorageAccessLevel defaultAccessLevel;
private int defaultUrlExpiration;

private Map<String, AWSS3StorageService> awsS3StorageServicesByBucketName = new HashMap<>();
private Context context;
@SuppressLint("UnsafeOptInUsageError")
private List<AmplifyOutputsData.StorageBucket> configuredBuckets;

/**
* Constructs the AWS S3 Storage Plugin initializing the executor service.
*/
Expand Down Expand Up @@ -194,6 +208,7 @@ public String getPluginKey() {
return AWS_S3_STORAGE_PLUGIN_KEY;
}

@SuppressLint("UnsafeOptInUsageError")
@Override
@SuppressWarnings("deprecation")
public void configure(
Expand Down Expand Up @@ -237,7 +252,8 @@ public void configure(
);
}

configure(context, region, bucket);
BucketInfo bucketInfo = new BucketInfo(bucket, region);
configure(context, region, (ResolvedStorageBucket) StorageBucket.fromBucketInfo(bucketInfo));
}

@Override
Expand All @@ -252,17 +268,26 @@ public void configure(@NonNull AmplifyOutputsData configuration, @NonNull Contex
);
}

configure(context, storage.getAwsRegion(), storage.getBucketName());
this.configuredBuckets = storage.getBuckets();
phantumcode marked this conversation as resolved.
Show resolved Hide resolved
BucketInfo bucketInfo = new BucketInfo(storage.getBucketName(), storage.getAwsRegion());
configure(context, storage.getAwsRegion(), (ResolvedStorageBucket) StorageBucket.fromBucketInfo(bucketInfo));
}

@SuppressWarnings("deprecation")
@SuppressLint("UnsafeOptInUsageError")
private void configure(
@NonNull Context context,
@NonNull String region,
@NonNull String bucket
@NonNull Context context,
@NonNull String region,
@NonNull ResolvedStorageBucket bucket
) throws StorageException {
try {
this.storageService = (AWSS3StorageService) storageServiceFactory.create(context, region, bucket);
this.context = context;
this.defaultStorageService = (AWSS3StorageService) storageServiceFactory.create(
context,
region,
bucket.getBucketInfo().getName());
this.awsS3StorageServicesByBucketName.clear();
this.awsS3StorageServicesByBucketName.put(bucket.getBucketInfo().getName(), this.defaultStorageService);
} catch (RuntimeException exception) {
throw new StorageException(
"Failed to create storage service.",
Expand All @@ -280,7 +305,7 @@ private void configure(
@NonNull
@Override
public S3Client getEscapeHatch() {
return storageService.getClient();
return defaultStorageService.getClient();
}

@NonNull
Expand Down Expand Up @@ -334,6 +359,18 @@ public StorageGetUrlOperation<?> getUrl(
validateObjectExistence
);

AWSS3StorageService storageService = defaultStorageService;
try {
storageService = getStorageService(options.getBucket());
} catch (InvalidStorageBucketException exception) {
onError.accept(
new StorageException(
"Unable to find bucket from name in Amplify Outputs.",
exception,
"Ensure the bucket name used is available in Amplify Outputs.")
);
}

AWSS3StorageGetPresignedUrlOperation operation =
new AWSS3StorageGetPresignedUrlOperation(
storageService,
Expand Down Expand Up @@ -369,6 +406,18 @@ public StorageGetUrlOperation<?> getUrl(
validateObjectExistence
);

AWSS3StorageService storageService = defaultStorageService;
try {
storageService = getStorageService(options.getBucket());
} catch (InvalidStorageBucketException exception) {
onError.accept(
new StorageException(
"Unable to find bucket from name in Amplify Outputs.",
exception,
"Ensure the bucket name used is available in Amplify Outputs.")
);
}

AWSS3StoragePathGetPresignedUrlOperation operation =
new AWSS3StoragePathGetPresignedUrlOperation(
storageService,
Expand Down Expand Up @@ -457,7 +506,7 @@ public StorageDownloadFileOperation<?> downloadFile(
);

AWSS3StorageDownloadFileOperation operation = new AWSS3StorageDownloadFileOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
request,
Expand Down Expand Up @@ -493,7 +542,7 @@ public StorageDownloadFileOperation<?> downloadFile(

AWSS3StoragePathDownloadFileOperation operation = new AWSS3StoragePathDownloadFileOperation(
request,
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
onProgress,
Expand Down Expand Up @@ -584,7 +633,7 @@ public StorageUploadFileOperation<?> uploadFile(
);

AWSS3StorageUploadFileOperation operation = new AWSS3StorageUploadFileOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
request,
Expand Down Expand Up @@ -623,7 +672,7 @@ public StorageUploadFileOperation<?> uploadFile(

AWSS3StoragePathUploadFileOperation operation = new AWSS3StoragePathUploadFileOperation(
request,
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
onProgress,
Expand Down Expand Up @@ -712,7 +761,7 @@ public StorageUploadInputStreamOperation<?> uploadInputStream(
);

AWSS3StorageUploadInputStreamOperation operation = new AWSS3StorageUploadInputStreamOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
awsS3StoragePluginConfiguration,
Expand Down Expand Up @@ -752,7 +801,7 @@ public StorageUploadInputStreamOperation<?> uploadInputStream(
AWSS3StoragePathUploadInputStreamOperation operation =
new AWSS3StoragePathUploadInputStreamOperation(
request,
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
onProgress,
Expand Down Expand Up @@ -804,7 +853,7 @@ public StorageRemoveOperation<?> remove(

AWSS3StorageRemoveOperation operation =
new AWSS3StorageRemoveOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
request,
Expand All @@ -829,7 +878,7 @@ public StorageRemoveOperation<?> remove(

AWSS3StoragePathRemoveOperation operation =
new AWSS3StoragePathRemoveOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
request,
Expand All @@ -849,12 +898,12 @@ public void getTransfer(
@NonNull Consumer<StorageException> onError) {
executorService.submit(() -> {
try {
TransferRecord transferRecord = storageService.getTransfer(transferId);
TransferRecord transferRecord = defaultStorageService.getTransfer(transferId);
if (transferRecord != null) {
TransferObserver transferObserver =
new TransferObserver(
transferRecord.getId(),
storageService.getTransferManager().getTransferStatusUpdater(),
defaultStorageService.getTransferManager().getTransferStatusUpdater(),
transferRecord.getBucketName(),
transferRecord.getKey(),
transferRecord.getFile(),
Expand All @@ -867,7 +916,7 @@ public void getTransfer(
AWSS3StorageUploadInputStreamOperation operation =
new AWSS3StorageUploadInputStreamOperation(
transferId,
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
awsS3StoragePluginConfiguration,
Expand All @@ -878,7 +927,7 @@ public void getTransfer(
AWSS3StorageUploadFileOperation operation =
new AWSS3StorageUploadFileOperation(
transferId,
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
awsS3StoragePluginConfiguration,
Expand All @@ -892,7 +941,7 @@ public void getTransfer(
downloadFileOperation = new AWSS3StorageDownloadFileOperation(
transferId,
new File(transferRecord.getFile()),
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
awsS3StoragePluginConfiguration,
Expand Down Expand Up @@ -960,7 +1009,7 @@ public StorageListOperation<?> list(@NonNull String path,

AWSS3StorageListOperation operation =
new AWSS3StorageListOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
request,
Expand Down Expand Up @@ -989,7 +1038,7 @@ public StorageListOperation<?> list(

AWSS3StoragePathListOperation operation =
new AWSS3StoragePathListOperation(
storageService,
defaultStorageService,
executorService,
authCredentialsProvider,
request,
Expand All @@ -1001,6 +1050,63 @@ public StorageListOperation<?> list(
return operation;
}

@SuppressLint("UnsafeOptInUsageError")
@VisibleForTesting
@NonNull
AWSS3StorageService getStorageService(@Nullable StorageBucket bucket) throws InvalidStorageBucketException {
if (bucket == null) {
return defaultStorageService;
}

if (bucket instanceof OutputsStorageBucket) {
AWSS3StorageService service = getAWSS3StorageService((OutputsStorageBucket) bucket);
if (service == null) {
throw new InvalidStorageBucketException();
} else {
return service;
}
}

if (bucket instanceof ResolvedStorageBucket) {
return getAWSS3StorageService((ResolvedStorageBucket) bucket);
}

return defaultStorageService;
}

@SuppressLint("UnsafeOptInUsageError")
private AWSS3StorageService getAWSS3StorageService(OutputsStorageBucket outputsStorageBucket) {
if (configuredBuckets != null && !configuredBuckets.isEmpty()) {
String name = outputsStorageBucket.getName();
for (AmplifyOutputsData.StorageBucket configuredBucket : configuredBuckets) {
if (configuredBucket.getName().equals(name)) {
String bucketName = configuredBucket.getBucketName();
AWSS3StorageService service = awsS3StorageServicesByBucketName.get(bucketName);
if (service == null) {
String region = configuredBucket.getAwsRegion();
service = (AWSS3StorageService) storageServiceFactory.create(context, region, bucketName);
awsS3StorageServicesByBucketName.put(bucketName, service);
}

return service;
}
}
}
return null;
}

@SuppressLint("UnsafeOptInUsageError")
private AWSS3StorageService getAWSS3StorageService(ResolvedStorageBucket resolvedStorageBucket) {
String bucketName = resolvedStorageBucket.getBucketInfo().getName();
AWSS3StorageService service = awsS3StorageServicesByBucketName.get(bucketName);
if (service == null) {
String region = resolvedStorageBucket.getBucketInfo().getRegion();
service = (AWSS3StorageService) storageServiceFactory.create(context, region, bucketName);
awsS3StorageServicesByBucketName.put(bucketName, service);
}
return service;
}

/**
* Holds the keys for the various configuration properties for this plugin.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public static Builder from(@NonNull AWSS3StorageGetPresignedUrlOptions options)
.targetIdentityId(options.getTargetIdentityId())
.expires(options.getExpires())
.setValidateObjectExistence(options.getValidateObjectExistence())
.expires(options.getExpires());
.expires(options.getExpires())
.bucket(options.getBucket());
}

/**
Expand Down Expand Up @@ -106,6 +107,7 @@ public boolean equals(Object obj) {
return ObjectsCompat.equals(getAccessLevel(), that.getAccessLevel()) &&
ObjectsCompat.equals(getTargetIdentityId(), that.getTargetIdentityId()) &&
ObjectsCompat.equals(getExpires(), that.getExpires()) &&
ObjectsCompat.equals(getBucket(), that.getBucket()) &&
ObjectsCompat.equals(getValidateObjectExistence(), that.getValidateObjectExistence());
}
}
Expand All @@ -117,7 +119,8 @@ public int hashCode() {
getAccessLevel(),
getTargetIdentityId(),
getExpires(),
getValidateObjectExistence()
getValidateObjectExistence(),
getBucket()
);
}

Expand All @@ -130,6 +133,7 @@ public String toString() {
", targetIdentityId=" + getTargetIdentityId() +
", expires=" + getExpires() +
", validateObjectExistence=" + getValidateObjectExistence() +
", bucket=" + getBucket() +
'}';
}

Expand Down
Loading