Skip to content

Commit 60ed414

Browse files
brennanMKEroyjit
authored andcommitted
fix(storage): adds custom extensions to fix behavior in current SDK release (#101)
1 parent 6857603 commit 60ed414

File tree

7 files changed

+235
-48
lines changed

7 files changed

+235
-48
lines changed

AmplifyPlugins/Storage/AWSS3StoragePlugin/Dependency/AWSS3Adapter.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import AWSClientRuntime
1919
/// same method using the AWSS3 instance.
2020
class AWSS3Adapter: AWSS3Behavior {
2121
let awsS3: S3Client
22+
let config: AWSClientRuntime.AWSClientConfiguration
2223

23-
init(_ awsS3: S3Client) {
24+
init(_ awsS3: S3Client, config: AWSClientRuntime.AWSClientConfiguration) {
2425
self.awsS3 = awsS3
26+
self.config = config
2527
}
2628

2729
/// Deletes object identify by request.
@@ -85,7 +87,7 @@ class AWSS3Adapter: AWSS3Behavior {
8587
key: request.key,
8688
metadata: request.metadata)
8789

88-
awsS3.createMultipartUpload(input: input) { result in
90+
awsS3.createMultipartUpload(config: config, input: input) { result in
8991
switch result {
9092
case .success(let response):
9193
guard let bucket = response.bucket, let key = response.key, let uploadId = response.uploadId else {
@@ -109,14 +111,14 @@ class AWSS3Adapter: AWSS3Behavior {
109111
}
110112
let completedMultipartUpload = S3ClientTypes.CompletedMultipartUpload(parts: parts)
111113
let input = CompleteMultipartUploadInput(bucket: request.bucket, key: request.key, multipartUpload: completedMultipartUpload, uploadId: request.uploadId)
112-
awsS3.completeMultipartUpload(input: input) { result in
114+
awsS3.completeMultipartUpload(config: config, input: input) { result in
113115
switch result {
114116
case .success(let response):
115-
guard let bucket = response.bucket, let key = response.key, let eTag = response.eTag else {
117+
guard let eTag = response.eTag else {
116118
completion(.failure(StorageError.unknown("Invalid response for completing multipart upload", nil)))
117119
return
118120
}
119-
completion(.success(AWSS3CompleteMultipartUploadResponse(bucket: bucket, key: key, eTag: eTag)))
121+
completion(.success(AWSS3CompleteMultipartUploadResponse(bucket: request.bucket, key: request.key, eTag: eTag)))
120122
case .failure(let error):
121123
completion(.failure(error.storageError))
122124
}
@@ -145,4 +147,3 @@ class AWSS3Adapter: AWSS3Behavior {
145147
return awsS3
146148
}
147149
}
148-
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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 Foundation
9+
10+
import AWSS3
11+
import ClientRuntime
12+
import AWSClientRuntime
13+
14+
extension S3Client {
15+
16+
func completeMultipartUpload(config: AWSClientRuntime.AWSClientConfiguration,
17+
input: CompleteMultipartUploadInput,
18+
completion: @escaping (ClientRuntime.SdkResult<CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>) -> Void)
19+
{
20+
let serviceName = "S3"
21+
let client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration)
22+
let encoder = ClientRuntime.XMLEncoder()
23+
encoder.dateEncodingStrategy = .secondsSince1970
24+
let decoder = ClientRuntime.XMLDecoder()
25+
decoder.dateDecodingStrategy = .secondsSince1970
26+
decoder.trimValueWhitespaces = false
27+
decoder.removeWhitespaceElements = true
28+
29+
let context = ClientRuntime.HttpContextBuilder()
30+
.withEncoder(value: encoder)
31+
.withDecoder(value: decoder)
32+
.withMethod(value: .post)
33+
.withServiceName(value: serviceName)
34+
.withOperation(value: "completeMultipartUpload")
35+
.withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator)
36+
.withLogger(value: config.logger)
37+
.withCredentialsProvider(value: config.credentialsProvider)
38+
.withRegion(value: config.region)
39+
.withSigningName(value: "s3")
40+
.withSigningRegion(value: config.signingRegion)
41+
var operation = ClientRuntime.OperationStack<CompleteMultipartUploadInput, CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>(id: "completeMultipartUpload")
42+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware<CompleteMultipartUploadInput, CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>())
43+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware<CompleteMultipartUploadInput, CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>())
44+
operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.EndpointResolverMiddleware(endpointResolver: config.endpointResolver, serviceId: serviceName))
45+
let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0")
46+
operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata)))
47+
operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.HeaderMiddleware<CompleteMultipartUploadInput, CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>())
48+
operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.QueryItemMiddleware<CompleteMultipartUploadInput, CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>())
49+
operation.serializeStep.intercept(position: .after, middleware: ContentTypeMiddleware<CompleteMultipartUploadInput, CompleteMultipartUploadOutputResponse, CompleteMultipartUploadOutputError>(contentType: "application/xml"))
50+
operation.serializeStep.intercept(position: .after, middleware: CompleteMultipartUploadInputBodyMiddleware())
51+
operation.finalizeStep.intercept(position: .before, middleware: ClientRuntime.ContentLengthMiddleware())
52+
operation.finalizeStep.intercept(position: .after, middleware: AWSClientRuntime.RetryerMiddleware(retryer: config.retryer))
53+
let sigv4Config = AWSClientRuntime.SigV4Config(useDoubleURIEncode: true, shouldNormalizeURIPath: true, signedBodyHeader: .contentSha256, unsignedBody: false)
54+
operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config))
55+
operation.deserializeStep.intercept(position: .before, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode))
56+
operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware())
57+
let result = operation.handleMiddleware(context: context.build(), input: input, next: client.getHandler())
58+
completion(result)
59+
}
60+
61+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 Foundation
9+
10+
import AWSS3
11+
import ClientRuntime
12+
import AWSClientRuntime
13+
14+
extension S3Client {
15+
16+
func createMultipartUpload(config: AWSClientRuntime.AWSClientConfiguration,
17+
input: CreateMultipartUploadInput,
18+
completion: @escaping (ClientRuntime.SdkResult<CreateMultipartUploadOutputResponse, CreateMultipartUploadOutputError>) -> Void)
19+
{
20+
let serviceName = "S3"
21+
let client = ClientRuntime.SdkHttpClient(engine: config.httpClientEngine, config: config.httpClientConfiguration)
22+
let encoder = ClientRuntime.XMLEncoder()
23+
encoder.dateEncodingStrategy = .secondsSince1970
24+
let decoder = ClientRuntime.XMLDecoder()
25+
decoder.dateDecodingStrategy = .secondsSince1970
26+
decoder.trimValueWhitespaces = false
27+
decoder.removeWhitespaceElements = true
28+
29+
let context = ClientRuntime.HttpContextBuilder()
30+
.withEncoder(value: encoder)
31+
.withDecoder(value: decoder)
32+
.withMethod(value: .post)
33+
.withServiceName(value: serviceName)
34+
.withOperation(value: "createMultipartUpload")
35+
.withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator)
36+
.withLogger(value: config.logger)
37+
.withCredentialsProvider(value: config.credentialsProvider)
38+
.withRegion(value: config.region)
39+
.withSigningName(value: "s3")
40+
.withSigningRegion(value: config.signingRegion)
41+
var operation = ClientRuntime.OperationStack<CreateMultipartUploadInput, CreateMultipartUploadOutputResponse, CreateMultipartUploadOutputError>(id: "createMultipartUpload")
42+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware<CreateMultipartUploadInput, CreateMultipartUploadOutputResponse, CreateMultipartUploadOutputError>())
43+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware<CreateMultipartUploadInput, CreateMultipartUploadOutputResponse, CreateMultipartUploadOutputError>())
44+
operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.EndpointResolverMiddleware(endpointResolver: config.endpointResolver, serviceId: serviceName))
45+
let apiMetadata = AWSClientRuntime.APIMetadata(serviceId: serviceName, version: "1.0")
46+
operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.UserAgentMiddleware(metadata: AWSClientRuntime.AWSUserAgentMetadata.fromEnv(apiMetadata: apiMetadata, frameworkMetadata: config.frameworkMetadata)))
47+
operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.HeaderMiddleware<CreateMultipartUploadInput, CreateMultipartUploadOutputResponse, CreateMultipartUploadOutputError>())
48+
operation.serializeStep.intercept(position: .after, middleware: ClientRuntime.QueryItemMiddleware<CreateMultipartUploadInput, CreateMultipartUploadOutputResponse, CreateMultipartUploadOutputError>())
49+
operation.finalizeStep.intercept(position: .after, middleware: AWSClientRuntime.RetryerMiddleware(retryer: config.retryer))
50+
let sigv4Config = AWSClientRuntime.SigV4Config(useDoubleURIEncode: true, shouldNormalizeURIPath: true, signedBodyHeader: .contentSha256, unsignedBody: false)
51+
operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config))
52+
operation.deserializeStep.intercept(position: .before, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode))
53+
operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware())
54+
let result = operation.handleMiddleware(context: context.build(), input: input, next: client.getHandler())
55+
completion(result)
56+
}
57+
58+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 Foundation
9+
10+
import AWSS3
11+
import ClientRuntime
12+
import AWSClientRuntime
13+
14+
extension PutObjectInput {
15+
func presignURL(config: AWSClientRuntime.AWSClientConfiguration, expiration: Swift.Int64) -> ClientRuntime.URL? {
16+
let serviceName = "S3"
17+
let input = self
18+
let encoder = ClientRuntime.XMLEncoder()
19+
encoder.dateEncodingStrategy = .secondsSince1970
20+
let decoder = ClientRuntime.XMLDecoder()
21+
decoder.dateDecodingStrategy = .secondsSince1970
22+
decoder.trimValueWhitespaces = false
23+
decoder.removeWhitespaceElements = true
24+
let context = ClientRuntime.HttpContextBuilder()
25+
.withEncoder(value: encoder)
26+
.withDecoder(value: decoder)
27+
.withMethod(value: .put)
28+
.withServiceName(value: serviceName)
29+
.withOperation(value: "putObject")
30+
.withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator)
31+
.withLogger(value: config.logger)
32+
.withCredentialsProvider(value: config.credentialsProvider)
33+
.withRegion(value: config.region)
34+
.withSigningName(value: "s3")
35+
.withSigningRegion(value: config.signingRegion)
36+
var operation = ClientRuntime.OperationStack<PutObjectInput, PutObjectOutputResponse, PutObjectOutputError>(id: "putObject")
37+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware<PutObjectInput, PutObjectOutputResponse, PutObjectOutputError>())
38+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware<PutObjectInput, PutObjectOutputResponse, PutObjectOutputError>())
39+
operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.EndpointResolverMiddleware(endpointResolver: config.endpointResolver, serviceId: serviceName))
40+
operation.serializeStep.intercept(position: .after, middleware: PutObjectInputBodyMiddleware())
41+
operation.finalizeStep.intercept(position: .after, middleware: AWSClientRuntime.RetryerMiddleware(retryer: config.retryer))
42+
let sigv4Config = AWSClientRuntime.SigV4Config(signatureType: .requestQueryParams, expiration: expiration, unsignedBody: true)
43+
operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config))
44+
operation.deserializeStep.intercept(position: .before, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode))
45+
operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware())
46+
let presignedRequestBuilder = operation.presignedRequest(context: context.build(), input: input, next: ClientRuntime.NoopHandler())
47+
guard let builtRequest = presignedRequestBuilder?.build(), let presignedURL = builtRequest.endpoint.url else {
48+
return nil
49+
}
50+
return presignedURL
51+
}
52+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 Foundation
9+
10+
import AWSS3
11+
import ClientRuntime
12+
import AWSClientRuntime
13+
14+
extension UploadPartInput {
15+
func presignURL(config: AWSClientRuntime.AWSClientConfiguration, expiration: Swift.Int64) -> ClientRuntime.URL? {
16+
let serviceName = "S3"
17+
let input = self
18+
let encoder = ClientRuntime.XMLEncoder()
19+
encoder.dateEncodingStrategy = .secondsSince1970
20+
let decoder = ClientRuntime.XMLDecoder()
21+
decoder.dateDecodingStrategy = .secondsSince1970
22+
decoder.trimValueWhitespaces = false
23+
decoder.removeWhitespaceElements = true
24+
let context = ClientRuntime.HttpContextBuilder()
25+
.withEncoder(value: encoder)
26+
.withDecoder(value: decoder)
27+
.withMethod(value: .put)
28+
.withServiceName(value: serviceName)
29+
.withOperation(value: "uploadPart")
30+
.withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator)
31+
.withLogger(value: config.logger)
32+
.withCredentialsProvider(value: config.credentialsProvider)
33+
.withRegion(value: config.region)
34+
.withSigningName(value: "s3")
35+
.withSigningRegion(value: config.signingRegion)
36+
var operation = ClientRuntime.OperationStack<UploadPartInput, UploadPartOutputResponse, UploadPartOutputError>(id: "uploadPart")
37+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLPathMiddleware<UploadPartInput, UploadPartOutputResponse, UploadPartOutputError>())
38+
operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware<UploadPartInput, UploadPartOutputResponse, UploadPartOutputError>())
39+
operation.buildStep.intercept(position: .before, middleware: AWSClientRuntime.EndpointResolverMiddleware(endpointResolver: config.endpointResolver, serviceId: serviceName))
40+
operation.serializeStep.intercept(position: .after, middleware: UploadPartInputBodyMiddleware())
41+
operation.serializeStep.intercept(position: .after, middleware: QueryItemMiddleware())
42+
operation.finalizeStep.intercept(position: .after, middleware: AWSClientRuntime.RetryerMiddleware(retryer: config.retryer))
43+
let sigv4Config = AWSClientRuntime.SigV4Config(signatureType: .requestQueryParams, expiration: expiration, unsignedBody: true)
44+
operation.finalizeStep.intercept(position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config))
45+
operation.deserializeStep.intercept(position: .before, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode))
46+
operation.deserializeStep.intercept(position: .after, middleware: ClientRuntime.DeserializeMiddleware())
47+
let presignedRequestBuilder = operation.presignedRequest(context: context.build(), input: input, next: ClientRuntime.NoopHandler())
48+
guard let builtRequest = presignedRequestBuilder?.build(), let presignedURL = builtRequest.endpoint.url else {
49+
return nil
50+
}
51+
return presignedURL
52+
}
53+
}

0 commit comments

Comments
 (0)