168 lines
7.6 KiB
Swift
168 lines
7.6 KiB
Swift
//
|
|
// Copyright Amazon.com Inc. or its affiliates.
|
|
// All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import Foundation
|
|
|
|
import Amplify
|
|
|
|
import AWSS3
|
|
import ClientRuntime
|
|
import AWSClientRuntime
|
|
|
|
/// The class conforming to AWSS3Behavior which uses an instance of the AWSS3 to perform its methods.
|
|
/// This class acts as a wrapper to expose AWSS3 functionality through an instance over a singleton,
|
|
/// and allows for mocking in unit tests. The methods contain no other logic other than calling the
|
|
/// same method using the AWSS3 instance.
|
|
class AWSS3Adapter: AWSS3Behavior {
|
|
let awsS3: S3Client
|
|
let config: AWSClientRuntime.AWSClientConfiguration
|
|
|
|
init(_ awsS3: S3Client, config: AWSClientRuntime.AWSClientConfiguration) {
|
|
self.awsS3 = awsS3
|
|
self.config = config
|
|
}
|
|
|
|
/// Deletes object identify by request.
|
|
/// - Parameters:
|
|
/// - request:: request identifying object
|
|
/// - completion: handle indicates when the operation is done
|
|
func deleteObject(_ request: AWSS3DeleteObjectRequest, completion: @escaping (Result<Void, StorageError>) -> Void) {
|
|
Task {
|
|
let input = DeleteObjectInput(bucket: request.bucket, key: request.key)
|
|
do {
|
|
_ = try await awsS3.deleteObject(input: input, config: config)
|
|
completion(.success(()))
|
|
} catch {
|
|
completion(.failure(StorageError(error: error)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Lists objects in the bucket specified by `request`.
|
|
/// - Parameters:
|
|
/// - request: request identifying bucket and options
|
|
/// - completion: handle which return a result with list of items
|
|
func listObjectsV2(_ request: AWSS3ListObjectsV2Request, completion: @escaping (Result<StorageListResult, StorageError>) -> Void) {
|
|
Task {
|
|
var finalPrefix: String?
|
|
if let prefix = request.prefix {
|
|
finalPrefix = prefix + (request.path ?? "")
|
|
}
|
|
let input = ListObjectsV2Input(bucket: request.bucket,
|
|
continuationToken: request.continuationToken,
|
|
delimiter: request.delimiter,
|
|
maxKeys: request.maxKeys,
|
|
prefix: finalPrefix,
|
|
startAfter: request.startAfter)
|
|
do {
|
|
let response = try await awsS3.listObjectsV2(input: input)
|
|
let contents: S3BucketContents = response.contents ?? []
|
|
let items = try contents.map {
|
|
try StorageListResult.Item(s3Object: $0, prefix: request.prefix ?? "")
|
|
}
|
|
let listResult = StorageListResult(items: items)
|
|
completion(.success(listResult))
|
|
} catch let error as SdkError<ListObjectsV2OutputError> {
|
|
completion(.failure(error.storageError))
|
|
} catch {
|
|
completion(.failure(StorageError(error: error)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Creates a MultipartUpload
|
|
/// - Parameters:
|
|
/// - request: request
|
|
/// - completion: handler which returns a result with uploadId
|
|
func createMultipartUpload(_ request: CreateMultipartUploadRequest, completion: @escaping (Result<AWSS3CreateMultipartUploadResponse, StorageError>) -> Void) {
|
|
Task {
|
|
let input = CreateMultipartUploadInput(bucket: request.bucket,
|
|
cacheControl: request.cacheControl,
|
|
contentDisposition: request.contentDisposition,
|
|
contentEncoding: request.contentEncoding,
|
|
contentLanguage: request.contentLanguage,
|
|
contentType: request.contentType,
|
|
expires: request.expires,
|
|
key: request.key,
|
|
metadata: request.metadata)
|
|
|
|
do {
|
|
let response = try await awsS3.createMultipartUpload(input: input, config: config)
|
|
guard let bucket = response.bucket, let key = response.key, let uploadId = response.uploadId else {
|
|
completion(.failure(StorageError.unknown("Invalid response for creating multipart upload", nil)))
|
|
return
|
|
}
|
|
completion(.success(AWSS3CreateMultipartUploadResponse(bucket: bucket, key: key, uploadId: uploadId)))
|
|
} catch {
|
|
completion(.failure(StorageError(error: error)))
|
|
}
|
|
}
|
|
}
|
|
|
|
func listParts(bucket: String, key: String, uploadId: UploadID, completion: @escaping (Result<AWSS3ListUploadPartResponse, StorageError>) -> Void) {
|
|
Task {
|
|
let input = ListPartsInput(bucket: bucket, key: key, uploadId: uploadId)
|
|
do {
|
|
let sdkResponse = try await awsS3.listParts(input: input)
|
|
guard let response = AWSS3ListUploadPartResponse(response: sdkResponse) else {
|
|
completion(.failure(StorageError.unknown("ListParts response is invalid", nil)))
|
|
return
|
|
}
|
|
completion(.success(response))
|
|
} catch {
|
|
completion(.failure(StorageError(error: error)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Completed a MultipartUpload
|
|
/// - Parameters:
|
|
/// - request: request which includes uploadId
|
|
/// - completion: handler which returns a result with object details
|
|
func completeMultipartUpload(_ request: AWSS3CompleteMultipartUploadRequest, completion: @escaping (Result<AWSS3CompleteMultipartUploadResponse, StorageError>) -> Void) {
|
|
Task {
|
|
let parts = request.parts.map {
|
|
S3ClientTypes.CompletedPart(eTag: $0.eTag, partNumber: $0.partNumber)
|
|
}
|
|
let completedMultipartUpload = S3ClientTypes.CompletedMultipartUpload(parts: parts)
|
|
let input = CompleteMultipartUploadInput(bucket: request.bucket, key: request.key, multipartUpload: completedMultipartUpload, uploadId: request.uploadId)
|
|
do {
|
|
let response = try await awsS3.completeMultipartUpload(input: input, config: config)
|
|
guard let eTag = response.eTag else {
|
|
completion(.failure(StorageError.unknown("Invalid response for completing multipart upload", nil)))
|
|
return
|
|
}
|
|
completion(.success(AWSS3CompleteMultipartUploadResponse(bucket: request.bucket, key: request.key, eTag: eTag)))
|
|
} catch {
|
|
completion(.failure(StorageError(error: error)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Aborts a MultipartUpload
|
|
/// - Parameters:
|
|
/// - request: request which includes uploadId
|
|
/// - completion: handler which indicates when the operation is done
|
|
func abortMultipartUpload(_ request: AWSS3AbortMultipartUploadRequest, completion: @escaping (Result<Void, StorageError>) -> Void) {
|
|
Task {
|
|
let input = AbortMultipartUploadInput(bucket: request.bucket, key: request.key, uploadId: request.uploadId)
|
|
do {
|
|
_ = try await awsS3.abortMultipartUpload(input: input)
|
|
completion(.success(()))
|
|
} catch {
|
|
completion(.failure(StorageError(error: error)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Instance of S3 service.
|
|
/// - Returns: S3 service instance.
|
|
func getS3() -> S3Client {
|
|
return awsS3
|
|
}
|
|
}
|