Skip to content

Commit

Permalink
feat(storage) Parity: AWS SDK S3 accelerate via useAccelerateEndpoint…
Browse files Browse the repository at this point in the history
… pluginOptions.
  • Loading branch information
harsh62 committed Jul 13, 2023
1 parent 4c7a84b commit 3522927
Show file tree
Hide file tree
Showing 19 changed files with 466 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Amplify
import Foundation

/// - Tag: AWSS3PluginOptions
struct AWSS3PluginOptions {

/// - Tag: AWSS3PluginOptionsCodingKeys
enum CodingKeys: String, CodingKey {

/// See: https://docs.amplify.aws/lib/storage/transfer-acceleration/q/platform/js/
/// - Tag: AWSS3PluginOptionsCodingKeys.useAccelerateEndpoint
case useAccelerateEndpoint
}

/// Attempts to extract the boolean under the
/// [useAccelerateEndpoint](x-source-tag://AWSS3PluginOptionsCodingKeys.useAccelerateEndpoint)
/// contained in the given dictionary.
///
/// In other words, a non-nil boolean is returned if:
///
/// * The `pluginOptions` parameter is a dictionary ([String: Any])
/// * The `pluginOptions` dictionary contains a boolean key under the [useAccelerateEndpoint](x-source-tag://AWSS3PluginOptionsCodingKeys.useAccelerateEndpoint) key.
///
/// - Tag: AWSS3PluginOptions.accelerateValue
static func accelerateValue(pluginOptions: Any?) throws -> Bool? {
guard let pluginOptions = pluginOptions as? [String:Any] else {
return nil
}
guard let value = pluginOptions[CodingKeys.useAccelerateEndpoint.rawValue] else {
return nil
}
guard let boolValue = value as? Bool else {
throw StorageError.validation(CodingKeys.useAccelerateEndpoint.rawValue,
"Expecting boolean value for key \(CodingKeys.useAccelerateEndpoint.rawValue)",
"Ensure the value associated with \(CodingKeys.useAccelerateEndpoint.rawValue) is a boolean",
nil)
}
return boolValue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ class AWSS3PreSignedURLBuilderAdapter: AWSS3PreSignedURLBuilderBehavior {
/// - Returns: Pre-Signed URL
func getPreSignedURL(key: String,
signingOperation: AWSS3SigningOperation,
accelerate: Bool? = nil,
expires: Int64? = nil) async throws -> URL {
let expiresDate = Date(timeIntervalSinceNow: Double(expires ?? defaultExpiration))
let expiration = expiresDate.timeIntervalSinceNow
let expiration = Int64(expiresDate.timeIntervalSinceNow)
let config = (accelerate == nil) ? self.config : S3ClientConfigurationProxy(target: self.config,
accelerateOverride: accelerate)
let preSignedUrl: URL?
switch signingOperation {
case .getObject:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,36 @@ import AWSS3
import ClientRuntime
import AWSClientRuntime

/// - Tag: AWSS3PreSignedURLBuilderError
enum AWSS3PreSignedURLBuilderError: Error {

/// Returned by an implementation of a
/// [AWSS3PreSignedURLBuilderBehavior](x-source-tag://AWSS3PreSignedURLBuilderBehavior)
///
/// - Tag: AWSS3PreSignedURLBuilderError.failed
case failed(reason: String, error: Error?)
}

// Behavior that the implemenation class for AWSS3PreSignedURLBuilder will use.
/// Behavior that the implemenation class for AWSS3PreSignedURLBuilder will use.
///
/// - Tag: AWSS3PreSignedURLBuilderBehavior
protocol AWSS3PreSignedURLBuilderBehavior {

/// Gets a pre-signed URL.
/// Attempts to generate a pre-signed URL.
///
/// - Parameters:
/// - key: String represnting the key of an S3 object.
/// - signingOperation: [AWSS3SigningOperation](x-source-tag://AWSS3SigningOperation)
/// (get, put, upload part) for which the URL will be generated.
/// - accelerate: Optional boolean indicating wether or not to enable S3 bucket
/// [transfer acceleration](https://docs.amplify.aws/lib/storage/transfer-acceleration/q/platform/js/)
/// - expires: Int64 indicating the expiration as the number of milliseconds since the 1970 epoc.
/// - Returns: Pre-Signed URL
func getPreSignedURL(key: String, signingOperation: AWSS3SigningOperation, expires: Int64?) async throws -> URL
///
/// - Tag: AWSS3PreSignedURLBuilderBehavior.getPreSignedURL
func getPreSignedURL(key: String,
signingOperation: AWSS3SigningOperation,
accelerate: Bool?,
expires: Int64?) async throws -> URL

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0

import AWSS3
import AWSClientRuntime
import ClientRuntime
import Foundation

/// Convenience proxy class around a
/// [S3ClientConfigurationProtocol](x-source-tag://S3ClientConfigurationProtocol)
/// implementaitons that allows Amplify to change configuration values JIT.
///
/// - Tag: S3ClientConfigurationProxy
struct S3ClientConfigurationProxy {

/// - Tag: S3ClientConfigurationProxy.target
var target: S3ClientConfigurationProtocol

/// - Tag: S3ClientConfigurationProxy.accelerateOverride
var accelerateOverride: Bool?
}

extension S3ClientConfigurationProxy: S3ClientConfigurationProtocol {

var accelerate: Bool? {
if let accelerateOverride = accelerateOverride {
return accelerateOverride
}
return target.accelerate
}

var disableMultiRegionAccessPoints: Bool? {
return target.disableMultiRegionAccessPoints
}

var endpointResolver: EndpointResolver {
return target.endpointResolver
}

var forcePathStyle: Bool? {
return target.forcePathStyle
}

var useArnRegion: Bool? {
return target.useArnRegion
}

var useGlobalEndpoint: Bool? {
return target.useGlobalEndpoint
}

var credentialsProvider: AWSClientRuntime.CredentialsProvider {
get {
return target.credentialsProvider
}
set(newValue) {
target.credentialsProvider = newValue
}
}

var region: String? {
get {
return target.region
}
set(newValue) {
target.region = newValue
}
}

var signingRegion: String? {
get {
return target.signingRegion
}
set(newValue) {
target.signingRegion = newValue
}
}

var regionResolver: RegionResolver? {
get {
return target.regionResolver
}
set(newValue) {
target.regionResolver = newValue
}
}

var frameworkMetadata: FrameworkMetadata? {
get {
return target.frameworkMetadata
}
set(newValue) {
target.frameworkMetadata = newValue
}
}

var useFIPS: Bool? {
get {
return target.useFIPS
}
set(newValue) {
target.useFIPS = newValue
}
}

var useDualStack: Bool? {
get {
return target.useDualStack
}
set(newValue) {
target.useDualStack = newValue
}
}

var logger: LogAgent {
return target.logger
}

var retryer: ClientRuntime.SDKRetryer {
return target.retryer
}

var endpoint: String? {
get {
return target.endpoint
}
set(newValue) {
target.endpoint = newValue
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ class AWSS3StorageDownloadDataOperation: AmplifyInProcessReportingOperation<
do {
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
let serviceKey = prefix + request.key
storageService.download(serviceKey: serviceKey, fileURL: nil) { [weak self] event in
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
storageService.download(serviceKey: serviceKey, fileURL: nil, accelerate: accelerate) { [weak self] event in
self?.onServiceEvent(event: event)
}
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ class AWSS3StorageDownloadFileOperation: AmplifyInProcessReportingOperation<
do {
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
let serviceKey = prefix + request.key
storageService.download(serviceKey: serviceKey, fileURL: self.request.local) { [weak self] event in
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
storageService.download(serviceKey: serviceKey, fileURL: self.request.local, accelerate: accelerate) { [weak self] event in
self?.onServiceEvent(event: event)
}
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,21 @@ class AWSS3StorageUploadDataOperation: AmplifyInProcessReportingOperation<
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
let serviceKey = prefix + request.key
let serviceMetadata = StorageRequestUtils.getServiceMetadata(request.options.metadata)
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
if request.data.count > StorageUploadDataRequest.Options.multiPartUploadSizeThreshold {
storageService.multiPartUpload(serviceKey: serviceKey,
uploadSource: .data(request.data),
contentType: request.options.contentType,
metadata: serviceMetadata) { [weak self] event in
metadata: serviceMetadata,
accelerate: accelerate) { [weak self] event in
self?.onServiceEvent(event: event)
}
} else {
storageService.upload(serviceKey: serviceKey,
uploadSource: .data(request.data),
contentType: request.options.contentType,
metadata: serviceMetadata) { [weak self] event in
metadata: serviceMetadata,
accelerate: accelerate) { [weak self] event in
self?.onServiceEvent(event: event)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,21 @@ class AWSS3StorageUploadFileOperation: AmplifyInProcessReportingOperation<
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
let serviceKey = prefix + request.key
let serviceMetadata = StorageRequestUtils.getServiceMetadata(request.options.metadata)
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
if uploadSize > StorageUploadFileRequest.Options.multiPartUploadSizeThreshold {
storageService.multiPartUpload(serviceKey: serviceKey,
uploadSource: .local(request.local),
contentType: request.options.contentType,
metadata: serviceMetadata) { [weak self] event in
metadata: serviceMetadata,
accelerate: accelerate) { [weak self] event in
self?.onServiceEvent(event: event)
}
} else {
storageService.upload(serviceKey: serviceKey,
uploadSource: .local(request.local),
contentType: request.options.contentType,
metadata: serviceMetadata) { [weak self] event in
metadata: serviceMetadata,
accelerate: accelerate) { [weak self] event in
self?.onServiceEvent(event: event)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ extension AWSS3StorageService {

func download(serviceKey: String,
fileURL: URL?,
onEvent: @escaping StorageServiceDownloadEventHandler) {
accelerate: Bool?,
onEvent: @escaping StorageServiceDownloadEventHandler) {
let fail: (Error) -> Void = { error in
let storageError = StorageError(error: error)
onEvent(.failed(storageError))
Expand All @@ -27,7 +28,10 @@ extension AWSS3StorageService {

Task {
do {
let preSignedURL = try await preSignedURLBuilder.getPreSignedURL(key: serviceKey, signingOperation: .getObject, expires: nil)
let preSignedURL = try await preSignedURLBuilder.getPreSignedURL(key: serviceKey,
signingOperation: .getObject,
accelerate: accelerate,
expires: nil)
startDownload(preSignedURL: preSignedURL, transferTask: transferTask)
} catch {
onEvent(.failed(StorageError.unknown("Failed to get pre-signed URL", nil)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,20 @@ import Amplify
extension AWSS3StorageService {

func getPreSignedURL(serviceKey: String,
signingOperation: AWSS3SigningOperation,
expires: Int) async throws -> URL {
return try await preSignedURLBuilder.getPreSignedURL(key: serviceKey, signingOperation: signingOperation, expires: Int64(expires))
signingOperation: AWSS3SigningOperation = .getObject,
accelerate: Bool?,
expires: Int,
onEvent: @escaping StorageServiceGetPreSignedURLEventHandler) {
Task {
do {
onEvent(.completed(try await preSignedURLBuilder.getPreSignedURL(key: serviceKey,
signingOperation: signingOperation,
accelerate: accelerate,
expires: Int64(expires))))
} catch {
onEvent(.failed(StorageError.unknown("Failed to get pre-signed URL", nil)))
}
}
}

func validateObjectExistence(serviceKey: String) async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extension AWSS3StorageService {
uploadSource: UploadSource,
contentType: String?,
metadata: [String: String]?,
accelerate: Bool?,
onEvent: @escaping StorageServiceMultiPartUploadEventHandler) {
let fail: (Error) -> Void = { error in
let storageError = StorageError(error: error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extension AWSS3StorageService {
uploadSource: UploadSource,
contentType: String?,
metadata: [String: String]?,
accelerate: Bool?,
onEvent: @escaping StorageServiceUploadEventHandler) {
let fail: (Error) -> Void = { error in
let storageError = StorageError(error: error)
Expand All @@ -35,6 +36,7 @@ extension AWSS3StorageService {
do {
let preSignedURL = try await preSignedURLBuilder.getPreSignedURL(key: serviceKey,
signingOperation: .putObject,
accelerate: accelerate,
expires: nil)
startUpload(preSignedURL: preSignedURL,
fileURL: uploadFileURL,
Expand Down
Loading

0 comments on commit 3522927

Please sign in to comment.