From 0425e51c1d3937ceccff1d995427a15232343f7b Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:23:16 -0400 Subject: [PATCH] feat(Storage): Implementing support for multiple buckets (#3817) --- .../Configuration/AmplifyOutputsData.swift | 19 + ...SS3StoragePlugin+AsyncClientBehavior.swift | 23 +- .../AWSS3StoragePlugin+ClientBehavior.swift | 2 +- .../AWSS3StoragePlugin+Configure.swift | 76 +++- .../AWSS3StoragePlugin+Reset.swift | 13 +- .../AWSS3StoragePlugin+StorageBucket.swift | 74 ++++ .../AWSS3StoragePlugin.swift | 23 +- .../AWSS3StorageDownloadDataOperation.swift | 14 +- .../AWSS3StorageDownloadFileOperation.swift | 14 +- .../AWSS3StorageUploadDataOperation.swift | 16 +- .../AWSS3StorageUploadFileOperation.swift | 16 +- .../Service/Storage/AWSS3StorageService.swift | 6 +- .../Storage/AWSS3StorageServiceBehavior.swift | 1 + .../Internal/StorageConfiguration.swift | 10 +- .../AWSS3StoragePluginResetTests.swift | 2 +- .../AWSS3StoragePluginTestBase.swift | 23 +- ...ageOperations+StorageServiceProvider.swift | 99 +++++ api-dump/AWSDataStorePlugin.json | 2 +- api-dump/AWSPluginsCore.json | 2 +- api-dump/Amplify.json | 346 +++++++++++++++++- api-dump/CoreMLPredictionsPlugin.json | 2 +- 21 files changed, 723 insertions(+), 60 deletions(-) create mode 100644 AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+StorageBucket.swift create mode 100644 AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/AWSS3StorageOperations+StorageServiceProvider.swift diff --git a/Amplify/Core/Configuration/AmplifyOutputsData.swift b/Amplify/Core/Configuration/AmplifyOutputsData.swift index 93a2f60e6a..6d2fdfff75 100644 --- a/Amplify/Core/Configuration/AmplifyOutputsData.swift +++ b/Amplify/Core/Configuration/AmplifyOutputsData.swift @@ -176,6 +176,25 @@ public struct AmplifyOutputsData: Codable { public struct Storage: Codable { public let awsRegion: AWSRegion public let bucketName: String + public let buckets: [Bucket]? + + @_spi(InternalAmplifyConfiguration) + public struct Bucket: Codable { + public let name: String + public let bucketName: String + public let awsRegion: AWSRegion + } + + // Internal init used for testing + init( + awsRegion: AWSRegion, + bucketName: String, + buckets: [Bucket]? = nil + ) { + self.awsRegion = awsRegion + self.bucketName = bucketName + self.buckets = buckets + } } @_spi(InternalAmplifyConfiguration) diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+AsyncClientBehavior.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+AsyncClientBehavior.swift index a960ec1e07..80e454554f 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+AsyncClientBehavior.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+AsyncClientBehavior.swift @@ -27,6 +27,8 @@ extension AWSS3StoragePlugin { let prefix = try await prefixResolver.resolvePrefix(for: options.accessLevel, targetIdentityId: options.targetIdentityId) let serviceKey = prefix + request.key + + let storageService = try storageService(for: options.bucket) if let pluginOptions = options.pluginOptions as? AWSStorageGetURLOptions, pluginOptions.validateObjectExistence { try await storageService.validateObjectExistence(serviceKey: serviceKey) } @@ -51,6 +53,7 @@ extension AWSS3StoragePlugin { ) async throws -> URL { let options = options ?? StorageGetURLRequest.Options() let request = StorageGetURLRequest(path: path, options: options) + let storageService = try storageService(for: options.bucket) let task = AWSS3StorageGetURLTask( request, storageBehaviour: storageService) @@ -65,7 +68,7 @@ extension AWSS3StoragePlugin { let request = StorageDownloadDataRequest(path: path, options: options) let operation = AWSS3StorageDownloadDataOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -82,7 +85,7 @@ extension AWSS3StoragePlugin { let request = StorageDownloadDataRequest(key: key, options: options) let operation = AWSS3StorageDownloadDataOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -100,7 +103,7 @@ extension AWSS3StoragePlugin { let request = StorageDownloadFileRequest(key: key, local: local, options: options) let operation = AWSS3StorageDownloadFileOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -118,7 +121,7 @@ extension AWSS3StoragePlugin { let request = StorageDownloadFileRequest(path: path, local: local, options: options) let operation = AWSS3StorageDownloadFileOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -136,7 +139,7 @@ extension AWSS3StoragePlugin { let request = StorageUploadDataRequest(key: key, data: data, options: options) let operation = AWSS3StorageUploadDataOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -154,7 +157,7 @@ extension AWSS3StoragePlugin { let request = StorageUploadDataRequest(path: path, data: data, options: options) let operation = AWSS3StorageUploadDataOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -172,7 +175,7 @@ extension AWSS3StoragePlugin { let request = StorageUploadFileRequest(key: key, local: local, options: options) let operation = AWSS3StorageUploadFileOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -190,7 +193,7 @@ extension AWSS3StoragePlugin { let request = StorageUploadFileRequest(path: path, local: local, options: options) let operation = AWSS3StorageUploadFileOperation(request, storageConfiguration: storageConfiguration, - storageService: storageService, + storageServiceProvider: storageServiceProvider(for: options.bucket), authService: authService) let taskAdapter = AmplifyInProcessReportingOperationTaskAdapter(operation: operation) queue.addOperation(operation) @@ -205,6 +208,7 @@ extension AWSS3StoragePlugin { ) async throws -> String { let options = options ?? StorageRemoveRequest.Options() let request = StorageRemoveRequest(key: key, options: options) + let storageService = try storageService(for: options.bucket) let operation = AWSS3StorageRemoveOperation(request, storageConfiguration: storageConfiguration, storageService: storageService, @@ -222,6 +226,7 @@ extension AWSS3StoragePlugin { ) async throws -> String { let options = options ?? StorageRemoveRequest.Options() let request = StorageRemoveRequest(path: path, options: options) + let storageService = try storageService(for: options.bucket) let task = AWSS3StorageRemoveTask( request, storageConfiguration: storageConfiguration, @@ -233,6 +238,7 @@ extension AWSS3StoragePlugin { options: StorageListRequest.Options? = nil ) async throws -> StorageListResult { let options = options ?? StorageListRequest.Options() + let storageService = try storageService(for: options.bucket) let prefixResolver = storageConfiguration.prefixResolver ?? StorageAccessLevelAwarePrefixResolver(authService: authService) let prefix = try await prefixResolver.resolvePrefix(for: options.accessLevel, targetIdentityId: options.targetIdentityId) let result = try await storageService.list(prefix: prefix, options: options) @@ -250,6 +256,7 @@ extension AWSS3StoragePlugin { ) async throws -> StorageListResult { let options = options ?? StorageListRequest.Options() let request = StorageListRequest(path: path, options: options) + let storageService = try storageService(for: options.bucket) let task = AWSS3StorageListObjectsTask( request, storageConfiguration: storageConfiguration, diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+ClientBehavior.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+ClientBehavior.swift index 9778b9cab0..67ad544afb 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+ClientBehavior.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+ClientBehavior.swift @@ -19,6 +19,6 @@ extension AWSS3StoragePlugin { /// /// - Tag: AWSS3StoragePlugin.getEscapeHatch public func getEscapeHatch() -> S3Client { - return storageService.getEscapeHatch() + return defaultStorageService.getEscapeHatch() } } diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Configure.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Configure.swift index 9b40e63906..c589208366 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Configure.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Configure.swift @@ -8,6 +8,7 @@ import Foundation @_spi(InternalAmplifyConfiguration) import Amplify import AWSPluginsCore +import InternalAmplifyCredentials extension AWSS3StoragePlugin { @@ -27,6 +28,7 @@ extension AWSS3StoragePlugin { let configClosures: ConfigurationClosures if let config = configuration as? AmplifyOutputsData { configClosures = try retrieveConfiguration(config) + additionalBucketsByName = retrieveAdditionalBucketsByName(from: config.storage) } else if let config = configuration as? JSONValue { configClosures = try retrieveConfiguration(config) } else { @@ -38,15 +40,24 @@ extension AWSS3StoragePlugin { do { let authService = AWSAuthService() let defaultAccessLevel = try configClosures.retrieveDefaultAccessLevel() - let storageService = try AWSS3StorageService(authService: authService, - region: configClosures.retrieveRegion(), - bucket: configClosures.retrieveBucket(), - httpClientEngineProxy: self.httpClientEngineProxy) - storageService.urlRequestDelegate = self.urlRequestDelegate - - configure(storageService: storageService, - authService: authService, - defaultAccessLevel: defaultAccessLevel) + let defaultBucket: ResolvedStorageBucket = try .fromBucketInfo( + .init( + bucketName: configClosures.retrieveBucket(), + region: configClosures.retrieveRegion() + ) + ) + + let storageService = try createStorageService( + authService: authService, + bucketInfo: defaultBucket.bucketInfo + ) + + configure( + defaultBucket: defaultBucket, + storageService: storageService, + authService: authService, + defaultAccessLevel: defaultAccessLevel + ) } catch let storageError as StorageError { throw storageError } catch { @@ -67,18 +78,38 @@ extension AWSS3StoragePlugin { /// Called from the configure method which implements the Plugin protocol. Useful for testing by passing in mocks. /// /// - Parameters: - /// - storageService: The S3 storage service object. + /// - defaultBucket: The bucket to be used for all API calls by default. + /// - storageService: The S3 storage service object associated with the default bucket /// - authService: The authentication service object. /// - defaultAccessLevel: The access level to be used for all API calls by default. /// - queue: The queue which operations are stored and dispatched for asychronous processing. - func configure(storageService: AWSS3StorageServiceBehavior, - authService: AWSAuthServiceBehavior, - defaultAccessLevel: StorageAccessLevel, - queue: OperationQueue = OperationQueue()) { - self.storageService = storageService + func configure( + defaultBucket: ResolvedStorageBucket, + storageService: AWSS3StorageServiceBehavior, + authService: AWSAuthCredentialsProviderBehavior, + defaultAccessLevel: StorageAccessLevel, + queue: OperationQueue = OperationQueue() + ) { + self.defaultBucket = defaultBucket self.authService = authService self.queue = queue self.defaultAccessLevel = defaultAccessLevel + self.storageServicesByBucket[defaultBucket.bucketInfo.bucketName] = storageService + } + + /// Creates a new AWSS3StorageServiceBehavior for the given BucketInfo + func createStorageService( + authService: AWSAuthCredentialsProviderBehavior, + bucketInfo: BucketInfo + ) throws -> AWSS3StorageServiceBehavior { + let storageService = try AWSS3StorageService( + authService: authService, + region: bucketInfo.region, + bucket: bucketInfo.bucketName, + httpClientEngineProxy: httpClientEngineProxy + ) + storageService.urlRequestDelegate = urlRequestDelegate + return storageService } // MARK: Private helper methods @@ -127,6 +158,21 @@ extension AWSS3StoragePlugin { retrieveDefaultAccessLevel: defaultAccessLevelClosure) } + /// Retrieves the configured buckets from the configuration grouped by their names. + /// If no buckets are provided in the configuration, an empty dictionary is returned instead. + private func retrieveAdditionalBucketsByName( + from configuration: AmplifyOutputsData.Storage? + ) -> [String: AmplifyOutputsData.Storage.Bucket] { + guard let configuration, + let buckets = configuration.buckets else { + return [:] + } + + return buckets.reduce(into: [:]) { dictionary, bucket in + dictionary[bucket.name] = bucket + } + } + /// Retrieves the region from configuration, validates, and returns it. private static func getRegion(_ configuration: [String: JSONValue]) throws -> String { guard let region = configuration[PluginConstants.region] else { diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Reset.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Reset.swift index 5bbceb00b9..6d4c1afaa8 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Reset.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+Reset.swift @@ -18,10 +18,19 @@ extension AWSS3StoragePlugin { /// /// - Tag: AWSS3StoragePlugin.reset public func reset() async { - if storageService != nil { + if defaultBucket != nil { + defaultBucket = nil + } + + for storageService in storageServicesByBucket.values { storageService.reset() - storageService = nil } + storageServicesByBucket.removeAll() + + if additionalBucketsByName != nil { + additionalBucketsByName = nil + } + if authService != nil { authService = nil } diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+StorageBucket.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+StorageBucket.swift new file mode 100644 index 0000000000..19b7fb2063 --- /dev/null +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+StorageBucket.swift @@ -0,0 +1,74 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(InternalAmplifyConfiguration) import Amplify +import Foundation + +extension AWSS3StoragePlugin { + /// Returns a AWSS3StorageServiceBehavior instance for the given StorageBucket + func storageService(for bucket: (any StorageBucket)?) throws -> AWSS3StorageServiceBehavior { + guard let bucket else { + // When no bucket is provided, use the default one + return defaultStorageService + } + + let bucketInfo = try bucketInfo(from: bucket) + guard let storageService = storageServicesByBucket[bucketInfo.bucketName] else { + // If no service was found for the bucket, create one + let storageService = try createStorageService( + authService: authService, + bucketInfo: bucketInfo + ) + storageServicesByBucket[bucketInfo.bucketName] = storageService + return storageService + } + + return storageService + } + + /// Returns a AWSS3StorageServiceProvider callback for the given StorageBucket + func storageServiceProvider(for bucket: (any StorageBucket)?) -> AWSS3StorageServiceProvider { + let storageServiceResolver: () throws -> AWSS3StorageServiceBehavior = { [weak self] in + guard let self = self else { + throw StorageError.unknown("AWSS3StoragePlugin was deallocated", nil) + } + return try self.storageService(for: bucket) + } + return storageServiceResolver + } + + /// Returns a valid `BucketInfo` instance from the given StorageBucket + private func bucketInfo(from bucket: any StorageBucket) throws -> BucketInfo { + switch bucket { + case let outputsBucket as OutputsStorageBucket: + guard let additionalBucketsByName else { + let errorDescription = "Amplify was not configured using an Amplify Outputs file" + let recoverySuggestion = "Make sure that `Amplify.configure(with:)` is invoked" + throw StorageError.validation("bucket", errorDescription, recoverySuggestion, nil) + } + + guard let awsBucket = additionalBucketsByName[outputsBucket.name] else { + let errorDescription = "Unable to lookup bucket from provided name in Amplify Outputs" + let recoverySuggestion = "Make sure the bucket name exists in the Amplify Outputs file" + throw StorageError.validation("bucket", errorDescription, recoverySuggestion, nil) + } + + return .init( + bucketName: awsBucket.bucketName, + region: awsBucket.awsRegion + ) + + case let resolvedBucket as ResolvedStorageBucket: + return resolvedBucket.bucketInfo + + default: + let errorDescription = "The specified StorageBucket is not supported" + let recoverySuggestion = "Please specify a StorageBucket from the Outputs file or from BucketInfo" + throw StorageError.validation("bucket", errorDescription, recoverySuggestion, nil) + } + } +} diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin.swift index dc4bbe3f09..d4bd01f795 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin.swift @@ -5,9 +5,10 @@ // SPDX-License-Identifier: Apache-2.0 // -import Amplify +@_spi(InternalAmplifyConfiguration) import Amplify import Foundation import AWSPluginsCore +import InternalAmplifyCredentials /// The AWSS3StoragePlugin which conforms to the Amplify plugin protocols and implements the Storage /// Plugin APIs for AWS S3. @@ -15,11 +16,25 @@ import AWSPluginsCore /// - Tag: AWSS3StoragePlugin final public class AWSS3StoragePlugin: StorageCategoryPlugin { - /// An instance of the S3 storage service. - var storageService: AWSS3StorageServiceBehavior! + /// The default S3 storage service. + var defaultStorageService: AWSS3StorageServiceBehavior! { + guard let defaultBucket else { + return nil + } + return storageServicesByBucket[defaultBucket.bucketInfo.bucketName] + } + + /// The default bucket + var defaultBucket: ResolvedStorageBucket! + + /// A dictionary of S3 storage service instances grouped by a specific bucket + var storageServicesByBucket: AtomicDictionary = [:] + + /// A dictionary of additional Outputs-based buckets, grouped by their names + var additionalBucketsByName: [String: AmplifyOutputsData.Storage.Bucket]? /// An instance of the authentication service. - var authService: AWSAuthServiceBehavior! + var authService: AWSAuthCredentialsProviderBehavior! /// A queue that regulates the execution of operations. var queue: OperationQueue! diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadDataOperation.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadDataOperation.swift index 2e56183048..0a1bc25de8 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadDataOperation.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadDataOperation.swift @@ -22,7 +22,7 @@ class AWSS3StorageDownloadDataOperation: AmplifyInProcessReportingOperation< >, StorageDownloadDataOperation { let storageConfiguration: AWSS3StoragePluginConfiguration - let storageService: AWSS3StorageServiceBehavior + let storageServiceProvider: AWSS3StorageServiceProvider let authService: AWSAuthServiceBehavior var storageTaskReference: StorageTaskReference? @@ -30,15 +30,21 @@ class AWSS3StorageDownloadDataOperation: AmplifyInProcessReportingOperation< // Serial queue for synchronizing access to `storageTaskReference`. private let storageTaskActionQueue = DispatchQueue(label: "com.amazonaws.amplify.StorageTaskActionQueue") + private var storageService: AWSS3StorageServiceBehavior { + get throws { + return try storageServiceProvider() + } + } + init(_ request: StorageDownloadDataRequest, storageConfiguration: AWSS3StoragePluginConfiguration, - storageService: AWSS3StorageServiceBehavior, + storageServiceProvider: @escaping AWSS3StorageServiceProvider, authService: AWSAuthServiceBehavior, progressListener: InProcessListener? = nil, resultListener: ResultListener? = nil) { self.storageConfiguration = storageConfiguration - self.storageService = storageService + self.storageServiceProvider = storageServiceProvider self.authService = authService super.init(categoryType: .storage, eventName: HubPayload.EventName.Storage.downloadData, @@ -97,7 +103,7 @@ class AWSS3StorageDownloadDataOperation: AmplifyInProcessReportingOperation< } let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions) - storageService.download(serviceKey: serviceKey, fileURL: nil, accelerate: accelerate) { [weak self] event in + try storageService.download(serviceKey: serviceKey, fileURL: nil, accelerate: accelerate) { [weak self] event in self?.onServiceEvent(event: event) } } catch { diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadFileOperation.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadFileOperation.swift index 86d75956b6..4605459215 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadFileOperation.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageDownloadFileOperation.swift @@ -25,23 +25,29 @@ class AWSS3StorageDownloadFileOperation: AmplifyInProcessReportingOperation< >, StorageDownloadFileOperation { let storageConfiguration: AWSS3StoragePluginConfiguration - let storageService: AWSS3StorageServiceBehavior + let storageServiceProvider: AWSS3StorageServiceProvider let authService: AWSAuthServiceBehavior var storageTaskReference: StorageTaskReference? // Serial queue for synchronizing access to `storageTaskReference`. private let storageTaskActionQueue = DispatchQueue(label: "com.amazonaws.amplify.StorageTaskActionQueue") + private var storageService: AWSS3StorageServiceBehavior { + get throws { + return try storageServiceProvider() + } + } + init(_ request: StorageDownloadFileRequest, storageConfiguration: AWSS3StoragePluginConfiguration, - storageService: AWSS3StorageServiceBehavior, + storageServiceProvider: @escaping AWSS3StorageServiceProvider, authService: AWSAuthServiceBehavior, progressListener: InProcessListener? = nil, resultListener: ResultListener? = nil ) { self.storageConfiguration = storageConfiguration - self.storageService = storageService + self.storageServiceProvider = storageServiceProvider self.authService = authService super.init(categoryType: .storage, eventName: HubPayload.EventName.Storage.downloadFile, @@ -99,7 +105,7 @@ class AWSS3StorageDownloadFileOperation: AmplifyInProcessReportingOperation< serviceKey = prefix + request.key } let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions) - storageService.download( + try storageService.download( serviceKey: serviceKey, fileURL: self.request.local, accelerate: accelerate diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadDataOperation.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadDataOperation.swift index 7dd70d4f28..a2751a4a1b 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadDataOperation.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadDataOperation.swift @@ -22,7 +22,7 @@ class AWSS3StorageUploadDataOperation: AmplifyInProcessReportingOperation< >, StorageUploadDataOperation { let storageConfiguration: AWSS3StoragePluginConfiguration - let storageService: AWSS3StorageServiceBehavior + let storageServiceProvider: AWSS3StorageServiceProvider let authService: AWSAuthServiceBehavior var storageTaskReference: StorageTaskReference? @@ -30,15 +30,21 @@ class AWSS3StorageUploadDataOperation: AmplifyInProcessReportingOperation< /// Serial queue for synchronizing access to `storageTaskReference`. private let storageTaskActionQueue = DispatchQueue(label: "com.amazonaws.amplify.StorageTaskActionQueue") + private var storageService: AWSS3StorageServiceBehavior { + get throws { + return try storageServiceProvider() + } + } + init(_ request: StorageUploadDataRequest, storageConfiguration: AWSS3StoragePluginConfiguration, - storageService: AWSS3StorageServiceBehavior, + storageServiceProvider: @escaping AWSS3StorageServiceProvider, authService: AWSAuthServiceBehavior, progressListener: InProcessListener? = nil, resultListener: ResultListener? = nil) { self.storageConfiguration = storageConfiguration - self.storageService = storageService + self.storageServiceProvider = storageServiceProvider self.authService = authService super.init(categoryType: .storage, eventName: HubPayload.EventName.Storage.uploadData, @@ -102,7 +108,7 @@ class AWSS3StorageUploadDataOperation: AmplifyInProcessReportingOperation< let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions) if request.data.count > StorageUploadDataRequest.Options.multiPartUploadSizeThreshold { - storageService.multiPartUpload( + try storageService.multiPartUpload( serviceKey: serviceKey, uploadSource: .data(request.data), contentType: request.options.contentType, @@ -112,7 +118,7 @@ class AWSS3StorageUploadDataOperation: AmplifyInProcessReportingOperation< self?.onServiceEvent(event: event) } } else { - storageService.upload( + try storageService.upload( serviceKey: serviceKey, uploadSource: .data(request.data), contentType: request.options.contentType, diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadFileOperation.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadFileOperation.swift index db8ced505c..10d3ceb685 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadFileOperation.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Operation/AWSS3StorageUploadFileOperation.swift @@ -22,7 +22,7 @@ class AWSS3StorageUploadFileOperation: AmplifyInProcessReportingOperation< >, StorageUploadFileOperation { let storageConfiguration: AWSS3StoragePluginConfiguration - let storageService: AWSS3StorageServiceBehavior + let storageServiceProvider: AWSS3StorageServiceProvider let authService: AWSAuthServiceBehavior var storageTaskReference: StorageTaskReference? @@ -30,15 +30,21 @@ class AWSS3StorageUploadFileOperation: AmplifyInProcessReportingOperation< /// Serial queue for synchronizing access to `storageTaskReference`. private let storageTaskActionQueue = DispatchQueue(label: "com.amazonaws.amplify.StorageTaskActionQueue") + private var storageService: AWSS3StorageServiceBehavior { + get throws { + return try storageServiceProvider() + } + } + init(_ request: StorageUploadFileRequest, storageConfiguration: AWSS3StoragePluginConfiguration, - storageService: AWSS3StorageServiceBehavior, + storageServiceProvider: @escaping AWSS3StorageServiceProvider, authService: AWSAuthServiceBehavior, progressListener: InProcessListener? = nil, resultListener: ResultListener? = nil) { self.storageConfiguration = storageConfiguration - self.storageService = storageService + self.storageServiceProvider = storageServiceProvider self.authService = authService super.init(categoryType: .storage, eventName: HubPayload.EventName.Storage.uploadFile, @@ -124,7 +130,7 @@ class AWSS3StorageUploadFileOperation: AmplifyInProcessReportingOperation< let accelerate = try AWSS3PluginOptions.accelerateValue(pluginOptions: request.options.pluginOptions) if uploadSize > StorageUploadFileRequest.Options.multiPartUploadSizeThreshold { - storageService.multiPartUpload( + try storageService.multiPartUpload( serviceKey: serviceKey, uploadSource: .local(request.local), contentType: request.options.contentType, @@ -134,7 +140,7 @@ class AWSS3StorageUploadFileOperation: AmplifyInProcessReportingOperation< self?.onServiceEvent(event: event) } } else { - storageService.upload( + try storageService.upload( serviceKey: serviceKey, uploadSource: .local(request.local), contentType: request.options.contentType, diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift index c311eaaeb8..a07858a643 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift @@ -57,13 +57,14 @@ class AWSS3StorageService: AWSS3StorageServiceBehavior, StorageServiceProxy { region: String, bucket: String, httpClientEngineProxy: HttpClientEngineProxy? = nil, - storageConfiguration: StorageConfiguration = .default, + storageConfiguration: StorageConfiguration? = nil, storageTransferDatabase: StorageTransferDatabase = .default, fileSystem: FileSystem = .default, sessionConfiguration: URLSessionConfiguration? = nil, delegateQueue: OperationQueue? = nil, logger: Logger = storageLogger) throws { let credentialsProvider = authService.getCredentialsProvider() + let storageConfiguration = storageConfiguration ?? .init(forBucket: bucket) let clientConfig = try S3Client.S3ClientConfiguration( region: region, credentialsProvider: credentialsProvider, @@ -113,7 +114,7 @@ class AWSS3StorageService: AWSS3StorageServiceBehavior, StorageServiceProxy { } init(authService: AWSAuthCredentialsProviderBehavior, - storageConfiguration: StorageConfiguration = .default, + storageConfiguration: StorageConfiguration? = nil, storageTransferDatabase: StorageTransferDatabase = .default, fileSystem: FileSystem = .default, sessionConfiguration: URLSessionConfiguration, @@ -123,6 +124,7 @@ class AWSS3StorageService: AWSS3StorageServiceBehavior, StorageServiceProxy { preSignedURLBuilder: AWSS3PreSignedURLBuilderBehavior, awsS3: AWSS3Behavior, bucket: String) { + let storageConfiguration = storageConfiguration ?? .init(forBucket: bucket) self.storageConfiguration = storageConfiguration self.storageTransferDatabase = storageTransferDatabase self.fileSystem = fileSystem diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageServiceBehavior.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageServiceBehavior.swift index 6491546d8d..9fd748454a 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageServiceBehavior.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageServiceBehavior.swift @@ -9,6 +9,7 @@ import Foundation import Amplify import AWSS3 +typealias AWSS3StorageServiceProvider = () throws -> AWSS3StorageServiceBehavior protocol AWSS3StorageServiceBehavior { typealias StorageServiceDownloadEventHandler = (StorageServiceDownloadEvent) -> Void typealias StorageServiceDownloadEvent = diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageConfiguration.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageConfiguration.swift index 71e9bcfd06..b1b722f065 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageConfiguration.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageConfiguration.swift @@ -20,10 +20,6 @@ struct StorageConfiguration { let allowsCellularAccess: Bool let timeoutIntervalForResource: TimeInterval - static let `default`: StorageConfiguration = { - StorageConfiguration() - }() - init(sessionIdentifier: String = Defaults.sessionIdentifier, sharedContainerIdentifier: String = Defaults.sharedContainerIdentifier, allowsCellularAccess: Bool = Defaults.allowsCellularAccess, @@ -33,4 +29,10 @@ struct StorageConfiguration { self.allowsCellularAccess = allowsCellularAccess self.timeoutIntervalForResource = timeoutIntervalForResource } + + init(forBucket bucket: String) { + self.init( + sessionIdentifier: "\(Defaults.sessionIdentifier).\(bucket)" + ) + } } diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginResetTests.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginResetTests.swift index 7fa01ac1a4..3983a0cc7d 100644 --- a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginResetTests.swift +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginResetTests.swift @@ -16,7 +16,7 @@ class AWSS3StoragePluginResetTests: AWSS3StoragePluginTests { await resettable.reset() XCTAssertNil(storagePlugin.authService) - XCTAssertNil(storagePlugin.storageService) + XCTAssertNil(storagePlugin.defaultStorageService) XCTAssertNil(storagePlugin.queue) } } diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginTestBase.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginTestBase.swift index 5ed47beb9e..423f99399e 100644 --- a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginTestBase.swift +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginTestBase.swift @@ -10,6 +10,7 @@ import Amplify @testable import AWSS3StoragePlugin @testable import AmplifyTestCommon @testable import AWSPluginsTestCommon +import InternalAmplifyCredentials class AWSS3StoragePluginTests: XCTestCase { var storagePlugin: AWSS3StoragePlugin! @@ -33,9 +34,29 @@ class AWSS3StoragePluginTests: XCTestCase { authService = MockAWSAuthService() queue = MockOperationQueue() - storagePlugin.configure(storageService: storageService, + storagePlugin.configure(defaultBucket: .fromBucketInfo(.init(bucketName: testBucket, region: testRegion)), + storageService: storageService, authService: authService, defaultAccessLevel: defaultAccessLevel, queue: queue) } } + + +extension AWSS3StoragePlugin { + /// Convenience configuration method for testing purposes, with a default bucket with name "test-bucket" and region "us-east-1" + func configure( + storageService: AWSS3StorageServiceBehavior, + authService: AWSAuthCredentialsProviderBehavior, + defaultAccessLevel: StorageAccessLevel, + queue: OperationQueue = OperationQueue() + ) { + configure( + defaultBucket: .fromBucketInfo(.init(bucketName: "test-bucket", region: "us-east-1")), + storageService: storageService, + authService: authService, + defaultAccessLevel: defaultAccessLevel, + queue: queue + ) + } +} diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/AWSS3StorageOperations+StorageServiceProvider.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/AWSS3StorageOperations+StorageServiceProvider.swift new file mode 100644 index 0000000000..71931f237a --- /dev/null +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/AWSS3StorageOperations+StorageServiceProvider.swift @@ -0,0 +1,99 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@testable import Amplify +@testable import AWSPluginsCore +@testable import AWSS3StoragePlugin +import Foundation + +extension AWSS3StorageDownloadFileOperation { + convenience init( + _ request: StorageDownloadFileRequest, + storageConfiguration: AWSS3StoragePluginConfiguration, + storageService: AWSS3StorageServiceBehavior, + authService: AWSAuthServiceBehavior, + progressListener: InProcessListener? = nil, + resultListener: ResultListener? = nil + ) { + self.init( + request, + storageConfiguration: storageConfiguration, + storageServiceProvider: { + return storageService + }, + authService: authService, + progressListener: progressListener, + resultListener: resultListener + ) + } +} + +extension AWSS3StorageDownloadDataOperation { + convenience init( + _ request: StorageDownloadDataRequest, + storageConfiguration: AWSS3StoragePluginConfiguration, + storageService: AWSS3StorageServiceBehavior, + authService: AWSAuthServiceBehavior, + progressListener: InProcessListener? = nil, + resultListener: ResultListener? = nil + ) { + self.init( + request, + storageConfiguration: storageConfiguration, + storageServiceProvider: { + return storageService + }, + authService: authService, + progressListener: progressListener, + resultListener: resultListener + ) + } +} + +extension AWSS3StorageUploadDataOperation { + convenience init( + _ request: StorageUploadDataRequest, + storageConfiguration: AWSS3StoragePluginConfiguration, + storageService: AWSS3StorageServiceBehavior, + authService: AWSAuthServiceBehavior, + progressListener: InProcessListener? = nil, + resultListener: ResultListener? = nil + ) { + self.init( + request, + storageConfiguration: storageConfiguration, + storageServiceProvider: { + return storageService + }, + authService: authService, + progressListener: progressListener, + resultListener: resultListener + ) + } +} + +extension AWSS3StorageUploadFileOperation { + convenience init( + _ request: StorageUploadFileRequest, + storageConfiguration: AWSS3StoragePluginConfiguration, + storageService: AWSS3StorageServiceBehavior, + authService: AWSAuthServiceBehavior, + progressListener: InProcessListener? = nil, + resultListener: ResultListener? = nil + ) { + self.init( + request, + storageConfiguration: storageConfiguration, + storageServiceProvider: { + return storageService + }, + authService: authService, + progressListener: progressListener, + resultListener: resultListener + ) + } +} diff --git a/api-dump/AWSDataStorePlugin.json b/api-dump/AWSDataStorePlugin.json index 4065e4f66b..086d183e4d 100644 --- a/api-dump/AWSDataStorePlugin.json +++ b/api-dump/AWSDataStorePlugin.json @@ -8205,7 +8205,7 @@ "-module", "AWSDataStorePlugin", "-o", - "\/var\/folders\/m_\/cksx93ys47x4621g0zbw_m4m0000gn\/T\/tmp.xrcnJX8DBh\/AWSDataStorePlugin.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.7LdqMpKABA\/AWSDataStorePlugin.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/AWSPluginsCore.json b/api-dump/AWSPluginsCore.json index 502beff81b..536fd6842a 100644 --- a/api-dump/AWSPluginsCore.json +++ b/api-dump/AWSPluginsCore.json @@ -24273,7 +24273,7 @@ "-module", "AWSPluginsCore", "-o", - "\/var\/folders\/m_\/cksx93ys47x4621g0zbw_m4m0000gn\/T\/tmp.xrcnJX8DBh\/AWSPluginsCore.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.7LdqMpKABA\/AWSPluginsCore.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/Amplify.json b/api-dump/Amplify.json index 88d1949ba0..66a9b92624 100644 --- a/api-dump/Amplify.json +++ b/api-dump/Amplify.json @@ -155102,6 +155102,350 @@ } ] }, + { + "kind": "Var", + "name": "buckets", + "printedName": "buckets", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "[Amplify.AmplifyOutputsData.Storage.Bucket]?", + "children": [ + { + "kind": "TypeNominal", + "name": "Array", + "printedName": "[Amplify.AmplifyOutputsData.Storage.Bucket]", + "children": [ + { + "kind": "TypeNominal", + "name": "Bucket", + "printedName": "Amplify.AmplifyOutputsData.Storage.Bucket", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV" + } + ], + "usr": "s:Sa" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify0A11OutputsDataV7StorageV7bucketsSayAE6BucketVGSgvp", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV7bucketsSayAE6BucketVGSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "[Amplify.AmplifyOutputsData.Storage.Bucket]?", + "children": [ + { + "kind": "TypeNominal", + "name": "Array", + "printedName": "[Amplify.AmplifyOutputsData.Storage.Bucket]", + "children": [ + { + "kind": "TypeNominal", + "name": "Bucket", + "printedName": "Amplify.AmplifyOutputsData.Storage.Bucket", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV" + } + ], + "usr": "s:Sa" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify0A11OutputsDataV7StorageV7bucketsSayAE6BucketVGSgvg", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV7bucketsSayAE6BucketVGSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "TypeDecl", + "name": "Bucket", + "printedName": "Bucket", + "children": [ + { + "kind": "Var", + "name": "name", + "printedName": "name", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Var", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV4nameSSvp", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV4nameSSvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV4nameSSvg", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV4nameSSvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Var", + "name": "bucketName", + "printedName": "bucketName", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Var", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV10bucketNameSSvp", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV10bucketNameSSvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV10bucketNameSSvg", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV10bucketNameSSvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Var", + "name": "awsRegion", + "printedName": "awsRegion", + "children": [ + { + "kind": "TypeNameAlias", + "name": "AWSRegion", + "printedName": "Amplify.AmplifyOutputsData.AWSRegion", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ] + } + ], + "declKind": "Var", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV9awsRegionSSvp", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV9awsRegionSSvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNameAlias", + "name": "AWSRegion", + "printedName": "Amplify.AmplifyOutputsData.AWSRegion", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ] + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV9awsRegionSSvg", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV9awsRegionSSvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Function", + "name": "encode", + "printedName": "encode(to:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Void", + "printedName": "()" + }, + { + "kind": "TypeNominal", + "name": "Encoder", + "printedName": "any Swift.Encoder", + "usr": "s:s7EncoderP" + } + ], + "declKind": "Func", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV6encode2toys7Encoder_p_tKF", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV6encode2toys7Encoder_p_tKF", + "moduleName": "Amplify", + "implicit": true, + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "throwing": true, + "funcSelfKind": "NonMutating" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(from:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Bucket", + "printedName": "Amplify.AmplifyOutputsData.Storage.Bucket", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV" + }, + { + "kind": "TypeNominal", + "name": "Decoder", + "printedName": "any Swift.Decoder", + "usr": "s:s7DecoderP" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV4fromAGs7Decoder_p_tKcfc", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV4fromAGs7Decoder_p_tKcfc", + "moduleName": "Amplify", + "implicit": true, + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "throwing": true, + "init_kind": "Designated" + } + ], + "declKind": "Struct", + "usr": "s:7Amplify0A11OutputsDataV7StorageV6BucketV", + "mangledName": "$s7Amplify0A11OutputsDataV7StorageV6BucketV", + "moduleName": "Amplify", + "declAttributes": [ + "SPIAccessControl" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "conformances": [ + { + "kind": "Conformance", + "name": "Decodable", + "printedName": "Decodable", + "usr": "s:Se", + "mangledName": "$sSe" + }, + { + "kind": "Conformance", + "name": "Encodable", + "printedName": "Encodable", + "usr": "s:SE", + "mangledName": "$sSE" + } + ] + }, { "kind": "Function", "name": "encode", @@ -180993,7 +181337,7 @@ "-module", "Amplify", "-o", - "\/var\/folders\/m_\/cksx93ys47x4621g0zbw_m4m0000gn\/T\/tmp.xrcnJX8DBh\/Amplify.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.7LdqMpKABA\/Amplify.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/CoreMLPredictionsPlugin.json b/api-dump/CoreMLPredictionsPlugin.json index 03a015b749..71fc795af3 100644 --- a/api-dump/CoreMLPredictionsPlugin.json +++ b/api-dump/CoreMLPredictionsPlugin.json @@ -430,7 +430,7 @@ "-module", "CoreMLPredictionsPlugin", "-o", - "\/var\/folders\/m_\/cksx93ys47x4621g0zbw_m4m0000gn\/T\/tmp.xrcnJX8DBh\/CoreMLPredictionsPlugin.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.7LdqMpKABA\/CoreMLPredictionsPlugin.json", "-I", ".build\/debug", "-sdk-version",