Skip to content

Commit 38e98f9

Browse files
authored
feat(storage) AWS SDK S3 accelerate via useAccelerateEndpoint pluginOptions. (#3099)
1 parent 543ccda commit 38e98f9

23 files changed

+502
-41
lines changed

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+AsyncClientBehavior.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ extension AWSS3StoragePlugin {
3030
if let pluginOptions = options.pluginOptions as? AWSStorageGetURLOptions, pluginOptions.validateObjectExistence {
3131
try await storageService.validateObjectExistence(serviceKey: serviceKey)
3232
}
33-
let result = try await storageService.getPreSignedURL(serviceKey: serviceKey,
34-
signingOperation: .getObject,
35-
expires: options.expires)
33+
let accelerate = try AWSS3PluginOptions.accelerateValue(
34+
pluginOptions: options.pluginOptions)
35+
let result = try await storageService.getPreSignedURL(
36+
serviceKey: serviceKey,
37+
signingOperation: .getObject,
38+
accelerate: accelerate,
39+
expires: options.expires)
3640

3741
let channel = HubChannel(from: categoryType)
3842
let payload = HubPayload(eventName: HubPayload.EventName.Storage.getURL, context: options, data: result)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Amplify
9+
import Foundation
10+
11+
/// - Tag: AWSS3PluginOptions
12+
struct AWSS3PluginOptions {
13+
14+
/// - Tag: AWSS3PluginOptionsCodingKeys
15+
enum CodingKeys: String, CodingKey {
16+
17+
/// See: https://docs.amplify.aws/lib/storage/transfer-acceleration/q/platform/js/
18+
/// - Tag: AWSS3PluginOptionsCodingKeys.useAccelerateEndpoint
19+
case useAccelerateEndpoint
20+
}
21+
22+
/// Attempts to extract the boolean under the
23+
/// [useAccelerateEndpoint](x-source-tag://AWSS3PluginOptionsCodingKeys.useAccelerateEndpoint)
24+
/// contained in the given dictionary.
25+
///
26+
/// In other words, a non-nil boolean is returned if:
27+
///
28+
/// * The `pluginOptions` parameter is a dictionary ([String: Any])
29+
/// * The `pluginOptions` dictionary contains a boolean key under the [useAccelerateEndpoint](x-source-tag://AWSS3PluginOptionsCodingKeys.useAccelerateEndpoint) key.
30+
///
31+
/// - Tag: AWSS3PluginOptions.accelerateValue
32+
static func accelerateValue(pluginOptions: Any?) throws -> Bool? {
33+
guard let pluginOptions = pluginOptions as? [String:Any] else {
34+
return nil
35+
}
36+
guard let value = pluginOptions[CodingKeys.useAccelerateEndpoint.rawValue] else {
37+
return nil
38+
}
39+
guard let boolValue = value as? Bool else {
40+
throw StorageError.validation(CodingKeys.useAccelerateEndpoint.rawValue,
41+
"Expecting boolean value for key \(CodingKeys.useAccelerateEndpoint.rawValue)",
42+
"Ensure the value associated with \(CodingKeys.useAccelerateEndpoint.rawValue) is a boolean",
43+
nil)
44+
}
45+
return boolValue
46+
}
47+
}

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/AWSS3PreSignedURLBuilderAdapter.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ class AWSS3PreSignedURLBuilderAdapter: AWSS3PreSignedURLBuilderBehavior {
3434
/// - Returns: Pre-Signed URL
3535
func getPreSignedURL(key: String,
3636
signingOperation: AWSS3SigningOperation,
37+
accelerate: Bool? = nil,
3738
expires: Int64? = nil) async throws -> URL {
3839
let expiresDate = Date(timeIntervalSinceNow: Double(expires ?? defaultExpiration))
3940
let expiration = expiresDate.timeIntervalSinceNow
41+
let config = (accelerate == nil) ? self.config : S3ClientConfigurationProxy(
42+
target: self.config,
43+
accelerateOverride: accelerate)
4044
let preSignedUrl: URL?
4145
switch signingOperation {
4246
case .getObject:

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/AWSS3PreSignedURLBuilderBehavior.swift

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,36 @@ import AWSS3
1212
import ClientRuntime
1313
import AWSClientRuntime
1414

15+
/// - Tag: AWSS3PreSignedURLBuilderError
1516
enum AWSS3PreSignedURLBuilderError: Error {
17+
18+
/// Returned by an implementation of a
19+
/// [AWSS3PreSignedURLBuilderBehavior](x-source-tag://AWSS3PreSignedURLBuilderBehavior)
20+
///
21+
/// - Tag: AWSS3PreSignedURLBuilderError.failed
1622
case failed(reason: String, error: Error?)
1723
}
1824

19-
// Behavior that the implemenation class for AWSS3PreSignedURLBuilder will use.
25+
/// Behavior that the implemenation class for AWSS3PreSignedURLBuilder will use.
26+
///
27+
/// - Tag: AWSS3PreSignedURLBuilderBehavior
2028
protocol AWSS3PreSignedURLBuilderBehavior {
2129

22-
/// Gets a pre-signed URL.
30+
/// Attempts to generate a pre-signed URL.
31+
///
32+
/// - Parameters:
33+
/// - key: String represnting the key of an S3 object.
34+
/// - signingOperation: [AWSS3SigningOperation](x-source-tag://AWSS3SigningOperation)
35+
/// (get, put, upload part) for which the URL will be generated.
36+
/// - accelerate: Optional boolean indicating wether or not to enable S3 bucket
37+
/// [transfer acceleration](https://docs.amplify.aws/lib/storage/transfer-acceleration/q/platform/js/)
38+
/// - expires: Int64 indicating the expiration as the number of milliseconds since the 1970 epoc.
2339
/// - Returns: Pre-Signed URL
24-
func getPreSignedURL(key: String, signingOperation: AWSS3SigningOperation, expires: Int64?) async throws -> URL
40+
///
41+
/// - Tag: AWSS3PreSignedURLBuilderBehavior.getPreSignedURL
42+
func getPreSignedURL(key: String,
43+
signingOperation: AWSS3SigningOperation,
44+
accelerate: Bool?,
45+
expires: Int64?) async throws -> URL
46+
2547
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright Amazon.com Inc. or its affiliates.
2+
// All Rights Reserved.
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
import AWSS3
7+
import AWSClientRuntime
8+
import ClientRuntime
9+
import Foundation
10+
11+
/// Convenience proxy class around a
12+
/// [S3ClientConfigurationProtocol](x-source-tag://S3ClientConfigurationProtocol)
13+
/// implementaitons that allows Amplify to change configuration values JIT.
14+
///
15+
/// - Tag: S3ClientConfigurationProxy
16+
struct S3ClientConfigurationProxy {
17+
18+
/// - Tag: S3ClientConfigurationProxy.target
19+
var target: S3ClientConfigurationProtocol
20+
21+
/// - Tag: S3ClientConfigurationProxy.accelerateOverride
22+
var accelerateOverride: Bool?
23+
}
24+
25+
extension S3ClientConfigurationProxy: S3ClientConfigurationProtocol {
26+
27+
var accelerate: Bool? {
28+
if let accelerateOverride = accelerateOverride {
29+
return accelerateOverride
30+
}
31+
return target.accelerate
32+
}
33+
34+
var disableMultiRegionAccessPoints: Bool? {
35+
return target.disableMultiRegionAccessPoints
36+
}
37+
38+
var endpointResolver: EndpointResolver {
39+
return target.endpointResolver
40+
}
41+
42+
var forcePathStyle: Bool? {
43+
return target.forcePathStyle
44+
}
45+
46+
var useArnRegion: Bool? {
47+
return target.useArnRegion
48+
}
49+
50+
var useGlobalEndpoint: Bool? {
51+
return target.useGlobalEndpoint
52+
}
53+
54+
var credentialsProvider: AWSClientRuntime.CredentialsProvider {
55+
get {
56+
return target.credentialsProvider
57+
}
58+
set(newValue) {
59+
target.credentialsProvider = newValue
60+
}
61+
}
62+
63+
var region: String? {
64+
get {
65+
return target.region
66+
}
67+
set(newValue) {
68+
target.region = newValue
69+
}
70+
}
71+
72+
var signingRegion: String? {
73+
get {
74+
return target.signingRegion
75+
}
76+
set(newValue) {
77+
target.signingRegion = newValue
78+
}
79+
}
80+
81+
var regionResolver: RegionResolver? {
82+
get {
83+
return target.regionResolver
84+
}
85+
set(newValue) {
86+
target.regionResolver = newValue
87+
}
88+
}
89+
90+
var frameworkMetadata: FrameworkMetadata? {
91+
get {
92+
return target.frameworkMetadata
93+
}
94+
set(newValue) {
95+
target.frameworkMetadata = newValue
96+
}
97+
}
98+
99+
var useFIPS: Bool? {
100+
get {
101+
return target.useFIPS
102+
}
103+
set(newValue) {
104+
target.useFIPS = newValue
105+
}
106+
}
107+
108+
var useDualStack: Bool? {
109+
get {
110+
return target.useDualStack
111+
}
112+
set(newValue) {
113+
target.useDualStack = newValue
114+
}
115+
}
116+
117+
var logger: LogAgent {
118+
return target.logger
119+
}
120+
121+
var retryer: ClientRuntime.SDKRetryer {
122+
return target.retryer
123+
}
124+
125+
var endpoint: String? {
126+
get {
127+
return target.endpoint
128+
}
129+
set(newValue) {
130+
target.endpoint = newValue
131+
}
132+
}
133+
134+
var encoder: ClientRuntime.RequestEncoder? {
135+
return target.encoder
136+
}
137+
138+
var decoder: ClientRuntime.ResponseDecoder? {
139+
return target.decoder
140+
}
141+
142+
var httpClientEngine: ClientRuntime.HttpClientEngine {
143+
return target.httpClientEngine
144+
}
145+
146+
var httpClientConfiguration: ClientRuntime.HttpClientConfiguration {
147+
return target.httpClientConfiguration
148+
}
149+
150+
var idempotencyTokenGenerator: ClientRuntime.IdempotencyTokenGenerator {
151+
return target.idempotencyTokenGenerator
152+
}
153+
154+
var clientLogMode: ClientRuntime.ClientLogMode {
155+
return target.clientLogMode
156+
}
157+
158+
var partitionID: String? {
159+
return target.partitionID
160+
}
161+
}

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadDataOperation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ class AWSS3StorageDownloadDataOperation: AmplifyInProcessReportingOperation<
9191
do {
9292
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
9393
let serviceKey = prefix + request.key
94-
storageService.download(serviceKey: serviceKey, fileURL: nil) { [weak self] event in
94+
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
95+
storageService.download(serviceKey: serviceKey, fileURL: nil, accelerate: accelerate) { [weak self] event in
9596
self?.onServiceEvent(event: event)
9697
}
9798
} catch {

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadFileOperation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ class AWSS3StorageDownloadFileOperation: AmplifyInProcessReportingOperation<
9494
do {
9595
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
9696
let serviceKey = prefix + request.key
97-
storageService.download(serviceKey: serviceKey, fileURL: self.request.local) { [weak self] event in
97+
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
98+
storageService.download(serviceKey: serviceKey, fileURL: self.request.local, accelerate: accelerate) { [weak self] event in
9899
self?.onServiceEvent(event: event)
99100
}
100101
} catch {

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadDataOperation.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,21 @@ class AWSS3StorageUploadDataOperation: AmplifyInProcessReportingOperation<
9292
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
9393
let serviceKey = prefix + request.key
9494
let serviceMetadata = StorageRequestUtils.getServiceMetadata(request.options.metadata)
95+
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
9596
if request.data.count > StorageUploadDataRequest.Options.multiPartUploadSizeThreshold {
9697
storageService.multiPartUpload(serviceKey: serviceKey,
9798
uploadSource: .data(request.data),
9899
contentType: request.options.contentType,
99-
metadata: serviceMetadata) { [weak self] event in
100+
metadata: serviceMetadata,
101+
accelerate: accelerate) { [weak self] event in
100102
self?.onServiceEvent(event: event)
101103
}
102104
} else {
103105
storageService.upload(serviceKey: serviceKey,
104106
uploadSource: .data(request.data),
105107
contentType: request.options.contentType,
106-
metadata: serviceMetadata) { [weak self] event in
108+
metadata: serviceMetadata,
109+
accelerate: accelerate) { [weak self] event in
107110
self?.onServiceEvent(event: event)
108111
}
109112
}

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadFileOperation.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,21 @@ class AWSS3StorageUploadFileOperation: AmplifyInProcessReportingOperation<
116116
let prefix = try await prefixResolver.resolvePrefix(for: request.options.accessLevel, targetIdentityId: request.options.targetIdentityId)
117117
let serviceKey = prefix + request.key
118118
let serviceMetadata = StorageRequestUtils.getServiceMetadata(request.options.metadata)
119+
let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions)
119120
if uploadSize > StorageUploadFileRequest.Options.multiPartUploadSizeThreshold {
120121
storageService.multiPartUpload(serviceKey: serviceKey,
121122
uploadSource: .local(request.local),
122123
contentType: request.options.contentType,
123-
metadata: serviceMetadata) { [weak self] event in
124+
metadata: serviceMetadata,
125+
accelerate: accelerate) { [weak self] event in
124126
self?.onServiceEvent(event: event)
125127
}
126128
} else {
127129
storageService.upload(serviceKey: serviceKey,
128130
uploadSource: .local(request.local),
129131
contentType: request.options.contentType,
130-
metadata: serviceMetadata) { [weak self] event in
132+
metadata: serviceMetadata,
133+
accelerate: accelerate) { [weak self] event in
131134
self?.onServiceEvent(event: event)
132135
}
133136
}

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService+DownloadBehavior.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ extension AWSS3StorageService {
1212

1313
func download(serviceKey: String,
1414
fileURL: URL?,
15-
onEvent: @escaping StorageServiceDownloadEventHandler) {
15+
accelerate: Bool?,
16+
onEvent: @escaping StorageServiceDownloadEventHandler) {
1617
let fail: (Error) -> Void = { error in
1718
let storageError = StorageError(error: error)
1819
onEvent(.failed(storageError))
@@ -27,7 +28,10 @@ extension AWSS3StorageService {
2728

2829
Task {
2930
do {
30-
let preSignedURL = try await preSignedURLBuilder.getPreSignedURL(key: serviceKey, signingOperation: .getObject, expires: nil)
31+
let preSignedURL = try await preSignedURLBuilder.getPreSignedURL(key: serviceKey,
32+
signingOperation: .getObject,
33+
accelerate: accelerate,
34+
expires: nil)
3135
startDownload(preSignedURL: preSignedURL, transferTask: transferTask)
3236
} catch {
3337
onEvent(.failed(StorageError.unknown("Failed to get pre-signed URL", nil)))

0 commit comments

Comments
 (0)