From aeb8d13919772078944e8722ec0c64a8b693af85 Mon Sep 17 00:00:00 2001 From: Tyler Roach Date: Wed, 14 Aug 2024 12:13:04 -0400 Subject: [PATCH 01/50] fix action (#3813) --- .github/workflows/issue_closed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_closed.yml b/.github/workflows/issue_closed.yml index 5bb579e9ab..d5c0340e7b 100644 --- a/.github/workflows/issue_closed.yml +++ b/.github/workflows/issue_closed.yml @@ -10,7 +10,7 @@ permissions: jobs: cleanup-labels: runs-on: ubuntu-latest - if: ${{ (contains(github.event.issue.labels.*.name, 'pending-community-response') || (contains(github.event.issue.labels.*.name, 'pending-maintainer-response') || contains(github.event.issue.labels.*.name, 'closing soon') || contains(github.event.issue.labels.*.name, 'pending-release')|| contains(github.event.issue.labels.*.name, 'pending-triage')) }} + if: ${{ (contains(github.event.issue.labels.*.name, 'pending-community-response') || contains(github.event.issue.labels.*.name, 'pending-maintainer-response') || contains(github.event.issue.labels.*.name, 'closing soon') || contains(github.event.issue.labels.*.name, 'pending-release')|| contains(github.event.issue.labels.*.name, 'pending-triage')) }} steps: - name: remove unnecessary labels after closing shell: bash From 611368cb4325cf2cd3664ad1078e8a9284587c61 Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:02:00 -0400 Subject: [PATCH 02/50] chore: Updating jazzy to 0.15.1 [skip ci] (#3819) --- Gemfile | 2 +- Gemfile.lock | 51 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 7943313c25..3b0fac1afc 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,6 @@ source 'https://rubygems.org' gem 'xcpretty', '0.3.0' gem 'fastlane', '2.205.1' -gem 'jazzy', '0.14.2' +gem 'jazzy', '0.15.1' eval_gemfile('fastlane/Pluginfile') diff --git a/Gemfile.lock b/Gemfile.lock index c00707a60a..d949113527 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,13 +13,18 @@ GEM base64 nkf rexml - activesupport (7.0.7.2) + activesupport (7.1.3.4) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) @@ -43,13 +48,14 @@ GEM aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) + bigdecimal (3.1.8) claide (1.1.0) - cocoapods (1.12.0) + cocoapods (1.15.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.12.0) + cocoapods-core (= 1.15.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.6.0, < 2.0) + cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) cocoapods-trunk (>= 1.6.0, < 2.0) @@ -61,8 +67,8 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.12.0) + xcodeproj (>= 1.23.0, < 2.0) + cocoapods-core (1.15.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -73,7 +79,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.6.3) + cocoapods-downloader (2.1) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) @@ -85,13 +91,15 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.2.2) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) declarative (0.0.20) digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.8.1) + drb (2.2.1) emoji_regex (3.2.3) escape (0.0.4) ethon (0.16.0) @@ -165,7 +173,7 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - ffi (1.15.5) + ffi (1.17.0) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) @@ -211,15 +219,15 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.14.1) + i18n (1.14.5) concurrent-ruby (~> 1.0) - jazzy (0.14.2) + jazzy (0.15.1) cocoapods (~> 1.5) mustache (~> 1.1) open4 (~> 1.3) redcarpet (~> 3.4) - rexml (~> 3.2) - rouge (>= 2.0.6, < 4.0) + rexml (>= 3.2.7, < 4.0) + rouge (>= 2.0.6, < 5.0) sassc (~> 2.1) sqlite3 (~> 1.3) xcinvoke (~> 0.3.0) @@ -230,12 +238,13 @@ GEM memoist (0.16.2) mini_magick (4.12.0) mini_mime (1.1.2) - mini_portile2 (2.8.1) - minitest (5.19.0) + mini_portile2 (2.8.7) + minitest (5.25.1) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.0.0) mustache (1.1.1) + mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) naturally (2.2.1) @@ -253,7 +262,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.4) + rexml (3.3.5) strscan rouge (2.0.7) ruby-macho (2.5.1) @@ -270,7 +279,7 @@ GEM simctl (1.6.10) CFPropertyList naturally - sqlite3 (1.6.0) + sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) strscan (3.1.0) terminal-notifier (2.0.0) @@ -281,7 +290,7 @@ GEM tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) - typhoeus (1.4.0) + typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -312,7 +321,7 @@ PLATFORMS DEPENDENCIES fastlane (= 2.205.1) fastlane-plugin-release_actions! - jazzy (= 0.14.2) + jazzy (= 0.15.1) xcpretty (= 0.3.0) BUNDLED WITH From 5d54b5d156cec85d529d131e4ca600f79a0717dc Mon Sep 17 00:00:00 2001 From: Abhash Kumar Singh Date: Mon, 19 Aug 2024 11:28:31 -0700 Subject: [PATCH 03/50] feat(predictions): add web socket retry for clock skew (#3816) * feat(predictions): add web socket retry for clock skew * address review comments --- .../AWSTranscribeStreamingAdapter.swift | 10 +-- .../Service/FaceLivenessSession.swift | 63 +++++++++++++----- .../Liveness/Service/WebSocketSession.swift | 66 +++++++++++++++---- 3 files changed, 105 insertions(+), 34 deletions(-) diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift index ec0cbeb219..5c70b79948 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift @@ -131,17 +131,17 @@ class AWSTranscribeStreamingAdapter: AWSTranscribeStreamingBehavior { continuation.yield(transcribedPayload) let isPartial = transcribedPayload.transcript?.results?.map(\.isPartial) ?? [] let shouldContinue = isPartial.allSatisfy { $0 } - return shouldContinue + return shouldContinue ? .continueToReceive : .stopAndInvalidateSession } catch { - return true + return .continueToReceive } case .success(.string): - return true + return .continueToReceive case .failure(let error): continuation.finish(throwing: error) - return false + return .stopAndInvalidateSession @unknown default: - return true + return .continueToReceive } } } diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/FaceLivenessSession.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/FaceLivenessSession.swift index ffc2a9abac..092532d8e3 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/FaceLivenessSession.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/FaceLivenessSession.swift @@ -17,6 +17,14 @@ public final class FaceLivenessSession: LivenessService { let baseURL: URL var serverEventListeners: [LivenessEventKind.Server: (FaceLivenessSession.SessionConfiguration) -> Void] = [:] var onComplete: (ServerDisconnection) -> Void = { _ in } + var serverDate: Date? + var savedURLForReconnect: URL? + var connectingState: ConnectingState = .normal + + enum ConnectingState { + case normal + case reconnect + } private let livenessServiceDispatchQueue = DispatchQueue( label: "com.amazon.aws.amplify.liveness.service", @@ -35,12 +43,16 @@ public final class FaceLivenessSession: LivenessService { self.websocket = websocket websocket.onMessageReceived { [weak self] result in - self?.receive(result: result) ?? false + self?.receive(result: result) ?? .stopAndInvalidateSession } websocket.onSocketClosed { [weak self] closeCode in self?.onComplete(.unexpectedClosure(closeCode)) } + + websocket.onServerDateReceived { [weak self] serverDate in + self?.serverDate = serverDate + } } public var onServiceException: (FaceLivenessSessionError) -> Void = { _ in } @@ -75,6 +87,7 @@ public final class FaceLivenessSession: LivenessService { guard let url = components?.url else { throw FaceLivenessSessionError.invalidURL } + savedURLForReconnect = url let signedConnectionURL = signer.sign(url: url) websocket.open(url: signedConnectionURL) } @@ -93,17 +106,22 @@ public final class FaceLivenessSession: LivenessService { ] ) - let eventDate = eventDate() + let dateForSigning: Date + if let serverDate = serverDate { + dateForSigning = serverDate + } else { + dateForSigning = eventDate() + } let signedPayload = self.signer.signWithPreviousSignature( payload: encodedPayload, - dateHeader: (key: ":date", value: eventDate) + dateHeader: (key: ":date", value: dateForSigning) ) let encodedEvent = self.eventStreamEncoder.encode( payload: encodedPayload, headers: [ - ":date": .timestamp(eventDate), + ":date": .timestamp(dateForSigning), ":chunk-signature": .data(signedPayload) ] ) @@ -115,7 +133,7 @@ public final class FaceLivenessSession: LivenessService { } } - private func fallbackDecoding(_ message: EventStream.Message) -> Bool { + private func fallbackDecoding(_ message: EventStream.Message) -> WebSocketSession.WebSocketMessageResult { // We only care about two events above. // Just in case the header value changes (it shouldn't) // We'll try to decode each of these events @@ -124,12 +142,12 @@ public final class FaceLivenessSession: LivenessService { self.serverEventListeners[.challenge]?(sessionConfiguration) } else if (try? JSONDecoder().decode(DisconnectEvent.self, from: message.payload)) != nil { onComplete(.disconnectionEvent) - return false + return .stopAndInvalidateSession } - return true + return .continueToReceive } - private func receive(result: Result) -> Bool { + private func receive(result: Result) -> WebSocketSession.WebSocketMessageResult { switch result { case .success(.data(let data)): do { @@ -145,28 +163,41 @@ public final class FaceLivenessSession: LivenessService { ) let sessionConfiguration = sessionConfiguration(from: payload) serverEventListeners[.challenge]?(sessionConfiguration) - return true + return .continueToReceive case .disconnect: // :event-type DisconnectionEvent onComplete(.disconnectionEvent) - return false + return .stopAndInvalidateSession default: - return true + return .continueToReceive } } else if let exceptionType = message.headers.first(where: { $0.name == ":exception-type" }) { let exceptionEvent = LivenessEventKind.Exception(rawValue: exceptionType.value) - onServiceException(.init(event: exceptionEvent)) - return false + Amplify.log.verbose("\(#function): Received exception: \(exceptionEvent)") + guard exceptionEvent == .invalidSignature, + connectingState == .normal, + let savedURLForReconnect = savedURLForReconnect, + let serverDate = serverDate else { + onServiceException(.init(event: exceptionEvent)) + return .stopAndInvalidateSession + } + + connectingState = .reconnect + let signedConnectionURL = signer.sign( + url: savedURLForReconnect, + date: { serverDate } + ) + return .invalidateSessionAndRetry(url: signedConnectionURL) } else { return fallbackDecoding(message) } } catch { - return false + return .stopAndInvalidateSession } case .success: - return true + return .continueToReceive case .failure: - return false + return .stopAndInvalidateSession } } } diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/WebSocketSession.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/WebSocketSession.swift index c0c96f3c17..f900f3dfc3 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/WebSocketSession.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Service/WebSocketSession.swift @@ -6,13 +6,15 @@ // import Foundation +import Amplify final class WebSocketSession { private let urlSessionWebSocketDelegate: Delegate private let session: URLSession private var task: URLSessionWebSocketTask? - private var receiveMessage: ((Result) -> Bool)? + private var receiveMessage: ((Result) -> WebSocketMessageResult)? private var onSocketClosed: ((URLSessionWebSocketTask.CloseCode) -> Void)? + private var onServerDateReceived: ((Date?) -> Void)? init() { self.urlSessionWebSocketDelegate = Delegate() @@ -23,7 +25,7 @@ final class WebSocketSession { ) } - func onMessageReceived(_ receive: @escaping (Result) -> Bool) { + func onMessageReceived(_ receive: @escaping (Result) -> WebSocketMessageResult) { self.receiveMessage = receive } @@ -34,25 +36,32 @@ final class WebSocketSession { func onSocketOpened(_ onOpen: @escaping () -> Void) { urlSessionWebSocketDelegate.onOpen = onOpen } + + func onServerDateReceived(_ onServerDateReceived: @escaping (Date?) -> Void) { + urlSessionWebSocketDelegate.onServerDateReceived = onServerDateReceived + } - func receive(shouldContinue: Bool) { - guard shouldContinue else { + func receive(result: WebSocketMessageResult) { + switch result { + case .continueToReceive: + task?.receive(completionHandler: { [weak self] result in + if let webSocketResult = self?.receiveMessage?(result) { + self?.receive(result: webSocketResult) + } + }) + case .stopAndInvalidateSession: + session.finishTasksAndInvalidate() + case .invalidateSessionAndRetry(let url): session.finishTasksAndInvalidate() - return + open(url: url) } - - task?.receive(completionHandler: { [weak self] result in - if let shouldContinue = self?.receiveMessage?(result) { - self?.receive(shouldContinue: shouldContinue) - } - }) } func open(url: URL) { var request = URLRequest(url: url) request.setValue("no-store", forHTTPHeaderField: "Cache-Control") task = session.webSocketTask(with: request) - receive(shouldContinue: true) + receive(result: .continueToReceive) task?.resume() } @@ -77,10 +86,12 @@ final class WebSocketSession { ) } - final class Delegate: NSObject, URLSessionWebSocketDelegate { + final class Delegate: NSObject, URLSessionWebSocketDelegate, URLSessionTaskDelegate { var onClose: (URLSessionWebSocketTask.CloseCode) -> Void = { _ in } var onOpen: () -> Void = {} + var onServerDateReceived: (Date?) -> Void = { _ in } + // MARK: - URLSessionWebSocketDelegate methods func urlSession( _ session: URLSession, webSocketTask: URLSessionWebSocketTask, @@ -97,5 +108,34 @@ final class WebSocketSession { ) { onClose(closeCode) } + + // MARK: - URLSessionTaskDelegate methods + func urlSession(_ session: URLSession, + task: URLSessionTask, + didFinishCollecting metrics: URLSessionTaskMetrics + ) { + guard let httpResponse = metrics.transactionMetrics.first?.response as? HTTPURLResponse, + let dateString = httpResponse.value(forHTTPHeaderField: "Date") else { + Amplify.log.verbose("\(#function): Couldn't find Date header in URLSession metrics") + onServerDateReceived(nil) + return + } + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss z" + guard let serverDate = dateFormatter.date(from: dateString) else { + Amplify.log.verbose("\(#function): Error parsing Date header in expected format") + onServerDateReceived(nil) + return + } + + onServerDateReceived(serverDate) + } + } + + enum WebSocketMessageResult { + case continueToReceive + case stopAndInvalidateSession + case invalidateSessionAndRetry(url: URL) } } From 1e8f77a36b48d1c4e73776b9e218a913042c9f00 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Mon, 19 Aug 2024 20:44:40 +0000 Subject: [PATCH 04/50] chore: release 2.38.0 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 2d3a77f173..dec4e0be94 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.37.0" + public static let amplifyVersion = "2.38.0" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 79d062d354bb190666774e8ef3c83ad52f023889 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Mon, 19 Aug 2024 20:46:14 +0000 Subject: [PATCH 05/50] chore: finalize release 2.38.0 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef5b8d73a..3c3eb78a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.38.0 (2024-08-19) + +### Features + +- **predictions**: add web socket retry for clock skew (#3816) + ## 2.37.0 (2024-08-12) ### Features From bdfa37ad47ccb529ad468eabe9ad258248224d0a Mon Sep 17 00:00:00 2001 From: Harsh <6162866+harsh62@users.noreply.github.com> Date: Wed, 21 Aug 2024 11:28:36 -0400 Subject: [PATCH 06/50] chore(auth): Add logging to non throwing tasks (#3791) * fix(Auth): Add logging to non throwing tasks * update logs * update * added documentation * Update API dumps for new version --------- Co-authored-by: aws-amplify-ops --- .../Task/AWSAuthChangePasswordTask.swift | 1 - ...uthClearFederationToIdentityPoolTask.swift | 1 - .../AWSAuthConfirmResetPasswordTask.swift | 1 - .../Task/AWSAuthConfirmSignInTask.swift | 1 - .../Task/AWSAuthConfirmSignUpTask.swift | 1 - .../Task/AWSAuthDeleteUserTask.swift | 1 - .../AWSAuthFederateToIdentityPoolTask.swift | 1 - .../Task/AWSAuthFetchSessionTask.swift | 1 - .../Task/AWSAuthResendSignUpCodeTask.swift | 1 - .../Task/AWSAuthResetPasswordTask.swift | 1 - .../Task/AWSAuthSignInTask.swift | 1 - .../Task/AWSAuthSignOutTask.swift | 1 - .../Task/AWSAuthSignUpTask.swift | 1 - .../Task/AWSAuthWebUISignInTask.swift | 1 - .../AmplifyAuthTaskNonThrowing.swift | 4 +- .../Auth/AuthAWSCredentialsProvider.swift | 36 +++++- .../Keychain/KeychainStore.swift | 8 +- api-dump/AWSDataStorePlugin.json | 2 +- api-dump/AWSPluginsCore.json | 122 +++++++++--------- api-dump/Amplify.json | 2 +- api-dump/CoreMLPredictionsPlugin.json | 2 +- 21 files changed, 102 insertions(+), 88 deletions(-) diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthChangePasswordTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthChangePasswordTask.swift index 0fb50718c1..84921606c9 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthChangePasswordTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthChangePasswordTask.swift @@ -33,7 +33,6 @@ class AWSAuthChangePasswordTask: AuthChangePasswordTask, DefaultLogger { } func execute() async throws { - log.verbose("Starting execution") do { await taskHelper.didStateMachineConfigured() let accessToken = try await taskHelper.getAccessToken() diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthClearFederationToIdentityPoolTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthClearFederationToIdentityPoolTask.swift index 293ae92b2f..c42bbc3ff1 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthClearFederationToIdentityPoolTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthClearFederationToIdentityPoolTask.swift @@ -35,7 +35,6 @@ public class AWSAuthClearFederationToIdentityPoolTask: AuthClearFederationToIden } public func execute() async throws { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() try await clearFederationHelper.clearFederation(authStateMachine) log.verbose("Cleared federation") diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmResetPasswordTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmResetPasswordTask.swift index ceabc14ed2..ffa1750e4a 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmResetPasswordTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmResetPasswordTask.swift @@ -27,7 +27,6 @@ class AWSAuthConfirmResetPasswordTask: AuthConfirmResetPasswordTask, DefaultLogg } func execute() async throws { - log.verbose("Starting execution") if let validationError = request.hasError() { throw validationError } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignInTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignInTask.swift index 3c19a16d61..cc824c6f25 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignInTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignInTask.swift @@ -29,7 +29,6 @@ class AWSAuthConfirmSignInTask: AuthConfirmSignInTask, DefaultLogger { } func execute() async throws -> AuthSignInResult { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() // Check if we have a user pool configuration diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignUpTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignUpTask.swift index dbbb086118..a7b7124245 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignUpTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthConfirmSignUpTask.swift @@ -24,7 +24,6 @@ class AWSAuthConfirmSignUpTask: AuthConfirmSignUpTask, DefaultLogger { } func execute() async throws -> AuthSignUpResult { - log.verbose("Starting execution") try request.hasError() let userPoolEnvironment = authEnvironment.userPoolEnvironment do { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthDeleteUserTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthDeleteUserTask.swift index 4539bcecba..2a0fbf7323 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthDeleteUserTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthDeleteUserTask.swift @@ -26,7 +26,6 @@ class AWSAuthDeleteUserTask: AuthDeleteUserTask, DefaultLogger { } func execute() async throws { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() let accessToken = try await taskHelper.getAccessToken() diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFederateToIdentityPoolTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFederateToIdentityPoolTask.swift index 9f3151a63f..1770b532c9 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFederateToIdentityPoolTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFederateToIdentityPoolTask.swift @@ -34,7 +34,6 @@ public class AWSAuthFederateToIdentityPoolTask: AuthFederateToIdentityPoolTask, } public func execute() async throws -> FederateToIdentityPoolResult { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() let state = await authStateMachine.currentState guard case .configured(let authNState, let authZState) = state else { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFetchSessionTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFetchSessionTask.swift index 7a00941ad9..f39263578d 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFetchSessionTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFetchSessionTask.swift @@ -26,7 +26,6 @@ class AWSAuthFetchSessionTask: AuthFetchSessionTask, DefaultLogger { } func execute() async throws -> AuthSession { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() let doesNeedForceRefresh = request.options.forceRefresh return try await fetchAuthSessionHelper.fetch(authStateMachine, diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResendSignUpCodeTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResendSignUpCodeTask.swift index 527b796585..ff81fc2a56 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResendSignUpCodeTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResendSignUpCodeTask.swift @@ -27,7 +27,6 @@ class AWSAuthResendSignUpCodeTask: AuthResendSignUpCodeTask, DefaultLogger { } func execute() async throws -> AuthCodeDeliveryDetails { - log.verbose("Starting execution") if let validationError = request.hasError() { throw validationError } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResetPasswordTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResetPasswordTask.swift index 823b0178e1..7f86e40f58 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResetPasswordTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthResetPasswordTask.swift @@ -28,7 +28,6 @@ class AWSAuthResetPasswordTask: AuthResetPasswordTask, DefaultLogger { } func execute() async throws -> AuthResetPasswordResult { - log.verbose("Starting execution") if let validationError = request.hasError() { throw validationError } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignInTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignInTask.swift index 3a68e3aeff..abfe5ec41b 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignInTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignInTask.swift @@ -29,7 +29,6 @@ class AWSAuthSignInTask: AuthSignInTask, DefaultLogger { } func execute() async throws -> AuthSignInResult { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() // Check if we have a user pool configuration guard let userPoolConfiguration = authConfiguration.getUserPoolConfiguration() else { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift index f5de608577..5363d3ffdf 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift @@ -25,7 +25,6 @@ class AWSAuthSignOutTask: AuthSignOutTask, DefaultLogger { } func execute() async -> AuthSignOutResult { - log.verbose("Starting execution") await taskHelper.didStateMachineConfigured() guard case .configured(let authNState, _) = await authStateMachine.currentState else { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignUpTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignUpTask.swift index 9e86b68daf..63194bcea9 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignUpTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignUpTask.swift @@ -25,7 +25,6 @@ class AWSAuthSignUpTask: AuthSignUpTask, DefaultLogger { } func execute() async throws -> AuthSignUpResult { - log.verbose("Starting execution") let userPoolEnvironment = authEnvironment.userPoolEnvironment try request.hasError() diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift index 564b5c177e..6e35e317ab 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift @@ -33,7 +33,6 @@ class AWSAuthWebUISignInTask: AuthWebUISignInTask, DefaultLogger { } func execute() async throws -> AuthSignInResult { - log.verbose("Starting execution") do { await taskHelper.didStateMachineConfigured() let result = try await helper.initiateSignIn() diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AmplifyAuthTaskNonThrowing.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AmplifyAuthTaskNonThrowing.swift index aecfd2cd7e..9377e8707c 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AmplifyAuthTaskNonThrowing.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AmplifyAuthTaskNonThrowing.swift @@ -23,10 +23,12 @@ protocol AmplifyAuthTaskNonThrowing { } -extension AmplifyAuthTaskNonThrowing { +extension AmplifyAuthTaskNonThrowing where Self: DefaultLogger { var value: Success { get async { + log.info("Starting execution for \(eventName)") let valueReturned = await execute() + log.info("Successfully completed execution for \(eventName) with result:\n\(valueReturned)") dispatch(result: valueReturned) return valueReturned } diff --git a/AmplifyPlugins/Core/AWSPluginsCore/Auth/AuthAWSCredentialsProvider.swift b/AmplifyPlugins/Core/AWSPluginsCore/Auth/AuthAWSCredentialsProvider.swift index c08e8821c8..9739e1066f 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/Auth/AuthAWSCredentialsProvider.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/Auth/AuthAWSCredentialsProvider.swift @@ -35,16 +35,42 @@ public protocol AWSCredentialsProvider { func fetchAWSCredentials() async throws -> AWSCredentials } -public protocol AWSTemporaryCredentials: AWSCredentials { - - var sessionToken: String { get } +/** + Represents AWS credentials. - var expiration: Date { get } -} + Typically refers to long-term credentials that do not expire unless manually rotated or deactivated. + These credentials are generally associated with an IAM (Identity and Access Management) user and are used to authenticate API requests to AWS services. + - Properties: + - accessKeyId: A unique identifier. + - secretAccessKey: A secret key used to sign requests cryptographically. + */ public protocol AWSCredentials { + /// A unique identifier. var accessKeyId: String { get } + /// A secret key used to sign requests cryptographically. var secretAccessKey: String { get } } + +/** + Represents temporary AWS credentials. + + Refers to short-term credentials generated by AWS STS (Security Token Service). + These credentials are used for temporary access, often for applications, temporary roles, federated users, or scenarios requiring limited-time access. + + - Inherits: AWSCredentials + + - Properties: + - sessionToken: A token that is required when using temporary security credentials to sign requests. + - expiration: The expiration date and time of the temporary credentials. + */ +public protocol AWSTemporaryCredentials: AWSCredentials { + + /// A token that is required when using temporary security credentials to sign requests. + var sessionToken: String { get } + + /// The expiration date and time of the temporary credentials. + var expiration: Date { get } +} diff --git a/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift b/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift index 6327676c83..5734a79491 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift @@ -92,7 +92,7 @@ public struct KeychainStore: KeychainStoreBehavior { log.error("[KeychainStore] Unable to create String from Data retrieved") throw KeychainStoreError.conversionError("Unable to create String from Data retrieved") } - log.verbose("[KeychainStore] Successfully retrieved string from the store") + log.verbose("[KeychainStore] Successfully retrieved `String` from the store") return string } @@ -175,7 +175,7 @@ public struct KeychainStore: KeychainStoreBehavior { log.error("[KeychainStore] Error updating item to keychain with status=\(updateStatus)") throw KeychainStoreError.securityError(updateStatus) } - log.verbose("[KeychainStore] Successfully updated `String` in keychain for key=\(key)") + log.verbose("[KeychainStore] Successfully updated `Data` in keychain for key=\(key)") #endif case errSecItemNotFound: log.verbose("[KeychainStore] Unable to find an existing item, creating new item") @@ -188,7 +188,7 @@ public struct KeychainStore: KeychainStoreBehavior { log.error("[KeychainStore] Error adding item to keychain with status=\(addStatus)") throw KeychainStoreError.securityError(addStatus) } - log.verbose("[KeychainStore] Successfully added `String` in keychain for key=\(key)") + log.verbose("[KeychainStore] Successfully added `Data` in keychain for key=\(key)") default: log.error("[KeychainStore] Error occurred while retrieving data from keychain when deciding to update or add with status=\(fetchStatus)") throw KeychainStoreError.securityError(fetchStatus) @@ -206,7 +206,7 @@ public struct KeychainStore: KeychainStoreBehavior { let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { - log.error("[KeychainStore] Error removing itms from keychain with status=\(status)") + log.error("[KeychainStore] Error removing items from keychain with status=\(status)") throw KeychainStoreError.securityError(status) } log.verbose("[KeychainStore] Successfully removed item from keychain") diff --git a/api-dump/AWSDataStorePlugin.json b/api-dump/AWSDataStorePlugin.json index 47c0e792de..d436f34410 100644 --- a/api-dump/AWSDataStorePlugin.json +++ b/api-dump/AWSDataStorePlugin.json @@ -8205,7 +8205,7 @@ "-module", "AWSDataStorePlugin", "-o", - "\/var\/folders\/hn\/5bx1f4_d4ds5vhwhkxc7vdcr0000gn\/T\/tmp.nAGRifhwH6\/AWSDataStorePlugin.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/AWSDataStorePlugin.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/AWSPluginsCore.json b/api-dump/AWSPluginsCore.json index 19b8e2fb57..76cf8a6e6c 100644 --- a/api-dump/AWSPluginsCore.json +++ b/api-dump/AWSPluginsCore.json @@ -3644,13 +3644,13 @@ }, { "kind": "TypeDecl", - "name": "AWSTemporaryCredentials", - "printedName": "AWSTemporaryCredentials", + "name": "AWSCredentials", + "printedName": "AWSCredentials", "children": [ { "kind": "Var", - "name": "sessionToken", - "printedName": "sessionToken", + "name": "accessKeyId", + "printedName": "accessKeyId", "children": [ { "kind": "TypeNominal", @@ -3660,8 +3660,8 @@ } ], "declKind": "Var", - "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvp", - "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvp", + "usr": "s:14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvp", + "mangledName": "$s14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvp", "moduleName": "AWSPluginsCore", "protocolReq": true, "accessors": [ @@ -3678,10 +3678,10 @@ } ], "declKind": "Accessor", - "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvg", - "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvg", + "usr": "s:14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvg", + "mangledName": "$s14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvg", "moduleName": "AWSPluginsCore", - "genericSig": "", + "genericSig": "", "protocolReq": true, "reqNewWitnessTableEntry": true, "accessorKind": "get" @@ -3690,19 +3690,19 @@ }, { "kind": "Var", - "name": "expiration", - "printedName": "expiration", + "name": "secretAccessKey", + "printedName": "secretAccessKey", "children": [ { "kind": "TypeNominal", - "name": "Date", - "printedName": "Foundation.Date", - "usr": "s:10Foundation4DateV" + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" } ], "declKind": "Var", - "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvp", - "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvp", + "usr": "s:14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvp", + "mangledName": "$s14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvp", "moduleName": "AWSPluginsCore", "protocolReq": true, "accessors": [ @@ -3713,16 +3713,16 @@ "children": [ { "kind": "TypeNominal", - "name": "Date", - "printedName": "Foundation.Date", - "usr": "s:10Foundation4DateV" + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" } ], "declKind": "Accessor", - "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvg", - "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvg", + "usr": "s:14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvg", + "mangledName": "$s14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvg", "moduleName": "AWSPluginsCore", - "genericSig": "", + "genericSig": "", "protocolReq": true, "reqNewWitnessTableEntry": true, "accessorKind": "get" @@ -3731,29 +3731,19 @@ } ], "declKind": "Protocol", - "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP", - "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP", - "moduleName": "AWSPluginsCore", - "genericSig": "", - "conformances": [ - { - "kind": "Conformance", - "name": "AWSCredentials", - "printedName": "AWSCredentials", - "usr": "s:14AWSPluginsCore14AWSCredentialsP", - "mangledName": "$s14AWSPluginsCore14AWSCredentialsP" - } - ] + "usr": "s:14AWSPluginsCore14AWSCredentialsP", + "mangledName": "$s14AWSPluginsCore14AWSCredentialsP", + "moduleName": "AWSPluginsCore" }, { "kind": "TypeDecl", - "name": "AWSCredentials", - "printedName": "AWSCredentials", + "name": "AWSTemporaryCredentials", + "printedName": "AWSTemporaryCredentials", "children": [ { "kind": "Var", - "name": "accessKeyId", - "printedName": "accessKeyId", + "name": "sessionToken", + "printedName": "sessionToken", "children": [ { "kind": "TypeNominal", @@ -3763,8 +3753,8 @@ } ], "declKind": "Var", - "usr": "s:14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvp", - "mangledName": "$s14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvp", + "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvp", + "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvp", "moduleName": "AWSPluginsCore", "protocolReq": true, "accessors": [ @@ -3781,10 +3771,10 @@ } ], "declKind": "Accessor", - "usr": "s:14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvg", - "mangledName": "$s14AWSPluginsCore14AWSCredentialsP11accessKeyIdSSvg", + "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvg", + "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP12sessionTokenSSvg", "moduleName": "AWSPluginsCore", - "genericSig": "", + "genericSig": "", "protocolReq": true, "reqNewWitnessTableEntry": true, "accessorKind": "get" @@ -3793,19 +3783,19 @@ }, { "kind": "Var", - "name": "secretAccessKey", - "printedName": "secretAccessKey", + "name": "expiration", + "printedName": "expiration", "children": [ { "kind": "TypeNominal", - "name": "String", - "printedName": "Swift.String", - "usr": "s:SS" + "name": "Date", + "printedName": "Foundation.Date", + "usr": "s:10Foundation4DateV" } ], "declKind": "Var", - "usr": "s:14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvp", - "mangledName": "$s14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvp", + "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvp", + "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvp", "moduleName": "AWSPluginsCore", "protocolReq": true, "accessors": [ @@ -3816,16 +3806,16 @@ "children": [ { "kind": "TypeNominal", - "name": "String", - "printedName": "Swift.String", - "usr": "s:SS" + "name": "Date", + "printedName": "Foundation.Date", + "usr": "s:10Foundation4DateV" } ], "declKind": "Accessor", - "usr": "s:14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvg", - "mangledName": "$s14AWSPluginsCore14AWSCredentialsP15secretAccessKeySSvg", + "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvg", + "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP10expiration10Foundation4DateVvg", "moduleName": "AWSPluginsCore", - "genericSig": "", + "genericSig": "", "protocolReq": true, "reqNewWitnessTableEntry": true, "accessorKind": "get" @@ -3834,9 +3824,19 @@ } ], "declKind": "Protocol", - "usr": "s:14AWSPluginsCore14AWSCredentialsP", - "mangledName": "$s14AWSPluginsCore14AWSCredentialsP", - "moduleName": "AWSPluginsCore" + "usr": "s:14AWSPluginsCore23AWSTemporaryCredentialsP", + "mangledName": "$s14AWSPluginsCore23AWSTemporaryCredentialsP", + "moduleName": "AWSPluginsCore", + "genericSig": "", + "conformances": [ + { + "kind": "Conformance", + "name": "AWSCredentials", + "printedName": "AWSCredentials", + "usr": "s:14AWSPluginsCore14AWSCredentialsP", + "mangledName": "$s14AWSPluginsCore14AWSCredentialsP" + } + ] }, { "kind": "TypeDecl", @@ -24273,7 +24273,7 @@ "-module", "AWSPluginsCore", "-o", - "\/var\/folders\/hn\/5bx1f4_d4ds5vhwhkxc7vdcr0000gn\/T\/tmp.nAGRifhwH6\/AWSPluginsCore.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/AWSPluginsCore.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/Amplify.json b/api-dump/Amplify.json index 1bb4d36d2c..c8b9c833c4 100644 --- a/api-dump/Amplify.json +++ b/api-dump/Amplify.json @@ -179735,7 +179735,7 @@ "-module", "Amplify", "-o", - "\/var\/folders\/hn\/5bx1f4_d4ds5vhwhkxc7vdcr0000gn\/T\/tmp.nAGRifhwH6\/Amplify.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/Amplify.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/CoreMLPredictionsPlugin.json b/api-dump/CoreMLPredictionsPlugin.json index 1a9879407f..f7aeb94b9c 100644 --- a/api-dump/CoreMLPredictionsPlugin.json +++ b/api-dump/CoreMLPredictionsPlugin.json @@ -430,7 +430,7 @@ "-module", "CoreMLPredictionsPlugin", "-o", - "\/var\/folders\/hn\/5bx1f4_d4ds5vhwhkxc7vdcr0000gn\/T\/tmp.nAGRifhwH6\/CoreMLPredictionsPlugin.json", + "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/CoreMLPredictionsPlugin.json", "-I", ".build\/debug", "-sdk-version", From 673a075c78455521406d2e8b1c86990df4adcf49 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:35:35 -0400 Subject: [PATCH 07/50] feat: Add AppSync components (#3825) * add Amplify components for AppSync * Add AWSAppSyncConfigurationTests * Add signing tests * remove unnecessary public apis * add doc comments * Update API dumps for new version --------- Co-authored-by: aws-amplify-ops --- .../Configuration/AmplifyOutputsData.swift | 3 +- .../AWSCognitoAuthPlugin+AppSyncSigner.swift | 140 +++++++++++++ ...SCognitoAuthPluginAppSyncSignerTests.swift | 40 ++++ .../AuthHostApp.xcodeproj/project.pbxproj | 12 ++ .../AppSyncSignerTests.swift | 36 ++++ .../API/AWSAppSyncConfiguration.swift | 46 +++++ .../API/AWSAppSyncConfigurationTests.swift | 31 +++ .../AmplifyAWSCredentialsProvider.swift | 2 +- api-dump/AWSDataStorePlugin.json | 2 +- api-dump/AWSPluginsCore.json | 192 +++++++++++++++++- api-dump/Amplify.json | 76 ++++++- api-dump/CoreMLPredictionsPlugin.json | 2 +- 12 files changed, 576 insertions(+), 6 deletions(-) create mode 100644 AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift create mode 100644 AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift create mode 100644 AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift create mode 100644 AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift create mode 100644 AmplifyPlugins/Core/AWSPluginsCoreTests/API/AWSAppSyncConfigurationTests.swift diff --git a/Amplify/Core/Configuration/AmplifyOutputsData.swift b/Amplify/Core/Configuration/AmplifyOutputsData.swift index 93a2f60e6a..57e388a3ba 100644 --- a/Amplify/Core/Configuration/AmplifyOutputsData.swift +++ b/Amplify/Core/Configuration/AmplifyOutputsData.swift @@ -251,7 +251,8 @@ public struct AmplifyOutputsData: Codable { public struct AmplifyOutputs { /// A closure that resolves the `AmplifyOutputsData` configuration - let resolveConfiguration: () throws -> AmplifyOutputsData + @_spi(InternalAmplifyConfiguration) + public let resolveConfiguration: () throws -> AmplifyOutputsData /// Resolves configuration with `amplify_outputs.json` in the main bundle. public static let amplifyOutputs: AmplifyOutputs = { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift new file mode 100644 index 0000000000..b77c3ad92e --- /dev/null +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift @@ -0,0 +1,140 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +import Amplify // Amplify.Auth +import AWSPluginsCore // AuthAWSCredentialsProvider +import AWSClientRuntime // AWSClientRuntime.CredentialsProviding +import ClientRuntime // SdkHttpRequestBuilder +import AwsCommonRuntimeKit // CommonRuntimeKit.initialize() + +extension AWSCognitoAuthPlugin { + + + /// Creates a AWS IAM SigV4 signer capable of signing AWS AppSync requests. + /// + /// **Note**. Although this method is static, **Amplify.Auth** is required to be configured with **AWSCognitoAuthPlugin** as + /// it depends on the credentials provider from Cognito through `Amplify.Auth.fetchAuthSession()`. The static type allows + /// developers to simplify their callsite without having to access the method on the plugin instance. + /// + /// - Parameter region: The region of the AWS AppSync API + /// - Returns: A closure that takes in a requestand returns a signed request. + public static func createAppSyncSigner(region: String) -> ((URLRequest) async throws -> URLRequest) { + return { request in + try await signAppSyncRequest(request, + region: region) + } + } + + static func signAppSyncRequest(_ urlRequest: URLRequest, + region: Swift.String, + signingName: Swift.String = "appsync", + date: ClientRuntime.Date = Date()) async throws -> URLRequest { + CommonRuntimeKit.initialize() + + // Convert URLRequest to SDK's HTTPRequest + guard let requestBuilder = try createAppSyncSdkHttpRequestBuilder( + urlRequest: urlRequest) else { + return urlRequest + } + + // Retrieve the credentials from credentials provider + let credentials: AWSClientRuntime.AWSCredentials + let authSession = try await Amplify.Auth.fetchAuthSession() + if let awsCredentialsProvider = authSession as? AuthAWSCredentialsProvider { + let awsCredentials = try awsCredentialsProvider.getAWSCredentials().get() + credentials = awsCredentials.toAWSSDKCredentials() + } else { + let error = AuthError.unknown("Auth session does not include AWS credentials information") + throw error + } + + // Prepare signing + let flags = SigningFlags(useDoubleURIEncode: true, + shouldNormalizeURIPath: true, + omitSessionToken: false) + let signedBodyHeader: AWSSignedBodyHeader = .none + let signedBodyValue: AWSSignedBodyValue = .empty + let signingConfig = AWSSigningConfig(credentials: credentials, + signedBodyHeader: signedBodyHeader, + signedBodyValue: signedBodyValue, + flags: flags, + date: date, + service: signingName, + region: region, + signatureType: .requestHeaders, + signingAlgorithm: .sigv4) + + // Sign request + guard let httpRequest = await AWSSigV4Signer.sigV4SignedRequest( + requestBuilder: requestBuilder, + + signingConfig: signingConfig + ) else { + return urlRequest + } + + // Update original request with new headers + return setHeaders(from: httpRequest, to: urlRequest) + } + + static func setHeaders(from sdkRequest: SdkHttpRequest, to urlRequest: URLRequest) -> URLRequest { + var urlRequest = urlRequest + for header in sdkRequest.headers.headers { + urlRequest.setValue(header.value.joined(separator: ","), forHTTPHeaderField: header.name) + } + return urlRequest + } + + static func createAppSyncSdkHttpRequestBuilder(urlRequest: URLRequest) throws -> SdkHttpRequestBuilder? { + + guard let url = urlRequest.url, + let host = url.host else { + return nil + } + + var headers = urlRequest.allHTTPHeaderFields ?? [:] + headers.updateValue(host, forKey: "host") + + let httpMethod = (urlRequest.httpMethod?.uppercased()) + .flatMap(HttpMethodType.init(rawValue:)) ?? .get + + let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems? + .map { ClientRuntime.SDKURLQueryItem(name: $0.name, value: $0.value)} ?? [] + + let requestBuilder = SdkHttpRequestBuilder() + .withHost(host) + .withPath(url.path) + .withQueryItems(queryItems) + .withMethod(httpMethod) + .withPort(443) + .withProtocol(.https) + .withHeaders(.init(headers)) + .withBody(.data(urlRequest.httpBody)) + + return requestBuilder + } +} + +extension AWSPluginsCore.AWSCredentials { + + func toAWSSDKCredentials() -> AWSClientRuntime.AWSCredentials { + if let tempCredentials = self as? AWSTemporaryCredentials { + return AWSClientRuntime.AWSCredentials( + accessKey: tempCredentials.accessKeyId, + secret: tempCredentials.secretAccessKey, + expirationTimeout: tempCredentials.expiration, + sessionToken: tempCredentials.sessionToken) + } else { + return AWSClientRuntime.AWSCredentials( + accessKey: accessKeyId, + secret: secretAccessKey, + expirationTimeout: Date()) + } + + } +} diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift new file mode 100644 index 0000000000..609fe1c774 --- /dev/null +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift @@ -0,0 +1,40 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import Amplify +@testable import AWSCognitoAuthPlugin + +class AWSCognitoAuthPluginAppSyncSignerTests: XCTestCase { + + /// Tests translating the URLRequest to the SDKRequest + /// The translation should account for expected fields, as asserted in the test. + func testCreateAppSyncSdkHttpRequestBuilder() throws { + var urlRequest = URLRequest(url: URL(string: "http://graphql.com")!) + urlRequest.httpMethod = "post" + let dataObject = Data() + urlRequest.httpBody = dataObject + guard let sdkRequestBuilder = try AWSCognitoAuthPlugin.createAppSyncSdkHttpRequestBuilder(urlRequest: urlRequest) else { + XCTFail("Could not create SDK request") + return + } + + let request = sdkRequestBuilder.build() + XCTAssertEqual(request.host, "graphql.com") + XCTAssertEqual(request.path, "") + XCTAssertEqual(request.queryItems, []) + XCTAssertEqual(request.method, .post) + XCTAssertEqual(request.endpoint.port, 443) + XCTAssertEqual(request.endpoint.protocolType, .https) + XCTAssertEqual(request.endpoint.headers?.headers, [.init(name: "host", value: "graphql.com")]) + guard case let .data(data) = request.body else { + XCTFail("Unexpected body") + return + } + XCTAssertEqual(data, dataObject) + } +} diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj index efe5f198a7..2444c39e1a 100644 --- a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 21CFD7C62C7524570071C70F /* AppSyncSignerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CFD7C52C7524570071C70F /* AppSyncSignerTests.swift */; }; 21F762A52BD6B1AA0048845A /* AuthSessionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485CB5B727B61F0F006CCEC7 /* AuthSessionHelper.swift */; }; 21F762A62BD6B1AA0048845A /* AsyncTesting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 681DFEA828E747B80000C36A /* AsyncTesting.swift */; }; 21F762A72BD6B1AA0048845A /* AuthSRPSignInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485CB5BE27B61F1D006CCEC7 /* AuthSRPSignInTests.swift */; }; @@ -169,6 +170,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 21CFD7C52C7524570071C70F /* AppSyncSignerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncSignerTests.swift; sourceTree = ""; }; 21F762CB2BD6B1AA0048845A /* AuthGen2IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AuthGen2IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 21F762CC2BD6B1CD0048845A /* AuthGen2IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AuthGen2IntegrationTests.xctestplan; sourceTree = ""; }; 4821B2F1286B5F74000EC1D7 /* AuthDeleteUserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthDeleteUserTests.swift; sourceTree = ""; }; @@ -268,6 +270,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 21CFD7C42C75243B0071C70F /* AppSyncSignerTests */ = { + isa = PBXGroup; + children = ( + 21CFD7C52C7524570071C70F /* AppSyncSignerTests.swift */, + ); + path = AppSyncSignerTests; + sourceTree = ""; + }; 4821B2F0286B5F74000EC1D7 /* AuthDeleteUserTests */ = { isa = PBXGroup; children = ( @@ -355,6 +365,7 @@ 485CB5A027B61E04006CCEC7 /* AuthIntegrationTests */ = { isa = PBXGroup; children = ( + 21CFD7C42C75243B0071C70F /* AppSyncSignerTests */, 21F762CC2BD6B1CD0048845A /* AuthGen2IntegrationTests.xctestplan */, 48916F362A412AF800E3E1B1 /* MFATests */, 97B370C32878DA3500F1C088 /* DeviceTests */, @@ -851,6 +862,7 @@ 681DFEAC28E747B80000C36A /* AsyncExpectation.swift in Sources */, 48E3AB3128E52590004EE395 /* GetCurrentUserTests.swift in Sources */, 48916F3A2A412CEE00E3E1B1 /* TOTPHelper.swift in Sources */, + 21CFD7C62C7524570071C70F /* AppSyncSignerTests.swift in Sources */, 485CB5B127B61EAC006CCEC7 /* AWSAuthBaseTest.swift in Sources */, 485CB5C027B61F1E006CCEC7 /* SignedOutAuthSessionTests.swift in Sources */, 485CB5BA27B61F10006CCEC7 /* AuthSignInHelper.swift in Sources */, diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift new file mode 100644 index 0000000000..02cedefdfb --- /dev/null +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift @@ -0,0 +1,36 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import Amplify +import AWSCognitoAuthPlugin + +class AppSyncSignerTests: AWSAuthBaseTest { + + /// Test signing an AppSync request with a live credentials provider + /// + /// - Given: Base test configures Amplify and adds AWSCognitoAuthPlugin + /// - When: + /// - I invoke AWSCognitoAuthPlugin's AppSync signer + /// - Then: + /// - I should get a signed request. + /// + func testSignAppSyncRequest() async throws { + let request = URLRequest(url: URL(string: "http://graphql.com")!) + let signer = AWSCognitoAuthPlugin.createAppSyncSigner(region: "us-east-1") + let signedRequest = try await signer(request) + guard let headers = signedRequest.allHTTPHeaderFields else { + XCTFail("Missing headers") + return + } + XCTAssertEqual(headers.count, 4) + let containsExpectedHeaders = headers.keys.contains(where: { key in + key == "Authorization" || key == "Host" || key == "X-Amz-Security-Token" || key == "X-Amz-Date" + }) + XCTAssertTrue(containsExpectedHeaders) + } +} diff --git a/AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift b/AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift new file mode 100644 index 0000000000..e7d41af6d7 --- /dev/null +++ b/AmplifyPlugins/Core/AWSPluginsCore/API/AWSAppSyncConfiguration.swift @@ -0,0 +1,46 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +@_spi(InternalAmplifyConfiguration) import Amplify + + +/// Hold necessary AWS AppSync configuration values to interact with the AppSync API +public struct AWSAppSyncConfiguration { + + /// The region of the AWS AppSync API + public let region: String + + /// The endpoint of the AWS AppSync API + public let endpoint: URL + + /// API key for API Key authentication. + public let apiKey: String? + + + /// Initializes an `AWSAppSyncConfiguration` instance using the provided AmplifyOutputs file. + /// AmplifyOutputs support multiple ways to read the `amplify_outputs.json` configuration file + /// + /// For example, `try AWSAppSyncConfiguraton(with: .amplifyOutputs)` will read the + /// `amplify_outputs.json` file from the main bundle. + public init(with amplifyOutputs: AmplifyOutputs) throws { + let resolvedConfiguration = try amplifyOutputs.resolveConfiguration() + + guard let dataCategory = resolvedConfiguration.data else { + throw ConfigurationError.invalidAmplifyOutputsFile( + "Missing data category", "", nil) + } + + self.region = dataCategory.awsRegion + guard let endpoint = URL(string: dataCategory.url) else { + throw ConfigurationError.invalidAmplifyOutputsFile( + "Missing region from data category", "", nil) + } + self.endpoint = endpoint + self.apiKey = dataCategory.apiKey + } +} diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/API/AWSAppSyncConfigurationTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/API/AWSAppSyncConfigurationTests.swift new file mode 100644 index 0000000000..42813dc3f7 --- /dev/null +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/API/AWSAppSyncConfigurationTests.swift @@ -0,0 +1,31 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +import AWSPluginsCore +@_spi(InternalAmplifyConfiguration) @testable import Amplify + +final class AWSAppSyncConfigurationTests: XCTestCase { + + func testSuccess() throws { + let config = AmplifyOutputsData(data: .init( + awsRegion: "us-east-1", + url: "http://www.example.com", + modelIntrospection: nil, + apiKey: "apiKey123", + defaultAuthorizationType: .amazonCognitoUserPools, + authorizationTypes: [.apiKey, .awsIAM])) + let encoder = JSONEncoder() + let data = try! encoder.encode(config) + + let configuration = try AWSAppSyncConfiguration(with: .data(data)) + + XCTAssertEqual(configuration.region, "us-east-1") + XCTAssertEqual(configuration.endpoint, URL(string: "http://www.example.com")!) + XCTAssertEqual(configuration.apiKey, "apiKey123") + } +} diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift index 63d1197a6d..8a45c1d64a 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift @@ -12,7 +12,7 @@ import AWSPluginsCore import Foundation public class AmplifyAWSCredentialsProvider: AWSClientRuntime.CredentialsProviding { - + public func getCredentials() async throws -> AWSClientRuntime.AWSCredentials { let authSession = try await Amplify.Auth.fetchAuthSession() if let awsCredentialsProvider = authSession as? AuthAWSCredentialsProvider { diff --git a/api-dump/AWSDataStorePlugin.json b/api-dump/AWSDataStorePlugin.json index d436f34410..981969d8c6 100644 --- a/api-dump/AWSDataStorePlugin.json +++ b/api-dump/AWSDataStorePlugin.json @@ -8205,7 +8205,7 @@ "-module", "AWSDataStorePlugin", "-o", - "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/AWSDataStorePlugin.json", + "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/AWSDataStorePlugin.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/AWSPluginsCore.json b/api-dump/AWSPluginsCore.json index 76cf8a6e6c..088d2a9ea1 100644 --- a/api-dump/AWSPluginsCore.json +++ b/api-dump/AWSPluginsCore.json @@ -136,6 +136,196 @@ "mangledName": "$s14AWSPluginsCore21AWSAPIAuthInformationP", "moduleName": "AWSPluginsCore" }, + { + "kind": "TypeDecl", + "name": "AWSAppSyncConfiguration", + "printedName": "AWSAppSyncConfiguration", + "children": [ + { + "kind": "Var", + "name": "region", + "printedName": "region", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Var", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV6regionSSvp", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV6regionSSvp", + "moduleName": "AWSPluginsCore", + "declAttributes": [ + "HasStorage" + ], + "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:14AWSPluginsCore23AWSAppSyncConfigurationV6regionSSvg", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV6regionSSvg", + "moduleName": "AWSPluginsCore", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Var", + "name": "endpoint", + "printedName": "endpoint", + "children": [ + { + "kind": "TypeNominal", + "name": "URL", + "printedName": "Foundation.URL", + "usr": "s:10Foundation3URLV" + } + ], + "declKind": "Var", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV8endpoint10Foundation3URLVvp", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV8endpoint10Foundation3URLVvp", + "moduleName": "AWSPluginsCore", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "URL", + "printedName": "Foundation.URL", + "usr": "s:10Foundation3URLV" + } + ], + "declKind": "Accessor", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV8endpoint10Foundation3URLVvg", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV8endpoint10Foundation3URLVvg", + "moduleName": "AWSPluginsCore", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Var", + "name": "apiKey", + "printedName": "apiKey", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV6apiKeySSSgvp", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV6apiKeySSSgvp", + "moduleName": "AWSPluginsCore", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV6apiKeySSSgvg", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV6apiKeySSSgvg", + "moduleName": "AWSPluginsCore", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(with:)", + "children": [ + { + "kind": "TypeNominal", + "name": "AWSAppSyncConfiguration", + "printedName": "AWSPluginsCore.AWSAppSyncConfiguration", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV" + }, + { + "kind": "TypeNominal", + "name": "AmplifyOutputs", + "printedName": "Amplify.AmplifyOutputs", + "usr": "s:7Amplify0A7OutputsV" + } + ], + "declKind": "Constructor", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV4withAC7Amplify0G7OutputsV_tKcfc", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV4withAC7Amplify0G7OutputsV_tKcfc", + "moduleName": "AWSPluginsCore", + "throwing": true, + "init_kind": "Designated" + } + ], + "declKind": "Struct", + "usr": "s:14AWSPluginsCore23AWSAppSyncConfigurationV", + "mangledName": "$s14AWSPluginsCore23AWSAppSyncConfigurationV", + "moduleName": "AWSPluginsCore" + }, { "kind": "TypeDecl", "name": "AppSyncErrorType", @@ -24273,7 +24463,7 @@ "-module", "AWSPluginsCore", "-o", - "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/AWSPluginsCore.json", + "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/AWSPluginsCore.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/Amplify.json b/api-dump/Amplify.json index c8b9c833c4..70967c7128 100644 --- a/api-dump/Amplify.json +++ b/api-dump/Amplify.json @@ -155992,6 +155992,80 @@ "name": "AmplifyOutputs", "printedName": "AmplifyOutputs", "children": [ + { + "kind": "Var", + "name": "resolveConfiguration", + "printedName": "resolveConfiguration", + "children": [ + { + "kind": "TypeFunc", + "name": "Function", + "printedName": "() throws -> Amplify.AmplifyOutputsData", + "children": [ + { + "kind": "TypeNominal", + "name": "AmplifyOutputsData", + "printedName": "Amplify.AmplifyOutputsData", + "usr": "s:7Amplify0A11OutputsDataV" + }, + { + "kind": "TypeNominal", + "name": "Void", + "printedName": "()" + } + ] + } + ], + "declKind": "Var", + "usr": "s:7Amplify0A7OutputsV20resolveConfigurationAA0aB4DataVyKcvp", + "mangledName": "$s7Amplify0A7OutputsV20resolveConfigurationAA0aB4DataVyKcvp", + "moduleName": "Amplify", + "declAttributes": [ + "SPIAccessControl", + "HasStorage" + ], + "spi_group_names": [ + "InternalAmplifyConfiguration" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeFunc", + "name": "Function", + "printedName": "() throws -> Amplify.AmplifyOutputsData", + "children": [ + { + "kind": "TypeNominal", + "name": "AmplifyOutputsData", + "printedName": "Amplify.AmplifyOutputsData", + "usr": "s:7Amplify0A11OutputsDataV" + }, + { + "kind": "TypeNominal", + "name": "Void", + "printedName": "()" + } + ] + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify0A7OutputsV20resolveConfigurationAA0aB4DataVyKcvg", + "mangledName": "$s7Amplify0A7OutputsV20resolveConfigurationAA0aB4DataVyKcvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "amplifyOutputs", @@ -179735,7 +179809,7 @@ "-module", "Amplify", "-o", - "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/Amplify.json", + "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/Amplify.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/CoreMLPredictionsPlugin.json b/api-dump/CoreMLPredictionsPlugin.json index f7aeb94b9c..618df7be0e 100644 --- a/api-dump/CoreMLPredictionsPlugin.json +++ b/api-dump/CoreMLPredictionsPlugin.json @@ -430,7 +430,7 @@ "-module", "CoreMLPredictionsPlugin", "-o", - "\/var\/folders\/hw\/1f0gcr8d6kn9ms0_wn0_57qc0000gn\/T\/tmp.rjSPtedPzR\/CoreMLPredictionsPlugin.json", + "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/CoreMLPredictionsPlugin.json", "-I", ".build\/debug", "-sdk-version", From 81894600d20aa32f4145eaa157a46c01ceb11652 Mon Sep 17 00:00:00 2001 From: Di Wu Date: Fri, 23 Aug 2024 10:41:11 -0700 Subject: [PATCH 08/50] fix(api): storing cancelablles with actor methods in AppSyncRTC (#3824) * fix(api): storing cancelablles with actor methods in AppSyncRTC * add unit test cases * remove internal modifier --- .../AppSyncRealTimeClient.swift | 66 ++++++++++++------- .../AppSyncRealTimeClientTests.swift | 28 ++++++++ 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift index 4e374e9fdf..7f3d75abbc 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift @@ -165,14 +165,15 @@ actor AppSyncRealTimeClient: AppSyncRealTimeClientProtocol { // Placing the actual subscription work in a deferred task and // promptly returning the filtered publisher for downstream consumption of all error messages. defer { - Task { [weak self] in + let task = Task { [weak self] in guard let self = self else { return } if !(await self.isConnected) { try await connect() try await waitForState(.connected) } - await self.bindCancellableToConnection(try await self.startSubscription(id)) - }.toAnyCancellable.store(in: &cancellablesBindToConnection) + await self.storeInConnectionCancellables(try await self.startSubscription(id)) + } + self.storeInConnectionCancellables(task.toAnyCancellable) } return filterAppSyncSubscriptionEvent(with: id) @@ -236,24 +237,29 @@ actor AppSyncRealTimeClient: AppSyncRealTimeClientProtocol { } private func subscribeToWebSocketEvent() async { - await self.webSocketClient.publisher.sink { [weak self] _ in + let cancellable = await self.webSocketClient.publisher.sink { [weak self] _ in self?.log.debug("[AppSyncRealTimeClient] WebSocketClient terminated") } receiveValue: { webSocketEvent in Task { [weak self] in - await self?.onWebSocketEvent(webSocketEvent) - }.toAnyCancellable.store(in: &self.cancellables) + let task = Task { [weak self] in + await self?.onWebSocketEvent(webSocketEvent) + } + await self?.storeInCancellables(task.toAnyCancellable) + } } - .store(in: &cancellables) + self.storeInCancellables(cancellable) } private func resumeExistingSubscriptions() { log.debug("[AppSyncRealTimeClient] Resuming existing subscriptions") for (id, _) in self.subscriptions { - Task { + Task { [weak self] in do { - try await self.startSubscription(id).store(in: &cancellablesBindToConnection) + if let cancellable = try await self?.startSubscription(id) { + await self?.storeInConnectionCancellables(cancellable) + } } catch { - log.debug("[AppSyncRealTimeClient] Failed to resume existing subscription with id: (\(id))") + Self.log.debug("[AppSyncRealTimeClient] Failed to resume existing subscription with id: (\(id))") } } } @@ -286,7 +292,7 @@ actor AppSyncRealTimeClient: AppSyncRealTimeClientProtocol { subject.filter { switch $0 { case .success(let response): return response.id == id || response.type == .connectionError - case .failure(let error): return true + case .failure: return true } } .map { result -> AppSyncSubscriptionEvent? in @@ -350,10 +356,6 @@ actor AppSyncRealTimeClient: AppSyncRealTimeClientProtocol { return errors.compactMap(AppSyncRealTimeRequest.parseResponseError(error:)) } - private func bindCancellableToConnection(_ cancellable: AnyCancellable) { - cancellable.store(in: &cancellablesBindToConnection) - } - } // MARK: - On WebSocket Events @@ -366,8 +368,11 @@ extension AppSyncRealTimeClient { if self.state.value == .connectionDropped { log.debug("[AppSyncRealTimeClient] reconnecting appSyncClient after connection drop") Task { [weak self] in - try? await self?.connect() - }.toAnyCancellable.store(in: &cancellablesBindToConnection) + let task = Task { [weak self] in + try? await self?.connect() + } + await self?.storeInConnectionCancellables(task.toAnyCancellable) + } } case let .disconnected(closeCode, reason): // @@ -425,24 +430,37 @@ extension AppSyncRealTimeClient { } } - private func monitorHeartBeats(_ connectionAck: JSONValue?) { + func monitorHeartBeats(_ connectionAck: JSONValue?) { let timeoutMs = connectionAck?.connectionTimeoutMs?.intValue ?? 0 log.debug("[AppSyncRealTimeClient] Starting heart beat monitor with interval \(timeoutMs) ms") - heartBeats.eraseToAnyPublisher() + let cancellable = heartBeats.eraseToAnyPublisher() .debounce(for: .milliseconds(timeoutMs), scheduler: DispatchQueue.global()) .first() - .sink(receiveValue: { - self.log.debug("[AppSyncRealTimeClient] KeepAlive timed out, disconnecting") + .sink(receiveValue: { [weak self] in + Self.log.debug("[AppSyncRealTimeClient] KeepAlive timed out, disconnecting") Task { [weak self] in - await self?.reconnect() - }.toAnyCancellable.store(in: &self.cancellables) + let task = Task { [weak self] in + await self?.reconnect() + } + await self?.storeInCancellables(task.toAnyCancellable) + } }) - .store(in: &cancellablesBindToConnection) + self.storeInConnectionCancellables(cancellable) // start counting down heartBeats.send(()) } } +extension AppSyncRealTimeClient { + private func storeInCancellables(_ cancellable: AnyCancellable) { + self.cancellables.insert(cancellable) + } + + private func storeInConnectionCancellables(_ cancellable: AnyCancellable) { + self.cancellablesBindToConnection.insert(cancellable) + } +} + extension Publisher where Output == AppSyncRealTimeSubscription.State, Failure == Never { func toAppSyncSubscriptionEventStream() -> AnyPublisher { self.compactMap { subscriptionState -> AppSyncSubscriptionEvent? in diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeClientTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeClientTests.swift index 83c2c58216..99b36943f1 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeClientTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeClientTests.swift @@ -551,4 +551,32 @@ class AppSyncRealTimeClientTests: XCTestCase { await fulfillment(of: [startTriggered, errorReceived], timeout: 2) } + + func testReconnect_whenHeartBeatSignalIsNotReceived() async throws { + var cancellables = Set() + let timeout = 1.0 + let mockWebSocketClient = MockWebSocketClient() + let mockAppSyncRequestInterceptor = MockAppSyncRequestInterceptor() + let appSyncClient = AppSyncRealTimeClient( + endpoint: URL(string: "https://example.com")!, + requestInterceptor: mockAppSyncRequestInterceptor, + webSocketClient: mockWebSocketClient + ) + + // start monitoring + await appSyncClient.monitorHeartBeats(.object([ + "connectionTimeoutMs": 100 + ])) + + let reconnect = expectation(description: "webSocket triggers event to connection") + await mockWebSocketClient.actionSubject.sink { action in + switch action { + case .connect: + reconnect.fulfill() + default: break + } + }.store(in: &cancellables) + await fulfillment(of: [reconnect], timeout: 2) + } + } From c8286725ce4f9ff0658b6a82d8fcd3be320d6032 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:37:46 -0400 Subject: [PATCH 09/50] chore(deps): bump rexml from 3.3.4 to 3.3.6 in /canaries/example (#3830) Bumps [rexml](https://github.com/ruby/rexml) from 3.3.4 to 3.3.6. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.4...v3.3.6) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- canaries/example/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/canaries/example/Gemfile.lock b/canaries/example/Gemfile.lock index e9e2c22c7f..6bf1c1890d 100644 --- a/canaries/example/Gemfile.lock +++ b/canaries/example/Gemfile.lock @@ -168,7 +168,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.4) + rexml (3.3.6) strscan rouge (2.0.7) ruby2_keywords (0.0.5) From 610dccdd26a8162ee2a3fcd1673f84ba0cb4e059 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:40:06 -0400 Subject: [PATCH 10/50] chore(deps): bump rexml from 3.3.5 to 3.3.6 (#3836) Bumps [rexml](https://github.com/ruby/rexml) from 3.3.5 to 3.3.6. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.5...v3.3.6) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d949113527..1ab3463fe4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -262,7 +262,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.5) + rexml (3.3.6) strscan rouge (2.0.7) ruby-macho (2.5.1) From cb80b91c38d99932af28df6be07633ee0563be08 Mon Sep 17 00:00:00 2001 From: Harsh <6162866+harsh62@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:40:29 -0400 Subject: [PATCH 11/50] fix(auth): clear credentials values only if namespacing has changed (#3827) * fix(auth): clear credentials values only if namespacing has changed * fix session errors --- .../AWSCognitoAuthCredentialStore.swift | 4 +- .../FetchAuthSessionOperationHelper.swift | 90 +++++-------------- .../CredentialStoreConfigurationTests.swift | 10 ++- 3 files changed, 34 insertions(+), 70 deletions(-) diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/AWSCognitoAuthCredentialStore.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/AWSCognitoAuthCredentialStore.swift index 3bb2a2e1bb..28654896ea 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/AWSCognitoAuthCredentialStore.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/AWSCognitoAuthCredentialStore.swift @@ -65,12 +65,12 @@ struct AWSCognitoAuthCredentialStore { newIdentityConfigData != nil && oldIdentityPoolConfiguration == newIdentityConfigData { - // retrieve data from the old namespace and save with the new namespace if let oldCognitoCredentialsData = try? keychain._getData(oldNameSpace) { try? keychain._set(oldCognitoCredentialsData, key: newNameSpace) } - } else if oldAuthConfigData != currentAuthConfig { + } else if oldAuthConfigData != currentAuthConfig && + oldNameSpace != newNameSpace { // Clear the old credentials try? keychain._remove(oldNameSpace) } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/FetchAuthSessionOperationHelper.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/FetchAuthSessionOperationHelper.swift index b4a7aa9d4a..98504afe89 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/FetchAuthSessionOperationHelper.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/FetchAuthSessionOperationHelper.swift @@ -8,7 +8,7 @@ import Foundation import Amplify -class FetchAuthSessionOperationHelper: DefaultLogger { +class FetchAuthSessionOperationHelper { typealias FetchAuthSessionCompletion = (Result) -> Void @@ -108,85 +108,41 @@ class FetchAuthSessionOperationHelper: DefaultLogger { "Auth plugin is in an invalid state") } - func sessionResultWithError(_ error: AuthorizationError, - authenticationState: AuthenticationState) - throws -> AuthSession { - log.verbose("Received error - \(error)") + func sessionResultWithError( + _ error: AuthorizationError, + authenticationState: AuthenticationState + ) throws -> AuthSession { + log.verbose("Received fetch auth session error - \(error)") var isSignedIn = false - if case .signedIn = authenticationState { - isSignedIn = true - } - switch error { - case .sessionError(let fetchError, let credentials): - return try sessionResultWithFetchError(fetchError, - authenticationState: authenticationState, - existingCredentials: credentials) - case .sessionExpired(let error): - let session = AuthCognitoSignedInSessionHelper.makeExpiredSignedInSession( - underlyingError: error) - return session - default: - let message = "Unknown error occurred" - let error = AuthError.unknown(message) - let session = AWSAuthCognitoSession(isSignedIn: isSignedIn, - identityIdResult: .failure(error), - awsCredentialsResult: .failure(error), - cognitoTokensResult: .failure(error)) - return session - } - } - - func sessionResultWithFetchError(_ error: FetchSessionError, - authenticationState: AuthenticationState, - existingCredentials: AmplifyCredentials) - throws -> AuthSession { + var authError: AuthError = error.authError - var isSignedIn = false if case .signedIn = authenticationState { isSignedIn = true } switch error { - - case .notAuthorized, .noCredentialsToRefresh: - if !isSignedIn { + case .sessionError(let fetchError, _): + if (fetchError == .notAuthorized || fetchError == .noCredentialsToRefresh) && !isSignedIn { return AuthCognitoSignedOutSessionHelper.makeSessionWithNoGuestAccess() - } - - case .service(let error): - var authError: AuthError - if let convertedAuthError = (error as? AuthErrorConvertible)?.authError { - authError = convertedAuthError } else { - authError = AuthError.service( - "Unknown service error occurred", - "See the attached error for more details", - error) + authError = fetchError.authError } - let session = AWSAuthCognitoSession( - isSignedIn: isSignedIn, - identityIdResult: .failure(authError), - awsCredentialsResult: .failure(authError), - cognitoTokensResult: .failure(authError)) + case .sessionExpired(let error): + let session = AuthCognitoSignedInSessionHelper.makeExpiredSignedInSession( + underlyingError: error) return session - default: break - + default: + break } - let message = "Unknown error occurred" - let error = AuthError.unknown(message) - let session = AWSAuthCognitoSession(isSignedIn: isSignedIn, - identityIdResult: .failure(error), - awsCredentialsResult: .failure(error), - cognitoTokensResult: .failure(error)) - return session - } - public static var log: Logger { - Amplify.Logging.logger(forCategory: CategoryType.auth.displayName, forNamespace: String(describing: self)) - } - - public var log: Logger { - Self.log + let session = AWSAuthCognitoSession( + isSignedIn: isSignedIn, + identityIdResult: .failure(authError), + awsCredentialsResult: .failure(authError), + cognitoTokensResult: .failure(authError)) + return session } } + +extension FetchAuthSessionOperationHelper: DefaultLogger { } diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/CredentialStore/CredentialStoreConfigurationTests.swift b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/CredentialStore/CredentialStoreConfigurationTests.swift index 0dd8d34e9f..8efbebdefd 100644 --- a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/CredentialStore/CredentialStoreConfigurationTests.swift +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/CredentialStore/CredentialStoreConfigurationTests.swift @@ -89,8 +89,16 @@ class CredentialStoreConfigurationTests: AWSAuthBaseTest { XCTFail("Unable to save credentials") } + // When configuration changed + let updatedConfig = AuthConfiguration.userPoolsAndIdentityPools( + UserPoolConfigurationData(poolId: Defaults.userPoolId, + clientId: Defaults.appClientId, + region: Defaults.regionString, + clientSecret: Defaults.appClientSecret, + pinpointAppId: "somethingNew"), + Defaults.makeIdentityConfigData()) // When configuration don't change changed - let newCredentialStore = AWSCognitoAuthCredentialStore(authConfiguration: initialAuthConfig) + let newCredentialStore = AWSCognitoAuthCredentialStore(authConfiguration: updatedConfig) // Then guard let credentials = try? newCredentialStore.retrieveCredential(), From 5cd1439d36f7b2ae788eb828140d716f9e84e4d1 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Wed, 28 Aug 2024 17:58:22 +0000 Subject: [PATCH 12/50] chore: release 2.39.0 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index dec4e0be94..71fce3145f 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.38.0" + public static let amplifyVersion = "2.39.0" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From c13d7b723b236a68d32d04718011ad1f4c6f2f49 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Wed, 28 Aug 2024 18:00:29 +0000 Subject: [PATCH 13/50] chore: finalize release 2.39.0 [skip ci] --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c3eb78a2f..22996d41dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 2.39.0 (2024-08-28) + +### Features + +- Add AppSync components (#3825) + +### Bug Fixes + +- **auth**: clear credentials values only if namespacing has changed (#3827) +- **api**: storing cancelablles with actor methods in AppSyncRTC (#3824) + ## 2.38.0 (2024-08-19) ### Features From 59eb80739cb12c8f1fa4713d583e109f73ff5b3e Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Fri, 6 Sep 2024 18:00:16 -0400 Subject: [PATCH 14/50] chore: Avoid updating labels on closed issues [skip ci] (#3856) --- .github/workflows/issue_comment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/issue_comment.yml b/.github/workflows/issue_comment.yml index 822ff84a1c..2670f9d622 100644 --- a/.github/workflows/issue_comment.yml +++ b/.github/workflows/issue_comment.yml @@ -21,6 +21,7 @@ jobs: adjust-labels: runs-on: ubuntu-latest + if: ${{ github.event.issue.state == 'open' }} permissions: issues: write env: From 0b255bc70105b351b07ecd95ac832789cd4e773c Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:46:36 -0400 Subject: [PATCH 15/50] chore: Marking Storage's key-based API as deprecated in the Category implementation. (#3863) --- .../Storage/StorageCategory+ClientBehavior.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Amplify/Categories/Storage/StorageCategory+ClientBehavior.swift b/Amplify/Categories/Storage/StorageCategory+ClientBehavior.swift index 55b69bbe43..e9197c34ce 100644 --- a/Amplify/Categories/Storage/StorageCategory+ClientBehavior.swift +++ b/Amplify/Categories/Storage/StorageCategory+ClientBehavior.swift @@ -9,6 +9,7 @@ import Foundation extension StorageCategory: StorageCategoryBehavior { + @available(*, deprecated, message: "Use getURL(path:options:)") @discardableResult public func getURL( key: String, @@ -25,6 +26,7 @@ extension StorageCategory: StorageCategoryBehavior { try await plugin.getURL(path: path, options: options) } + @available(*, deprecated, message: "Use downloadData(path:options:)") @discardableResult public func downloadData( key: String, @@ -41,6 +43,7 @@ extension StorageCategory: StorageCategoryBehavior { plugin.downloadData(path: path, options: options) } + @available(*, deprecated, message: "Use downloadFile(path:options:)") @discardableResult public func downloadFile( key: String, @@ -59,6 +62,7 @@ extension StorageCategory: StorageCategoryBehavior { plugin.downloadFile(path: path, local: local, options: options) } + @available(*, deprecated, message: "Use uploadData(path:options:)") @discardableResult public func uploadData( key: String, @@ -77,6 +81,7 @@ extension StorageCategory: StorageCategoryBehavior { plugin.uploadData(path: path, data: data, options: options) } + @available(*, deprecated, message: "Use uploadFile(path:options:)") @discardableResult public func uploadFile( key: String, @@ -95,6 +100,7 @@ extension StorageCategory: StorageCategoryBehavior { plugin.uploadFile(path: path, local: local, options: options) } + @available(*, deprecated, message: "Use remove(path:options:)") @discardableResult public func remove( key: String, @@ -111,6 +117,7 @@ extension StorageCategory: StorageCategoryBehavior { try await plugin.remove(path: path, options: options) } + @available(*, deprecated, message: "Use list(path:options:)") @discardableResult public func list( options: StorageListOperation.Request.Options? = nil From 4782923d87414c56ac0d9716e9ebd8a2cc84aa0b Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 13 Sep 2024 13:03:06 -0700 Subject: [PATCH 16/50] chore: Update typo in README (#3859) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb77a62dbf..28552426e4 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ Amplify requires Xcode 15.0 or later for all the supported platforms. ## Escape Hatch -All services and features not listed in the [**Features/API sectios**](#featuresapis) are supported via the [Swift SDK](https://github.com/awslabs/aws-sdk-swift) or if supported by a category can be accessed via the Escape Hatch like below: +All services and features not listed in the [**Features/API sections**](#featuresapis) are supported via the [Swift SDK](https://github.com/awslabs/aws-sdk-swift) or if supported by a category can be accessed via the Escape Hatch like below: ```swift import Amplify From 39de25da46ddcef642f6140cc2958ff90de6a7b6 Mon Sep 17 00:00:00 2001 From: Di Wu Date: Mon, 16 Sep 2024 13:31:28 -0700 Subject: [PATCH 17/50] fix(analytics): iterate sqlite rows with failableNext (#3857) --- .../Analytics/LocalStorage/AnalyticsEventSQLStorage.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/LocalStorage/AnalyticsEventSQLStorage.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/LocalStorage/AnalyticsEventSQLStorage.swift index c0597a6e98..260c982b1d 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/LocalStorage/AnalyticsEventSQLStorage.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/LocalStorage/AnalyticsEventSQLStorage.swift @@ -142,9 +142,9 @@ class AnalyticsEventSQLStorage: AnalyticsEventStorage { ORDER BY timestamp ASC LIMIT ? """ - let rows = try dbAdapter.executeQuery(queryStatement, [limit]) + let rows = try dbAdapter.executeQuery(queryStatement, [limit]).makeIterator() var result = [PinpointEvent]() - for element in rows { + while let element = try rows.failableNext() { if let event = PinpointEvent.convertToEvent(element) { result.append(event) } From a0c36b3a51071bf0e8b289dc97620c3b9eff0aea Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Mon, 16 Sep 2024 22:22:02 +0000 Subject: [PATCH 18/50] chore: release 2.39.1 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 71fce3145f..b557aaa8c7 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.39.0" + public static let amplifyVersion = "2.39.1" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 847e58f68f0311ed2b373b286a9432959cfa1e12 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Mon, 16 Sep 2024 22:23:54 +0000 Subject: [PATCH 19/50] chore: finalize release 2.39.1 [skip ci] --- CHANGELOG.md | 6 ++++++ Package.resolved | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22996d41dc..d4a92b6f65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.39.1 (2024-09-16) + +### Bug Fixes + +- **analytics**: iterate sqlite rows with failableNext (#3857) + ## 2.39.0 (2024-08-28) ### Features diff --git a/Package.resolved b/Package.resolved index 156b876c86..36c02fdfea 100644 --- a/Package.resolved +++ b/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mattgallagher/CwlCatchException.git", "state" : { - "revision" : "3ef6999c73b6938cc0da422f2c912d0158abb0a0", - "version" : "2.2.0" + "revision" : "07b2ba21d361c223e25e3c1e924288742923f08c", + "version" : "2.2.1" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", "state" : { - "revision" : "2ef56b2caf25f55fa7eef8784c30d5a767550f54", - "version" : "2.2.1" + "revision" : "0139c665ebb45e6a9fbdb68aabfd7c39f3fe0071", + "version" : "2.2.2" } }, { From 251c18447439be718bd1c07a96ac3d120032d4c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 22:24:33 +0000 Subject: [PATCH 20/50] chore(deps): bump serve-static and express Bumps [serve-static](https://github.com/expressjs/serve-static) to 1.16.2 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together. Updates `serve-static` from 1.15.0 to 1.16.2 - [Release notes](https://github.com/expressjs/serve-static/releases) - [Changelog](https://github.com/expressjs/serve-static/blob/v1.16.2/HISTORY.md) - [Commits](https://github.com/expressjs/serve-static/compare/v1.15.0...v1.16.2) Updates `express` from 4.19.2 to 4.21.0 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md) - [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0) --- updated-dependencies: - dependency-name: serve-static dependency-type: indirect - dependency-name: express dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../LocalServer/package-lock.json | 100 ++++++++++-------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json index 90c039c020..7608478472 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json @@ -30,9 +30,9 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -42,7 +42,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -157,9 +157,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -197,36 +197,36 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -238,12 +238,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -399,9 +399,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", @@ -455,9 +458,12 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -482,9 +488,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -499,11 +505,11 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -559,9 +565,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -581,20 +587,28 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" From c229b654fa6d5bf3c2e9738a14097290c33caf28 Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:38:39 -0400 Subject: [PATCH 21/50] feat(Storage): Implementing support for multiple buckets (#3839) --- .../Request/StorageDownloadDataRequest.swift | 26 +- .../Request/StorageDownloadFileRequest.swift | 27 +- .../Request/StorageGetURLRequest.swift | 36 +- .../Request/StorageListRequest.swift | 40 +- .../Request/StorageRemoveRequest.swift | 22 +- .../Request/StorageUploadDataRequest.swift | 40 +- .../Request/StorageUploadFileRequest.swift | 40 +- .../Categories/Storage/StorageBucket.swift | 63 + .../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 +- .../Tasks/AWSS3StorageListObjectsTask.swift | 1 + .../AWSS3StoragePluginResetTests.swift | 2 +- ...AWSS3StoragePluginStorageBucketTests.swift | 174 ++ .../AWSS3StoragePluginTestBase.swift | 23 +- ...ageOperations+StorageServiceProvider.swift | 99 + ...WSS3StoragePluginMultipleBucketTests.swift | 498 +++++ .../AWSS3StoragePluginTestBase.swift | 46 +- .../StorageHostApp.xcodeproj/project.pbxproj | 4 + .../StorageStressTests.swift | 16 +- api-dump/AWSDataStorePlugin.json | 2 +- api-dump/AWSPluginsCore.json | 2 +- api-dump/Amplify.json | 1725 ++++++++++++++++- api-dump/CoreMLPredictionsPlugin.json | 2 +- 35 files changed, 3075 insertions(+), 120 deletions(-) create mode 100644 Amplify/Categories/Storage/StorageBucket.swift create mode 100644 AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/AWSS3StoragePlugin+StorageBucket.swift create mode 100644 AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginStorageBucketTests.swift create mode 100644 AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/AWSS3StorageOperations+StorageServiceProvider.swift create mode 100644 AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginMultipleBucketTests.swift diff --git a/Amplify/Categories/Storage/Operation/Request/StorageDownloadDataRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageDownloadDataRequest.swift index 1a8ed260b9..73cf9f57fc 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageDownloadDataRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageDownloadDataRequest.swift @@ -64,6 +64,11 @@ public extension StorageDownloadDataRequest { @available(*, deprecated, message: "Use `path` in Storage API instead of `Options`") public let targetIdentityId: String? + /// A Storage Bucket that contains the object to download. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageDownloadDataRequest.bucket + public let bucket: (any StorageBucket)? + /// Extra plugin specific options, only used in special circumstances when the existing options do not provide /// a way to utilize the underlying storage system's functionality. See plugin documentation for expected /// key/values @@ -91,11 +96,14 @@ public extension StorageDownloadDataRequest { /// /// - Tag: StorageDownloadDataRequestOptions.init @available(*, deprecated, message: "Use init(pluginOptions)") - public init(accessLevel: StorageAccessLevel = .guest, - targetIdentityId: String? = nil, - pluginOptions: Any? = nil) { + public init( + accessLevel: StorageAccessLevel = .guest, + targetIdentityId: String? = nil, + pluginOptions: Any? = nil + ) { self.accessLevel = accessLevel self.targetIdentityId = targetIdentityId + self.bucket = nil self.pluginOptions = pluginOptions } @@ -104,6 +112,18 @@ public extension StorageDownloadDataRequest { public init(pluginOptions: Any? = nil) { self.accessLevel = .guest self.targetIdentityId = nil + self.bucket = nil + self.pluginOptions = pluginOptions + } + + /// - Tag: StorageDownloadDataRequestOptions.init + public init( + bucket: some StorageBucket, + pluginOptions: Any? = nil + ) { + self.accessLevel = .guest + self.targetIdentityId = nil + self.bucket = bucket self.pluginOptions = pluginOptions } } diff --git a/Amplify/Categories/Storage/Operation/Request/StorageDownloadFileRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageDownloadFileRequest.swift index 7ec34c222f..cc68d539bd 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageDownloadFileRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageDownloadFileRequest.swift @@ -71,6 +71,11 @@ public extension StorageDownloadFileRequest { @available(*, deprecated, message: "Use `path` in Storage API instead of `Options`") public let targetIdentityId: String? + /// A Storage Bucket that contains the object to download. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageDownloadDataRequest.bucket + public let bucket: (any StorageBucket)? + /// Extra plugin specific options, only used in special circumstances when the existing options do not provide /// a way to utilize the underlying storage system's functionality. See plugin documentation for expected /// key/values @@ -80,19 +85,33 @@ public extension StorageDownloadFileRequest { /// - Tag: StorageDownloadFileRequestOptions.init @available(*, deprecated, message: "Use init(pluginOptions)") - public init(accessLevel: StorageAccessLevel = .guest, - targetIdentityId: String? = nil, - pluginOptions: Any? = nil) { + public init( + accessLevel: StorageAccessLevel = .guest, + targetIdentityId: String? = nil, + pluginOptions: Any? = nil + ) { self.accessLevel = accessLevel self.targetIdentityId = targetIdentityId + self.bucket = nil self.pluginOptions = pluginOptions } /// - Tag: StorageDownloadFileRequestOptions.init - @available(*, deprecated, message: "Use init(pluginOptions)") public init(pluginOptions: Any? = nil) { self.accessLevel = .guest self.targetIdentityId = nil + self.bucket = nil + self.pluginOptions = pluginOptions + } + + /// - Tag: StorageDownloadFileRequestOptions.init + public init( + bucket: some StorageBucket, + pluginOptions: Any? = nil + ) { + self.accessLevel = .guest + self.targetIdentityId = nil + self.bucket = bucket self.pluginOptions = pluginOptions } } diff --git a/Amplify/Categories/Storage/Operation/Request/StorageGetURLRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageGetURLRequest.swift index e8ffd22c00..c86db0b4ba 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageGetURLRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageGetURLRequest.swift @@ -73,6 +73,11 @@ public extension StorageGetURLRequest { /// - Tag: StorageGetURLRequest.Options.expires public let expires: Int + /// A Storage Bucket that contains the object. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageDownloadDataRequest.bucket + public let bucket: (any StorageBucket)? + /// Extra plugin specific options, only used in special circumstances when the existing options do /// not provide a way to utilize the underlying storage system's functionality. See plugin /// documentation or @@ -84,20 +89,39 @@ public extension StorageGetURLRequest { /// - Tag: StorageGetURLRequest.Options.init @available(*, deprecated, message: "Use init(expires:pluginOptions)") - public init(accessLevel: StorageAccessLevel = .guest, - targetIdentityId: String? = nil, - expires: Int = Options.defaultExpireInSeconds, - pluginOptions: Any? = nil) { + public init( + accessLevel: StorageAccessLevel = .guest, + targetIdentityId: String? = nil, + expires: Int = Options.defaultExpireInSeconds, + pluginOptions: Any? = nil + ) { self.accessLevel = accessLevel self.targetIdentityId = targetIdentityId self.expires = expires + self.bucket = nil self.pluginOptions = pluginOptions } /// - Tag: StorageGetURLRequest.Options.init - public init(expires: Int = Options.defaultExpireInSeconds, - pluginOptions: Any? = nil) { + public init( + expires: Int = Options.defaultExpireInSeconds, + pluginOptions: Any? = nil + ) { + self.expires = expires + self.bucket = nil + self.pluginOptions = pluginOptions + self.accessLevel = .guest + self.targetIdentityId = nil + } + + /// - Tag: StorageGetURLRequest.Options.init + public init( + expires: Int = Options.defaultExpireInSeconds, + bucket: some StorageBucket, + pluginOptions: Any? = nil + ) { self.expires = expires + self.bucket = bucket self.pluginOptions = pluginOptions self.accessLevel = .guest self.targetIdentityId = nil diff --git a/Amplify/Categories/Storage/Operation/Request/StorageListRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageListRequest.swift index 71119f71f8..0c66976aeb 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageListRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageListRequest.swift @@ -78,6 +78,11 @@ public extension StorageListRequest { /// - Tag: StorageListRequestOptions.pageSize public let pageSize: UInt + /// A Storage Bucket that contains the objects to list. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageDownloadDataRequest.bucket + public let bucket: (any StorageBucket)? + /// Opaque string indicating the page offset at which to resume a listing. This is usually a copy of /// the value from [StorageListResult.nextToken](x-source-tag://StorageListResult.nextToken). /// @@ -96,18 +101,39 @@ public extension StorageListRequest { public let pluginOptions: Any? /// - Tag: StorageListRequestOptions.init - public init(accessLevel: StorageAccessLevel = .guest, - targetIdentityId: String? = nil, - path: String? = nil, - subpathStrategy: SubpathStrategy = .include, - pageSize: UInt = 1000, - nextToken: String? = nil, - pluginOptions: Any? = nil) { + public init( + accessLevel: StorageAccessLevel = .guest, + targetIdentityId: String? = nil, + path: String? = nil, + subpathStrategy: SubpathStrategy = .include, + pageSize: UInt = 1000, + nextToken: String? = nil, + pluginOptions: Any? = nil + ) { self.accessLevel = accessLevel self.targetIdentityId = targetIdentityId self.path = path self.subpathStrategy = subpathStrategy self.pageSize = pageSize + self.bucket = nil + self.nextToken = nextToken + self.pluginOptions = pluginOptions + } + + /// - Tag: StorageListRequestOptions.init + public init( + subpathStrategy: SubpathStrategy = .include, + pageSize: UInt = 1000, + bucket: some StorageBucket, + nextToken: String? = nil, + pluginOptions: Any? = nil + ) { + self.accessLevel = .guest + self.targetIdentityId = nil + self.path = nil + self.subpathStrategy = subpathStrategy + self.pageSize = pageSize + self.bucket = bucket self.nextToken = nextToken self.pluginOptions = pluginOptions } diff --git a/Amplify/Categories/Storage/Operation/Request/StorageRemoveRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageRemoveRequest.swift index 31ae1de4f3..39b10e099d 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageRemoveRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageRemoveRequest.swift @@ -56,6 +56,11 @@ public extension StorageRemoveRequest { @available(*, deprecated, message: "Use `path` in Storage API instead of `Options`") public let accessLevel: StorageAccessLevel + /// A Storage Bucket that contains the object to remove. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageDownloadDataRequest.bucket + public let bucket: (any StorageBucket)? + /// Extra plugin specific options, only used in special circumstances when the existing options do not provide /// a way to utilize the underlying storage system's functionality. See plugin documentation for expected /// key/values @@ -64,9 +69,22 @@ public extension StorageRemoveRequest { public let pluginOptions: Any? /// - Tag: StorageRemoveRequestOptions.init - public init(accessLevel: StorageAccessLevel = .guest, - pluginOptions: Any? = nil) { + public init( + accessLevel: StorageAccessLevel = .guest, + pluginOptions: Any? = nil + ) { self.accessLevel = accessLevel + self.bucket = nil + self.pluginOptions = pluginOptions + } + + /// - Tag: StorageRemoveRequestOptions.init + public init( + bucket: some StorageBucket, + pluginOptions: Any? = nil + ) { + self.accessLevel = .guest + self.bucket = bucket self.pluginOptions = pluginOptions } } diff --git a/Amplify/Categories/Storage/Operation/Request/StorageUploadDataRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageUploadDataRequest.swift index 572b160bf8..174aa06fc9 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageUploadDataRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageUploadDataRequest.swift @@ -75,6 +75,11 @@ public extension StorageUploadDataRequest { /// - Tag: StorageUploadDataRequestOptions.metadata public let metadata: [String: String]? + /// A specific Storage Bucket to upload the data. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageDownloadDataRequest.bucket + public let bucket: (any StorageBucket)? + /// The standard MIME type describing the format of the object to store /// /// - Tag: StorageUploadDataRequestOptions.contentType @@ -89,27 +94,46 @@ public extension StorageUploadDataRequest { /// - Tag: StorageUploadDataRequestOptions.init @available(*, deprecated, message: "Use init(metadata:contentType:options)") - public init(accessLevel: StorageAccessLevel = .guest, - targetIdentityId: String? = nil, - metadata: [String: String]? = nil, - contentType: String? = nil, - pluginOptions: Any? = nil + public init( + accessLevel: StorageAccessLevel = .guest, + targetIdentityId: String? = nil, + metadata: [String: String]? = nil, + contentType: String? = nil, + pluginOptions: Any? = nil ) { self.accessLevel = accessLevel self.targetIdentityId = targetIdentityId self.metadata = metadata + self.bucket = nil + self.contentType = contentType + self.pluginOptions = pluginOptions + } + + /// - Tag: StorageUploadDataRequestOptions.init + public init( + metadata: [String: String]? = nil, + contentType: String? = nil, + pluginOptions: Any? = nil + ) { + self.accessLevel = .guest + self.targetIdentityId = nil + self.metadata = metadata + self.bucket = nil self.contentType = contentType self.pluginOptions = pluginOptions } /// - Tag: StorageUploadDataRequestOptions.init - public init(metadata: [String: String]? = nil, - contentType: String? = nil, - pluginOptions: Any? = nil + public init( + metadata: [String: String]? = nil, + bucket: some StorageBucket, + contentType: String? = nil, + pluginOptions: Any? = nil ) { self.accessLevel = .guest self.targetIdentityId = nil self.metadata = metadata + self.bucket = bucket self.contentType = contentType self.pluginOptions = pluginOptions } diff --git a/Amplify/Categories/Storage/Operation/Request/StorageUploadFileRequest.swift b/Amplify/Categories/Storage/Operation/Request/StorageUploadFileRequest.swift index 23c8d159f6..b7c2e73830 100644 --- a/Amplify/Categories/Storage/Operation/Request/StorageUploadFileRequest.swift +++ b/Amplify/Categories/Storage/Operation/Request/StorageUploadFileRequest.swift @@ -72,6 +72,11 @@ public extension StorageUploadFileRequest { /// - Tag: StorageUploadFileRequestOptions.metadata public let metadata: [String: String]? + /// A specific Storage Bucket to upload the file. Defaults to `nil`, in which case the default one will be used. + /// + /// - Tag: StorageUploadFileRequestOptions.bucket + public let bucket: (any StorageBucket)? + /// The standard MIME type describing the format of the object to store /// /// - Tag: StorageUploadFileRequestOptions.contentType @@ -86,27 +91,46 @@ public extension StorageUploadFileRequest { /// - Tag: StorageUploadFileRequestOptions.init @available(*, deprecated, message: "Use init(metadata:contentType:pluginOptions)") - public init(accessLevel: StorageAccessLevel = .guest, - targetIdentityId: String? = nil, - metadata: [String: String]? = nil, - contentType: String? = nil, - pluginOptions: Any? = nil + public init( + accessLevel: StorageAccessLevel = .guest, + targetIdentityId: String? = nil, + metadata: [String: String]? = nil, + contentType: String? = nil, + pluginOptions: Any? = nil ) { self.accessLevel = accessLevel self.targetIdentityId = targetIdentityId self.metadata = metadata + self.bucket = nil + self.contentType = contentType + self.pluginOptions = pluginOptions + } + + /// - Tag: StorageUploadFileRequestOptions.init + public init( + metadata: [String: String]? = nil, + contentType: String? = nil, + pluginOptions: Any? = nil + ) { + self.accessLevel = .guest + self.targetIdentityId = nil + self.metadata = metadata + self.bucket = nil self.contentType = contentType self.pluginOptions = pluginOptions } /// - Tag: StorageUploadFileRequestOptions.init - public init(metadata: [String: String]? = nil, - contentType: String? = nil, - pluginOptions: Any? = nil + public init( + metadata: [String: String]? = nil, + bucket: some StorageBucket, + contentType: String? = nil, + pluginOptions: Any? = nil ) { self.accessLevel = .guest self.targetIdentityId = nil self.metadata = metadata + self.bucket = bucket self.contentType = contentType self.pluginOptions = pluginOptions } diff --git a/Amplify/Categories/Storage/StorageBucket.swift b/Amplify/Categories/Storage/StorageBucket.swift new file mode 100644 index 0000000000..f987f2b949 --- /dev/null +++ b/Amplify/Categories/Storage/StorageBucket.swift @@ -0,0 +1,63 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +/// Protocol that represents a Storage bucket. +/// +/// - Tag: StorageBucket +public protocol StorageBucket { } + +/// Represents information about a Storage bucket +/// +/// - Tag: BucketInfo +public struct BucketInfo: Hashable { + /// The name of the bucket + /// - Tag: BucketInfo.bucketName + public let bucketName: String + + /// The region of the bucket + /// - Tag: BucketInfo.region + public let region: String + + public init(bucketName: String, region: String) { + self.bucketName = bucketName + self.region = region + } +} + +public extension StorageBucket where Self == OutputsStorageBucket { + /// References a `StorageBucket` in the AmplifyOutputs file using the given name. + /// + /// - Parameter name: The name of the bucket + static func fromOutputs(name: String) -> Self { + return OutputsStorageBucket(name: name) + } +} + +public extension StorageBucket where Self == ResolvedStorageBucket { + /// References a `StorageBucket` using the data from the given `BucketInfo`. + /// + /// - Parameter bucketInfo: A `BucketInfo` instance + static func fromBucketInfo(_ bucketInfo: BucketInfo) -> Self { + return ResolvedStorageBucket(bucketInfo: bucketInfo) + } +} + +/// Conforms to `StorageBucket`. Represents a Storage Bucket defined by a name in the AmplifyOutputs file. +/// +/// - Tag: OutputsStorageBucket +public struct OutputsStorageBucket: StorageBucket { + public let name: String +} + +/// Conforms to `StorageBucket`. Represents a Storage Bucket defined by a name and a region defined in `BucketInfo`. +/// +/// - Tag: ResolvedStorageBucket +public struct ResolvedStorageBucket: StorageBucket { + public let bucketInfo: BucketInfo +} diff --git a/Amplify/Core/Configuration/AmplifyOutputsData.swift b/Amplify/Core/Configuration/AmplifyOutputsData.swift index 57e388a3ba..8362306eda 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/Sources/AWSS3StoragePlugin/Tasks/AWSS3StorageListObjectsTask.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Tasks/AWSS3StorageListObjectsTask.swift index ed01a7a1bb..8b860798d0 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Tasks/AWSS3StorageListObjectsTask.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Tasks/AWSS3StorageListObjectsTask.swift @@ -56,6 +56,7 @@ class AWSS3StorageListObjectsTask: StorageListObjectsTask, DefaultLogger { } return StorageListResult.Item( path: path, + size: s3Object.size, eTag: s3Object.eTag, lastModified: s3Object.lastModified ) 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/AWSS3StoragePluginStorageBucketTests.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginStorageBucketTests.swift new file mode 100644 index 0000000000..d5b9b2072c --- /dev/null +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/AWSS3StoragePluginStorageBucketTests.swift @@ -0,0 +1,174 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@_spi(InternalAmplifyConfiguration) @testable import Amplify +@testable import AWSS3StoragePlugin +@testable import AWSPluginsTestCommon + +class AWSS3StoragePluginStorageBucketTests: XCTestCase { + private var storagePlugin: AWSS3StoragePlugin! + private var defaultService: MockAWSS3StorageService! + private var authService: MockAWSAuthService! + private var queue: OperationQueue! + private let defaultBucketInfo = BucketInfo( + bucketName: "bucketName", + region: "us-east-1" + ) + private let additionalBucketInfo = BucketInfo( + bucketName: "anotherBucketName", + region: "us-east-2" + ) + + private var additionalS3Bucket: AmplifyOutputsData.Storage.Bucket { + return .init( + name: "anotherBucket", + bucketName: additionalBucketInfo.bucketName, + awsRegion: additionalBucketInfo.region + ) + } + + override func setUp() { + storagePlugin = AWSS3StoragePlugin() + defaultService = MockAWSS3StorageService() + authService = MockAWSAuthService() + queue = OperationQueue() + storagePlugin.configure( + defaultBucket: .fromBucketInfo(defaultBucketInfo), + storageService: defaultService, + authService: authService, + defaultAccessLevel: .guest, + queue: queue + ) + } + + override func tearDown() async throws { + try await Task.sleep(seconds: 0.1) // This is unfortunate but necessary to give the DB time to recover the URLSession tasks + await storagePlugin.reset() + queue.cancelAllOperations() + storagePlugin = nil + defaultService = nil + authService = nil + queue = nil + } + + /// Given: A configured AWSS3StoragePlugin + /// When: storageService(for:) is invoked with nil + /// Then: The default storage service should be returned + func testStorageService_withNil_shouldReturnDefaultService() throws { + let storageService = try storagePlugin.storageService(for: nil) + guard let mockService = storageService as? MockAWSS3StorageService else { + XCTFail("Expected a MockAWSS3StorageService, got \(type(of: storageService))") + return + } + XCTAssertTrue(mockService === defaultService) + } + + /// Given: A AWSS3StoragePlugin configured with additional bucket names + /// When: storageService(for:) is invoked with .fromOutputs with an existing value + /// Then: A valid AWSS3StorageService should be returned pointing to that bucket + func testStorageService_withBucketFromOutputs_shouldReturnStorageService() throws { + storagePlugin.additionalBucketsByName = [ + additionalS3Bucket.name: additionalS3Bucket + ] + let storageService = try storagePlugin.storageService(for: .fromOutputs(name: additionalS3Bucket.name)) + guard let newService = storageService as? AWSS3StorageService else { + XCTFail("Expected a AWSS3StorageService, got \(type(of: storageService))") + return + } + XCTAssertFalse(newService === defaultService) + XCTAssertEqual(newService.bucket, additionalS3Bucket.bucketName) + } + + /// Given: A AWSS3StoragePlugin configured without additional buckets (i.e. no AmplifyOutputs) + /// When: storageService(for:) is invoked with .fromOutputs + /// Then: A StorageError.validation error is thrown + func testStorageService_withBucketFromOutputs_withoutConfiguringOutputs_shouldThrowValidationException() { + storagePlugin.additionalBucketsByName = nil + do { + _ = try storagePlugin.storageService(for: .fromOutputs(name: "anotherBucket")) + XCTFail("Expected StorageError.validation to be thrown") + } catch { + guard let storageError = error as? StorageError, + case .validation(let field, _, _, _) = storageError else { + XCTFail("Expected StorageError.validation, got \(error)") + return + } + XCTAssertEqual(field, "bucket") + } + } + + /// Given: A AWSS3StoragePlugin configured with additional bucket names + /// When: storageService(for:) is invoked with .fromOutputs with a non-existing value + /// Then: A StorageError.validation error is thrown + func testStorageService_withInvalidBucketFromOutputs_shouldThrowValidationException() { + storagePlugin.additionalBucketsByName = [ + additionalS3Bucket.name: additionalS3Bucket + ] + do { + _ = try storagePlugin.storageService(for: .fromOutputs(name: "invalidBucket")) + XCTFail("Expected StorageError.validation to be thrown") + } catch { + guard let storageError = error as? StorageError, + case .validation(let field, _, _, _) = storageError else { + XCTFail("Expected StorageError.validation, got \(error)") + return + } + XCTAssertEqual(field, "bucket") + } + } + + /// Given: A configured AWSS3StoragePlugin + /// When: storageService(for:) is invoked with .fromBucketInfo + /// Then: A valid AWSS3StorageService should be returned pointing to that bucket + func testStorageService_withBucketFromBucketInfo_shouldReturnStorageService() throws { + let storageService = try storagePlugin.storageService(for: .fromBucketInfo(additionalBucketInfo)) + guard let newService = storageService as? AWSS3StorageService else { + XCTFail("Expected a AWSS3StorageService, got \(type(of: storageService))") + return + } + XCTAssertFalse(newService === defaultService) + XCTAssertEqual(newService.bucket, additionalBucketInfo.bucketName) + } + + /// Given: A configured AWSS3StoragePlugin + /// When: storageService(for:) is invoked with an invalid instance that conforms to StorageBucket + /// Then: A StorageError.validation error is thrown + func testStorageService_withInvalidStorageBucket_shouldThrowValidationException() { + do { + _ = try storagePlugin.storageService(for: InvalidBucket()) + XCTFail("Expected StorageError.validation to be thrown") + } catch { + guard let storageError = error as? StorageError, + case .validation(let field, _, _, _) = storageError else { + XCTFail("Expected StorageError.validation, got \(error)") + return + } + XCTAssertEqual(field, "bucket") + } + } + + /// Given: A configured AWSS3StoragePlugin + /// When: storageService(for:) is invoked for a bucket that was not accessed before (i.e. a new one) + /// Then: A new Storage Service should be created + func testStorageService_withNewBucket_shouldReturnNewService() throws { + XCTAssertEqual(storagePlugin.storageServicesByBucket.count, 1) + _ = try storagePlugin.storageService(for: .fromBucketInfo(additionalBucketInfo)) + XCTAssertEqual(storagePlugin.storageServicesByBucket.count, 2) + } + + /// Given: A configured AWSS3StoragePlugin + /// When: storageService(for:) is invoked for a bucket that was accessed before (e.g. the default one) + /// Then: A new Storage Service should not be created + func testStorageService_withPreviouslyAccessedBucket_shouldReturnExistingService() throws { + XCTAssertEqual(storagePlugin.storageServicesByBucket.count, 1) + _ = try storagePlugin.storageService(for: .fromBucketInfo(defaultBucketInfo)) + XCTAssertEqual(storagePlugin.storageServicesByBucket.count, 1) + } + + private struct InvalidBucket: StorageBucket {} +} 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/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginMultipleBucketTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginMultipleBucketTests.swift new file mode 100644 index 0000000000..e1e52e3789 --- /dev/null +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginMultipleBucketTests.swift @@ -0,0 +1,498 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +@_spi(InternalAmplifyConfiguration) @testable import Amplify +@testable import AWSS3StoragePlugin +import XCTest + +class AWSS3StoragePluginMultipleBucketTests: AWSS3StoragePluginTestBase { + private var customBucket: ResolvedStorageBucket! + + override func setUp() async throws { + guard let outputs = try? AmplifyOutputs.amplifyOutputs.resolveConfiguration(), + let additionalBucket = outputs.storage?.buckets?.first else { + throw XCTSkip("Multibucket has not been configured. Skipping test") + } + customBucket = .fromBucketInfo(.init( + bucketName: additionalBucket.bucketName, + region: additionalBucket.awsRegion + )) + try await super.setUp() + } + + override func tearDown() async throws { + try await Task.sleep(seconds: 0.1) + try await super.tearDown() + } + + /// Given: An data object + /// When: Upload the data to a custom buckets using keys + /// Then: The operation completes successfully + func testUploadData_toCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + let data = Data(key.utf8) + + let uploaded = try await Amplify.Storage.uploadData( + key: key, + data: data, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, key) + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: An data object + /// When: Upload the data to a custom bucket using StoragePath + /// Then: The operation completes successfully + func testUploadData_toCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let data = Data(id.utf8) + let path: StringStoragePath = .fromString("public/\(id)") + + let uploaded = try await Amplify.Storage.uploadData( + path: path, + data: data, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, path.string) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: A file with contents + /// When: Upload the file to a custom bucket using key + /// Then: The operation completes successfully and all URLSession and SDK requests include a user agent + func testUploadFile_toCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + let filePath = NSTemporaryDirectory() + key + ".tmp" + + let fileURL = URL(fileURLWithPath: filePath) + FileManager.default.createFile(atPath: filePath, contents: Data(key.utf8), attributes: nil) + + let uploaded = try await Amplify.Storage.uploadFile( + key: key, + local: fileURL, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, key) + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: A file with contents + /// When: Upload the file to a custom bucket using StoragePath + /// Then: The operation completes successfully and all URLSession and SDK requests include a user agent + func testUploadFile_toCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let filePath = NSTemporaryDirectory() + id + ".tmp" + let path: StringStoragePath = .fromString("public/\(id)") + + let fileURL = URL(fileURLWithPath: filePath) + FileManager.default.createFile(atPath: filePath, contents: Data(id.utf8), attributes: nil) + + let uploaded = try await Amplify.Storage.uploadFile( + path: path, + local: fileURL, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, path.string) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: A large data object + /// When: Upload the data to a custom bucket using key + /// Then: The operation completes successfully + func testUploadLargeData_toCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + + let uploaded = try await Amplify.Storage.uploadData( + key: key, + data: AWSS3StoragePluginTestBase.largeDataObject, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, key) + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: A large data object + /// When: Upload the data to a custom bucket using StoragePath + /// Then: The operation completes successfully + func testUploadLargeData_toCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let path: StringStoragePath = .fromString("public/\(id)") + + let uploaded = try await Amplify.Storage.uploadData( + path: path, + data: AWSS3StoragePluginTestBase.largeDataObject, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, path.string) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: A large file + /// When: Upload the file to a custom bucket using key + /// Then: The operation completes successfully + func testUploadLargeFile_toCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + let filePath = NSTemporaryDirectory() + key + ".tmp" + let fileURL = URL(fileURLWithPath: filePath) + + FileManager.default.createFile( + atPath: filePath, + contents: AWSS3StoragePluginTestBase.largeDataObject, + attributes: nil + ) + + let uploaded = try await Amplify.Storage.uploadFile( + key: key, + local: fileURL, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, key) + + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: A large file + /// When: Upload the file to a custom bucket using key + /// Then: The operation completes successfully + func testUploadLargeFile_toCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let filePath = NSTemporaryDirectory() + id + ".tmp" + let fileURL = URL(fileURLWithPath: filePath) + let path: StringStoragePath = .fromString("public/\(id)") + + FileManager.default.createFile( + atPath: filePath, + contents: AWSS3StoragePluginTestBase.largeDataObject, + attributes: nil + ) + + let uploaded = try await Amplify.Storage.uploadFile( + path: path, + local: fileURL, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(uploaded, path.string) + + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the downloadData API using key + /// Then: The operation completes successfully with the data retrieved + func testDownloadData_fromCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + let data = Data(key.utf8) + try await uploadData( + key: key, + data: data, + options: .init(bucket: customBucket) + ) + + let downloaded = try await Amplify.Storage.downloadData( + key: key, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(data.count, downloaded.count) + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the downloadData API using StoragePath + /// Then: The operation completes successfully with the data retrieved + func testDownloadData_fromCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let data = Data(id.utf8) + let path: StringStoragePath = .fromString("public/\(id)") + try await uploadData( + path: path, + data: data, + options: .init(bucket: customBucket) + ) + + let downloaded = try await Amplify.Storage.downloadData( + path: path, + options: .init(bucket: customBucket) + ).value + XCTAssertEqual(data.count, downloaded.count) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the downloadFile API using key + /// Then: The operation completes successfully the local file containing the data from the object + func testDownloadFile_fromCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + let timestamp = String(Date().timeIntervalSince1970) + let timestampData = Data(timestamp.utf8) + try await uploadData( + key: key, + data: timestampData, + options: .init(bucket: customBucket) + ) + let filePath = NSTemporaryDirectory() + key + ".tmp" + let fileURL = URL(fileURLWithPath: filePath) + removeFileIfExisting(fileURL) + + try await Amplify.Storage.downloadFile( + key: key, + local: fileURL, + options: .init(bucket: customBucket) + ).value + + XCTAssertTrue(FileManager.default.fileExists(atPath: fileURL.path)) + do { + let result = try String(contentsOf: fileURL, encoding: .utf8) + XCTAssertEqual(result, timestamp) + } catch { + XCTFail("Failed to read downloaded file") + } + + removeFileIfExisting(fileURL) + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the downloadFile API using StoragePath + /// Then: The operation completes successfully the local file containing the data from the object + func testDownloadFile_fromCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let timestamp = String(Date().timeIntervalSince1970) + let timestampData = Data(timestamp.utf8) + let path: StringStoragePath = .fromString("public/\(id)") + try await uploadData( + path: path, + data: timestampData, + options: .init(bucket: customBucket) + ) + let filePath = NSTemporaryDirectory() + id + ".tmp" + let fileURL = URL(fileURLWithPath: filePath) + removeFileIfExisting(fileURL) + + try await Amplify.Storage.downloadFile( + path: path, + local: fileURL, + options: .init(bucket: customBucket) + ).value + + XCTAssertTrue(FileManager.default.fileExists(atPath: fileURL.path)) + do { + let result = try String(contentsOf: fileURL, encoding: .utf8) + XCTAssertEqual(result, timestamp) + } catch { + XCTFail("Failed to read downloaded file") + } + + removeFileIfExisting(fileURL) + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the getURL API using key + /// Then: The operation completes successfully with the URL retrieved + func testGetRemoteURL_fromCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + try await uploadData( + key: key, + data: Data(key.utf8), + options: .init(bucket: customBucket) + ) + + let remoteURL = try await Amplify.Storage.getURL( + key: key, + options: .init(bucket: customBucket) + ) + + let (data, response) = try await URLSession.shared.data(from: remoteURL) + let httpResponse = try XCTUnwrap(response as? HTTPURLResponse) + XCTAssertEqual(httpResponse.statusCode, 200) + + let dataString = try XCTUnwrap(String(data: data, encoding: .utf8)) + XCTAssertEqual(dataString, key) + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the getURL API using StoragePath + /// Then: The operation completes successfully with the URL retrieved + func testGetRemoteURL_fromCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let path: StringStoragePath = .fromString("public/\(id)") + try await uploadData( + path: path, + data: Data(id.utf8), + options: .init(bucket: customBucket) + ) + + let remoteURL = try await Amplify.Storage.getURL( + path: path, + options: .init(bucket: customBucket) + ) + + let (data, response) = try await URLSession.shared.data(from: remoteURL) + let httpResponse = try XCTUnwrap(response as? HTTPURLResponse) + XCTAssertEqual(httpResponse.statusCode, 200) + + let dataString = try XCTUnwrap(String(data: data, encoding: .utf8)) + XCTAssertEqual(dataString, id) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the list API using StoragePath + /// Then: The operation completes successfully with the key retrieved + func testList_fromOtherBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let path: StringStoragePath = .fromString("public/\(id)") + try await uploadData( + path: path, + data: Data(id.utf8), + options: .init(bucket: customBucket) + ) + + let result = try await Amplify.Storage.list( + path: path, + options: .init(bucket: customBucket) + ) + let items = try XCTUnwrap(result.items) + + XCTAssertEqual(items.count, 1) + let item = try XCTUnwrap(items.first) + XCTAssertEqual(item.path, path.string) + XCTAssertNotNil(item.eTag) + XCTAssertNotNil(item.lastModified) + XCTAssertNotNil(item.size) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the remove API using key + /// Then: The operation completes successfully with the key removed from storage + func testRemoveKey_fromCustomBucket_usingKey_shouldSucceed() async throws { + let key = UUID().uuidString + try await uploadData( + key: key, + data: Data(key.utf8), + options: .init(bucket: customBucket) + ) + + let deleted = try await Amplify.Storage.remove( + key: key, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, key) + } + + /// Given: An object in storage in a custom bucket + /// When: Call the remove API using StoragePath + /// Then: The operation completes successfully with the key removed from storage + func testRemoveKey_fromCustomBucket_usingStoragePath_shouldSucceed() async throws { + let id = UUID().uuidString + let path: StringStoragePath = .fromString("public/\(id)") + try await uploadData( + path: path, + data: Data(id.utf8), + options: .init(bucket: customBucket) + ) + + let deleted = try await Amplify.Storage.remove( + path: path, + options: .init(bucket: customBucket) + ) + XCTAssertEqual(deleted, path.string) + } + + private func removeFileIfExisting(_ fileURL: URL) { + guard FileManager.default.fileExists(atPath: fileURL.path) else { + return + } + do { + try FileManager.default.removeItem(at: fileURL) + } catch { + XCTFail("Failed to remove file at \(fileURL)") + } + } +} + +private extension StringStoragePath { + var string: String { + return resolve("") + } +} diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift index 35e4721be2..fde9054957 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift @@ -87,13 +87,17 @@ class AWSS3StoragePluginTestBase: XCTestCase { Amplify.Storage.downloadData(key: key) } - func uploadData(key: String, data: Data) async throws { + func uploadData( + key: String, + data: Data, + options: StorageUploadDataRequest.Options? = nil + ) async throws { let completeInvoked = expectation(description: "Completed is invoked") Task { let result = try await Amplify.Storage.uploadData( key: key, data: data, - options: nil + options: options ).value XCTAssertNotNil(result) @@ -102,7 +106,27 @@ class AWSS3StoragePluginTestBase: XCTestCase { await fulfillment(of: [completeInvoked], timeout: 60) } - + + func uploadData( + path: any StoragePath, + data: Data, + options: StorageUploadDataRequest.Options? = nil + ) async throws { + let completeInvoked = expectation(description: "Completed is invoked") + Task { + let result = try await Amplify.Storage.uploadData( + path: path, + data: data, + options: options + ).value + + XCTAssertNotNil(result) + completeInvoked.fulfill() + } + + await fulfillment(of: [completeInvoked], timeout: 60) + } + func remove(key: String, accessLevel: StorageAccessLevel? = nil) async { var removeOptions: StorageRemoveRequest.Options? = nil if let accessLevel = accessLevel { @@ -196,15 +220,19 @@ class AWSS3StoragePluginTestBase: XCTestCase { private func invalidateCurrentSession() { Self.logger.debug("Invalidating URLSession") - guard let plugin = try? Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin, - let service = plugin.storageService as? AWSS3StorageService else { - print("Unable to to cast to AWSS3StorageService") + guard let plugin = try? Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin else { + print("Unable to to cast to AWSS3StoragePlugin") return } - if let delegate = service.urlSession.delegate as? StorageServiceSessionDelegate { - delegate.storageService = nil + for serviceBehaviour in plugin.storageServicesByBucket.values { + guard let service = serviceBehaviour as? AWSS3StorageService else { + continue + } + if let delegate = service.urlSession.delegate as? StorageServiceSessionDelegate { + delegate.storageService = nil + } + service.urlSession.invalidateAndCancel() } - service.urlSession.invalidateAndCancel() } } diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj index c363202fbf..a15257b61c 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 68828E4628C2736C006E7C0A /* AWSS3StoragePluginProgressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 684FB08C28BEAF8E00C8A6EB /* AWSS3StoragePluginProgressTests.swift */; }; 68828E4728C27745006E7C0A /* AWSS3StoragePluginPutDataResumabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 684FB08828BEAF8E00C8A6EB /* AWSS3StoragePluginPutDataResumabilityTests.swift */; }; 68828E4828C2AAA6006E7C0A /* AWSS3StoragePluginGetDataResumabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 684FB08B28BEAF8E00C8A6EB /* AWSS3StoragePluginGetDataResumabilityTests.swift */; }; + 68DAFA7A2C7796CD00346A43 /* AWSS3StoragePluginMultipleBucketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68DAFA792C7796CD00346A43 /* AWSS3StoragePluginMultipleBucketTests.swift */; }; 734605222BACB5CC0039F0EB /* AWSS3StoragePluginUploadIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 734605212BACB5CC0039F0EB /* AWSS3StoragePluginUploadIntegrationTests.swift */; }; 734605242BACB60E0039F0EB /* AWSS3StoragePluginDownloadIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 734605232BACB60E0039F0EB /* AWSS3StoragePluginDownloadIntegrationTests.swift */; }; 901AB3E92AE2C2DC000F825B /* AWSS3StoragePluginUploadMetadataTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 901AB3E82AE2C2DC000F825B /* AWSS3StoragePluginUploadMetadataTestCase.swift */; }; @@ -166,6 +167,7 @@ 684FB0A928BEB07200C8A6EB /* AWSS3StoragePluginIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AWSS3StoragePluginIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 684FB0C228BEB45600C8A6EB /* AuthSignInHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthSignInHelper.swift; sourceTree = ""; }; 684FB0C528BEB84800C8A6EB /* StorageHostApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = StorageHostApp.entitlements; sourceTree = ""; }; + 68DAFA792C7796CD00346A43 /* AWSS3StoragePluginMultipleBucketTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSS3StoragePluginMultipleBucketTests.swift; sourceTree = ""; }; 734605212BACB5CC0039F0EB /* AWSS3StoragePluginUploadIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSS3StoragePluginUploadIntegrationTests.swift; sourceTree = ""; }; 734605232BACB60E0039F0EB /* AWSS3StoragePluginDownloadIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSS3StoragePluginDownloadIntegrationTests.swift; sourceTree = ""; }; 901AB3E82AE2C2DC000F825B /* AWSS3StoragePluginUploadMetadataTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSS3StoragePluginUploadMetadataTestCase.swift; sourceTree = ""; }; @@ -321,6 +323,7 @@ 488C2A722BAE04DC009AD2BA /* AWSS3StoragePluginRemoveIntegrationTests.swift */, 488C2A742BAFCA7C009AD2BA /* AWSS3StoragePluginListObjectsIntegrationTests.swift */, 488C2A762BAFD4B3009AD2BA /* AWSS3StoragePluginGetURLIntegrationTests.swift */, + 68DAFA792C7796CD00346A43 /* AWSS3StoragePluginMultipleBucketTests.swift */, ); path = AWSS3StoragePluginIntegrationTests; sourceTree = ""; @@ -647,6 +650,7 @@ 21F7630F2BD6B8640048845A /* AsyncTesting.swift in Sources */, 21F763102BD6B8640048845A /* AWSS3StoragePluginGetDataResumabilityTests.swift in Sources */, 21F763112BD6B8640048845A /* AWSS3StoragePluginUploadMetadataTestCase.swift in Sources */, + 68DAFA7A2C7796CD00346A43 /* AWSS3StoragePluginMultipleBucketTests.swift in Sources */, 21F763122BD6B8640048845A /* AsyncExpectation.swift in Sources */, 21F763132BD6B8640048845A /* AWSS3StoragePluginProgressTests.swift in Sources */, 21F763142BD6B8640048845A /* AWSS3StoragePluginAccessLevelTests.swift in Sources */, diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageStressTests/StorageStressTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageStressTests/StorageStressTests.swift index ffc25e3d2c..7e06693fbd 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageStressTests/StorageStressTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageStressTests/StorageStressTests.swift @@ -230,15 +230,19 @@ final class StorageStressTests: XCTestCase { private func invalidateCurrentSession() { Self.logger.debug("Invalidating URLSession") - guard let plugin = try? Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin, - let service = plugin.storageService as? AWSS3StorageService else { - print("Unable to to cast to AWSS3StorageService") + guard let plugin = try? Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin else { + print("Unable to to cast to AWSS3StoragePlugin") return } - if let delegate = service.urlSession.delegate as? StorageServiceSessionDelegate { - delegate.storageService = nil + for serviceBehaviour in plugin.storageServicesByBucket.values { + guard let service = serviceBehaviour as? AWSS3StorageService else { + continue + } + if let delegate = service.urlSession.delegate as? StorageServiceSessionDelegate { + delegate.storageService = nil + } + service.urlSession.invalidateAndCancel() } - service.urlSession.invalidateAndCancel() } } diff --git a/api-dump/AWSDataStorePlugin.json b/api-dump/AWSDataStorePlugin.json index 981969d8c6..6666c984bf 100644 --- a/api-dump/AWSDataStorePlugin.json +++ b/api-dump/AWSDataStorePlugin.json @@ -8205,7 +8205,7 @@ "-module", "AWSDataStorePlugin", "-o", - "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/AWSDataStorePlugin.json", + "\/var\/folders\/k7\/4hy4p20s2rv4clw77d3qcl380000gr\/T\/tmp.nF2YaRsn2M\/AWSDataStorePlugin.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/AWSPluginsCore.json b/api-dump/AWSPluginsCore.json index 088d2a9ea1..b9e3b9e454 100644 --- a/api-dump/AWSPluginsCore.json +++ b/api-dump/AWSPluginsCore.json @@ -24463,7 +24463,7 @@ "-module", "AWSPluginsCore", "-o", - "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/AWSPluginsCore.json", + "\/var\/folders\/k7\/4hy4p20s2rv4clw77d3qcl380000gr\/T\/tmp.nF2YaRsn2M\/AWSPluginsCore.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/Amplify.json b/api-dump/Amplify.json index 70967c7128..9fad52bcce 100644 --- a/api-dump/Amplify.json +++ b/api-dump/Amplify.json @@ -138551,6 +138551,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify26StorageDownloadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify26StorageDownloadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify26StorageDownloadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify26StorageDownloadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "pluginOptions", @@ -138700,6 +138778,43 @@ "mangledName": "$s7Amplify26StorageDownloadDataRequestV7OptionsV06pluginF0AEypSg_tcfc", "moduleName": "Amplify", "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(bucket:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageDownloadDataRequest.Options", + "usr": "s:7Amplify26StorageDownloadDataRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify26StorageDownloadDataRequestV7OptionsV6bucket06pluginF0AEx_ypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify26StorageDownloadDataRequestV7OptionsV6bucket06pluginF0AEx_ypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", + "init_kind": "Designated" } ], "declKind": "Struct", @@ -139154,6 +139269,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify26StorageDownloadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify26StorageDownloadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify26StorageDownloadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify26StorageDownloadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "pluginOptions", @@ -139302,10 +139495,43 @@ "usr": "s:7Amplify26StorageDownloadFileRequestV7OptionsV06pluginF0AEypSg_tcfc", "mangledName": "$s7Amplify26StorageDownloadFileRequestV7OptionsV06pluginF0AEypSg_tcfc", "moduleName": "Amplify", - "deprecated": true, - "declAttributes": [ - "Available" + "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(bucket:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageDownloadFileRequest.Options", + "usr": "s:7Amplify26StorageDownloadFileRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } ], + "declKind": "Constructor", + "usr": "s:7Amplify26StorageDownloadFileRequestV7OptionsV6bucket06pluginF0AEx_ypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify26StorageDownloadFileRequestV7OptionsV6bucket06pluginF0AEx_ypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", "init_kind": "Designated" } ], @@ -139798,6 +140024,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify20StorageGetURLRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify20StorageGetURLRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify20StorageGetURLRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify20StorageGetURLRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "pluginOptions", @@ -139961,6 +140265,50 @@ "mangledName": "$s7Amplify20StorageGetURLRequestV7OptionsV7expires06pluginE0AESi_ypSgtcfc", "moduleName": "Amplify", "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(expires:bucket:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageGetURLRequest.Options", + "usr": "s:7Amplify20StorageGetURLRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "hasDefaultArg": true, + "usr": "s:Si" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify20StorageGetURLRequestV7OptionsV7expires6bucket06pluginE0AESi_xypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify20StorageGetURLRequestV7OptionsV7expires6bucket06pluginE0AESi_xypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", + "init_kind": "Designated" } ], "declKind": "Struct", @@ -140459,6 +140807,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify18StorageListRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify18StorageListRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify18StorageListRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify18StorageListRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "nextToken", @@ -140679,6 +141105,72 @@ "moduleName": "Amplify", "init_kind": "Designated" }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(subpathStrategy:pageSize:bucket:nextToken:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageListRequest.Options", + "usr": "s:7Amplify18StorageListRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "SubpathStrategy", + "printedName": "Amplify.StorageListRequest.Options.SubpathStrategy", + "hasDefaultArg": true, + "usr": "s:7Amplify18StorageListRequestV7OptionsV15SubpathStrategyO" + }, + { + "kind": "TypeNominal", + "name": "UInt", + "printedName": "Swift.UInt", + "hasDefaultArg": true, + "usr": "s:Su" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify18StorageListRequestV7OptionsV15subpathStrategy8pageSize6bucket9nextToken06pluginE0A2E07SubpathG0O_SuxSSSgypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify18StorageListRequestV7OptionsV15subpathStrategy8pageSize6bucket9nextToken06pluginE0A2E07SubpathG0O_SuxSSSgypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", + "init_kind": "Designated" + }, { "kind": "TypeDecl", "name": "SubpathStrategy", @@ -141170,6 +141662,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify20StorageRemoveRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify20StorageRemoveRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify20StorageRemoveRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify20StorageRemoveRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "pluginOptions", @@ -141268,6 +141838,43 @@ "mangledName": "$s7Amplify20StorageRemoveRequestV7OptionsV11accessLevel06pluginE0AeA0b6AccessG0O_ypSgtcfc", "moduleName": "Amplify", "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(bucket:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageRemoveRequest.Options", + "usr": "s:7Amplify20StorageRemoveRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify20StorageRemoveRequestV7OptionsV6bucket06pluginE0AEx_ypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify20StorageRemoveRequestV7OptionsV6bucket06pluginE0AEx_ypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", + "init_kind": "Designated" } ], "declKind": "Struct", @@ -141812,6 +142419,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify24StorageUploadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify24StorageUploadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify24StorageUploadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify24StorageUploadDataRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "contentType", @@ -142111,6 +142796,87 @@ "mangledName": "$s7Amplify24StorageUploadDataRequestV7OptionsV8metadata11contentType06pluginF0AESDyS2SGSg_SSSgypSgtcfc", "moduleName": "Amplify", "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(metadata:bucket:contentType:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageUploadDataRequest.Options", + "usr": "s:7Amplify24StorageUploadDataRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "[Swift.String : Swift.String]?", + "children": [ + { + "kind": "TypeNominal", + "name": "Dictionary", + "printedName": "[Swift.String : Swift.String]", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:SD" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify24StorageUploadDataRequestV7OptionsV8metadata6bucket11contentType06pluginF0AESDyS2SGSg_xSSSgypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify24StorageUploadDataRequestV7OptionsV8metadata6bucket11contentType06pluginF0AESDyS2SGSg_xSSSgypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", + "init_kind": "Designated" } ], "declKind": "Struct", @@ -142655,6 +143421,84 @@ } ] }, + { + "kind": "Var", + "name": "bucket", + "printedName": "bucket", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Var", + "usr": "s:7Amplify24StorageUploadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "mangledName": "$s7Amplify24StorageUploadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "(any Amplify.StorageBucket)?", + "children": [ + { + "kind": "TypeNominal", + "name": "Paren", + "printedName": "(any Amplify.StorageBucket)", + "children": [ + { + "kind": "TypeNominal", + "name": "StorageBucket", + "printedName": "any Amplify.StorageBucket", + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:7Amplify13StorageBucketP" + } + ], + "usr": "s:Sq" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify24StorageUploadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "mangledName": "$s7Amplify24StorageUploadFileRequestV7OptionsV6bucketAA0B6Bucket_pSgvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, { "kind": "Var", "name": "contentType", @@ -142954,6 +143798,87 @@ "mangledName": "$s7Amplify24StorageUploadFileRequestV7OptionsV8metadata11contentType06pluginF0AESDyS2SGSg_SSSgypSgtcfc", "moduleName": "Amplify", "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(metadata:bucket:contentType:pluginOptions:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Options", + "printedName": "Amplify.StorageUploadFileRequest.Options", + "usr": "s:7Amplify24StorageUploadFileRequestV7OptionsV" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "[Swift.String : Swift.String]?", + "children": [ + { + "kind": "TypeNominal", + "name": "Dictionary", + "printedName": "[Swift.String : Swift.String]", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:SD" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "some Amplify.StorageBucket" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Any?", + "children": [ + { + "kind": "TypeNominal", + "name": "ProtocolComposition", + "printedName": "Any" + } + ], + "hasDefaultArg": true, + "usr": "s:Sq" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify24StorageUploadFileRequestV7OptionsV8metadata6bucket11contentType06pluginF0AESDyS2SGSg_xSSSgypSgtcAA0B6BucketRzlufc", + "mangledName": "$s7Amplify24StorageUploadFileRequestV7OptionsV8metadata6bucket11contentType06pluginF0AESDyS2SGSg_xSSSgypSgtcAA0B6BucketRzlufc", + "moduleName": "Amplify", + "init_kind": "Designated" } ], "declKind": "Struct", @@ -144416,6 +145341,446 @@ } ] }, + { + "kind": "TypeDecl", + "name": "StorageBucket", + "printedName": "StorageBucket", + "children": [ + { + "kind": "Function", + "name": "fromOutputs", + "printedName": "fromOutputs(name:)", + "children": [ + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "Self" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Func", + "usr": "s:7Amplify13StorageBucketPA2A07OutputsbC0VRszrlE04fromD04nameAESS_tFZ", + "mangledName": "$s7Amplify13StorageBucketPA2A07OutputsbC0VRszrlE04fromD04nameAESS_tFZ", + "moduleName": "Amplify", + "genericSig": "", + "static": true, + "isFromExtension": true, + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "fromBucketInfo", + "printedName": "fromBucketInfo(_:)", + "children": [ + { + "kind": "TypeNominal", + "name": "GenericTypeParam", + "printedName": "Self" + }, + { + "kind": "TypeNominal", + "name": "BucketInfo", + "printedName": "Amplify.BucketInfo", + "usr": "s:7Amplify10BucketInfoV" + } + ], + "declKind": "Func", + "usr": "s:7Amplify13StorageBucketPA2A08ResolvedbC0VRszrlE04fromC4InfoyAeA0cF0VFZ", + "mangledName": "$s7Amplify13StorageBucketPA2A08ResolvedbC0VRszrlE04fromC4InfoyAeA0cF0VFZ", + "moduleName": "Amplify", + "genericSig": "", + "static": true, + "isFromExtension": true, + "funcSelfKind": "NonMutating" + } + ], + "declKind": "Protocol", + "usr": "s:7Amplify13StorageBucketP", + "mangledName": "$s7Amplify13StorageBucketP", + "moduleName": "Amplify" + }, + { + "kind": "TypeDecl", + "name": "BucketInfo", + "printedName": "BucketInfo", + "children": [ + { + "kind": "Var", + "name": "bucketName", + "printedName": "bucketName", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Var", + "usr": "s:7Amplify10BucketInfoV10bucketNameSSvp", + "mangledName": "$s7Amplify10BucketInfoV10bucketNameSSvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "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:7Amplify10BucketInfoV10bucketNameSSvg", + "mangledName": "$s7Amplify10BucketInfoV10bucketNameSSvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Var", + "name": "region", + "printedName": "region", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Var", + "usr": "s:7Amplify10BucketInfoV6regionSSvp", + "mangledName": "$s7Amplify10BucketInfoV6regionSSvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "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:7Amplify10BucketInfoV6regionSSvg", + "mangledName": "$s7Amplify10BucketInfoV6regionSSvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(bucketName:region:)", + "children": [ + { + "kind": "TypeNominal", + "name": "BucketInfo", + "printedName": "Amplify.BucketInfo", + "usr": "s:7Amplify10BucketInfoV" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Constructor", + "usr": "s:7Amplify10BucketInfoV10bucketName6regionACSS_SStcfc", + "mangledName": "$s7Amplify10BucketInfoV10bucketName6regionACSS_SStcfc", + "moduleName": "Amplify", + "init_kind": "Designated" + }, + { + "kind": "Function", + "name": "hash", + "printedName": "hash(into:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Void", + "printedName": "()" + }, + { + "kind": "TypeNominal", + "name": "Hasher", + "printedName": "Swift.Hasher", + "paramValueOwnership": "InOut", + "usr": "s:s6HasherV" + } + ], + "declKind": "Func", + "usr": "s:7Amplify10BucketInfoV4hash4intoys6HasherVz_tF", + "mangledName": "$s7Amplify10BucketInfoV4hash4intoys6HasherVz_tF", + "moduleName": "Amplify", + "implicit": true, + "funcSelfKind": "NonMutating" + }, + { + "kind": "Function", + "name": "__derived_struct_equals", + "printedName": "__derived_struct_equals(_:_:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Bool", + "printedName": "Swift.Bool", + "usr": "s:Sb" + }, + { + "kind": "TypeNominal", + "name": "BucketInfo", + "printedName": "Amplify.BucketInfo", + "usr": "s:7Amplify10BucketInfoV" + }, + { + "kind": "TypeNominal", + "name": "BucketInfo", + "printedName": "Amplify.BucketInfo", + "usr": "s:7Amplify10BucketInfoV" + } + ], + "declKind": "Func", + "usr": "s:7Amplify10BucketInfoV23__derived_struct_equalsySbAC_ACtFZ", + "mangledName": "$s7Amplify10BucketInfoV23__derived_struct_equalsySbAC_ACtFZ", + "moduleName": "Amplify", + "static": true, + "implicit": true, + "funcSelfKind": "NonMutating" + }, + { + "kind": "Var", + "name": "hashValue", + "printedName": "hashValue", + "children": [ + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ], + "declKind": "Var", + "usr": "s:7Amplify10BucketInfoV9hashValueSivp", + "mangledName": "$s7Amplify10BucketInfoV9hashValueSivp", + "moduleName": "Amplify", + "implicit": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify10BucketInfoV9hashValueSivg", + "mangledName": "$s7Amplify10BucketInfoV9hashValueSivg", + "moduleName": "Amplify", + "implicit": true, + "accessorKind": "get" + } + ] + } + ], + "declKind": "Struct", + "usr": "s:7Amplify10BucketInfoV", + "mangledName": "$s7Amplify10BucketInfoV", + "moduleName": "Amplify", + "conformances": [ + { + "kind": "Conformance", + "name": "Hashable", + "printedName": "Hashable", + "usr": "s:SH", + "mangledName": "$sSH" + }, + { + "kind": "Conformance", + "name": "Equatable", + "printedName": "Equatable", + "usr": "s:SQ", + "mangledName": "$sSQ" + } + ] + }, + { + "kind": "TypeDecl", + "name": "OutputsStorageBucket", + "printedName": "OutputsStorageBucket", + "children": [ + { + "kind": "Var", + "name": "name", + "printedName": "name", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "declKind": "Var", + "usr": "s:7Amplify20OutputsStorageBucketV4nameSSvp", + "mangledName": "$s7Amplify20OutputsStorageBucketV4nameSSvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "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:7Amplify20OutputsStorageBucketV4nameSSvg", + "mangledName": "$s7Amplify20OutputsStorageBucketV4nameSSvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + } + ], + "declKind": "Struct", + "usr": "s:7Amplify20OutputsStorageBucketV", + "mangledName": "$s7Amplify20OutputsStorageBucketV", + "moduleName": "Amplify", + "conformances": [ + { + "kind": "Conformance", + "name": "StorageBucket", + "printedName": "StorageBucket", + "usr": "s:7Amplify13StorageBucketP", + "mangledName": "$s7Amplify13StorageBucketP" + } + ] + }, + { + "kind": "TypeDecl", + "name": "ResolvedStorageBucket", + "printedName": "ResolvedStorageBucket", + "children": [ + { + "kind": "Var", + "name": "bucketInfo", + "printedName": "bucketInfo", + "children": [ + { + "kind": "TypeNominal", + "name": "BucketInfo", + "printedName": "Amplify.BucketInfo", + "usr": "s:7Amplify10BucketInfoV" + } + ], + "declKind": "Var", + "usr": "s:7Amplify21ResolvedStorageBucketV10bucketInfoAA0dF0Vvp", + "mangledName": "$s7Amplify21ResolvedStorageBucketV10bucketInfoAA0dF0Vvp", + "moduleName": "Amplify", + "declAttributes": [ + "HasStorage" + ], + "isLet": true, + "hasStorage": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "BucketInfo", + "printedName": "Amplify.BucketInfo", + "usr": "s:7Amplify10BucketInfoV" + } + ], + "declKind": "Accessor", + "usr": "s:7Amplify21ResolvedStorageBucketV10bucketInfoAA0dF0Vvg", + "mangledName": "$s7Amplify21ResolvedStorageBucketV10bucketInfoAA0dF0Vvg", + "moduleName": "Amplify", + "implicit": true, + "declAttributes": [ + "Transparent" + ], + "accessorKind": "get" + } + ] + } + ], + "declKind": "Struct", + "usr": "s:7Amplify21ResolvedStorageBucketV", + "mangledName": "$s7Amplify21ResolvedStorageBucketV", + "moduleName": "Amplify", + "conformances": [ + { + "kind": "Conformance", + "name": "StorageBucket", + "printedName": "StorageBucket", + "usr": "s:7Amplify13StorageBucketP", + "mangledName": "$s7Amplify13StorageBucketP" + } + ] + }, { "kind": "TypeDecl", "name": "StorageCategory", @@ -153844,6 +155209,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", @@ -178788,7 +180497,7 @@ ], "declKind": "Struct", "usr": "c:@S@CLLocationCoordinate2D", - "location": "\/Applications\/Xcode_15.4.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreLocation.framework\/Headers\/CLLocation.h:79:8", + "location": "\/Applications\/Xcode-15.4.0.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/CoreLocation.framework\/Headers\/CLLocation.h:79:8", "moduleName": "CoreLocation", "isExternal": true }, @@ -179448,7 +181157,7 @@ ], "declKind": "Class", "usr": "c:objc(cs)OS_dispatch_source", - "location": "\/Applications\/Xcode_15.4.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/dispatch\/source.h:58:1", + "location": "\/Applications\/Xcode-15.4.0.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/dispatch\/source.h:58:1", "moduleName": "Dispatch", "isOpen": true, "objc_name": "OS_dispatch_source", @@ -179728,7 +181437,7 @@ ], "declKind": "Protocol", "usr": "c:objc(pl)NSLocking", - "location": "\/Applications\/Xcode_15.4.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Foundation.framework\/Headers\/NSLock.h:11:11", + "location": "\/Applications\/Xcode-15.4.0.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/Foundation.framework\/Headers\/NSLock.h:11:11", "moduleName": "Foundation", "genericSig": "", "objc_name": "NSLocking", @@ -179804,12 +181513,12 @@ "json_format_version": 8, "tool_arguments": [ "-sdk", - "\/Applications\/Xcode_15.4.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk", + "\/Applications\/Xcode-15.4.0.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk", "-dump-sdk", "-module", "Amplify", "-o", - "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/Amplify.json", + "\/var\/folders\/k7\/4hy4p20s2rv4clw77d3qcl380000gr\/T\/tmp.nF2YaRsn2M\/Amplify.json", "-I", ".build\/debug", "-sdk-version", diff --git a/api-dump/CoreMLPredictionsPlugin.json b/api-dump/CoreMLPredictionsPlugin.json index 618df7be0e..f3073090da 100644 --- a/api-dump/CoreMLPredictionsPlugin.json +++ b/api-dump/CoreMLPredictionsPlugin.json @@ -430,7 +430,7 @@ "-module", "CoreMLPredictionsPlugin", "-o", - "\/var\/folders\/4d\/0gnh84wj53j7wyk695q0tc_80000gn\/T\/tmp.BZQxGLOyZz\/CoreMLPredictionsPlugin.json", + "\/var\/folders\/k7\/4hy4p20s2rv4clw77d3qcl380000gr\/T\/tmp.nF2YaRsn2M\/CoreMLPredictionsPlugin.json", "-I", ".build\/debug", "-sdk-version", From 1af46aa83a4d619d549f7d0bfeb77df94024c178 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 17 Sep 2024 20:29:44 +0000 Subject: [PATCH 22/50] chore: release 2.40.0 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index b557aaa8c7..fb7bd92060 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.39.1" + public static let amplifyVersion = "2.40.0" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 4aee7e2ecb5d3cbe428429a4813c029aa148c6e0 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 17 Sep 2024 20:33:54 +0000 Subject: [PATCH 23/50] chore: finalize release 2.40.0 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4a92b6f65..c379854026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.40.0 (2024-09-17) + +### Features + +- **Storage**: Implementing support for multiple buckets (#3839) + ## 2.39.1 (2024-09-16) ### Bug Fixes From aa18599b6760dcba11bcbebd8c43a3a6f62496bd Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:43:48 -0400 Subject: [PATCH 24/50] feat(Core): Updating to AWS SDK for Swift 1.0.0 (#3858) * feat: Updating to AWS SDK for Swift 0.70.0 * test: Fixing compilation issues in Integration Tests * chore: Fixing SwiftLint issues * test: Fixing Integration Tests * feat: Updating to SDK 0.74.0. Also fixing the Decodable conformance for Transcribe types * fix: Fixing presigned URL for Multi part upload * feat: Updating to AWS SDK for Swift 0.76.0 Also fixing Storage unit test for aborted uploads * chore: Improving formatting * feat: Updating to AWS SDK for Swift 0.76.2 * feat: Updating to AWS SDK for Swift 0.77.0 also addressing some PR comments * feat: Upgrading to AWS SDK for Swift 1.0.0 --- .../IAMURLRequestInterceptor.swift | 11 +- .../IAMAuthInterceptor.swift | 12 +- .../AppSyncRealTimeClientFactory.swift | 4 +- ...inpointAnalyticsPluginConfigureTests.swift | 2 +- .../AWSCognitoAuthPlugin+AppSyncSigner.swift | 47 +++-- .../AWSCognitoAuthPlugin+Configure.swift | 19 +- .../Models/AuthChallengeType.swift | 2 +- ...dentityProvider+AuthErrorConvertible.swift | 2 +- .../ClientError+AuthErrorConvertible.swift | 22 ++- .../Service/SdkTypealiases.swift | 3 +- .../Data/UserPoolConfigurationData.swift | 1 + .../CustomEndpoint/AWSEndpointResolving.swift | 11 +- .../CustomEndpoint/EndpointResolving.swift | 6 +- .../Support/Utils/HttpClientEngineProxy.swift | 2 +- ...SCognitoAuthPluginAppSyncSignerTests.swift | 2 +- .../VerifyPasswordSRPTests.swift | 2 +- .../SignOut/RevokeTokenTests.swift | 4 +- .../SignOut/SignOutGloballyTests.swift | 4 +- .../VerifySignInChallengeTests.swift | 2 +- .../ClientSecretConfigurationTests.swift | 2 +- .../AuthHubEventHandlerTests.swift | 14 +- ...AuthFetchSignInSessionOperationTests.swift | 8 +- ...uthenticationProviderDeleteUserTests.swift | 56 +++--- ...entBehaviorConfirmResetPasswordTests.swift | 14 +- .../ClientBehaviorResetPasswordTests.swift | 4 +- .../MFA/FetchMFAPreferenceTaskTests.swift | 2 +- .../MFA/SetUpTOTPTaskTests.swift | 2 +- .../MFA/UpdateMFAPreferenceTaskTests.swift | 10 +- .../MFA/VerifyTOTPSetupTaskTests.swift | 2 +- .../SignIn/AWSAuthSignInPluginTests.swift | 11 +- .../SignIn/SignInSetUpTOTPTests.swift | 2 +- .../SignUp/AWSAuthConfirmSignUpAPITests.swift | 6 +- .../AWSAuthConfirmSignUpTaskTests.swift | 4 +- .../AWSAuthResendSignUpCodeAPITests.swift | 4 +- .../SignUp/AWSAuthSignUpAPITests.swift | 4 +- .../SignUp/AWSAuthSignUpTaskTests.swift | 2 +- .../DeviceBehaviorFetchDevicesTests.swift | 6 +- .../DeviceBehaviorForgetDeviceTests.swift | 9 +- .../DeviceBehaviorRememberDeviceTests.swift | 11 +- .../AWSCognitoAuthUserBehaviorTests.swift | 4 +- ...ndUserAttributeVerificationCodeTests.swift | 7 +- .../UserBehaviorChangePasswordTests.swift | 9 +- .../UserBehaviorConfirmAttributeTests.swift | 9 +- .../UserBehaviorFetchAttributeTests.swift | 9 +- .../UserBehaviorUpdateAttributeTests.swift | 9 +- .../Input/InitiateAuthInput+Codable.swift | 2 + ...ChangePasswordOutputResponse+Codable.swift | 4 +- ...ForgotPasswordOutputResponse+Codable.swift | 33 +++- ...alsForIdentityOutputResponse+Codable.swift | 19 ++ .../Response/HttpResponse+Codable.swift | 20 ++- .../InitiateAuthOutputResponse+Codable.swift | 41 +++++ .../CognitoAPIDecodingHelper.swift | 2 +- ...estHarnessInput+MockIdentityProvider.swift | 20 ++- .../AppSyncSignerTests.swift | 2 +- .../SignOutTests/AuthSignOutTests.swift | 2 +- .../WebSocket/WebSocketClientTests.swift | 20 +++ .../MockAWSAuthService.swift | 18 +- .../MockAWSSignatureV4Signer.swift | 11 +- .../MockHttpResponse.swift | 6 +- .../AWSAuthCredentialsProviderBehavior.swift | 6 +- .../AWSAuthService+CredentialsProvider.swift | 9 +- .../AmplifyAWSCredentialsProvider.swift | 47 +++-- .../AmplifyAWSSignatureV4Signer.swift | 31 ++-- .../ClientRuntimeFoundationBridge.swift | 21 +-- .../FoundationClientEngine.swift | 8 +- .../FoundationClientEngineError.swift | 8 +- .../PluginClientEngine.swift | 4 +- .../SdkHttpRequest+updatingUserAgent.swift | 19 +- .../UserAgentSettingClientEngine.swift | 6 +- .../UserAgentSuffixAppender.swift | 7 +- .../IAMCredentialProvider.swift | 9 +- .../Auth/AWSAuthServiceTests.swift | 4 +- .../UserAgentSettingClientEngineTests.swift | 20 +-- .../Utils/UserAgentSuffixAppenderTests.swift | 11 +- .../AWSLocationGeoPlugin+Configure.swift | 5 +- .../Support/Utils/GeoErrorConvertible.swift | 2 +- .../Mocks/MockAWSClientConfiguration.swift | 14 +- .../Analytics/ClientError+IsRetryable.swift | 27 ++- .../Analytics/EventRecorder.swift | 7 +- .../Context/AWSPinpointFactory.swift | 4 +- .../Context/PinpointContext.swift | 9 +- .../PinpointClientTypes+Codable.swift | 136 ++++++++++++++ .../PinpointClient+CredentialsProvider.swift | 6 +- .../SDKModels+AmplifyStringConvertible.swift | 26 +-- .../Utils/AmplifyStringConvertible.swift | 146 +++++++++++++-- .../Utils/PinpointRequestsRegistry.swift | 4 +- .../AWSPinpointFactoryTests.swift | 2 +- .../Mocks/MockEndpointClient.swift | 6 - .../PinpointClientTypesCodableTests.swift | 70 ++++++++ .../PinpointRequestsRegistryTests.swift | 13 +- .../AWSCloudWatchLoggingCategoryClient.swift | 10 +- .../AWSCloudWatchLoggingPlugin.swift | 4 +- ...WSCloudWatchLoggingSessionController.swift | 11 +- ...aultRemoteLoggingConstraintsProvider.swift | 19 +- ...udWatchLoggingSessionControllerTests.swift | 2 +- ...nt+PushNotificationsErrorConvertible.swift | 2 +- ...ushNotificationsPluginConfigureTests.swift | 2 +- .../ErrorPushNotificationsTests.swift | 1 + .../AWSPredictionsPlugin+Configure.swift | 4 +- .../AWSTranscribeStreamingAdapter.swift | 9 +- ...scribeStreamingClientTypes+Decodable.swift | 169 ++++++++++++++++++ .../Predictions/AWSPredictionsService.swift | 24 ++- .../AWSS3PreSignedURLBuilderAdapter.swift | 1 - ...S3ClientConfiguration+withAccelerate.swift | 47 ++--- ...ClientConfigurationProtocol+Endpoint.swift | 10 +- .../Dependency/SdkTypealiases.swift | 5 +- .../UploadPartInput+presignURL.swift | 148 ++++++++------- .../Error/AWSS3+StorageErrorConvertible.swift | 2 +- .../Service/Storage/AWSS3StorageService.swift | 4 +- .../CreateMultipartUploadRequest.swift | 4 +- .../StorageMultipartUploadSession.swift | 14 +- .../StorageServiceSessionDelegate.swift | 12 ++ .../Support/Utils/HttpClientEngineProxy.swift | 2 +- .../S3ClientConfigurationProxyTests.swift | 29 ++- .../AWSS3StorageServiceListTests.swift | 6 +- .../Storage/AWSS3StorageServiceTests.swift | 11 +- .../StorageMultipartUploadSessionTests.swift | 3 +- ...ragePluginAccelerateIntegrationTests.swift | 22 ++- .../AWSS3StoragePluginAccessLevelTests.swift | 16 +- ...S3StoragePluginBasicIntegrationTests.swift | 37 ++-- ...3StoragePluginGetURLIntegrationTests.swift | 12 +- ...agePluginListObjectsIntegrationTests.swift | 90 ++++++---- .../AWSS3StoragePluginProgressTests.swift | 4 +- ...3StoragePluginRemoveIntegrationTests.swift | 38 ++-- .../AWSS3StoragePluginRequestRecorder.swift | 7 +- .../AWSS3StoragePluginTestBase.swift | 14 ++ ...3StoragePluginUploadIntegrationTests.swift | 34 ++-- ...3StoragePluginUploadMetadataTestCase.swift | 60 ++++--- ...toragePluginGetDataResumabilityTests.swift | 21 ++- .../Mocks/MockCredentialsProvider.swift | 11 +- Package.resolved | 12 +- Package.swift | 2 +- 132 files changed, 1494 insertions(+), 706 deletions(-) create mode 100644 AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointClientTypes+Codable.swift create mode 100644 AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointClientTypesCodableTests.swift create mode 100644 AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/TranscribeStreamingClientTypes+Decodable.swift diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/RequestInterceptor/IAMURLRequestInterceptor.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/RequestInterceptor/IAMURLRequestInterceptor.swift index 5b354abb1d..534f70f5da 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/RequestInterceptor/IAMURLRequestInterceptor.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/RequestInterceptor/IAMURLRequestInterceptor.swift @@ -9,7 +9,8 @@ import Amplify import AWSPluginsCore import InternalAmplifyCredentials import Foundation -import ClientRuntime +import SmithyHTTPAPI +import Smithy typealias AWSRegionType = String @@ -41,12 +42,12 @@ struct IAMURLRequestInterceptor: URLRequestInterceptor { request.setValue(userAgent, forHTTPHeaderField: URLRequestConstants.Header.userAgent) let httpMethod = (request.httpMethod?.uppercased()) - .flatMap(HttpMethodType.init(rawValue:)) ?? .get + .flatMap(HTTPMethodType.init(rawValue:)) ?? .get let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems? - .map { ClientRuntime.SDKURLQueryItem(name: $0.name, value: $0.value)} ?? [] + .map { URIQueryItem(name: $0.name, value: $0.value)} ?? [] - let requestBuilder = SdkHttpRequestBuilder() + let requestBuilder = HTTPRequestBuilder() .withHost(host) .withPath(url.path) .withQueryItems(queryItems) @@ -66,7 +67,7 @@ struct IAMURLRequestInterceptor: URLRequestInterceptor { guard let urlRequest = try await AmplifyAWSSignatureV4Signer().sigV4SignedRequest( requestBuilder: requestBuilder, - credentialsProvider: iamCredentialsProvider.getCredentialsProvider(), + credentialIdentityResolver: iamCredentialsProvider.getCredentialIdentityResolver(), signingName: signingName, signingRegion: region, date: Date() diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift index cd023676c7..675bdca4ee 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift @@ -9,15 +9,15 @@ import Foundation @_spi(WebSocket) import AWSPluginsCore import InternalAmplifyCredentials import Amplify -import AWSClientRuntime -import ClientRuntime +import SmithyHTTPAPI +import SmithyIdentity class IAMAuthInterceptor { - let authProvider: CredentialsProviding + let authProvider: any AWSCredentialIdentityResolver let region: AWSRegionType - init(_ authProvider: CredentialsProviding, region: AWSRegionType) { + init(_ authProvider: some AWSCredentialIdentityResolver, region: AWSRegionType) { self.authProvider = authProvider self.region = region } @@ -35,7 +35,7 @@ class IAMAuthInterceptor { /// /// 1. A request is created with the IAM based auth headers (date, accept, content encoding, content type, and /// additional headers. - let requestBuilder = SdkHttpRequestBuilder() + let requestBuilder = HTTPRequestBuilder() .withHost(host) .withPath(endpoint.path) .withMethod(.post) @@ -51,7 +51,7 @@ class IAMAuthInterceptor { /// the request headers as authorization and security token. do { guard let urlRequest = try await signer.sigV4SignedRequest(requestBuilder: requestBuilder, - credentialsProvider: authProvider, + credentialIdentityResolver: authProvider, signingName: "appsync", signingRegion: region, date: Date()) else { diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift index 57a3708e1e..61b4fb67ac 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift @@ -101,8 +101,8 @@ actor AppSyncRealTimeClientFactory: AppSyncRealTimeClientFactoryProtocol { let provider = AWSOIDCAuthProvider(authService: authService) return AuthTokenInterceptor(getLatestAuthToken: provider.getLatestAuthToken) case .awsIAM(let awsIAMConfiguration): - return IAMAuthInterceptor(authService.getCredentialsProvider(), - region: awsIAMConfiguration.region) + return IAMAuthInterceptor(authService.getCredentialIdentityResolver(), + region: awsIAMConfiguration.region) case .openIDConnect: guard let oidcAuthProvider = apiAuthProviderFactory.oidcAuthProvider() else { throw APIError.invalidConfiguration( diff --git a/AmplifyPlugins/Analytics/Tests/AWSPinpointAnalyticsPluginUnitTests/AWSPinpointAnalyticsPluginConfigureTests.swift b/AmplifyPlugins/Analytics/Tests/AWSPinpointAnalyticsPluginUnitTests/AWSPinpointAnalyticsPluginConfigureTests.swift index 4c14742004..83de1f08d4 100644 --- a/AmplifyPlugins/Analytics/Tests/AWSPinpointAnalyticsPluginUnitTests/AWSPinpointAnalyticsPluginConfigureTests.swift +++ b/AmplifyPlugins/Analytics/Tests/AWSPinpointAnalyticsPluginUnitTests/AWSPinpointAnalyticsPluginConfigureTests.swift @@ -14,7 +14,7 @@ import XCTest class AWSPinpointAnalyticsPluginConfigureTests: AWSPinpointAnalyticsPluginTestBase { override func setUp() async throws { - AWSPinpointFactory.credentialsProvider = MockCredentialsProvider() + AWSPinpointFactory.credentialIdentityResolver = MockCredentialsProvider() try await super.setUp() } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift index b77c3ad92e..51f3e66db4 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+AppSyncSigner.swift @@ -8,9 +8,13 @@ import Foundation import Amplify // Amplify.Auth import AWSPluginsCore // AuthAWSCredentialsProvider -import AWSClientRuntime // AWSClientRuntime.CredentialsProviding -import ClientRuntime // SdkHttpRequestBuilder import AwsCommonRuntimeKit // CommonRuntimeKit.initialize() +import AWSSDKHTTPAuth // AWSSigV4Signer +import Smithy // URIQueryItem +import SmithyHTTPAPI +import SmithyHTTPAuth +import SmithyHTTPAuthAPI +import SmithyIdentity // AWSCredentialIdentity extension AWSCognitoAuthPlugin { @@ -29,11 +33,15 @@ extension AWSCognitoAuthPlugin { region: region) } } - + + private static var signer = { + return AWSSigV4Signer() + }() + static func signAppSyncRequest(_ urlRequest: URLRequest, region: Swift.String, signingName: Swift.String = "appsync", - date: ClientRuntime.Date = Date()) async throws -> URLRequest { + date: Date = Date()) async throws -> URLRequest { CommonRuntimeKit.initialize() // Convert URLRequest to SDK's HTTPRequest @@ -43,11 +51,11 @@ extension AWSCognitoAuthPlugin { } // Retrieve the credentials from credentials provider - let credentials: AWSClientRuntime.AWSCredentials + let credentials: AWSCredentialIdentity let authSession = try await Amplify.Auth.fetchAuthSession() if let awsCredentialsProvider = authSession as? AuthAWSCredentialsProvider { let awsCredentials = try awsCredentialsProvider.getAWSCredentials().get() - credentials = awsCredentials.toAWSSDKCredentials() + credentials = try awsCredentials.toAWSSDKCredentials() } else { let error = AuthError.unknown("Auth session does not include AWS credentials information") throw error @@ -70,7 +78,7 @@ extension AWSCognitoAuthPlugin { signingAlgorithm: .sigv4) // Sign request - guard let httpRequest = await AWSSigV4Signer.sigV4SignedRequest( + guard let httpRequest = await signer.sigV4SignedRequest( requestBuilder: requestBuilder, signingConfig: signingConfig @@ -82,7 +90,7 @@ extension AWSCognitoAuthPlugin { return setHeaders(from: httpRequest, to: urlRequest) } - static func setHeaders(from sdkRequest: SdkHttpRequest, to urlRequest: URLRequest) -> URLRequest { + static func setHeaders(from sdkRequest: SmithyHTTPAPI.HTTPRequest, to urlRequest: URLRequest) -> URLRequest { var urlRequest = urlRequest for header in sdkRequest.headers.headers { urlRequest.setValue(header.value.joined(separator: ","), forHTTPHeaderField: header.name) @@ -90,7 +98,7 @@ extension AWSCognitoAuthPlugin { return urlRequest } - static func createAppSyncSdkHttpRequestBuilder(urlRequest: URLRequest) throws -> SdkHttpRequestBuilder? { + static func createAppSyncSdkHttpRequestBuilder(urlRequest: URLRequest) throws -> HTTPRequestBuilder? { guard let url = urlRequest.url, let host = url.host else { @@ -101,12 +109,12 @@ extension AWSCognitoAuthPlugin { headers.updateValue(host, forKey: "host") let httpMethod = (urlRequest.httpMethod?.uppercased()) - .flatMap(HttpMethodType.init(rawValue:)) ?? .get + .flatMap(HTTPMethodType.init(rawValue:)) ?? .get let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems? - .map { ClientRuntime.SDKURLQueryItem(name: $0.name, value: $0.value)} ?? [] + .map { URIQueryItem(name: $0.name, value: $0.value)} ?? [] - let requestBuilder = SdkHttpRequestBuilder() + let requestBuilder = HTTPRequestBuilder() .withHost(host) .withPath(url.path) .withQueryItems(queryItems) @@ -122,19 +130,20 @@ extension AWSCognitoAuthPlugin { extension AWSPluginsCore.AWSCredentials { - func toAWSSDKCredentials() -> AWSClientRuntime.AWSCredentials { + func toAWSSDKCredentials() throws -> AWSCredentialIdentity { if let tempCredentials = self as? AWSTemporaryCredentials { - return AWSClientRuntime.AWSCredentials( + return AWSCredentialIdentity( accessKey: tempCredentials.accessKeyId, secret: tempCredentials.secretAccessKey, - expirationTimeout: tempCredentials.expiration, - sessionToken: tempCredentials.sessionToken) + expiration: tempCredentials.expiration, + sessionToken: tempCredentials.sessionToken + ) } else { - return AWSClientRuntime.AWSCredentials( + return AWSCredentialIdentity( accessKey: accessKeyId, secret: secretAccessKey, - expirationTimeout: Date()) + expiration: nil + ) } - } } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+Configure.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+Configure.swift index fdcfbf9385..482d266575 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+Configure.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/AWSCognitoAuthPlugin+Configure.swift @@ -14,6 +14,8 @@ import ClientRuntime import AWSClientRuntime @_spi(PluginHTTPClientEngine) import InternalAmplifyCredentials @_spi(InternalHttpEngineProxy) import AWSPluginsCore +import SmithyRetriesAPI +import SmithyRetries extension AWSCognitoAuthPlugin { @@ -91,7 +93,8 @@ extension AWSCognitoAuthPlugin { case .userPools(let userPoolConfig), .userPoolsAndIdentityPools(let userPoolConfig, _): let configuration = try CognitoIdentityProviderClient.CognitoIdentityProviderClientConfiguration( region: userPoolConfig.region, - serviceSpecific: .init(endpointResolver: userPoolConfig.endpoint?.resolver) + signingRegion: userPoolConfig.region, + endpointResolver: userPoolConfig.endpoint?.resolver ) if var httpClientEngineProxy = httpClientEngineProxy { @@ -108,11 +111,14 @@ extension AWSCognitoAuthPlugin { } if let maxRetryUnwrapped = networkPreferences?.maxRetryCount { - configuration.retryStrategyOptions = RetryStrategyOptions(maxRetriesBase: Int(maxRetryUnwrapped)) + configuration.retryStrategyOptions = RetryStrategyOptions( + backoffStrategy: ExponentialBackoffStrategy(), + maxRetriesBase: Int(maxRetryUnwrapped) + ) } let authService = AWSAuthService() - configuration.credentialsProvider = authService.getCredentialsProvider() + configuration.awsCredentialIdentityResolver = authService.getCredentialIdentityResolver() return CognitoIdentityProviderClient(config: configuration) default: @@ -133,11 +139,14 @@ extension AWSCognitoAuthPlugin { } if let maxRetryUnwrapped = networkPreferences?.maxRetryCount { - configuration.retryStrategyOptions = RetryStrategyOptions(maxRetriesBase: Int(maxRetryUnwrapped)) + configuration.retryStrategyOptions = RetryStrategyOptions( + backoffStrategy: ExponentialBackoffStrategy(), + maxRetriesBase: Int(maxRetryUnwrapped) + ) } let authService = AWSAuthService() - configuration.credentialsProvider = authService.getCredentialsProvider() + configuration.awsCredentialIdentityResolver = authService.getCredentialIdentityResolver() return CognitoIdentityClient(config: configuration) default: diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/AuthChallengeType.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/AuthChallengeType.swift index 499aa62175..272ecc8702 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/AuthChallengeType.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/AuthChallengeType.swift @@ -26,7 +26,7 @@ enum AuthChallengeType { } -extension CognitoIdentityProviderClientTypes.ChallengeNameType { +extension CognitoIdentityProviderClientTypes.ChallengeNameType: Codable { var authChallengeType: AuthChallengeType { switch self { case .customChallenge: diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/AWSCongnitoIdentityProvider+AuthErrorConvertible.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/AWSCongnitoIdentityProvider+AuthErrorConvertible.swift index 0b3f3c2fa4..2328f1da8d 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/AWSCongnitoIdentityProvider+AuthErrorConvertible.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/AWSCongnitoIdentityProvider+AuthErrorConvertible.swift @@ -8,7 +8,7 @@ import Foundation import Amplify import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime extension ForbiddenException: AuthErrorConvertible { var fallbackDescription: String { "Access to the requested resource is forbidden" } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/ClientError+AuthErrorConvertible.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/ClientError+AuthErrorConvertible.swift index 942c410f70..b890ee76be 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/ClientError+AuthErrorConvertible.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/ErrorMapping/ClientError+AuthErrorConvertible.swift @@ -7,18 +7,26 @@ import Foundation import Amplify -import ClientRuntime - -extension ClientError: AuthErrorConvertible { - var fallbackDescription: String { "Client Error" } +import Smithy +import SmithyHTTPAPI +extension SmithyHTTPAPI.HTTPClientError: AuthErrorConvertible { var authError: AuthError { switch self { case .pathCreationFailed(let message), - .queryItemCreationFailed(let message), - .serializationFailed(let message), - .dataNotFound(let message): + .queryItemCreationFailed(let message): return .service(message, "", self) + } + } +} + +extension Smithy.ClientError: AuthErrorConvertible { + var authError: AuthError { + switch self { + case .serializationFailed(let message), + .dataNotFound(let message), + .invalidValue(let message): + return .service(message, "Check the underlying error and try again", self) case .authError(let message): return .notAuthorized( diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/SdkTypealiases.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/SdkTypealiases.swift index c4328aac58..6baf521d3b 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/SdkTypealiases.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Service/SdkTypealiases.swift @@ -8,5 +8,6 @@ import Foundation import AWSClientRuntime import ClientRuntime +import SmithyHTTPAPI -public typealias NetworkResult = (Result) -> Void +public typealias NetworkResult = (Result) -> Void diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/UserPoolConfigurationData.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/UserPoolConfigurationData.swift index ef95993e96..098f8cc299 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/UserPoolConfigurationData.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/UserPoolConfigurationData.swift @@ -7,6 +7,7 @@ import ClientRuntime @_spi(InternalAmplifyConfiguration) import Amplify +import SmithyHTTPAPI struct UserPoolConfigurationData: Equatable { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/AWSEndpointResolving.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/AWSEndpointResolving.swift index 47c6a89c63..96eb383f00 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/AWSEndpointResolving.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/AWSEndpointResolving.swift @@ -5,22 +5,21 @@ // SPDX-License-Identifier: Apache-2.0 // -import AWSClientRuntime -import ClientRuntime import AWSCognitoIdentityProvider +import SmithyHTTPAPI struct AWSEndpointResolving: AWSCognitoIdentityProvider.EndpointResolver { - func resolve(params: AWSCognitoIdentityProvider.EndpointParams) throws -> ClientRuntime.Endpoint { + func resolve(params: AWSCognitoIdentityProvider.EndpointParams) throws -> SmithyHTTPAPI.Endpoint { try endpoint() } - let endpoint: () throws -> ClientRuntime.Endpoint + let endpoint: () throws -> SmithyHTTPAPI.Endpoint - init(_ endpoint: @escaping () throws -> ClientRuntime.Endpoint) { + init(_ endpoint: @escaping () throws -> SmithyHTTPAPI.Endpoint) { self.endpoint = endpoint } - init(_ endpoint: @escaping @autoclosure () throws -> ClientRuntime.Endpoint) { + init(_ endpoint: @escaping @autoclosure () throws -> SmithyHTTPAPI.Endpoint) { self.endpoint = endpoint } } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/EndpointResolving.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/EndpointResolving.swift index 3b4dab259c..2caf2826e6 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/EndpointResolving.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/CustomEndpoint/EndpointResolving.swift @@ -6,10 +6,10 @@ // import Foundation -import ClientRuntime +import SmithyHTTPAPI struct EndpointResolving { - let run: (String) throws -> ClientRuntime.Endpoint + let run: (String) throws -> SmithyHTTPAPI.Endpoint } extension EndpointResolving { @@ -37,6 +37,6 @@ extension EndpointResolving { // Finally, let's confirm that the endpoint doesn't contain a path. try validate((components, endpoint), with: .pathIsEmpty()) - return ClientRuntime.Endpoint(host: host) + return SmithyHTTPAPI.Endpoint(host: host) } } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/Utils/HttpClientEngineProxy.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/Utils/HttpClientEngineProxy.swift index b3a5edf8d2..2a2fe1153b 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/Utils/HttpClientEngineProxy.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/Utils/HttpClientEngineProxy.swift @@ -6,8 +6,8 @@ // @_spi(InternalHttpEngineProxy) @_spi(InternalAmplifyPluginExtension) import InternalAmplifyCredentials -import ClientRuntime import Foundation +import SmithyHTTPAPI protocol HttpClientEngineProxy: HTTPClient { var target: HTTPClient? { get set } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift index 609fe1c774..eedb002b74 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/AWSCognitoAuthPluginAppSyncSignerTests.swift @@ -30,7 +30,7 @@ class AWSCognitoAuthPluginAppSyncSignerTests: XCTestCase { XCTAssertEqual(request.method, .post) XCTAssertEqual(request.endpoint.port, 443) XCTAssertEqual(request.endpoint.protocolType, .https) - XCTAssertEqual(request.endpoint.headers?.headers, [.init(name: "host", value: "graphql.com")]) + XCTAssertEqual(request.endpoint.headers.headers, [.init(name: "host", value: "graphql.com")]) guard case let .data(data) = request.body else { XCTFail("Unexpected body") return diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/InitiateAuthSRP/VerifyPasswordSRPTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/InitiateAuthSRP/VerifyPasswordSRPTests.swift index 7628638339..062fa9b7bb 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/InitiateAuthSRP/VerifyPasswordSRPTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/InitiateAuthSRP/VerifyPasswordSRPTests.swift @@ -7,7 +7,7 @@ import XCTest import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime @testable import AWSPluginsTestCommon @testable import AWSCognitoAuthPlugin diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/RevokeTokenTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/RevokeTokenTests.swift index f69a2e7fec..cc6ae2290d 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/RevokeTokenTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/RevokeTokenTests.swift @@ -18,7 +18,7 @@ class RevokeTokenTests: XCTestCase { MockIdentityProvider( mockRevokeTokenResponse: { _ in revokeTokenInvoked.fulfill() - return try await RevokeTokenOutput(httpResponse: MockHttpResponse.ok) + return RevokeTokenOutput() } ) } @@ -92,7 +92,7 @@ class RevokeTokenTests: XCTestCase { let identityProviderFactory: BasicUserPoolEnvironment.CognitoUserPoolFactory = { MockIdentityProvider( mockRevokeTokenResponse: { _ in - return try await RevokeTokenOutput(httpResponse: MockHttpResponse.ok) + return RevokeTokenOutput() } ) } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/SignOutGloballyTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/SignOutGloballyTests.swift index 44f7f866d0..92ab3956e4 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/SignOutGloballyTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/SignOut/SignOutGloballyTests.swift @@ -18,7 +18,7 @@ class SignOutGloballyTests: XCTestCase { MockIdentityProvider( mockGlobalSignOutResponse: { _ in globalSignOutInvoked.fulfill() - return try await GlobalSignOutOutput(httpResponse: MockHttpResponse.ok) + return GlobalSignOutOutput() } ) } @@ -88,7 +88,7 @@ class SignOutGloballyTests: XCTestCase { let identityProviderFactory: BasicUserPoolEnvironment.CognitoUserPoolFactory = { MockIdentityProvider( mockGlobalSignOutResponse: { _ in - return try await GlobalSignOutOutput(httpResponse: MockHttpResponse.ok) + return GlobalSignOutOutput() } ) } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/VerifySignInChallenge/VerifySignInChallengeTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/VerifySignInChallenge/VerifySignInChallengeTests.swift index 9937d86b74..ba635f3463 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/VerifySignInChallenge/VerifySignInChallengeTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ActionTests/VerifySignInChallenge/VerifySignInChallengeTests.swift @@ -7,7 +7,7 @@ import XCTest import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime @testable import AWSPluginsTestCommon @testable import AWSCognitoAuthPlugin diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/ClientSecretConfigurationTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/ClientSecretConfigurationTests.swift index 1a3cdea3c7..47c0b7c6f1 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/ClientSecretConfigurationTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/ClientSecretConfigurationTests.swift @@ -104,7 +104,7 @@ class ClientSecretConfigurationTests: XCTestCase { mockIdentityProvider = MockIdentityProvider( mockConfirmForgotPasswordOutput: { request in XCTAssertNotNil(request.secretHash) - return try await ConfirmForgotPasswordOutput(httpResponse: MockHttpResponse.ok) + return ConfirmForgotPasswordOutput() } ) try await plugin.confirmResetPassword( diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/HubEventTests/AuthHubEventHandlerTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/HubEventTests/AuthHubEventHandlerTests.swift index 496e7d2331..5dc7ee016a 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/HubEventTests/AuthHubEventHandlerTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/HubEventTests/AuthHubEventHandlerTests.swift @@ -358,12 +358,12 @@ class AuthHubEventHandlerTests: XCTestCase { let mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in - try await DeleteUserOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + DeleteUserOutput() } ) @@ -380,10 +380,10 @@ class AuthHubEventHandlerTests: XCTestCase { let mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() } ) @@ -418,9 +418,7 @@ class AuthHubEventHandlerTests: XCTestCase { let mockIdentityProvider = MockIdentityProvider( mockInitiateAuthResponse: { _ in - throw try await AWSCognitoIdentityProvider.NotAuthorizedException( - httpResponse: .init(body: .empty, statusCode: .ok) - ) + throw AWSCognitoIdentityProvider.NotAuthorizedException() }) configurePlugin(initialState: initialState, userPoolFactory: mockIdentityProvider) diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/AuthorizationTests/AWSAuthFetchSignInSessionOperationTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/AuthorizationTests/AWSAuthFetchSignInSessionOperationTests.swift index a67aa2522b..0afa434b9c 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/AuthorizationTests/AWSAuthFetchSignInSessionOperationTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/AuthorizationTests/AWSAuthFetchSignInSessionOperationTests.swift @@ -209,9 +209,7 @@ class AWSAuthFetchSignInSessionOperationTests: BaseAuthorizationTests { AmplifyCredentials.testDataWithExpiredTokens)) let initAuth: MockIdentityProvider.MockInitiateAuthResponse = { _ in - throw try await AWSCognitoIdentityProvider.NotAuthorizedException( - httpResponse: MockHttpResponse.ok - ) + throw AWSCognitoIdentityProvider.NotAuthorizedException() } let plugin = configurePluginWith(userPool: { MockIdentityProvider(mockInitiateAuthResponse: initAuth) }, initialState: initialState) @@ -266,9 +264,7 @@ class AWSAuthFetchSignInSessionOperationTests: BaseAuthorizationTests { } let awsCredentials: MockIdentity.MockGetCredentialsResponse = { _ in - throw try await AWSCognitoIdentityProvider.NotAuthorizedException( - httpResponse: MockHttpResponse.ok - ) + throw AWSCognitoIdentityProvider.NotAuthorizedException() } let plugin = configurePluginWith( diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/AuthenticationProviderDeleteUserTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/AuthenticationProviderDeleteUserTests.swift index d7d8080386..fcdd702657 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/AuthenticationProviderDeleteUserTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/AuthenticationProviderDeleteUserTests.swift @@ -13,19 +13,19 @@ import XCTest import AWSCognitoIdentityProvider import ClientRuntime import AwsCommonRuntimeKit -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserSuccess() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in - try await DeleteUserOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + DeleteUserOutput() } ) do { @@ -54,7 +54,7 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { throw AWSCognitoIdentityProvider.InternalErrorException() }, mockDeleteUserOutput: { _ in - try await DeleteUserOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + DeleteUserOutput() } ) do { @@ -79,9 +79,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testOfflineDeleteUser() async throws { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw CommonRunTimeError.crtError(CRTError(code: 1059)) @@ -113,9 +113,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testOfflineDeleteUserAndRetry() async throws { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw CommonRunTimeError.crtError(CRTError(code: 1059)) @@ -134,12 +134,12 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in - try await DeleteUserOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + DeleteUserOutput() } ) do { @@ -165,9 +165,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserInternalErrorException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSClientRuntime.UnknownAWSHTTPServiceError( @@ -203,9 +203,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithInvalidParameterException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.InvalidParameterException() @@ -237,9 +237,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithNotAuthorizedException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.NotAuthorizedException() @@ -270,9 +270,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithPasswordResetRequiredException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.PasswordResetRequiredException() @@ -304,9 +304,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithResourceNotFoundException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.ResourceNotFoundException() @@ -338,9 +338,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithTooManyRequestsException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.TooManyRequestsException() @@ -372,9 +372,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithUserNotConfirmedException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.UserNotConfirmedException() @@ -407,9 +407,9 @@ class AuthenticationProviderDeleteUserTests: BasePluginTest { func testDeleteUserWithUserNotFoundException() async { mockIdentityProvider = MockIdentityProvider( mockRevokeTokenResponse: { _ in - try await RevokeTokenOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + RevokeTokenOutput() }, mockGlobalSignOutResponse: { _ in - try await GlobalSignOutOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + GlobalSignOutOutput() }, mockDeleteUserOutput: { _ in throw AWSCognitoIdentityProvider.UserNotFoundException() diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorConfirmResetPasswordTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorConfirmResetPasswordTests.swift index b8445af966..e72b5fc73b 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorConfirmResetPasswordTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorConfirmResetPasswordTests.swift @@ -21,7 +21,7 @@ class ClientBehaviorConfirmResetPasswordTests: AWSCognitoAuthClientBehaviorTests super.setUp() mockIdentityProvider = MockIdentityProvider( mockConfirmForgotPasswordOutput: { _ in - try await ConfirmForgotPasswordOutput(httpResponse: MockHttpResponse.ok) + ConfirmForgotPasswordOutput() } ) } @@ -63,7 +63,7 @@ class ClientBehaviorConfirmResetPasswordTests: AWSCognitoAuthClientBehaviorTests func testSuccessfulConfirmResetPassword() async throws { mockIdentityProvider = MockIdentityProvider( mockConfirmForgotPasswordOutput: { _ in - try await ConfirmForgotPasswordOutput(httpResponse: MockHttpResponse.ok) + ConfirmForgotPasswordOutput() } ) try await plugin.confirmResetPassword(for: "username", with: "newpassword", confirmationCode: "code", options: nil) @@ -81,7 +81,7 @@ class ClientBehaviorConfirmResetPasswordTests: AWSCognitoAuthClientBehaviorTests mockIdentityProvider = MockIdentityProvider( mockConfirmForgotPasswordOutput: { _ in - try await ConfirmForgotPasswordOutput(httpResponse: MockHttpResponse.ok) + ConfirmForgotPasswordOutput() } ) do { @@ -109,7 +109,7 @@ class ClientBehaviorConfirmResetPasswordTests: AWSCognitoAuthClientBehaviorTests mockConfirmForgotPasswordOutput: { request in XCTAssertNoThrow(request.clientMetadata) XCTAssertEqual(request.clientMetadata?["key"], "value") - return try await ConfirmForgotPasswordOutput(httpResponse: MockHttpResponse.ok) + return ConfirmForgotPasswordOutput() } ) let pluginOptions = AWSAuthConfirmResetPasswordOptions(metadata: ["key": "value"]) @@ -131,7 +131,7 @@ class ClientBehaviorConfirmResetPasswordTests: AWSCognitoAuthClientBehaviorTests mockIdentityProvider = MockIdentityProvider( mockConfirmForgotPasswordOutput: { _ in - try await ConfirmForgotPasswordOutput(httpResponse: MockHttpResponse.ok) + ConfirmForgotPasswordOutput() } ) do { @@ -251,9 +251,7 @@ class ClientBehaviorConfirmResetPasswordTests: AWSCognitoAuthClientBehaviorTests func testConfirmResetPasswordWithInvalidLambdaResponseException() async throws { mockIdentityProvider = MockIdentityProvider( mockConfirmForgotPasswordOutput: { _ in - throw try await AWSCognitoIdentityProvider.InvalidLambdaResponseException( - httpResponse: .init(body: .empty, statusCode: .accepted) - ) + throw AWSCognitoIdentityProvider.InvalidLambdaResponseException() } ) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorResetPasswordTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorResetPasswordTests.swift index e24e688536..40661b0fbc 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorResetPasswordTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/ClientBehaviorResetPasswordTests.swift @@ -173,9 +173,7 @@ class ClientBehaviorResetPasswordTests: AWSCognitoAuthClientBehaviorTests { mockIdentityProvider = MockIdentityProvider( mockForgotPasswordOutput: { _ in - throw try await AWSCognitoIdentityProvider.InternalErrorException( - httpResponse: .init(body: .empty, statusCode: .accepted) - ) + throw AWSCognitoIdentityProvider.InternalErrorException() } ) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/FetchMFAPreferenceTaskTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/FetchMFAPreferenceTaskTests.swift index e713f439d8..9c63e5088a 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/FetchMFAPreferenceTaskTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/FetchMFAPreferenceTaskTests.swift @@ -11,7 +11,7 @@ import XCTest import Amplify @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime // swiftlint:disable type_body_length // swiftlint:disable file_length diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/SetUpTOTPTaskTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/SetUpTOTPTaskTests.swift index 7d21c937a3..9677d365c5 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/SetUpTOTPTaskTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/SetUpTOTPTaskTests.swift @@ -11,7 +11,7 @@ import XCTest import Amplify @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime // swiftlint:disable type_body_length // swiftlint:disable file_length diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/UpdateMFAPreferenceTaskTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/UpdateMFAPreferenceTaskTests.swift index a4c6fa9d27..75b367b5c0 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/UpdateMFAPreferenceTaskTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/UpdateMFAPreferenceTaskTests.swift @@ -11,7 +11,7 @@ import XCTest import Amplify @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime // swiftlint:disable type_body_length // swiftlint:disable file_length @@ -41,11 +41,11 @@ class UpdateMFAPreferenceTaskTests: BasePluginTest { }, mockSetUserMFAPreferenceResponse: { request in XCTAssertEqual( - request.smsMfaSettings, - smsPreference.smsSetting()) + request.smsMfaSettings?.preferredMfa, + smsPreference.smsSetting().preferredMfa) XCTAssertEqual( - request.softwareTokenMfaSettings, - totpPreference.softwareTokenSetting()) + request.softwareTokenMfaSettings?.preferredMfa, + totpPreference.softwareTokenSetting().preferredMfa) return .init() }) diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/VerifyTOTPSetupTaskTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/VerifyTOTPSetupTaskTests.swift index 0cd03bf4e9..e9b727624b 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/VerifyTOTPSetupTaskTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/MFA/VerifyTOTPSetupTaskTests.swift @@ -11,7 +11,7 @@ import XCTest import Amplify @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime // swiftlint:disable type_body_length // swiftlint:disable file_length diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/AWSAuthSignInPluginTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/AWSAuthSignInPluginTests.swift index 1b4a9c973d..22ae40ce2c 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/AWSAuthSignInPluginTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/AWSAuthSignInPluginTests.swift @@ -954,12 +954,7 @@ class AWSAuthSignInPluginTests: BasePluginTest { func testSignInWithPasswordResetRequiredException2() async { self.mockIdentityProvider = MockIdentityProvider(mockInitiateAuthResponse: { _ in - throw try await AWSCognitoIdentityProvider.PasswordResetRequiredException( - httpResponse: .init(body: .empty, statusCode: .badRequest), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.PasswordResetRequiredException() }) let options = AuthSignInRequest.Options() @@ -1132,9 +1127,7 @@ class AWSAuthSignInPluginTests: BasePluginTest { func testSignInWithUserNotConfirmedException2() async { self.mockIdentityProvider = MockIdentityProvider(mockInitiateAuthResponse: { _ in - throw try await AWSCognitoIdentityProvider.UserNotConfirmedException( - httpResponse: .init(body: .empty, statusCode: .badRequest) - ) + throw AWSCognitoIdentityProvider.UserNotConfirmedException() }) let options = AuthSignInRequest.Options() diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/SignInSetUpTOTPTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/SignInSetUpTOTPTests.swift index 387a70f340..65afb80f8a 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/SignInSetUpTOTPTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignIn/SignInSetUpTOTPTests.swift @@ -10,7 +10,7 @@ import AWSCognitoIdentity @testable import Amplify @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime class SignInSetUpTOTPTests: BasePluginTest { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpAPITests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpAPITests.swift index 92812c342d..0dea576291 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpAPITests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpAPITests.swift @@ -11,7 +11,7 @@ import AWSCognitoIdentity @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime class AWSAuthConfirmSignUpAPITests: BasePluginTest { @@ -177,9 +177,7 @@ class AWSAuthConfirmSignUpAPITests: BasePluginTest { self.mockIdentityProvider = MockIdentityProvider( mockConfirmSignUpResponse: { _ in - throw try await AWSCognitoIdentityProvider.InternalErrorException( - httpResponse: .init(body: .empty, statusCode: .accepted) - ) + throw AWSCognitoIdentityProvider.InternalErrorException() } ) diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpTaskTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpTaskTests.swift index 62a488fce2..74eed8cfb8 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpTaskTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthConfirmSignUpTaskTests.swift @@ -16,7 +16,7 @@ import XCTest @testable import AWSCognitoAuthPlugin @testable import AWSPluginsTestCommon import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime import AWSCognitoIdentityProvider class AWSAuthConfirmSignUpTaskTests: XCTestCase { @@ -33,7 +33,7 @@ class AWSAuthConfirmSignUpTaskTests: XCTestCase { let functionExpectation = expectation(description: "API call should be invoked") let confirmSignUp: MockIdentityProvider.MockConfirmSignUpResponse = { _ in functionExpectation.fulfill() - return try await .init(httpResponse: MockHttpResponse.ok) + return .init() } let authEnvironment = Defaults.makeDefaultAuthEnvironment( diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthResendSignUpCodeAPITests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthResendSignUpCodeAPITests.swift index 9ad2404e72..492b17f2a8 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthResendSignUpCodeAPITests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthResendSignUpCodeAPITests.swift @@ -148,9 +148,7 @@ class AWSAuthResendSignUpCodeAPITests: AWSCognitoAuthClientBehaviorTests { mockIdentityProvider = MockIdentityProvider( mockResendConfirmationCodeOutput: { _ in - throw try await AWSCognitoIdentityProvider.CodeDeliveryFailureException( - httpResponse: .init(body: .empty, statusCode: .accepted) - ) + throw AWSCognitoIdentityProvider.CodeDeliveryFailureException() } ) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpAPITests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpAPITests.swift index 206a32f568..bd44caa089 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpAPITests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpAPITests.swift @@ -245,9 +245,7 @@ class AWSAuthSignUpAPITests: BasePluginTest { self.mockIdentityProvider = MockIdentityProvider( mockSignUpResponse: { _ in - throw try await AWSCognitoIdentityProvider.InternalErrorException( - httpResponse: .init(body: .empty, statusCode: .accepted) - ) + throw AWSCognitoIdentityProvider.InternalErrorException() } ) diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpTaskTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpTaskTests.swift index df79b450dc..b2192c1357 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpTaskTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/ClientBehaviorTests/SignUp/AWSAuthSignUpTaskTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import AWSCognitoAuthPlugin @testable import AWSPluginsTestCommon import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime import AWSCognitoIdentityProvider diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorFetchDevicesTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorFetchDevicesTests.swift index b8fc44a77d..a0606fdac9 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorFetchDevicesTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorFetchDevicesTests.swift @@ -20,7 +20,7 @@ class DeviceBehaviorFetchDevicesTests: BasePluginTest { super.setUp() mockIdentityProvider = MockIdentityProvider( mockListDevicesOutput: { _ in - try await ListDevicesOutput(httpResponse: MockHttpResponse.ok) + try ListDevicesOutput() } ) } @@ -147,9 +147,7 @@ class DeviceBehaviorFetchDevicesTests: BasePluginTest { mockIdentityProvider = MockIdentityProvider( mockListDevicesOutput: { _ in - throw try await AWSCognitoIdentityProvider.InternalErrorException( - httpResponse: .init(body: .empty, statusCode: .accepted) - ) + throw AWSCognitoIdentityProvider.InternalErrorException() } ) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorForgetDeviceTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorForgetDeviceTests.swift index a8cc6e99cc..2bceafa709 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorForgetDeviceTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorForgetDeviceTests.swift @@ -20,7 +20,7 @@ class DeviceBehaviorForgetDeviceTests: BasePluginTest { super.setUp() mockIdentityProvider = MockIdentityProvider( mockForgetDeviceResponse: { _ in - try await ForgetDeviceOutput(httpResponse: MockHttpResponse.ok) + ForgetDeviceOutput() } ) } @@ -139,12 +139,7 @@ class DeviceBehaviorForgetDeviceTests: BasePluginTest { mockIdentityProvider = MockIdentityProvider( mockForgetDeviceResponse: { _ in - throw try await AWSCognitoIdentityProvider.InvalidParameterException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.InvalidParameterException() } ) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorRememberDeviceTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorRememberDeviceTests.swift index 5f92f37bcf..b0d262be4c 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorRememberDeviceTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/DeviceBehaviorTests/DeviceBehaviorRememberDeviceTests.swift @@ -20,9 +20,7 @@ class DeviceBehaviorRememberDeviceTests: BasePluginTest { super.setUp() mockIdentityProvider = MockIdentityProvider( mockRememberDeviceResponse: { _ in - try await UpdateDeviceStatusOutput( - httpResponse: MockHttpResponse.ok - ) + try UpdateDeviceStatusOutput() } ) } @@ -143,12 +141,7 @@ class DeviceBehaviorRememberDeviceTests: BasePluginTest { mockIdentityProvider = MockIdentityProvider( mockRememberDeviceResponse: { _ in - throw try await AWSCognitoIdentityProvider.InvalidUserPoolConfigurationException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.InvalidUserPoolConfigurationException() } ) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/AWSCognitoAuthUserBehaviorTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/AWSCognitoAuthUserBehaviorTests.swift index 3c3eebe100..b317ebfeca 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/AWSCognitoAuthUserBehaviorTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/AWSCognitoAuthUserBehaviorTests.swift @@ -27,10 +27,10 @@ class AWSCognitoAuthUserBehaviorTests: BasePluginTest { UpdateUserAttributesOutput() }, mockConfirmUserAttributeOutput: { _ in - try await VerifyUserAttributeOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + VerifyUserAttributeOutput() }, mockChangePasswordOutput: { _ in - try await ChangePasswordOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + ChangePasswordOutput() } ) } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/SendUserAttributeVerificationCodeTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/SendUserAttributeVerificationCodeTests.swift index cf2a4239d9..6271cafa3e 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/SendUserAttributeVerificationCodeTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/SendUserAttributeVerificationCodeTests.swift @@ -76,12 +76,7 @@ class SendUserAttributeVerificationCodeTests: BasePluginTest { func testSendVerificationCodeWithCodeMismatchException() async throws { mockIdentityProvider = MockIdentityProvider(mockGetUserAttributeVerificationCodeOutput: { _ in - throw try await AWSCognitoIdentityProvider.CodeDeliveryFailureException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.CodeDeliveryFailureException() }) do { _ = try await plugin.sendVerificationCode(forUserAttributeKey: .email) diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorChangePasswordTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorChangePasswordTests.swift index 4b17d9d05e..7ce3db71ac 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorChangePasswordTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorChangePasswordTests.swift @@ -25,7 +25,7 @@ class UserBehaviorChangePasswordTests: BasePluginTest { /// func testSuccessfulChangePassword() async throws { self.mockIdentityProvider = MockIdentityProvider(mockChangePasswordOutput: { _ in - return try await ChangePasswordOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + return ChangePasswordOutput() }) try await plugin.update(oldPassword: "old password", to: "new password") } @@ -133,12 +133,7 @@ class UserBehaviorChangePasswordTests: BasePluginTest { func testChangePasswordWithLimitExceededException() async throws { self.mockIdentityProvider = MockIdentityProvider(mockChangePasswordOutput: { _ in - throw try await AWSCognitoIdentityProvider.LimitExceededException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.LimitExceededException() }) do { try await plugin.update(oldPassword: "old password", to: "new password") diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorConfirmAttributeTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorConfirmAttributeTests.swift index 870afbbe4f..0a9974552e 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorConfirmAttributeTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorConfirmAttributeTests.swift @@ -23,7 +23,7 @@ class UserBehaviorConfirmAttributeTests: BasePluginTest { /// func testSuccessfulConfirmUpdateUserAttributes() async throws { mockIdentityProvider = MockIdentityProvider(mockConfirmUserAttributeOutput: { _ in - try await VerifyUserAttributeOutput(httpResponse: .init(body: .empty, statusCode: .ok)) + VerifyUserAttributeOutput() }) try await plugin.confirm(userAttribute: .email, confirmationCode: "code") } @@ -98,12 +98,7 @@ class UserBehaviorConfirmAttributeTests: BasePluginTest { func testcConfirmUpdateUserAttributesWithInternalErrorException() async throws { mockIdentityProvider = MockIdentityProvider(mockConfirmUserAttributeOutput: { _ in - throw try await AWSCognitoIdentityProvider.InternalErrorException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.InternalErrorException() }) do { try await plugin.confirm(userAttribute: .email, confirmationCode: "code") diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorFetchAttributeTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorFetchAttributeTests.swift index d814fe0090..a34aa32bc9 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorFetchAttributeTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorFetchAttributeTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime class UserBehaviorFetchAttributesTests: BasePluginTest { @@ -136,12 +136,7 @@ class UserBehaviorFetchAttributesTests: BasePluginTest { func testFetchUserAttributesWithNotAuthorizedException() async throws { mockIdentityProvider = MockIdentityProvider(mockGetUserAttributeResponse: { _ in - throw try await AWSCognitoIdentityProvider.NotAuthorizedException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.NotAuthorizedException() }) do { diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorUpdateAttributeTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorUpdateAttributeTests.swift index 75f7d52024..8df4a55b47 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorUpdateAttributeTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/UserBehaviourTests/UserBehaviorUpdateAttributeTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import AWSCognitoAuthPlugin import AWSCognitoIdentityProvider import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime class UserBehaviorUpdateAttributesTests: BasePluginTest { @@ -68,12 +68,7 @@ class UserBehaviorUpdateAttributesTests: BasePluginTest { func testUpdateUserAttributesWithAliasExistsException() async throws { mockIdentityProvider = MockIdentityProvider(mockUpdateUserAttributeResponse: { _ in - throw try await AWSCognitoIdentityProvider.AliasExistsException( - httpResponse: .init(body: .empty, statusCode: .accepted), - decoder: nil, - message: nil, - requestID: nil - ) + throw AWSCognitoIdentityProvider.AliasExistsException() }) do { _ = try await plugin.update(userAttribute: AuthUserAttribute(.email, value: "Amplify@amazon.com")) diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Input/InitiateAuthInput+Codable.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Input/InitiateAuthInput+Codable.swift index b37894ccb8..6aca69ede1 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Input/InitiateAuthInput+Codable.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Input/InitiateAuthInput+Codable.swift @@ -30,3 +30,5 @@ extension InitiateAuthInput: Decodable { clientMetadata: clientMetadata) } } + +extension CognitoIdentityProviderClientTypes.AuthFlowType: Decodable { } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ChangePasswordOutputResponse+Codable.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ChangePasswordOutputResponse+Codable.swift index ada8712912..a0cc62c640 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ChangePasswordOutputResponse+Codable.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ChangePasswordOutputResponse+Codable.swift @@ -6,7 +6,7 @@ // import AWSCognitoIdentityProvider -import ClientRuntime +import SmithyHTTPAPI extension ChangePasswordOutput: Codable { @@ -16,7 +16,7 @@ extension ChangePasswordOutput: Codable { public init(from decoder: Decoder) throws { let containerValues = try decoder.container(keyedBy: CodingKeys.self) - guard let httpResponse = try containerValues.decodeIfPresent(HttpResponse.self, forKey: .httpResponse) else { + guard let httpResponse = try containerValues.decodeIfPresent(HTTPResponse.self, forKey: .httpResponse) else { fatalError("Unable to decode http response") } self.init() diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ForgotPasswordOutputResponse+Codable.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ForgotPasswordOutputResponse+Codable.swift index ab21746125..0c35be603a 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ForgotPasswordOutputResponse+Codable.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/ForgotPasswordOutputResponse+Codable.swift @@ -15,10 +15,13 @@ extension ForgotPasswordOutput: Codable { } public init(from decoder: Swift.Decoder) throws { - self.init() - let containerValues = try decoder.container(keyedBy: CodingKeys.self) - let codeDeliveryDetailsDecoded = try containerValues.decodeIfPresent(CognitoIdentityProviderClientTypes.CodeDeliveryDetailsType.self, forKey: .codeDeliveryDetails) - codeDeliveryDetails = codeDeliveryDetailsDecoded + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + codeDeliveryDetails: container.decodeIfPresent( + CognitoIdentityProviderClientTypes.CodeDeliveryDetailsType.self, + forKey: .codeDeliveryDetails + ) + ) } public func encode(to encoder: Encoder) throws { @@ -26,3 +29,25 @@ extension ForgotPasswordOutput: Codable { } } + +extension CognitoIdentityProviderClientTypes.CodeDeliveryDetailsType: Decodable { + private enum CodingKeys: String, CodingKey { + case attributeName = "AttributeName" + case deliveryMedium = "DeliveryMedium" + case destination = "Destination" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + attributeName: container.decodeIfPresent(String.self, forKey: .attributeName), + deliveryMedium: container.decodeIfPresent( + CognitoIdentityProviderClientTypes.DeliveryMediumType.self, + forKey: .deliveryMedium + ), + destination: container.decodeIfPresent(String.self, forKey: .destination) + ) + } +} + +extension CognitoIdentityProviderClientTypes.DeliveryMediumType: Decodable {} diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/GetCredentialsForIdentityOutputResponse+Codable.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/GetCredentialsForIdentityOutputResponse+Codable.swift index 05b56dcaf9..c602cf7108 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/GetCredentialsForIdentityOutputResponse+Codable.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/GetCredentialsForIdentityOutputResponse+Codable.swift @@ -27,3 +27,22 @@ extension GetCredentialsForIdentityOutput: Codable { fatalError("This implementation is not needed") } } + +extension CognitoIdentityClientTypes.Credentials: Decodable { + private enum CodingKeys: String, CodingKey { + case accessKeyId = "AccessKeyId" + case expiration = "Expiration" + case secretKey = "SecretKey" + case sessionToken = "SessionToken" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + accessKeyId: container.decodeIfPresent(String.self, forKey: .accessKeyId), + expiration: container.decodeIfPresent(Date.self, forKey: .expiration), + secretKey: container.decodeIfPresent(String.self, forKey: .secretKey), + sessionToken: container.decodeIfPresent(String.self, forKey: .sessionToken) + ) + } +} diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/HttpResponse+Codable.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/HttpResponse+Codable.swift index 20c6f99f1e..f185186f5b 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/HttpResponse+Codable.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/HttpResponse+Codable.swift @@ -6,29 +6,31 @@ // import AWSCognitoIdentityProvider -import ClientRuntime +import SmithyHTTPAPI -extension HttpResponse: Codable { } +extension SmithyHTTPAPI.HTTPResponse: Codable { } -enum HttpResponseCodingKeys: String, CodingKey { +enum HTTPResponseCodingKeys: String, CodingKey { case statusCode = "statusCode" } -extension Encodable where Self: HttpResponse { +extension Encodable where Self: SmithyHTTPAPI.HTTPResponse { public func encode(to encoder: Encoder) throws { - - var container = encoder.container(keyedBy: HttpResponseCodingKeys.self) + var container = encoder.container(keyedBy: HTTPResponseCodingKeys.self) try container.encode(statusCode.rawValue, forKey: .statusCode) } } -extension Decodable where Self: HttpResponse { +extension Decodable where Self: SmithyHTTPAPI.HTTPResponse { public init(from decoder: Decoder) throws { - let containerValues = try decoder.container(keyedBy: HttpResponseCodingKeys.self) + let containerValues = try decoder.container(keyedBy: HTTPResponseCodingKeys.self) let httpStatusCode = try containerValues.decodeIfPresent(Int.self, forKey: .statusCode) - self = HttpResponse(body: .empty, statusCode: HttpStatusCode(rawValue: httpStatusCode ?? 404) ?? .notFound) as! Self + self = SmithyHTTPAPI.HTTPResponse( + body: .empty, + statusCode: SmithyHTTPAPI.HTTPStatusCode(rawValue: httpStatusCode ?? 404) ?? .notFound + ) as! Self } } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/InitiateAuthOutputResponse+Codable.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/InitiateAuthOutputResponse+Codable.swift index 5e9eae1202..155fd62081 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/InitiateAuthOutputResponse+Codable.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AuthCodableImplementations/Cognito/Response/InitiateAuthOutputResponse+Codable.swift @@ -44,3 +44,44 @@ extension InitiateAuthOutput: Codable { } } + +extension CognitoIdentityProviderClientTypes.AuthenticationResultType: Decodable { + private enum CodingKeys: String, CodingKey { + case accessToken = "AccessToken" + case expiresIn = "ExpiresIn" + case idToken = "IdToken" + case newDeviceMetadata = "NewDeviceMetadata" + case refreshToken = "RefreshToken" + case tokenType = "TokenType" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + accessToken: container.decodeIfPresent(String.self, forKey: .accessToken), + expiresIn: container.decode(Int.self, forKey: .expiresIn), + idToken: container.decodeIfPresent(String.self, forKey: .idToken), + newDeviceMetadata: container.decodeIfPresent( + CognitoIdentityProviderClientTypes.NewDeviceMetadataType.self, + forKey: .newDeviceMetadata + ), + refreshToken: container.decodeIfPresent(String.self, forKey: .refreshToken), + tokenType: container.decodeIfPresent(String.self, forKey: .tokenType) + ) + } +} + +extension CognitoIdentityProviderClientTypes.NewDeviceMetadataType: Decodable { + private enum CodingKeys: String, CodingKey { + case deviceGroupKey = "DeviceGroupKey" + case deviceKey = "DeviceKey" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + deviceGroupKey: container.decodeIfPresent(String.self, forKey: .deviceGroupKey), + deviceKey: container.decodeIfPresent(String.self, forKey: .deviceKey) + ) + } +} diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/CognitoAPIDecoding/CognitoAPIDecodingHelper.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/CognitoAPIDecoding/CognitoAPIDecodingHelper.swift index 2c33d8e7ab..6f80804dce 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/CognitoAPIDecoding/CognitoAPIDecodingHelper.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/CognitoAPIDecoding/CognitoAPIDecodingHelper.swift @@ -9,7 +9,7 @@ import AWSCognitoIdentity import AWSCognitoIdentityProvider import AWSPluginsCore import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime @testable import Amplify @testable import AWSCognitoAuthPlugin diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/Mocks/AuthTestHarnessInput+MockIdentityProvider.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/Mocks/AuthTestHarnessInput+MockIdentityProvider.swift index cca624fbb3..02cd19b66e 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/Mocks/AuthTestHarnessInput+MockIdentityProvider.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/Mocks/AuthTestHarnessInput+MockIdentityProvider.swift @@ -34,7 +34,9 @@ extension AuthTestHarnessInput { fatalError("Missing input") } if let request = apiData.expectedInput { - XCTAssertEqual(input, request) + XCTAssertEqual(input.clientId, request.clientId) + XCTAssertEqual(input.clientSecret, request.clientSecret) + XCTAssertEqual(input.token, request.token) } switch apiData.output { @@ -48,7 +50,9 @@ extension AuthTestHarnessInput { fatalError("Missing input") } if let request = apiData.expectedInput { - XCTAssertEqual(input, request) + XCTAssertEqual(input.clientId, request.clientId) + XCTAssertEqual(input.authParameters, request.authParameters) + XCTAssertEqual(input.clientMetadata, request.clientMetadata) } switch apiData.output { @@ -63,7 +67,7 @@ extension AuthTestHarnessInput { fatalError("Missing input") } if let request = apiData.expectedInput { - XCTAssertEqual(input, request) + XCTAssertEqual(input.accessToken, request.accessToken) } switch apiData.output { @@ -78,7 +82,10 @@ extension AuthTestHarnessInput { fatalError("Missing input") } if let request = apiData.expectedInput { - XCTAssertEqual(request, input) + XCTAssertEqual(request.challengeResponses, input.challengeResponses) + XCTAssertEqual(request.clientId, input.clientId) + XCTAssertEqual(request.clientMetadata, input.clientMetadata) + XCTAssertEqual(request.session, input.session) } switch apiData.output { @@ -122,7 +129,10 @@ extension AuthTestHarnessInput { fatalError("Missing input") } if let request = apiData.expectedInput { - XCTAssertEqual(request, input) + XCTAssertEqual(request.accessToken, input.accessToken) + XCTAssertEqual(request.deviceKey, input.deviceKey) + XCTAssertEqual(request.deviceName, input.deviceName) + XCTAssertEqual(request.deviceSecretVerifierConfig?.passwordVerifier, input.deviceSecretVerifierConfig?.passwordVerifier) } switch apiData.output { diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift index 02cedefdfb..c13675ea17 100644 --- a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/AppSyncSignerTests/AppSyncSignerTests.swift @@ -20,7 +20,7 @@ class AppSyncSignerTests: AWSAuthBaseTest { /// - I should get a signed request. /// func testSignAppSyncRequest() async throws { - let request = URLRequest(url: URL(string: "http://graphql.com")!) + let request = URLRequest(url: URL(string: "http://graphql.com?param=value")!) let signer = AWSCognitoAuthPlugin.createAppSyncSigner(region: "us-east-1") let signedRequest = try await signer(request) guard let headers = signedRequest.allHTTPHeaderFields else { diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/SignOutTests/AuthSignOutTests.swift b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/SignOutTests/AuthSignOutTests.swift index 2c71d77056..6b69e9d322 100644 --- a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/SignOutTests/AuthSignOutTests.swift +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthIntegrationTests/SignOutTests/AuthSignOutTests.swift @@ -18,7 +18,7 @@ class AuthSignOutTests: AWSAuthBaseTest { try await super.setUp() AuthSessionHelper.clearSession() if Self.setSDKLogLevelDebug { - await SDKLoggingSystem.initialize(logLevel: .debug) + await SDKLoggingSystem().initialize(logLevel: .debug) Self.setSDKLogLevelDebug = false } } diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift index f3e53669c1..052f79d716 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift @@ -211,3 +211,23 @@ fileprivate class MockNetworkMonitor: WebSocketNetworkMonitorProtocol { } + +private extension String { + var hexaData: Data { + .init(hexa) + } + + private var hexa: UnfoldSequence { + sequence(state: startIndex) { startIndex in + // bail if we've reached the end of the string + guard startIndex < self.endIndex else { return nil } + + // get the next two characters + let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex + defer { startIndex = endIndex } + + // convert the characters to a UInt8 + return UInt8(self[startIndex.. any SmithyIdentity.AWSCredentialIdentityResolver { + interactions.append(#function) + let cognitoCredentialsProvider = MyCustomCredentialsProvider() + return cognitoCredentialsProvider + } + public func getIdentityID() async throws -> String { interactions.append(#function) if let error = getIdentityIdError { @@ -61,11 +68,12 @@ public class MockAWSAuthService: AWSAuthCredentialsProviderBehavior { } } -struct MyCustomCredentialsProvider: CredentialsProviding { - func getCredentials() async throws -> AWSClientRuntime.AWSCredentials { - AWSCredentials( +struct MyCustomCredentialsProvider: CredentialsProviding, AWSCredentialIdentityResolver { + func getCredentials() async throws -> AwsCommonRuntimeKit.Credentials { + try AwsCommonRuntimeKit.Credentials( accessKey: "AKIDEXAMPLE", secret: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", - expirationTimeout: Date().addingTimeInterval(30)) + expiration: Date().addingTimeInterval(30) + ) } } diff --git a/AmplifyPlugins/Core/AWSPluginsTestCommon/MockAWSSignatureV4Signer.swift b/AmplifyPlugins/Core/AWSPluginsTestCommon/MockAWSSignatureV4Signer.swift index abeb670efb..0daae45de4 100644 --- a/AmplifyPlugins/Core/AWSPluginsTestCommon/MockAWSSignatureV4Signer.swift +++ b/AmplifyPlugins/Core/AWSPluginsTestCommon/MockAWSSignatureV4Signer.swift @@ -6,17 +6,18 @@ // import AWSPluginsCore -import ClientRuntime -import AWSClientRuntime +import AwsCommonRuntimeKit import InternalAmplifyCredentials import Foundation +import SmithyHTTPAPI +import SmithyIdentity class MockAWSSignatureV4Signer: AWSSignatureV4Signer { - func sigV4SignedRequest(requestBuilder: SdkHttpRequestBuilder, - credentialsProvider: CredentialsProviding, + func sigV4SignedRequest(requestBuilder: SmithyHTTPAPI.HTTPRequestBuilder, + credentialIdentityResolver: some AWSCredentialIdentityResolver, signingName: String, signingRegion: String, - date: Date) throws -> SdkHttpRequest? { + date: Date) throws -> SmithyHTTPAPI.HTTPRequest? { let originalRequest = requestBuilder.build() return originalRequest } diff --git a/AmplifyPlugins/Core/AWSPluginsTestCommon/MockHttpResponse.swift b/AmplifyPlugins/Core/AWSPluginsTestCommon/MockHttpResponse.swift index 684704a03b..3210b5f59b 100644 --- a/AmplifyPlugins/Core/AWSPluginsTestCommon/MockHttpResponse.swift +++ b/AmplifyPlugins/Core/AWSPluginsTestCommon/MockHttpResponse.swift @@ -5,10 +5,10 @@ // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime +import SmithyHTTPAPI class MockHttpResponse { - class var ok: HttpResponse { - HttpResponse(body: .empty, statusCode: .ok) + class var ok: SmithyHTTPAPI.HTTPResponse { + SmithyHTTPAPI.HTTPResponse(body: .empty, statusCode: .ok) } } diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthCredentialsProviderBehavior.swift b/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthCredentialsProviderBehavior.swift index 6d6cfe9277..04339f6bf0 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthCredentialsProviderBehavior.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthCredentialsProviderBehavior.swift @@ -7,11 +7,13 @@ import Foundation import Amplify -import AWSClientRuntime +import AwsCommonRuntimeKit import AWSPluginsCore +import SmithyIdentity public protocol AWSAuthCredentialsProviderBehavior: AWSAuthServiceBehavior { func getCredentialsProvider() -> CredentialsProviding -} + func getCredentialIdentityResolver() -> any AWSCredentialIdentityResolver +} diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthService+CredentialsProvider.swift b/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthService+CredentialsProvider.swift index ec8babe3d5..a2a7e58206 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthService+CredentialsProvider.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AWSAuthService+CredentialsProvider.swift @@ -7,11 +7,16 @@ import Foundation import Amplify -import AWSClientRuntime +import AwsCommonRuntimeKit import AWSPluginsCore +import SmithyIdentity extension AWSAuthService: AWSAuthCredentialsProviderBehavior { - public func getCredentialsProvider() -> AWSClientRuntime.CredentialsProviding { + public func getCredentialsProvider() -> AwsCommonRuntimeKit.CredentialsProviding { + return AmplifyAWSCredentialsProvider() + } + + public func getCredentialIdentityResolver() -> any AWSCredentialIdentityResolver { return AmplifyAWSCredentialsProvider() } } diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift index 8a45c1d64a..a0d6cc29c6 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSCredentialsProvider.swift @@ -6,18 +6,32 @@ // import Amplify -import AWSClientRuntime import AwsCommonRuntimeKit import AWSPluginsCore import Foundation +import Smithy +import SmithyIdentity -public class AmplifyAWSCredentialsProvider: AWSClientRuntime.CredentialsProviding { - - public func getCredentials() async throws -> AWSClientRuntime.AWSCredentials { +public class AmplifyAWSCredentialsProvider: AwsCommonRuntimeKit.CredentialsProviding { + + public func getCredentials() async throws -> AwsCommonRuntimeKit.Credentials { let authSession = try await Amplify.Auth.fetchAuthSession() if let awsCredentialsProvider = authSession as? AuthAWSCredentialsProvider { let credentials = try awsCredentialsProvider.getAWSCredentials().get() - return credentials.toAWSSDKCredentials() + return try credentials.toAWSSDKCredentials() + } else { + let error = AuthError.unknown("Auth session does not include AWS credentials information") + throw error + } + } +} + +extension AmplifyAWSCredentialsProvider: AWSCredentialIdentityResolver { + public func getIdentity(identityProperties: Smithy.Attributes? = nil) async throws -> AWSCredentialIdentity { + let authSession = try await Amplify.Auth.fetchAuthSession() + if let awsCredentialsProvider = authSession as? AuthAWSCredentialsProvider { + let credentials = try awsCredentialsProvider.getAWSCredentials().get() + return try credentials.toAWSCredentialIdentity() } else { let error = AuthError.unknown("Auth session does not include AWS credentials information") throw error @@ -27,19 +41,30 @@ public class AmplifyAWSCredentialsProvider: AWSClientRuntime.CredentialsProvidin extension AWSPluginsCore.AWSCredentials { - func toAWSSDKCredentials() -> AWSClientRuntime.AWSCredentials { + func toAWSSDKCredentials() throws -> AwsCommonRuntimeKit.Credentials { if let tempCredentials = self as? AWSTemporaryCredentials { - return AWSClientRuntime.AWSCredentials( + return try AwsCommonRuntimeKit.Credentials( accessKey: tempCredentials.accessKeyId, secret: tempCredentials.secretAccessKey, - expirationTimeout: tempCredentials.expiration, - sessionToken: tempCredentials.sessionToken) + sessionToken: tempCredentials.sessionToken, + expiration: tempCredentials.expiration + ) } else { - return AWSClientRuntime.AWSCredentials( + return try AwsCommonRuntimeKit.Credentials( accessKey: accessKeyId, secret: secretAccessKey, - expirationTimeout: Date()) + expiration: nil + ) } } + + func toAWSCredentialIdentity() throws -> SmithyIdentity.AWSCredentialIdentity { + return SmithyIdentity.AWSCredentialIdentity( + accessKey: accessKeyId, + secret: secretAccessKey, + expiration: (self as? AWSTemporaryCredentials)?.expiration, + sessionToken: (self as? AWSTemporaryCredentials)?.sessionToken + ) + } } diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift index 015f2e8395..6182e50802 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSSignatureV4Signer.swift @@ -7,36 +7,41 @@ import Foundation import Amplify -import ClientRuntime -import AWSClientRuntime -import AwsCommonRuntimeKit +import AWSSDKHTTPAuth +import SmithyHTTPAPI +import SmithyHTTPAuthAPI +import SmithyHTTPAuth +import SmithyIdentity public protocol AWSSignatureV4Signer { - func sigV4SignedRequest(requestBuilder: SdkHttpRequestBuilder, - credentialsProvider: AWSClientRuntime.CredentialsProviding, + func sigV4SignedRequest(requestBuilder: SmithyHTTPAPI.HTTPRequestBuilder, + credentialIdentityResolver: some AWSCredentialIdentityResolver, signingName: Swift.String, signingRegion: Swift.String, - date: ClientRuntime.Date) async throws -> SdkHttpRequest? + date: Date) async throws -> SmithyHTTPAPI.HTTPRequest? } public class AmplifyAWSSignatureV4Signer: AWSSignatureV4Signer { - public init() { + private let signer: AWSSigV4Signer + + public init(signer: AWSSigV4Signer = .init()) { + self.signer = signer } - public func sigV4SignedRequest(requestBuilder: SdkHttpRequestBuilder, - credentialsProvider: AWSClientRuntime.CredentialsProviding, + public func sigV4SignedRequest(requestBuilder: SmithyHTTPAPI.HTTPRequestBuilder, + credentialIdentityResolver: some AWSCredentialIdentityResolver, signingName: Swift.String, signingRegion: Swift.String, - date: ClientRuntime.Date) async throws -> SdkHttpRequest? { + date: Date) async throws -> SmithyHTTPAPI.HTTPRequest? { do { - let credentials = try await credentialsProvider.getCredentials() + let credentialIdentity = try await credentialIdentityResolver.getIdentity() let flags = SigningFlags(useDoubleURIEncode: true, shouldNormalizeURIPath: true, omitSessionToken: false) let signedBodyHeader: AWSSignedBodyHeader = .none let signedBodyValue: AWSSignedBodyValue = .empty - let signingConfig = AWSSigningConfig(credentials: credentials, + let signingConfig = AWSSigningConfig(credentials: credentialIdentity, signedBodyHeader: signedBodyHeader, signedBodyValue: signedBodyValue, flags: flags, @@ -46,7 +51,7 @@ public class AmplifyAWSSignatureV4Signer: AWSSignatureV4Signer { signatureType: .requestHeaders, signingAlgorithm: .sigv4) - let httpRequest = await AWSSigV4Signer.sigV4SignedRequest( + let httpRequest = await signer.sigV4SignedRequest( requestBuilder: requestBuilder, signingConfig: signingConfig ) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/ClientRuntimeFoundationBridge.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/ClientRuntimeFoundationBridge.swift index 18fa1470b2..100fd85a1b 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/ClientRuntimeFoundationBridge.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/ClientRuntimeFoundationBridge.swift @@ -6,30 +6,31 @@ // import Foundation -import ClientRuntime +import Smithy +import SmithyHTTPAPI extension Foundation.URLRequest { - init(sdkRequest: ClientRuntime.SdkHttpRequest) async throws { - guard let url = sdkRequest.endpoint.url else { - throw FoundationClientEngineError.invalidRequestURL(sdkRequest: sdkRequest) + init(from smithyRequest: SmithyHTTPAPI.HTTPRequest) async throws { + guard let url = smithyRequest.endpoint.url else { + throw FoundationClientEngineError.invalidRequestURL(smithyRequest: smithyRequest) } self.init(url: url) - httpMethod = sdkRequest.method.rawValue + httpMethod = smithyRequest.method.rawValue - for header in sdkRequest.headers.headers { + for header in smithyRequest.headers.headers { for value in header.value { addValue(value, forHTTPHeaderField: header.name) } } - httpBody = try await sdkRequest.body.readData() + httpBody = try await smithyRequest.body.readData() } } -extension ClientRuntime.HttpResponse { +extension SmithyHTTPAPI.HTTPResponse { private static func headers( from allHeaderFields: [AnyHashable: Any] - ) -> ClientRuntime.Headers { + ) -> SmithyHTTPAPI.Headers { var headers = Headers() for header in allHeaderFields { switch (header.key, header.value) { @@ -47,7 +48,7 @@ extension ClientRuntime.HttpResponse { let headers = Self.headers(from: httpURLResponse.allHeaderFields) let body = ByteStream.data(data) - guard let statusCode = HttpStatusCode(rawValue: httpURLResponse.statusCode) else { + guard let statusCode = HTTPStatusCode(rawValue: httpURLResponse.statusCode) else { // This shouldn't happen, but `HttpStatusCode` only exposes a failable // `init`. The alternative here is force unwrapping, but we can't // make the decision to crash here on behalf on consuming applications. diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngine.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngine.swift index d8f18fe889..6aa0bf7fee 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngine.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngine.swift @@ -6,13 +6,13 @@ // import Foundation -import ClientRuntime import Amplify +import SmithyHTTPAPI @_spi(FoundationClientEngine) public struct FoundationClientEngine: HTTPClient { - public func send(request: ClientRuntime.SdkHttpRequest) async throws -> ClientRuntime.HttpResponse { - let urlRequest = try await URLRequest(sdkRequest: request) + public func send(request: SmithyHTTPAPI.HTTPRequest) async throws -> SmithyHTTPAPI.HTTPResponse { + let urlRequest = try await URLRequest(from: request) let (data, response) = try await URLSession.shared.data(for: urlRequest) guard let httpURLResponse = response as? HTTPURLResponse else { @@ -22,7 +22,7 @@ public struct FoundationClientEngine: HTTPClient { throw FoundationClientEngineError.invalidURLResponse(urlRequest: response) } - let httpResponse = try HttpResponse( + let httpResponse = try HTTPResponse( httpURLResponse: httpURLResponse, data: data ) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngineError.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngineError.swift index 09e6df49ef..098a452431 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngineError.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/FoundationClientEngineError.swift @@ -7,7 +7,7 @@ import Foundation import Amplify -import ClientRuntime +import SmithyHTTPAPI struct FoundationClientEngineError: AmplifyError { let errorDescription: ErrorDescription @@ -37,11 +37,11 @@ extension FoundationClientEngineError { self.underlyingError = error } - static func invalidRequestURL(sdkRequest: ClientRuntime.SdkHttpRequest) -> Self { + static func invalidRequestURL(smithyRequest: SmithyHTTPAPI.HTTPRequest) -> Self { .init( errorDescription: """ - The SdkHttpRequest generated by ClientRuntime doesn't include a valid URL - - \(sdkRequest) + The HTTPRequest generated by SmithyHTTP doesn't include a valid URL + - \(smithyRequest) """, recoverySuggestion: """ Please open an issue at https://github.com/aws-amplify/amplify-swift diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/PluginClientEngine.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/PluginClientEngine.swift index 6a2e8d7544..964adff859 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/PluginClientEngine.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/PluginClientEngine.swift @@ -7,11 +7,11 @@ import Foundation import ClientRuntime -import AWSClientRuntime +import SmithyHTTPAPI @_spi(PluginHTTPClientEngine) public func baseClientEngine( - for configuration: AWSClientConfiguration + for configuration: ClientRuntime.DefaultHttpClientConfiguration ) -> HTTPClient { return FoundationClientEngine() } diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/SdkHttpRequest+updatingUserAgent.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/SdkHttpRequest+updatingUserAgent.swift index 690b8f932f..586a7a9127 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/SdkHttpRequest+updatingUserAgent.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/SdkHttpRequest+updatingUserAgent.swift @@ -6,27 +6,22 @@ // import Foundation -import ClientRuntime +import SmithyHTTPAPI @_spi(PluginHTTPClientEngine) -extension SdkHttpRequest { - public func updatingUserAgent(with value: String) -> SdkHttpRequest { +extension HTTPRequest { + public func updatingUserAgent(with value: String) -> HTTPRequest { let userAgentKey = "User-Agent" var headers = headers headers.remove(name: userAgentKey) headers.add(name: userAgentKey, value: value) - let endpoint = ClientRuntime.Endpoint( - host: endpoint.host, - path: endpoint.path, - port: endpoint.port, - queryItems: endpoint.queryItems, - protocolType: endpoint.protocolType, - headers: headers, - properties: endpoint.properties + let endpoint = SmithyHTTPAPI.Endpoint( + uri: endpoint.uri, + headers: headers ) - return SdkHttpRequest( + return HTTPRequest( method: method, endpoint: endpoint, body: body diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSettingClientEngine.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSettingClientEngine.swift index 807403f21a..81eb7f6213 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSettingClientEngine.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSettingClientEngine.swift @@ -7,7 +7,7 @@ import Foundation import ClientRuntime -import AWSClientRuntime +import SmithyHTTPAPI @_spi(PluginHTTPClientEngine) public struct UserAgentSettingClientEngine: AWSPluginExtension { @@ -28,7 +28,7 @@ extension UserAgentSettingClientEngine: HTTPClient { // as it's no longer necessary there. var lib: String { AmplifyAWSServiceConfiguration.userAgentLib } - public func send(request: SdkHttpRequest) async throws -> HttpResponse { + public func send(request: HTTPRequest) async throws -> HTTPResponse { let existingUserAgent = request.headers.value(for: userAgentKey) ?? "" let userAgent = "\(existingUserAgent) \(lib)" let updatedRequest = request.updatingUserAgent(with: userAgent) @@ -40,7 +40,7 @@ extension UserAgentSettingClientEngine: HTTPClient { @_spi(PluginHTTPClientEngine) extension HTTPClient where Self == UserAgentSettingClientEngine { public static func userAgentEngine( - for configuration: AWSClientConfiguration + for configuration: ClientRuntime.DefaultHttpClientConfiguration ) -> Self { let baseClientEngine = baseClientEngine(for: configuration) return self.init(target: baseClientEngine) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSuffixAppender.swift b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSuffixAppender.swift index bb82ae44f3..3e0b5139ff 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSuffixAppender.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/CustomHttpClientEngine/UserAgentSuffixAppender.swift @@ -5,7 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime +import SmithyHTTPAPI +import Smithy @_spi(InternalAmplifyPluginExtension) public class UserAgentSuffixAppender: AWSPluginExtension { @@ -21,9 +22,9 @@ public class UserAgentSuffixAppender: AWSPluginExtension { @_spi(InternalHttpEngineProxy) extension UserAgentSuffixAppender: HTTPClient { - public func send(request: SdkHttpRequest) async throws -> HttpResponse { + public func send(request: SmithyHTTPAPI.HTTPRequest) async throws -> SmithyHTTPAPI.HTTPResponse { guard let target = target else { - throw ClientError.unknownError("HttpClientEngine is not set") + throw Smithy.ClientError.unknownError("HttpClientEngine is not set") } let existingUserAgent = request.headers.value(for: userAgentKey) ?? "" diff --git a/AmplifyPlugins/Core/AmplifyCredentials/IAMCredentialProvider.swift b/AmplifyPlugins/Core/AmplifyCredentials/IAMCredentialProvider.swift index 1265a9130f..ce6617a7be 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/IAMCredentialProvider.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/IAMCredentialProvider.swift @@ -7,11 +7,14 @@ import Foundation import Amplify -import AWSClientRuntime +import AwsCommonRuntimeKit import AWSPluginsCore +import SmithyIdentity public protocol IAMCredentialsProvider { func getCredentialsProvider() -> CredentialsProviding + + func getCredentialIdentityResolver() -> any AWSCredentialIdentityResolver } public struct BasicIAMCredentialsProvider: IAMCredentialsProvider { @@ -24,4 +27,8 @@ public struct BasicIAMCredentialsProvider: IAMCredentialsProvider { public func getCredentialsProvider() -> CredentialsProviding { return authService.getCredentialsProvider() } + + public func getCredentialIdentityResolver() -> any AWSCredentialIdentityResolver { + authService.getCredentialIdentityResolver() + } } diff --git a/AmplifyPlugins/Core/AmplifyCredentialsTests/Auth/AWSAuthServiceTests.swift b/AmplifyPlugins/Core/AmplifyCredentialsTests/Auth/AWSAuthServiceTests.swift index abca5ad99c..85c209e12b 100644 --- a/AmplifyPlugins/Core/AmplifyCredentialsTests/Auth/AWSAuthServiceTests.swift +++ b/AmplifyPlugins/Core/AmplifyCredentialsTests/Auth/AWSAuthServiceTests.swift @@ -167,7 +167,7 @@ class AWSAuthServiceTests: XCTestCase { accessKeyId: "accessKeyId", secretAccessKey: "secretAccessKey", expiration: Date().addingTimeInterval(100)) - let sdkCredentials = credentials.toAWSSDKCredentials() + let sdkCredentials = try credentials.toAWSSDKCredentials() XCTAssertNotNil(sdkCredentials) } @@ -181,7 +181,7 @@ class AWSAuthServiceTests: XCTestCase { accessKeyId: "accessKeyId", secretAccessKey: "secretAccessKey", expiration: Date().addingTimeInterval(-100)) - let sdkCredentials = credentials.toAWSSDKCredentials() + let sdkCredentials = try credentials.toAWSSDKCredentials() XCTAssertNotNil(sdkCredentials) } } diff --git a/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSettingClientEngineTests.swift b/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSettingClientEngineTests.swift index a6b2d3a800..b5ac23f362 100644 --- a/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSettingClientEngineTests.swift +++ b/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSettingClientEngineTests.swift @@ -9,7 +9,7 @@ @_spi(PluginHTTPClientEngine) @_spi(InternalHttpEngineProxy) import InternalAmplifyCredentials -import ClientRuntime +import SmithyHTTPAPI import XCTest class UserAgentSettingClientEngineTestCase: XCTestCase { @@ -19,7 +19,7 @@ class UserAgentSettingClientEngineTestCase: XCTestCase { /// When: A request is invoked **with** an existing User-Agent. /// Then: The `lib` component of the user-agent is added. func test_existingUserAgent_addsLibComponent() async throws { - let request: SdkHttpRequest = .mock + let request: SmithyHTTPAPI.HTTPRequest = .mock let existingUserAgent = "foo/bar/baz" request.withHeader(name: userAgentKey, value: existingUserAgent) @@ -38,7 +38,7 @@ class UserAgentSettingClientEngineTestCase: XCTestCase { /// When: A request is invoked **without** existing User-Agent. /// Then: The `lib` component of the user-agent is added. func test_nonExistingUserAgent_addsLibComponent() async throws { - let request: SdkHttpRequest = .mock + let request: SmithyHTTPAPI.HTTPRequest = .mock let target = MockTargetEngine() let engine = UserAgentSettingClientEngine(target: target) _ = try await engine.send(request: request) @@ -51,7 +51,7 @@ class UserAgentSettingClientEngineTestCase: XCTestCase { /// When: A request is invoked **with** existing User-Agent. /// Then: The `lib` component of the user-agent and the suffix are added. func test_existingUserAgentCombinedWithSuffixAppender_addLibAndSuffix() async throws { - let request: SdkHttpRequest = .mock + let request: SmithyHTTPAPI.HTTPRequest = .mock let existingUserAgent = "foo/bar/baz" request.withHeader(name: userAgentKey, value: existingUserAgent) @@ -73,7 +73,7 @@ class UserAgentSettingClientEngineTestCase: XCTestCase { /// When: A request is invoked **without** existing User-Agent. /// Then: The `lib` component of the user-agent and the suffix are added. func test_nonExistingUserAgentCombinedWithSuffixAppender_addLibAndSuffix() async throws { - let request: SdkHttpRequest = .mock + let request: SmithyHTTPAPI.HTTPRequest = .mock let target = MockTargetEngine() let suffix = "a/b/c" @@ -91,18 +91,18 @@ class UserAgentSettingClientEngineTestCase: XCTestCase { } class MockTargetEngine: HTTPClient { - var request: SdkHttpRequest? + var request: HTTPRequest? func send( - request: SdkHttpRequest - ) async throws -> HttpResponse { + request: HTTPRequest + ) async throws -> HTTPResponse { self.request = request return .init(body: .empty, statusCode: .accepted) } } -extension SdkHttpRequest { - static var mock: SdkHttpRequest { +extension HTTPRequest { + static var mock: HTTPRequest { .init( method: .get, endpoint: .init(host: "amplify") diff --git a/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSuffixAppenderTests.swift b/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSuffixAppenderTests.swift index dece4394d4..0d26bb0ddd 100644 --- a/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSuffixAppenderTests.swift +++ b/AmplifyPlugins/Core/AmplifyCredentialsTests/Utils/UserAgentSuffixAppenderTests.swift @@ -6,7 +6,8 @@ // @_spi(InternalAmplifyPluginExtension) @_spi(InternalHttpEngineProxy) import InternalAmplifyCredentials -import ClientRuntime +import Smithy +import SmithyHTTPAPI import XCTest class UserAgentSuffixAppenderTests: XCTestCase { @@ -73,8 +74,8 @@ class UserAgentSuffixAppenderTests: XCTestCase { } } - private func createRequest() -> SdkHttpRequest { - return SdkHttpRequest( + private func createRequest() -> HTTPRequest { + return HTTPRequest( method: .get, endpoint: .init(host: "customHost") ) @@ -83,8 +84,8 @@ class UserAgentSuffixAppenderTests: XCTestCase { private class MockHttpClientEngine: HTTPClient { var executeCount = 0 - var executeRequest: SdkHttpRequest? - func send(request: SdkHttpRequest) async throws -> HttpResponse { + var executeRequest: HTTPRequest? + func send(request: HTTPRequest) async throws -> HTTPResponse { executeCount += 1 executeRequest = request return .init(body: .empty, statusCode: .accepted) diff --git a/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/AWSLocationGeoPlugin+Configure.swift b/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/AWSLocationGeoPlugin+Configure.swift index 6b194b48af..221d9d9551 100644 --- a/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/AWSLocationGeoPlugin+Configure.swift +++ b/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/AWSLocationGeoPlugin+Configure.swift @@ -36,12 +36,13 @@ extension AWSLocationGeoPlugin { /// Configure AWSLocationPlugin programatically using AWSLocationPluginConfiguration public func configure(using configuration: AWSLocationGeoPluginConfiguration) throws { let authService = AWSAuthService() - let credentialsProvider = authService.getCredentialsProvider() + let credentialsProvider = authService.getCredentialIdentityResolver() let region = configuration.regionName // TODO: FrameworkMetadata Replacement let serviceConfiguration = try LocationClient.LocationClientConfiguration( + awsCredentialIdentityResolver: credentialsProvider, region: region, - credentialsProvider: credentialsProvider + signingRegion: region ) serviceConfiguration.httpClientEngine = .userAgentEngine(for: serviceConfiguration) diff --git a/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/Support/Utils/GeoErrorConvertible.swift b/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/Support/Utils/GeoErrorConvertible.swift index ad35aea33a..f1419059c8 100644 --- a/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/Support/Utils/GeoErrorConvertible.swift +++ b/AmplifyPlugins/Geo/Sources/AWSLocationGeoPlugin/Support/Utils/GeoErrorConvertible.swift @@ -8,7 +8,7 @@ import Amplify import Foundation import AWSLocation -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime protocol GeoErrorConvertible { var geoError: Geo.Error { get } diff --git a/AmplifyPlugins/Geo/Tests/AWSLocationGeoPluginTests/Mocks/MockAWSClientConfiguration.swift b/AmplifyPlugins/Geo/Tests/AWSLocationGeoPluginTests/Mocks/MockAWSClientConfiguration.swift index ec24a7486d..90031b94ae 100644 --- a/AmplifyPlugins/Geo/Tests/AWSLocationGeoPluginTests/Mocks/MockAWSClientConfiguration.swift +++ b/AmplifyPlugins/Geo/Tests/AWSLocationGeoPluginTests/Mocks/MockAWSClientConfiguration.swift @@ -10,6 +10,8 @@ import ClientRuntime import AWSLocation import Foundation import XCTest +import SmithyHTTPAPI +import Smithy @testable import AWSLocationGeoPlugin @testable import AWSPluginsTestCommon @@ -17,19 +19,17 @@ import XCTest extension LocationClient.LocationClientConfiguration { static func mock(region: String) throws -> LocationClient.LocationClientConfiguration { try .init( + awsCredentialIdentityResolver: MockAWSAuthService().getCredentialIdentityResolver(), + awsRetryMode: .standard, region: region, - credentialsProvider: MockAWSAuthService().getCredentialsProvider(), - serviceSpecific: .init( - endpointResolver: MockEndPointResolver() - ), - signingRegion: "", - retryMode: .standard + signingRegion: "", + endpointResolver: MockEndPointResolver() ) } } class MockEndPointResolver: EndpointResolver { - func resolve(params: AWSLocation.EndpointParams) throws -> ClientRuntime.Endpoint { + func resolve(params: AWSLocation.EndpointParams) throws -> SmithyHTTPAPI.Endpoint { return Endpoint(host: "MockHost") } } diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift index 77f1cb2c5e..9636e18627 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift @@ -5,20 +5,24 @@ // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime import Foundation +import Smithy +import SmithyHTTPAPI -extension ClientError { - // TODO: Should some of these really be retried? +// From an Analytics perspective, a non-retryable error thrown by a PutEvents request +// means that all those events should be immediately pruned from the local database. +// +// Any "transient" error should be retried on the next event submission, +// so only `ClientError.serializationFailed` is considered to be non-retryable. + +extension Smithy.ClientError { var isRetryable: Bool { switch self { case .authError: return true case .dataNotFound: return true - case .pathCreationFailed: - return true - case .queryItemCreationFailed: + case .invalidValue: return true case .serializationFailed: return false @@ -27,3 +31,14 @@ extension ClientError { } } } + +extension SmithyHTTPAPI.HTTPClientError { + var isRetryable: Bool { + switch self { + case .pathCreationFailed: + return true + case .queryItemCreationFailed: + return true + } + } +} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift index 0291d2f412..7782ddb733 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift @@ -11,6 +11,7 @@ import AWSPinpoint import ClientRuntime import enum AwsCommonRuntimeKit.CommonRunTimeError import Foundation +import SmithyHTTPAPI /// AnalyticsEventRecording saves and submits pinpoint events protocol AnalyticsEventRecording: Actor { @@ -157,7 +158,7 @@ actor EventRecorder: AnalyticsEventRecording { let endpointResponseMap = results.compactMap { $0.value.endpointItemResponse } for endpointResponse in endpointResponseMap { - if HttpStatusCode.accepted.rawValue == endpointResponse.statusCode { + if HTTPStatusCode.accepted.rawValue == endpointResponse.statusCode { log.verbose("EndpointProfile updated successfully.") } else { log.error("Unable to update EndpointProfile. Error: \(endpointResponse.message ?? "Unknown")") @@ -168,13 +169,13 @@ actor EventRecorder: AnalyticsEventRecording { for (eventId, eventResponse) in eventsResponseMap.flatMap({ $0 }) { guard let event = pinpointEventsById[eventId] else { continue } let responseMessage = eventResponse.message ?? "Unknown" - if HttpStatusCode.accepted.rawValue == eventResponse.statusCode, + if HTTPStatusCode.accepted.rawValue == eventResponse.statusCode, Constants.acceptedResponseMessage == responseMessage { // On successful submission, add the event to the list of submitted events and delete it from the local storage log.verbose("Successful submit for event with id \(eventId)") submittedEvents.append(event) deleteEvent(eventId: eventId) - } else if HttpStatusCode.badRequest.rawValue == eventResponse.statusCode { + } else if HTTPStatusCode.badRequest.rawValue == eventResponse.statusCode { // On bad request responses, mark the event as dirty log.error("Server rejected submission of event. Event with id \(eventId) will be discarded. Error: \(responseMessage)") setDirtyEvent(eventId: eventId) diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/AWSPinpointFactory.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/AWSPinpointFactory.swift index 05f64ea9e1..d0b93f647d 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/AWSPinpointFactory.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/AWSPinpointFactory.swift @@ -20,7 +20,7 @@ public class AWSPinpointFactory { private init() {} - static var credentialsProvider = AWSAuthService().getCredentialsProvider() + static var credentialIdentityResolver = AWSAuthService().getCredentialIdentityResolver() static var provisioningProfileReader: ProvisioningProfileReader = .default @@ -46,7 +46,7 @@ public class AWSPinpointFactory { let configuration = PinpointContextConfiguration( appId: appId, region: region, - credentialsProvider: credentialsProvider, + credentialIdentityResolver: credentialIdentityResolver, isDebug: isDebug ) diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift index 4267e99084..dc5a653a7d 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift @@ -10,6 +10,7 @@ import AWSClientRuntime import AWSPinpoint import Foundation @_spi(KeychainStore) import AWSPluginsCore +import SmithyIdentity // MARK: - UserDefaultsBehaviour protocol UserDefaultsBehaviour { @@ -76,18 +77,18 @@ struct PinpointContextConfiguration { /// The Pinpoint region let region: String /// Used to retrieve the proper AWSCredentials when creating the PinpointCLient - let credentialsProvider: CredentialsProviding + let credentialIdentityResolver: any AWSCredentialIdentityResolver /// Indicates if the App is in Debug or Release build. Defaults to `false` /// Setting this flag to true will set the Endpoint Profile to have a channel type of "APNS_SANDBOX". let isDebug: Bool init(appId: String, region: String, - credentialsProvider: CredentialsProviding, + credentialIdentityResolver: some AWSCredentialIdentityResolver, isDebug: Bool = false) { self.appId = appId self.region = region - self.credentialsProvider = credentialsProvider + self.credentialIdentityResolver = credentialIdentityResolver self.isDebug = isDebug } } @@ -124,7 +125,7 @@ class PinpointContext { uniqueId = Self.retrieveUniqueId(applicationId: configuration.appId, storage: storage) let pinpointClient = try PinpointClient(region: configuration.region, - credentialsProvider: configuration.credentialsProvider) + credentialIdentityResolver: configuration.credentialIdentityResolver) endpointClient = EndpointClient(configuration: .init(appId: configuration.appId, uniqueDeviceId: uniqueId, diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointClientTypes+Codable.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointClientTypes+Codable.swift new file mode 100644 index 0000000000..c1964cb00b --- /dev/null +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Endpoint/PinpointClientTypes+Codable.swift @@ -0,0 +1,136 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import AWSPinpoint +import Foundation + +extension PinpointClientTypes.EndpointLocation: Codable, Equatable { + private enum CodingKeys: CodingKey { + case city + case country + case latitude + case longitude + case postalCode + case region + } + + public static func == ( + lhs: PinpointClientTypes.EndpointLocation, + rhs: PinpointClientTypes.EndpointLocation + ) -> Bool { + return lhs.city == rhs.city + && lhs.country == rhs.country + && lhs.latitude == rhs.latitude + && lhs.longitude == rhs.longitude + && lhs.postalCode == rhs.postalCode + && lhs.region == rhs.region + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + city: container.decodeIfPresent(String.self, forKey: .city), + country: container.decodeIfPresent(String.self, forKey: .country), + latitude: container.decodeIfPresent(Double.self, forKey: .latitude), + longitude: container.decodeIfPresent(Double.self, forKey: .longitude), + postalCode: container.decodeIfPresent(String.self, forKey: .postalCode), + region: container.decodeIfPresent(String.self, forKey: .region) + ) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(city, forKey: .city) + try container.encodeIfPresent(country, forKey: .country) + try container.encodeIfPresent(latitude, forKey: .latitude) + try container.encodeIfPresent(longitude, forKey: .longitude) + try container.encodeIfPresent(postalCode, forKey: .postalCode) + try container.encodeIfPresent(region, forKey: .region) + } +} + +extension PinpointClientTypes.EndpointDemographic: Codable, Equatable { + private enum CodingKeys: CodingKey { + case appVersion + case locale + case make + case model + case modelVersion + case platform + case platformVersion + case timezone + } + + public static func == ( + lhs: PinpointClientTypes.EndpointDemographic, + rhs: PinpointClientTypes.EndpointDemographic + ) -> Bool { + return lhs.appVersion == rhs.appVersion + && lhs.locale == rhs.locale + && lhs.make == rhs.make + && lhs.model == rhs.model + && lhs.modelVersion == rhs.modelVersion + && lhs.platform == rhs.platform + && lhs.platformVersion == rhs.platformVersion + && lhs.timezone == rhs.timezone + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + appVersion: container.decodeIfPresent(String.self, forKey: .appVersion), + locale: container.decodeIfPresent(String.self, forKey: .locale), + make: container.decodeIfPresent(String.self, forKey: .make), + model: container.decodeIfPresent(String.self, forKey: .model), + modelVersion: container.decodeIfPresent(String.self, forKey: .modelVersion), + platform: container.decodeIfPresent(String.self, forKey: .platform), + platformVersion: container.decodeIfPresent(String.self, forKey: .platformVersion), + timezone: container.decodeIfPresent(String.self, forKey: .timezone) + ) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(appVersion, forKey: .appVersion) + try container.encodeIfPresent(locale, forKey: .locale) + try container.encodeIfPresent(make, forKey: .make) + try container.encodeIfPresent(model, forKey: .model) + try container.encodeIfPresent(modelVersion, forKey: .modelVersion) + try container.encodeIfPresent(platform, forKey: .platform) + try container.encodeIfPresent(platformVersion, forKey: .platformVersion) + try container.encodeIfPresent(timezone, forKey: .timezone) + } +} + +extension PinpointClientTypes.EndpointUser: Codable, Equatable { + private enum CodingKeys: CodingKey { + case userAttributes + case userId + } + + public static func == ( + lhs: PinpointClientTypes.EndpointUser, + rhs: PinpointClientTypes.EndpointUser + ) -> Bool { + return lhs.userAttributes == rhs.userAttributes + && lhs.userId == rhs.userId + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + userAttributes: container.decodeIfPresent([String: [String]].self, forKey: .userAttributes), + userId: container.decodeIfPresent(String.self, forKey: .userId) + ) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(userAttributes, forKey: .userAttributes) + try container.encodeIfPresent(userId, forKey: .userId) + } +} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift index 1d7a9d7cd9..15c437b3ea 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift @@ -9,13 +9,15 @@ import AWSClientRuntime import AWSPluginsCore import AWSPinpoint @_spi(PluginHTTPClientEngine) import InternalAmplifyCredentials +import SmithyIdentity extension PinpointClient { - convenience init(region: String, credentialsProvider: CredentialsProviding) throws { + convenience init(region: String, credentialIdentityResolver: some AWSCredentialIdentityResolver) throws { // TODO: FrameworkMetadata Replacement let configuration = try PinpointClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: region, - credentialsProvider: credentialsProvider + signingRegion: region ) configuration.httpClientEngine = .userAgentEngine(for: configuration) diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SDKModels+AmplifyStringConvertible.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SDKModels+AmplifyStringConvertible.swift index 3421574914..7ac9d99bfd 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SDKModels+AmplifyStringConvertible.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SDKModels+AmplifyStringConvertible.swift @@ -10,30 +10,8 @@ import Foundation extension PutEventsInput: AmplifyStringConvertible {} -extension PutEventsOutput: AmplifyStringConvertible { - enum CodingKeys: Swift.String, Swift.CodingKey { - case eventsResponse = "EventsResponse" - } - - public func encode(to encoder: Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let eventsResponse = self.eventsResponse { - try encodeContainer.encode(eventsResponse, forKey: .eventsResponse) - } - } -} +extension PutEventsOutput: AmplifyStringConvertible {} extension UpdateEndpointInput: AmplifyStringConvertible {} -extension UpdateEndpointOutput: AmplifyStringConvertible { - enum CodingKeys: Swift.String, Swift.CodingKey { - case messageBody = "MessageBody" - } - - public func encode(to encoder: Encoder) throws { - var encodeContainer = encoder.container(keyedBy: CodingKeys.self) - if let messageBody = self.messageBody { - try encodeContainer.encode(messageBody, forKey: .messageBody) - } - } -} +extension UpdateEndpointOutput: AmplifyStringConvertible {} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/AmplifyStringConvertible.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/AmplifyStringConvertible.swift index 6f12af8567..97632cbb29 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/AmplifyStringConvertible.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/AmplifyStringConvertible.swift @@ -7,22 +7,146 @@ import Foundation -protocol AmplifyStringConvertible: CustomStringConvertible, Encodable {} +/// Conforming to this protocol automatically adds support to "prettify" the type when printing its value. +protocol AmplifyStringConvertible: CustomStringConvertible {} extension AmplifyStringConvertible { - private static var jsonEncoder: JSONEncoder { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .iso8601 - return encoder + public var description: String { + return prettyDescription(for: self) } - public var description: String { - if let data = try? Self.jsonEncoder.encode(self), - let result = String(data: data, encoding: .utf8) { - return result + private func prettyDescription( + for object: Any, + level: UInt = 1 + ) -> String { + return prettyDescription(for: object, level: level) { result, indentation in + switch object { + case let dictionary as Dictionary: + // Dictionaries follow the format: "": + for (key, value) in dictionary { + result.append( + contentsOf: "\n\(indentation)\"\(key)\": \(description(for: value, withLevel: level))," + ) + } + case let collection as any Collection: + // Other collections follow the format: , , ..., + for value in collection { + result.append(contentsOf: "\n\(indentation)\(description(for: value, withLevel: level)),") + } + default: + // Other objects follow the format: : + for child in Mirror(reflecting: object).children { + guard let label = child.label else { continue } + result.append( + contentsOf: "\n\(indentation)\(label): \(description(for: child.value, withLevel: level))," + ) + } + } + } + } + + private func prettyDescription( + for object: Any, + level: UInt = 1, + contentsBuilder: (_ result: inout String, _ indentation: String) -> Void + ) -> String { + var contents = "" + contentsBuilder(&contents, indentation(forLevel: level)) + if contents.isEmpty { + return emptyDescription(for: object) + } + + let tags = tags(for: object) + return appendClosingTag( + tags.closing, + to: tags.opening.appending(contents), + currentLevel: level + ) + } + + private func description( + for value: Any, + withLevel level: UInt + ) -> String { + let unwrappedValue = unwrappedValue(from: value) + switch unwrappedValue { + case let stringValue as String: + return stringValue.quoted + case is Int, is Bool, is Double, is Date, is Float: + return "\(unwrappedValue)" + case is any CaseIterable, is any RawRepresentable: + return ".\(unwrappedValue)" + default: + // When the value is another object, add one level of identation + return prettyDescription(for: unwrappedValue, level: level + 1) + } + } + + private func unwrappedValue(from value: Any) -> Any { + // This is to avoid printing optionals with the "Optional(x)" format, + // instead we just print "nil" or their actual values. + if let optional = value as? _AmplifyOptional { + return optional._amplifyUnwrap() ?? "nil" + } + return value + } + + private func indentation(forLevel level: UInt) -> String { + return String(repeating: " ", count: 4 * Int(level)) + } + + private func tags(for object: Any) -> (opening: String, closing: String) { + switch object { + case is any Collection: + return (opening: "[", closing: "]") + default: + return (opening: "{", closing: "}") + } + } + + private func emptyDescription(for object: Any) -> String { + switch object { + case is Dictionary: + return "[:]" + case is any Collection: + return "[]" + default: + return "{}" } + } + + private func appendClosingTag( + _ tag: String, + to value: String, + currentLevel level: UInt + ) -> String { + // Remove the last comma + let result = value.hasSuffix(",") ? String(value.dropLast()) : value + // As we're adding a closing tag, reduce one indentation level + return result.appending("\n\(indentation(forLevel: level - 1))\(tag)") + } +} + +// swiftlint:disable identifier_name +private protocol _AmplifyOptional { + func _amplifyUnwrap() -> Any? +} + +extension Optional: _AmplifyOptional { + func _amplifyUnwrap() -> Any? { + switch self { + case .none: + return nil + case .some(let wrapped): + return wrapped + } + } +} - return String(describing: self) +private extension String { + var quoted: String { + // Don't quote the String representing nil + if self == "nil" { return self } + return "\"\(self)\"" } } diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift index 9a2b02fa87..8c8f6f6f22 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift @@ -7,8 +7,8 @@ import Foundation import AWSPinpoint -import ClientRuntime @_spi(PluginHTTPClientEngine) import InternalAmplifyCredentials +import SmithyHTTPAPI @globalActor actor PinpointRequestsRegistry { static let shared = PinpointRequestsRegistry() @@ -60,7 +60,7 @@ private struct CustomPinpointHttpClientEngine: HTTPClient { self.httpClientEngine = httpClientEngine } - func send(request: ClientRuntime.SdkHttpRequest) async throws -> ClientRuntime.HttpResponse { + func send(request: HTTPRequest) async throws -> HTTPResponse { guard let url = request.endpoint.url, let pinpointApi = PinpointRequestsRegistry.API(from: url), let userAgentSuffix = await userAgent(for: pinpointApi) else { diff --git a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/AWSPinpointFactoryTests.swift b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/AWSPinpointFactoryTests.swift index e56ffc2670..bdef7cd7c5 100644 --- a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/AWSPinpointFactoryTests.swift +++ b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/AWSPinpointFactoryTests.swift @@ -19,7 +19,7 @@ final class AWSPinpointFactoryTests: XCTestCase { override func setUp() { mockedProfileReader = MockProvisioningProfileReader() AWSPinpointFactory.provisioningProfileReader = mockedProfileReader - AWSPinpointFactory.credentialsProvider = MockCredentialsProvider() + AWSPinpointFactory.credentialIdentityResolver = MockCredentialsProvider() } /// - Given: There is a provisioning profile that set the APS entitlement to production diff --git a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/Mocks/MockEndpointClient.swift b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/Mocks/MockEndpointClient.swift index 4af9110ff9..9955156cdb 100644 --- a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/Mocks/MockEndpointClient.swift +++ b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/Mocks/MockEndpointClient.swift @@ -13,12 +13,6 @@ import Foundation actor MockEndpointClient: EndpointClientBehaviour { let pinpointClient: PinpointClientProtocol = MockPinpointClient() - class MockCredentialsProvider: CredentialsProviding { - func getCredentials() async throws -> AWSCredentials { - return AWSCredentials(accessKey: "", secret: "", expirationTimeout: Date().addingTimeInterval(1000)) - } - } - var updateEndpointProfileCount = 0 func updateEndpointProfile() async throws { updateEndpointProfileCount += 1 diff --git a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointClientTypesCodableTests.swift b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointClientTypesCodableTests.swift new file mode 100644 index 0000000000..e46cab176f --- /dev/null +++ b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointClientTypesCodableTests.swift @@ -0,0 +1,70 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import AWSPinpoint +@testable import InternalAWSPinpoint +import XCTest + +class PinpointClientTypesCodableTests: XCTestCase { + /// Given: Instances of PinpointClient types that conform to Codable + /// When: They are encoded and decoded + /// Then: The encoded data can be decoded, and the decoded data is equal to the original one + func testCodableTypes_shouldEncodeAndDecodeSuccesfully() throws { + let location = PinpointClientTypes.EndpointLocation( + city: "city", + country: "country", + latitude: 10.0, + longitude: 10.0, + postalCode: "postalCode", + region: "region" + ) + + let demographic = PinpointClientTypes.EndpointDemographic( + appVersion: "appVersion", + locale: "locale", + make: "make", + model: "model", + modelVersion: "modelVersion", + platform: "platform", + platformVersion: "platformVersion", + timezone: "timezone" + ) + + let user = PinpointClientTypes.EndpointUser( + userAttributes: [ + "attributes": ["value1", "value2"] + ], + userId: "userId" + ) + + let archiver = AmplifyArchiver() + + // Encode types + let encodedLocation = try archiver.encode(location) + let encodedDemographic = try archiver.encode(demographic) + let encodedUser = try archiver.encode(user) + + // Decode types + let decodedLocation = try archiver.decode( + PinpointClientTypes.EndpointLocation.self, + from: encodedLocation + ) + XCTAssertEqual(decodedLocation, location) + + let decodedDemographic = try archiver.decode( + PinpointClientTypes.EndpointDemographic.self, + from: encodedDemographic + ) + XCTAssertEqual(decodedDemographic, demographic) + + let decodedUser = try archiver.decode( + PinpointClientTypes.EndpointUser.self, + from: encodedUser + ) + XCTAssertEqual(decodedUser, user) + } +} diff --git a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointRequestsRegistryTests.swift b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointRequestsRegistryTests.swift index 7b17dd5b74..0cd3feec43 100644 --- a/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointRequestsRegistryTests.swift +++ b/AmplifyPlugins/Internal/Tests/InternalAWSPinpointUnitTests/PinpointRequestsRegistryTests.swift @@ -6,9 +6,10 @@ // import AWSPinpoint -import ClientRuntime @_spi(InternalAWSPinpoint) @testable import InternalAWSPinpoint import XCTest +import SmithyHTTPAPI +import Smithy class PinpointRequestsRegistryTests: XCTestCase { private var mockedHttpSdkClient: MockHttpClientEngine! @@ -70,13 +71,13 @@ class PinpointRequestsRegistryTests: XCTestCase { pinpointConfiguration.httpClientEngine } - private func createSdkRequest(for api: PinpointRequestsRegistry.API?) throws -> SdkHttpRequest { + private func createSdkRequest(for api: PinpointRequestsRegistry.API?) throws -> HTTPRequest { let apiPath = api?.rawValue ?? "otherApi" let endpoint = try Endpoint( urlString: "https://host:42/path/\(apiPath)/suffix", headers: .init(["User-Agent": "mocked_user_agent"]) ) - return SdkHttpRequest( + return HTTPRequest( method: .put, endpoint: endpoint ) @@ -91,9 +92,9 @@ private extension HTTPClient { private class MockHttpClientEngine: HTTPClient { var executeCount = 0 - var request: SdkHttpRequest? + var request: HTTPRequest? - func send(request: SdkHttpRequest) async throws -> HttpResponse { + func send(request: HTTPRequest) async throws -> HTTPResponse { executeCount += 1 self.request = request return .init(body: .empty, statusCode: .accepted) @@ -106,6 +107,6 @@ private class MockLogAgent: LogAgent { let name = "MockLogAgent" var level: LogAgentLevel = .info - func log(level: ClientRuntime.LogAgentLevel, message: String, metadata: [String : String]?, source: String, file: String, function: String, line: UInt) { + func log(level: LogAgentLevel, message: String, metadata: [String : String]?, source: String, file: String, function: String, line: UInt) { } } diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift index 86a63bd956..9a0458f612 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift @@ -10,8 +10,8 @@ import Amplify import Combine import Foundation import AWSCloudWatchLogs -import AWSClientRuntime import Network +import SmithyIdentity /// Concrete implementation of /// [LoggingCategoryClientBehavior](x-source-tag://LoggingCategoryClientBehavior) @@ -26,7 +26,7 @@ final class AWSCloudWatchLoggingCategoryClient { private let lock = NSLock() private let logGroupName: String private let region: String - private let credentialsProvider: CredentialsProviding + private let credentialIdentityResolver: any AWSCredentialIdentityResolver private let authentication: AuthCategoryUserBehavior private var loggersByKey: [LoggerKey: AWSCloudWatchLoggingSessionController] = [:] private let localStoreMaxSizeInMB: Int @@ -38,7 +38,7 @@ final class AWSCloudWatchLoggingCategoryClient { init( enable: Bool, - credentialsProvider: CredentialsProviding, + credentialIdentityResolver: some AWSCredentialIdentityResolver, authentication: AuthCategoryUserBehavior, loggingConstraintsResolver: AWSCloudWatchLoggingConstraintsResolver, logGroupName: String, @@ -48,7 +48,7 @@ final class AWSCloudWatchLoggingCategoryClient { networkMonitor: LoggingNetworkMonitor = NWPathMonitor() ) { self.enabled = enable - self.credentialsProvider = credentialsProvider + self.credentialIdentityResolver = credentialIdentityResolver self.authentication = authentication self.logGroupName = logGroupName self.region = region @@ -145,7 +145,7 @@ extension AWSCloudWatchLoggingCategoryClient: LoggingCategoryClientBehavior { return existing } - let controller = AWSCloudWatchLoggingSessionController(credentialsProvider: credentialsProvider, + let controller = AWSCloudWatchLoggingSessionController(credentialIdentityResolver: credentialIdentityResolver, authentication: authentication, logFilter: self.logFilter, category: category, diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift index 2271e877d8..ba8721681f 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift @@ -42,7 +42,7 @@ public class AWSCloudWatchLoggingPlugin: LoggingCategoryPlugin { let authService = AWSAuthService() self.loggingClient = AWSCloudWatchLoggingCategoryClient( enable: configuration.enable, - credentialsProvider: authService.getCredentialsProvider(), + credentialIdentityResolver: authService.getCredentialIdentityResolver(), authentication: Amplify.Auth, loggingConstraintsResolver: AWSCloudWatchLoggingConstraintsResolver(loggingPluginConfiguration: configuration), logGroupName: configuration.logGroupName, @@ -127,7 +127,7 @@ public class AWSCloudWatchLoggingPlugin: LoggingCategoryPlugin { self.loggingClient = AWSCloudWatchLoggingCategoryClient( enable: configuration.enable, - credentialsProvider: authService.getCredentialsProvider(), + credentialIdentityResolver: authService.getCredentialIdentityResolver(), authentication: Amplify.Auth, loggingConstraintsResolver: AWSCloudWatchLoggingConstraintsResolver(loggingPluginConfiguration: configuration), logGroupName: configuration.logGroupName, diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift index de5fcd5c7e..85f2d86ed6 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift @@ -11,8 +11,8 @@ import Amplify import Combine import Foundation import AWSCloudWatchLogs -import AWSClientRuntime import Network +import SmithyIdentity /// Responsible for setting up and tearing-down log sessions for a given category/tag according to changes in /// user authentication sessions. @@ -25,7 +25,7 @@ final class AWSCloudWatchLoggingSessionController { private let logGroupName: String private let region: String private let localStoreMaxSizeInMB: Int - private let credentialsProvider: CredentialsProviding + private let credentialIdentityResolver: any AWSCredentialIdentityResolver private let authentication: AuthCategoryUserBehavior private let category: String private var session: AWSCloudWatchLoggingSession? @@ -59,7 +59,7 @@ final class AWSCloudWatchLoggingSessionController { } /// - Tag: CloudWatchLogSessionController.init - init(credentialsProvider: CredentialsProviding, + init(credentialIdentityResolver: some AWSCredentialIdentityResolver, authentication: AuthCategoryUserBehavior, logFilter: AWSCloudWatchLoggingFilterBehavior, category: String, @@ -71,7 +71,7 @@ final class AWSCloudWatchLoggingSessionController { userIdentifier: String?, networkMonitor: LoggingNetworkMonitor ) { - self.credentialsProvider = credentialsProvider + self.credentialIdentityResolver = credentialIdentityResolver self.authentication = authentication self.logFilter = logFilter self.category = category @@ -104,8 +104,9 @@ final class AWSCloudWatchLoggingSessionController { private func createConsumer() throws -> LogBatchConsumer? { if self.client == nil { let configuration = try CloudWatchLogsClient.CloudWatchLogsClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: region, - credentialsProvider: credentialsProvider + signingRegion: region ) configuration.httpClientEngine = .userAgentEngine(for: configuration) diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/Configuration/DefaultRemoteLoggingConstraintsProvider.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/Configuration/DefaultRemoteLoggingConstraintsProvider.swift index 6a11ded903..4cedbe40f1 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/Configuration/DefaultRemoteLoggingConstraintsProvider.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/Configuration/DefaultRemoteLoggingConstraintsProvider.swift @@ -9,13 +9,14 @@ import Foundation import Amplify import AWSPluginsCore import InternalAmplifyCredentials -import AWSClientRuntime -import ClientRuntime +import Smithy +import SmithyIdentity +import SmithyHTTPAPI public class DefaultRemoteLoggingConstraintsProvider: RemoteLoggingConstraintsProvider { public let refreshIntervalInSeconds: Int private let endpoint: URL - private let credentialProvider: CredentialsProviding? + private let credentialProvider: (any AWSCredentialIdentityResolver)? private let region: String private let loggingConstraintsLocalStore: LoggingConstraintsLocalStore = UserDefaults.standard @@ -32,12 +33,12 @@ public class DefaultRemoteLoggingConstraintsProvider: RemoteLoggingConstraintsPr public init( endpoint: URL, region: String, - credentialProvider: CredentialsProviding? = nil, + credentialProvider: (any AWSCredentialIdentityResolver)? = nil, refreshIntervalInSeconds: Int = 1200 ) { self.endpoint = endpoint if credentialProvider == nil { - self.credentialProvider = AWSAuthService().getCredentialsProvider() + self.credentialProvider = AWSAuthService().getCredentialIdentityResolver() } else { self.credentialProvider = credentialProvider } @@ -85,13 +86,13 @@ public class DefaultRemoteLoggingConstraintsProvider: RemoteLoggingConstraintsPr request.setValue(host, forHTTPHeaderField: "host") let httpMethod = (request.httpMethod?.uppercased()) - .flatMap(HttpMethodType.init(rawValue:)) ?? .get + .flatMap(HTTPMethodType.init(rawValue:)) ?? .get let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)? .queryItems? - .map { ClientRuntime.SDKURLQueryItem(name: $0.name, value: $0.value) } ?? [] + .map { URIQueryItem(name: $0.name, value: $0.value) } ?? [] - let requestBuilder = SdkHttpRequestBuilder() + let requestBuilder = HTTPRequestBuilder() .withHost(host) .withPath(url.path) .withQueryItems(queryItems) @@ -107,7 +108,7 @@ public class DefaultRemoteLoggingConstraintsProvider: RemoteLoggingConstraintsPr guard let urlRequest = try await AmplifyAWSSignatureV4Signer().sigV4SignedRequest( requestBuilder: requestBuilder, - credentialsProvider: credentialProvider, + credentialIdentityResolver: credentialProvider, signingName: "execute-api", signingRegion: region, date: Date() diff --git a/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingSessionControllerTests.swift b/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingSessionControllerTests.swift index 9457644826..3b768b2735 100644 --- a/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingSessionControllerTests.swift +++ b/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingSessionControllerTests.swift @@ -56,7 +56,7 @@ final class AWSCloudWatchLoggingSessionControllerTests: XCTestCase { contents: Data(bytes), attributes: [FileAttributeKey: Any]()) systemUnderTest = AWSCloudWatchLoggingSessionController( - credentialsProvider: mockCredentialProvider, + credentialIdentityResolver: mockCredentialProvider, authentication: mockAuth, logFilter: mockLoggingFilter, category: category, diff --git a/AmplifyPlugins/Notifications/Push/Sources/AWSPinpointPushNotificationsPlugin/Error/Pinpoint+PushNotificationsErrorConvertible.swift b/AmplifyPlugins/Notifications/Push/Sources/AWSPinpointPushNotificationsPlugin/Error/Pinpoint+PushNotificationsErrorConvertible.swift index 9c3aaf2749..b6203676d0 100644 --- a/AmplifyPlugins/Notifications/Push/Sources/AWSPinpointPushNotificationsPlugin/Error/Pinpoint+PushNotificationsErrorConvertible.swift +++ b/AmplifyPlugins/Notifications/Push/Sources/AWSPinpointPushNotificationsPlugin/Error/Pinpoint+PushNotificationsErrorConvertible.swift @@ -9,7 +9,7 @@ import Foundation import Amplify import AWSPinpoint import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime private func recoverySuggestion(for error: ClientRuntime.ModeledError) -> String { type(of: error).isRetryable diff --git a/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/AWSPinpointPushNotificationsPluginConfigureTests.swift b/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/AWSPinpointPushNotificationsPluginConfigureTests.swift index 2c3ce8461c..1543457e97 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/AWSPinpointPushNotificationsPluginConfigureTests.swift +++ b/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/AWSPinpointPushNotificationsPluginConfigureTests.swift @@ -22,7 +22,7 @@ class AWSPinpointPushNotificationsPluginConfigureTests: AWSPinpointPushNotificat override func setUp() async throws { try await super.setUp() - AWSPinpointFactory.credentialsProvider = MockCredentialsProvider() + AWSPinpointFactory.credentialIdentityResolver = MockCredentialsProvider() AmplifyRemoteNotificationsHelper.shared = mockRemoteNotifications } diff --git a/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/ErrorPushNotificationsTests.swift b/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/ErrorPushNotificationsTests.swift index fd5c80a93e..7b3a8fbb30 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/ErrorPushNotificationsTests.swift +++ b/AmplifyPlugins/Notifications/Push/Tests/AWSPinpointPushNotificationsPluginUnitTests/ErrorPushNotificationsTests.swift @@ -10,6 +10,7 @@ import AWSPinpoint @testable import AWSPinpointPushNotificationsPlugin import Foundation import XCTest +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime class ErrorPushNotificationsTests: XCTestCase { /// Given: A NSError error diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/AWSPredictionsPlugin+Configure.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/AWSPredictionsPlugin+Configure.swift index 42ddda79bb..6989caa06a 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/AWSPredictionsPlugin+Configure.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/AWSPredictionsPlugin+Configure.swift @@ -39,7 +39,7 @@ extension AWSPredictionsPlugin { } let authService = AWSAuthService() - let credentialsProvider = authService.getCredentialsProvider() + let credentialIdentityResolver = authService.getCredentialIdentityResolver() let coremlService: CoreMLPredictionBehavior? #if canImport(Speech) && canImport(Vision) coremlService = try CoreMLPredictionService(configuration: configuration) @@ -49,7 +49,7 @@ extension AWSPredictionsPlugin { let predictionsService = try AWSPredictionsService( configuration: predictionsConfiguration, - credentialsProvider: credentialsProvider, + credentialIdentityResolver: credentialIdentityResolver, identifier: key ) diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift index 5c70b79948..88d9acd913 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/AWSTranscribeStreamingAdapter.swift @@ -9,7 +9,8 @@ import Foundation import Amplify import AWSPluginsCore import AWSTranscribeStreaming -import AWSClientRuntime +import AwsCommonRuntimeKit +import SmithyIdentity class AWSTranscribeStreamingAdapter: AWSTranscribeStreamingBehavior { @@ -22,11 +23,11 @@ class AWSTranscribeStreamingAdapter: AWSTranscribeStreamingBehavior { let mediaSampleRateHertz: Int } - let credentialsProvider: CredentialsProviding + let credentialIdentityResolver: any AWSCredentialIdentityResolver let region: String - init(credentialsProvider: CredentialsProviding, region: String) { - self.credentialsProvider = credentialsProvider + init(credentialIdentityResolver: any AWSCredentialIdentityResolver, region: String) { + self.credentialIdentityResolver = credentialIdentityResolver self.region = region } diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/TranscribeStreamingClientTypes+Decodable.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/TranscribeStreamingClientTypes+Decodable.swift new file mode 100644 index 0000000000..7579667a60 --- /dev/null +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Dependency/TranscribeStreamingClientTypes+Decodable.swift @@ -0,0 +1,169 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import AWSTranscribeStreaming +import Foundation + +extension TranscribeStreamingClientTypes.TranscriptEvent: Decodable { + private enum CodingKeys: String, CodingKey { + case transcript = "Transcript" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + transcript: container.decode( + TranscribeStreamingClientTypes.Transcript.self, + forKey: .transcript + ) + ) + } +} + +extension TranscribeStreamingClientTypes.Transcript: Decodable { + private enum CodingKeys: String, CodingKey { + case results = "Results" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + results: container.decode([TranscribeStreamingClientTypes.Result].self, forKey: .results) + ) + } +} + +extension TranscribeStreamingClientTypes.Result: Decodable { + private enum CodingKeys: String, CodingKey { + case alternatives = "Alternatives" + case channelId = "ChannelId" + case endTime = "EndTime" + case isPartial = "IsPartial" + case languageCode = "LanguageCode" + case languageIdentification = "LanguageIdentification" + case resultId = "ResultId" + case startTime = "StartTime" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + alternatives: container.decodeIfPresent( + [TranscribeStreamingClientTypes.Alternative].self, + forKey: .alternatives + ), + channelId: container.decodeIfPresent(String.self, forKey: .channelId), + endTime: container.decode(Double.self, forKey: .endTime), + isPartial: container.decode(Bool.self, forKey: .isPartial), + languageCode: container.decodeIfPresent( + TranscribeStreamingClientTypes.LanguageCode.self, + forKey: .languageCode + ), + languageIdentification: container.decodeIfPresent( + [TranscribeStreamingClientTypes.LanguageWithScore].self, + forKey: .languageIdentification + ), + resultId: container.decodeIfPresent(String.self, forKey: .resultId), + startTime: container.decode(Double.self, forKey: .startTime) + ) + } +} + +extension TranscribeStreamingClientTypes.Alternative: Decodable { + private enum CodingKeys: String, CodingKey { + case entities = "Entities" + case items = "Items" + case transcript = "Transcript" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + entities: container.decodeIfPresent( + [TranscribeStreamingClientTypes.Entity].self, + forKey: .entities + ), + items: container.decodeIfPresent( + [TranscribeStreamingClientTypes.Item].self, + forKey: .items + ), + transcript: container.decodeIfPresent(String.self, forKey: .transcript) + ) + } +} + +extension TranscribeStreamingClientTypes.Entity: Decodable { + private enum CodingKeys: String, CodingKey { + case category = "Category" + case confidence = "Confidence" + case content = "Content" + case endTime = "EndTime" + case startTime = "StartTime" + case type = "Type" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + category: container.decodeIfPresent(String.self, forKey: .category), + confidence: container.decodeIfPresent(Double.self, forKey: .confidence), + content: container.decodeIfPresent(String.self, forKey: .content), + endTime: container.decode(Double.self, forKey: .endTime), + startTime: container.decode(Double.self, forKey: .startTime), + type: container.decodeIfPresent(String.self, forKey: .type) + ) + } +} + +extension TranscribeStreamingClientTypes.Item: Decodable { + private enum CodingKeys: String, CodingKey { + case confidence = "Confidence" + case content = "Content" + case endTime = "EndTime" + case speaker = "Speaker" + case stable = "Stable" + case startTime = "StartTime" + case type = "Type" + case vocabularyFilterMatch = "VocabularyFilterMatch" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + confidence: container.decodeIfPresent(Double.self, forKey: .confidence), + content: container.decodeIfPresent(String.self, forKey: .content), + endTime: container.decode(Double.self, forKey: .endTime), + speaker: container.decodeIfPresent(String.self, forKey: .speaker), + stable: container.decodeIfPresent(Bool.self, forKey: .stable), + startTime: container.decode(Double.self, forKey: .startTime), + type: container.decodeIfPresent(TranscribeStreamingClientTypes.ItemType.self, forKey: .type), + vocabularyFilterMatch: container.decode(Bool.self, forKey: .vocabularyFilterMatch) + ) + } +} + +extension TranscribeStreamingClientTypes.LanguageWithScore: Decodable { + private enum CodingKeys: String, CodingKey { + case languageCode = "LanguageCode" + case score = "Score" + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + try self.init( + languageCode: container.decodeIfPresent( + TranscribeStreamingClientTypes.LanguageCode.self, + forKey: .languageCode + ), + score: container.decode(Double.self, forKey: .score) + ) + } +} + +extension TranscribeStreamingClientTypes.ItemType: Decodable {} + +extension TranscribeStreamingClientTypes.LanguageCode: Decodable {} diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Service/Predictions/AWSPredictionsService.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Service/Predictions/AWSPredictionsService.swift index 9470da9c2b..1d829747dc 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Service/Predictions/AWSPredictionsService.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Service/Predictions/AWSPredictionsService.swift @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // +import protocol Amplify.Logger import Amplify import AWSRekognition import AWSTranslate @@ -14,9 +15,9 @@ import AWSPolly import AWSPluginsCore @_spi(PluginHTTPClientEngine) import InternalAmplifyCredentials import Foundation -import ClientRuntime -import AWSClientRuntime +import AwsCommonRuntimeKit import AWSTranscribeStreaming +import SmithyIdentity class AWSPredictionsService { var identifier: String! @@ -31,12 +32,13 @@ class AWSPredictionsService { convenience init( configuration: PredictionsPluginConfiguration, - credentialsProvider: CredentialsProviding, + credentialIdentityResolver: any AWSCredentialIdentityResolver, identifier: String ) throws { let translateClientConfiguration = try TranslateClient.TranslateClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: configuration.convert.region, - credentialsProvider: credentialsProvider + signingRegion: configuration.convert.region ) translateClientConfiguration.httpClientEngine = .userAgentEngine( for: translateClientConfiguration @@ -45,8 +47,9 @@ class AWSPredictionsService { let awsTranslateClient = TranslateClient(config: translateClientConfiguration) let pollyClientConfiguration = try PollyClient.PollyClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: configuration.convert.region, - credentialsProvider: credentialsProvider + signingRegion: configuration.convert.region ) pollyClientConfiguration.httpClientEngine = .userAgentEngine( for: pollyClientConfiguration @@ -54,8 +57,9 @@ class AWSPredictionsService { let awsPollyClient = PollyClient(config: pollyClientConfiguration) let comprehendClientConfiguration = try ComprehendClient.ComprehendClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: configuration.convert.region, - credentialsProvider: credentialsProvider + signingRegion: configuration.convert.region ) comprehendClientConfiguration.httpClientEngine = .userAgentEngine( for: comprehendClientConfiguration @@ -64,8 +68,9 @@ class AWSPredictionsService { let awsComprehendClient = ComprehendClient(config: comprehendClientConfiguration) let rekognitionClientConfiguration = try RekognitionClient.RekognitionClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: configuration.identify.region, - credentialsProvider: credentialsProvider + signingRegion: configuration.convert.region ) rekognitionClientConfiguration.httpClientEngine = .userAgentEngine( for: rekognitionClientConfiguration @@ -73,8 +78,9 @@ class AWSPredictionsService { let awsRekognitionClient = RekognitionClient(config: rekognitionClientConfiguration) let textractClientConfiguration = try TextractClient.TextractClientConfiguration( + awsCredentialIdentityResolver: credentialIdentityResolver, region: configuration.identify.region, - credentialsProvider: credentialsProvider + signingRegion: configuration.convert.region ) textractClientConfiguration.httpClientEngine = .userAgentEngine( for: textractClientConfiguration @@ -82,7 +88,7 @@ class AWSPredictionsService { let awsTextractClient = TextractClient(config: textractClientConfiguration) let awsTranscribeStreamingAdapter = AWSTranscribeStreamingAdapter( - credentialsProvider: credentialsProvider, + credentialIdentityResolver: credentialIdentityResolver, region: configuration.convert.region ) diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/AWSS3PreSignedURLBuilderAdapter.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/AWSS3PreSignedURLBuilderAdapter.swift index b935df2dd0..35c4e6f974 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/AWSS3PreSignedURLBuilderAdapter.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/AWSS3PreSignedURLBuilderAdapter.swift @@ -23,7 +23,6 @@ class AWSS3PreSignedURLBuilderAdapter: AWSS3PreSignedURLBuilderBehavior { let logger: Logger /// Creates a pre-signed URL builder. - /// - Parameter credentialsProvider: Credentials Provider. init(config: S3Client.S3ClientConfiguration, bucket: String, logger: Logger = storageLogger) { self.bucket = bucket self.config = config diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfiguration+withAccelerate.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfiguration+withAccelerate.swift index 526b90b1fe..2d87150438 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfiguration+withAccelerate.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfiguration+withAccelerate.swift @@ -16,35 +16,42 @@ extension S3Client.S3ClientConfiguration { } // if `shouldAccelerate` isn't `nil` and - // is equal to the exisiting config's `serviceSpecific.accelerate + // is equal to the exisiting config's `accelerate` // we can avoid allocating a new configuration object. - if shouldAccelerate == serviceSpecific.accelerate { + if shouldAccelerate == accelerate { return self } - // This shouldn't happen based on how we're initially - // creating the configuration, but we can't reasonably prove - // it at compile time - so we have to unwrap. - guard let region else { return self } - - // `S3Client.ServiceSpecificConfiguration` is a struct - // so we're copying by value here. - var serviceSpecific = serviceSpecific - serviceSpecific.accelerate = shouldAccelerate - // `S3Client.S3ClientConfiguration` is a `class` so we need to make // a deep copy here as not to change the value of the existing base // configuration. - let copy = try S3Client.S3ClientConfiguration( + let copy = try S3Client.S3ClientConfiguration( + useFIPS: useFIPS, + useDualStack: useDualStack, + appID: appID, + awsCredentialIdentityResolver: awsCredentialIdentityResolver, + awsRetryMode: awsRetryMode, region: region, - credentialsProvider: credentialsProvider, - endpoint: endpoint, - serviceSpecific: serviceSpecific, signingRegion: signingRegion, - useDualStack: useDualStack, - useFIPS: useFIPS, - retryMode: awsRetryMode, - appID: appID + forcePathStyle: forcePathStyle, + useArnRegion: useArnRegion, + disableMultiRegionAccessPoints: disableMultiRegionAccessPoints, + accelerate: shouldAccelerate, + disableS3ExpressSessionAuth: disableS3ExpressSessionAuth, + useGlobalEndpoint: useGlobalEndpoint, + endpointResolver: endpointResolver, + telemetryProvider: telemetryProvider, + retryStrategyOptions: retryStrategyOptions, + clientLogMode: clientLogMode, + endpoint: endpoint, + idempotencyTokenGenerator: idempotencyTokenGenerator, + httpClientEngine: httpClientEngine, + httpClientConfiguration: httpClientConfiguration, + authSchemes: authSchemes, + authSchemeResolver: authSchemeResolver, + bearerTokenIdentityResolver: bearerTokenIdentityResolver, + interceptorProviders: interceptorProviders, + httpInterceptorProviders: httpInterceptorProviders ) return copy diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfigurationProtocol+Endpoint.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfigurationProtocol+Endpoint.swift index 5eac989093..9f8c766831 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfigurationProtocol+Endpoint.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/S3ClientConfigurationProtocol+Endpoint.swift @@ -9,16 +9,16 @@ import AWSS3 extension S3Client.S3ClientConfiguration { func endpointParams(withBucket bucket: String?) -> EndpointParams { EndpointParams( - accelerate: serviceSpecific.accelerate ?? false, + accelerate: accelerate ?? false, bucket: bucket, - disableMultiRegionAccessPoints: serviceSpecific.disableMultiRegionAccessPoints ?? false, + disableMultiRegionAccessPoints: disableMultiRegionAccessPoints ?? false, endpoint: endpoint, - forcePathStyle: serviceSpecific.forcePathStyle ?? false, + forcePathStyle: forcePathStyle ?? false, region: region, - useArnRegion: serviceSpecific.useArnRegion, + useArnRegion: useArnRegion, useDualStack: useDualStack ?? false, useFIPS: useFIPS ?? false, - useGlobalEndpoint: serviceSpecific.useGlobalEndpoint ?? false + useGlobalEndpoint: useGlobalEndpoint ?? false ) } diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/SdkTypealiases.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/SdkTypealiases.swift index d0cdf91669..03a0ea9561 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/SdkTypealiases.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/SdkTypealiases.swift @@ -6,8 +6,7 @@ // import Foundation -import AWSClientRuntime -import ClientRuntime +import SmithyHTTPAPI /// - Tag: NetworkResult -public typealias NetworkResult = (Result) -> Void +public typealias NetworkResult = (Result) -> Void diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift index 6584e766f0..ae89c9359e 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift @@ -5,89 +5,113 @@ // SPDX-License-Identifier: Apache-2.0 import Foundation -import AWSS3 -import ClientRuntime +@testable import AWSS3 +@_spi(SmithyReadWrite) import ClientRuntime import AWSClientRuntime +import Smithy +import SmithyHTTPAPI +import SmithyRetries +// swiftlint:disable identifier_name +// swiftlint:disable line_length extension UploadPartInput { - func customPresignURL(config: S3Client.S3ClientConfiguration, expiration: TimeInterval) async throws -> ClientRuntime.URL? { + func customPresignURL( + config: S3Client.S3ClientConfiguration, + expiration: Foundation.TimeInterval + ) async throws -> Foundation.URL? { let serviceName = "S3" let input = self - let context = ClientRuntime.HttpContextBuilder() + let client: (SmithyHTTPAPI.HTTPRequest, Smithy.Context) async throws -> SmithyHTTPAPI.HTTPResponse = { (_, _) in + throw Smithy.ClientError.unknownError("No HTTP client configured for presigned request") + } + let context = Smithy.ContextBuilder() .withMethod(value: .put) .withServiceName(value: serviceName) .withOperation(value: "uploadPart") .withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator) .withLogger(value: config.logger) .withPartitionID(value: config.partitionID) - .withCredentialsProvider(value: config.credentialsProvider) + .withAuthSchemes(value: config.authSchemes ?? []) + .withAuthSchemeResolver(value: config.authSchemeResolver) + .withUnsignedPayloadTrait(value: false) + .withSocketTimeout(value: config.httpClientConfiguration.socketTimeout) + .withIdentityResolver(value: config.bearerTokenIdentityResolver, schemeID: "smithy.api#httpBearerAuth") + .withFlowType(value: .PRESIGN_URL) + .withExpiration(value: expiration) + .withIdentityResolver(value: config.awsCredentialIdentityResolver, schemeID: "aws.auth#sigv4") + .withIdentityResolver(value: config.awsCredentialIdentityResolver, schemeID: "aws.auth#sigv4a") .withRegion(value: config.region) .withSigningName(value: "s3") .withSigningRegion(value: config.signingRegion) + .withUnsignedPayloadTrait(value: true) .build() - var operation = ClientRuntime.OperationStack(id: "uploadPart") - operation.initializeStep.intercept( - position: .after, middleware: ClientRuntime.URLPathMiddleware(UploadPartInput.urlPathProvider(_:))) - operation.initializeStep.intercept(position: .after, middleware: ClientRuntime.URLHostMiddleware()) - operation.buildStep.intercept( - position: .before, - middleware: EndpointResolverMiddleware( - endpointResolver: config.serviceSpecific.endpointResolver, - endpointParams: config.endpointParams(withBucket: input.bucket) - ) - ) - operation.serializeStep.intercept( - position: .after, middleware: ClientRuntime.QueryItemMiddleware(UploadPartInput.queryItemProvider(_:))) - operation.finalizeStep.intercept( - position: .after, - middleware: ClientRuntime.RetryMiddleware( - options: config.retryStrategyOptions)) - let sigv4Config = AWSClientRuntime.SigV4Config( - signatureType: .requestQueryParams, - useDoubleURIEncode: false, - expiration: expiration, - unsignedBody: true, - signingAlgorithm: .sigv4) - operation.finalizeStep.intercept( - position: .before, middleware: AWSClientRuntime.SigV4Middleware(config: sigv4Config)) - operation.deserializeStep.intercept( - position: .after, middleware: ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) - operation.deserializeStep.intercept( - position: .after, middleware: AWSClientRuntime.AWSS3ErrorWith200StatusXMLMiddleware()) - let presignedRequestBuilder = try await operation.presignedRequest( - context: context, input: input, output: UploadPartOutput(), next: ClientRuntime.NoopHandler()) - guard let builtRequest = presignedRequestBuilder?.build(), let presignedURL = builtRequest.endpoint.url else { - return nil - } - return presignedURL - } - - static func urlPathProvider(_ value: UploadPartInput) -> Swift.String? { - guard let key = value.key else { - return nil + let builder = ClientRuntime.OrchestratorBuilder() + config.interceptorProviders.forEach { provider in + builder.interceptors.add(provider.create()) + } + config.httpInterceptorProviders.forEach { provider in + builder.interceptors.add(provider.create()) } - return "/\(key.urlPercentEncoding(encodeForwardSlash: false))" + builder.interceptors.add(ClientRuntime.URLPathMiddleware(UploadPartInput.urlPathProvider(_:))) + builder.interceptors.add(ClientRuntime.URLHostMiddleware()) + builder.deserialize(ClientRuntime.DeserializeMiddleware(UploadPartOutput.httpOutput(from:), PutObjectOutputError.httpError(from:))) + builder.interceptors.add(ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) + builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) + builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) + builder.applySigner(ClientRuntime.SignerMiddleware()) + let endpointParams = EndpointParams(accelerate: config.accelerate ?? false, bucket: input.bucket, disableMultiRegionAccessPoints: config.disableMultiRegionAccessPoints ?? false, disableS3ExpressSessionAuth: config.disableS3ExpressSessionAuth, endpoint: config.endpoint, forcePathStyle: config.forcePathStyle ?? false, key: input.key, region: config.region, useArnRegion: config.useArnRegion, useDualStack: config.useDualStack ?? false, useFIPS: config.useFIPS ?? false, useGlobalEndpoint: config.useGlobalEndpoint ?? false) + context.attributes.set(key: Smithy.AttributeKey(name: "EndpointParams"), value: endpointParams) + builder.applyEndpoint(AWSClientRuntime.EndpointResolverMiddleware(endpointResolverBlock: { [config] in try config.endpointResolver.resolve(params: $0) }, endpointParams: endpointParams)) + builder.selectAuthScheme(ClientRuntime.AuthSchemeMiddleware()) + builder.interceptors.add(AWSClientRuntime.AWSS3ErrorWith200StatusXMLMiddleware()) + builder.interceptors.add(AWSClientRuntime.FlexibleChecksumsRequestMiddleware(checksumAlgorithm: input.checksumAlgorithm?.rawValue)) + builder.serialize(UploadPartPresignedMiddleware()) + var metricsAttributes = Smithy.Attributes() + metricsAttributes.set(key: ClientRuntime.OrchestratorMetricsAttributesKeys.service, value: "S3") + metricsAttributes.set(key: ClientRuntime.OrchestratorMetricsAttributesKeys.method, value: "UploadPart") + let op = builder.attributes(context) + .telemetry(ClientRuntime.OrchestratorTelemetry( + telemetryProvider: config.telemetryProvider, + metricsAttributes: metricsAttributes, + meterScope: serviceName, + tracerScope: serviceName + )) + .executeRequest(client) + .build() + return try await op.presignRequest(input: input).endpoint.url } - } -extension UploadPartInput { +struct UploadPartPresignedMiddleware: Smithy.RequestMessageSerializer { + typealias InputType = UploadPartInput + typealias RequestType = SmithyHTTPAPI.HTTPRequest - static func queryItemProvider(_ value: UploadPartInput) throws -> [ClientRuntime.SDKURLQueryItem] { - var items = [ClientRuntime.SDKURLQueryItem]() - items.append(ClientRuntime.SDKURLQueryItem(name: "x-id", value: "UploadPart")) - guard let partNumber = value.partNumber else { - let message = "Creating a URL Query Item failed. partNumber is required and must not be nil." - throw ClientRuntime.ClientError.unknownError(message) + let id: Swift.String = "UploadPartPresignedMiddleware" + + func apply( + input: InputType, + builder: SmithyHTTPAPI.HTTPRequestBuilder, + attributes: Smithy.Context + ) throws { + builder.withQueryItem(.init( + name: "x-id", + value: "UploadPart") + ) + + guard let partNumber = input.partNumber else { + throw ClientError.invalidValue("partNumber is required and must not be nil.") } - let partNumberQueryItem = ClientRuntime.SDKURLQueryItem(name: "partNumber".urlPercentEncoding(), value: Swift.String(partNumber).urlPercentEncoding()) - items.append(partNumberQueryItem) - guard let uploadId = value.uploadId else { - let message = "Creating a URL Query Item failed. uploadId is required and must not be nil." - throw ClientRuntime.ClientError.unknownError(message) + builder.withQueryItem(.init( + name: "partNumber".urlPercentEncoding(), + value: Swift.String(partNumber).urlPercentEncoding()) + ) + + guard let uploadId = input.uploadId else { + throw ClientError.invalidValue("uploadId is required and must not be nil.") } - let uploadIdQueryItem = ClientRuntime.SDKURLQueryItem(name: "uploadId".urlPercentEncoding(), value: Swift.String(uploadId).urlPercentEncoding()) - items.append(uploadIdQueryItem) - return items + builder.withQueryItem(.init( + name: "uploadId".urlPercentEncoding(), + value: Swift.String(uploadId).urlPercentEncoding()) + ) } } diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Error/AWSS3+StorageErrorConvertible.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Error/AWSS3+StorageErrorConvertible.swift index 8b1e7e316e..41f770bc65 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Error/AWSS3+StorageErrorConvertible.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Error/AWSS3+StorageErrorConvertible.swift @@ -8,7 +8,7 @@ import Foundation import Amplify import AWSS3 -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime extension AWSS3.NoSuchBucket: StorageErrorConvertible { var storageError: StorageError { diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift index a07858a643..771a80876f 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift @@ -63,11 +63,11 @@ class AWSS3StorageService: AWSS3StorageServiceBehavior, StorageServiceProxy { sessionConfiguration: URLSessionConfiguration? = nil, delegateQueue: OperationQueue? = nil, logger: Logger = storageLogger) throws { - let credentialsProvider = authService.getCredentialsProvider() + let credentialsProvider = authService.getCredentialIdentityResolver() let storageConfiguration = storageConfiguration ?? .init(forBucket: bucket) let clientConfig = try S3Client.S3ClientConfiguration( + awsCredentialIdentityResolver: credentialsProvider, region: region, - credentialsProvider: credentialsProvider, signingRegion: region ) diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/CreateMultipartUploadRequest.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/CreateMultipartUploadRequest.swift index 922acf13f1..ba81b02859 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/CreateMultipartUploadRequest.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/CreateMultipartUploadRequest.swift @@ -11,7 +11,7 @@ struct CreateMultipartUploadRequest { let bucket: String let key: String - let expires: Date? + let expires: String? let cacheControl: String? let contentDisposition: String? let contentEncoding: String? @@ -20,7 +20,7 @@ struct CreateMultipartUploadRequest { let metadata: [String: String]? init(bucket: String, key: String, - expires: Date? = nil, + expires: String? = nil, cacheControl: String? = nil, contentDisposition: String? = nil, contentEncoding: String? = nil, diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageMultipartUploadSession.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageMultipartUploadSession.swift index de59ecc9bb..73fe7bcc67 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageMultipartUploadSession.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageMultipartUploadSession.swift @@ -24,6 +24,7 @@ enum StorageMultipartUploadBehavior { case progressive } +// swiftlint:disable type_body_length class StorageMultipartUploadSession { enum Failure: Error { case invalidStateTransition @@ -43,6 +44,7 @@ class StorageMultipartUploadSession { private let onEvent: AWSS3StorageServiceBehavior.StorageServiceMultiPartUploadEventHandler private let transferTask: StorageTransferTask + private var cancelationError: (any Error)? = nil init(client: StorageMultipartUploadClient, bucket: String, @@ -244,10 +246,11 @@ class StorageMultipartUploadSession { } case .completed: onEvent(.completed(())) - case .aborting: + case .aborting(_, let error): + cancelationError = error try abort() - case .aborted: - onEvent(.completed(())) + case .aborted(_, let error): + onEvent(.failed(StorageError.unknown("Unable to upload", cancelationError ?? error))) case .failed(_, _, let error): onEvent(.failed(StorageError(error: error))) default: @@ -331,6 +334,11 @@ class StorageMultipartUploadSession { let index = partNumber - 1 parts[index] = .pending(bytes: part.bytes) multipartUpload = .parts(uploadId: uploadId, uploadFile: uploadFile, partSize: partSize, parts: parts) + let remainingParts = parts.filter({ $0.inProgress }) + if remainingParts.isEmpty { + // If there are no remaining parts in progress, manually trigger the reupload + try client.uploadPart(partNumber: partNumber, multipartUpload: multipartUpload, subTask: createSubTask(partNumber: partNumber)) + } } else { fatalError("Invalid state") } diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageServiceSessionDelegate.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageServiceSessionDelegate.swift index 1b83d5e17f..58e079b3f7 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageServiceSessionDelegate.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageServiceSessionDelegate.swift @@ -120,6 +120,18 @@ extension StorageServiceSessionDelegate: URLSessionTaskDelegate { if response.isErrorRetriable { logURLSessionActivity("Task can be retried.") } + + // For multipart uploads, we need to handle the upload part failure when the session is not aborted + if case .multiPartUploadPart(let uploadId, let partNumber) = transferTask.transferType, + let multipartUploadSession = storageService.findMultipartUploadSession(uploadId: uploadId), + !multipartUploadSession.isAborted { + multipartUploadSession.handle( + uploadPartEvent: .failed( + partNumber: partNumber, + error: responseError + ) + ) + } return } diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Utils/HttpClientEngineProxy.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Utils/HttpClientEngineProxy.swift index 407dc79756..0bc06facce 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Utils/HttpClientEngineProxy.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Utils/HttpClientEngineProxy.swift @@ -5,8 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime import Foundation +import SmithyHTTPAPI /// Internal protocol that may be to inspect or decorate /// [ClientRuntime requests](x-source-tag://SdkHttpRequest). diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Configuration/S3ClientConfigurationProxyTests.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Configuration/S3ClientConfigurationProxyTests.swift index 5ac3ba6a9a..62aee4194e 100644 --- a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Configuration/S3ClientConfigurationProxyTests.swift +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Configuration/S3ClientConfigurationProxyTests.swift @@ -21,8 +21,8 @@ final class S3ClientConfigurationAccelerateTestCase: XCTestCase { func testPropertyOverrides() async throws { let baseConfiguration = try await configuration(accelerate: true) let sut = try baseConfiguration.withAccelerate(false) - XCTAssertEqual(sut.serviceSpecific.accelerate, false) - XCTAssertEqual(baseConfiguration.serviceSpecific.accelerate, true) + XCTAssertEqual(sut.accelerate, false) + XCTAssertEqual(baseConfiguration.accelerate, true) } /// Given: A client configuration. @@ -61,24 +61,19 @@ final class S3ClientConfigurationAccelerateTestCase: XCTestCase { // Helper configuration method private func configuration(accelerate: Bool) async throws -> S3Client.S3ClientConfiguration { - var serviceSpecific = try S3Client.ServiceSpecificConfiguration() - serviceSpecific.accelerate = accelerate - serviceSpecific.useArnRegion = .random() - serviceSpecific.useGlobalEndpoint = .random() - serviceSpecific.disableMultiRegionAccessPoints = .random() - serviceSpecific.forcePathStyle = .random() - let baseConfiguration = try await S3Client.S3ClientConfiguration( - credentialsProvider: nil, - endpoint: UUID().uuidString, - serviceSpecific: serviceSpecific, + useFIPS: .random(), + useDualStack: .random(), + appID: UUID().uuidString, + awsCredentialIdentityResolver: nil, region: "us-east-1", - regionResolver: nil, signingRegion: UUID().uuidString, - useDualStack: .random(), - useFIPS: .random(), - retryMode: .adaptive, - appID: UUID().uuidString + forcePathStyle: .random(), + useArnRegion: .random(), + disableMultiRegionAccessPoints: .random(), + accelerate: accelerate, + useGlobalEndpoint: .random(), + endpoint: UUID().uuidString ) return baseConfiguration diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceListTests.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceListTests.swift index 5a4f5dc878..3b183d14a3 100644 --- a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceListTests.swift +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceListTests.swift @@ -8,11 +8,11 @@ import AWSClientRuntime import AWSS3 import Amplify -import ClientRuntime import XCTest -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime @testable import AWSPluginsTestCommon @testable import AWSS3StoragePlugin +import SmithyHTTPAPI final class AWSS3StorageServiceListTests: XCTestCase { @@ -99,7 +99,7 @@ final class AWSS3StorageServiceListTests: XCTestCase { func testSdkError() async throws { client.listObjectsV2Handler = { _ in throw AWSClientRuntime.UnknownAWSHTTPServiceError( - httpResponse: HttpResponse(body: .empty, statusCode: .forbidden), + httpResponse: HTTPResponse(body: .empty, statusCode: .forbidden), message: nil, requestID: nil, typeName: nil diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceTests.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceTests.swift index a829dce219..4c11dbf9f6 100644 --- a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceTests.swift +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Service/Storage/AWSS3StorageServiceTests.swift @@ -8,9 +8,9 @@ @testable import Amplify @testable import AWSPluginsTestCommon @testable import AWSS3StoragePlugin -import ClientRuntime import AWSS3 import XCTest +import SmithyHTTPAPI class AWSS3StorageServiceTests: XCTestCase { private var service: AWSS3StorageService! @@ -340,6 +340,11 @@ class AWSS3StorageServiceTests: XCTestCase { func testUpload_withoutPreSignedURL_shouldSendFailEvent() async { let data = Data("someData".utf8) let expectation = self.expectation(description: "Upload") + let builder = MockAWSS3PreSignedURLBuilder() + builder.getPreSignedURLHandler = {_, _, _ in + throw StorageError.unknown("Unable to create URL", nil) + } + service.preSignedURLBuilder = builder service.upload( serviceKey: "key", uploadSource: .data(data), @@ -388,8 +393,8 @@ private class MockHttpClientEngineProxy: HttpClientEngineProxy { var target: HTTPClient? = nil var executeCount = 0 - var executeRequest: SdkHttpRequest? - func send(request: SdkHttpRequest) async throws -> HttpResponse { + var executeRequest: HTTPRequest? + func send(request: HTTPRequest) async throws -> HTTPResponse { executeCount += 1 executeRequest = request return .init(body: .empty, statusCode: .accepted) diff --git a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/StorageMultipartUploadSessionTests.swift b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/StorageMultipartUploadSessionTests.swift index f51f974e34..43e051033e 100644 --- a/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/StorageMultipartUploadSessionTests.swift +++ b/AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Support/Internal/StorageMultipartUploadSessionTests.swift @@ -133,9 +133,10 @@ class StorageMultipartUploadSessionTests: XCTestCase { print("Progress: \(String(format: "%.2f", progress.fractionCompleted))") case .failed(let error): print("Error: \(error)") - XCTFail("Must not fail") + completedExp.fulfill() case .completed: print("Completed") + XCTFail("Must not complete") completedExp.fulfill() } } diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccelerateIntegrationTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccelerateIntegrationTests.swift index 937a239daf..55c15521fe 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccelerateIntegrationTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccelerateIntegrationTests.swift @@ -25,7 +25,9 @@ class AWSS3StoragePluginAccelerateIntegrationTests: AWSS3StoragePluginTestBase { let task = Amplify.Storage.uploadData(key: key, data: data, options: .init(pluginOptions:["useAccelerateEndpoint": useAccelerateEndpoint])) - _ = try await task.value + await wait { + _ = try await task.value + } try await Amplify.Storage.remove(key: key) } @@ -63,7 +65,9 @@ class AWSS3StoragePluginAccelerateIntegrationTests: AWSS3StoragePluginTestBase { let task = Amplify.Storage.uploadFile(key: key, local: fileURL, options: .init(pluginOptions:["useAccelerateEndpoint": useAccelerateEndpoint])) - _ = try await task.value + await wait { + _ = try await task.value + } try await Amplify.Storage.remove(key: key) } @@ -75,7 +79,9 @@ class AWSS3StoragePluginAccelerateIntegrationTests: AWSS3StoragePluginTestBase { let task = Amplify.Storage.uploadData(key: key, data: AWSS3StoragePluginTestBase.largeDataObject, options: .init(pluginOptions:["useAccelerateEndpoint": useAccelerateEndpoint])) - _ = try await task.value + await wait(timeout: 60) { + _ = try await task.value + } try await Amplify.Storage.remove(key: key) } @@ -88,12 +94,16 @@ class AWSS3StoragePluginAccelerateIntegrationTests: AWSS3StoragePluginTestBase { let uploadTask = Amplify.Storage.uploadData(key: key, data: data, options: .init(pluginOptions:["useAccelerateEndpoint": useAccelerateEndpoint])) - _ = try await uploadTask.value + await wait { + _ = try await uploadTask.value + } let downloadTask = Amplify.Storage.downloadData(key: key, options: .init(pluginOptions:["useAccelerateEndpoint": useAccelerateEndpoint])) - let downloadedData = try await downloadTask.value - XCTAssertEqual(downloadedData, data) + await wait { + let downloadedData = try await downloadTask.value + XCTAssertEqual(downloadedData, data) + } try await Amplify.Storage.remove(key: key) } diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccessLevelTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccessLevelTests.swift index 5a0da31147..0c97e2ce2e 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccessLevelTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccessLevelTests.swift @@ -70,8 +70,10 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase { do { logger.debug("Upload [\(accessLevel)]") let uploadDataOptions = StorageUploadDataRequest.Options(accessLevel: accessLevel) - let uploadKey = try await Amplify.Storage.uploadData(key: key, data: dataInput, options: uploadDataOptions).value - XCTAssertEqual(key, uploadKey) + await wait { + let uploadKey = try await Amplify.Storage.uploadData(key: key, data: dataInput, options: uploadDataOptions).value + XCTAssertEqual(key, uploadKey) + } logger.debug("Remove [\(accessLevel)]") let removeOptions = StorageRemoveRequest.Options(accessLevel: accessLevel) @@ -120,8 +122,10 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase { let dataInput = Data(UUID().uuidString.utf8) logger.debug("Upload [\(accessLevel)]") let uploadDataOptions = StorageUploadDataRequest.Options(accessLevel: accessLevel) - let uploadKey = try await Amplify.Storage.uploadData(key: key, data: dataInput, options: uploadDataOptions).value - XCTAssertEqual(key, uploadKey) + await wait { + let uploadKey = try await Amplify.Storage.uploadData(key: key, data: dataInput, options: uploadDataOptions).value + XCTAssertEqual(key, uploadKey) + } logger.debug("List [\(accessLevel)]") let listOptions = StorageListRequest.Options(accessLevel: accessLevel, @@ -182,7 +186,9 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase { logger.debug("Uploading as user1 with \(testRun.accessLevel) access level") let options = StorageUploadDataRequest.Options(accessLevel: testRun.accessLevel) - _ = try await Amplify.Storage.uploadData(key: testRun.key, data: Data(testRun.key.utf8), options: options).value + await wait { + _ = try await Amplify.Storage.uploadData(key: testRun.key, data: Data(testRun.key.utf8), options: options).value + } logger.debug("Getting list as user1") let listOptions1 = StorageListRequest.Options(accessLevel: testRun.accessLevel, diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginBasicIntegrationTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginBasicIntegrationTests.swift index 8aadc25de6..f14682dea2 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginBasicIntegrationTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginBasicIntegrationTests.swift @@ -8,7 +8,7 @@ @testable import Amplify import AWSS3StoragePlugin -import ClientRuntime +import SmithyHTTPAPI import CryptoKit import XCTest @@ -62,8 +62,9 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { func testUploadData() async throws { let key = UUID().uuidString let data = Data(key.utf8) - - _ = try await Amplify.Storage.uploadData(key: key, data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(key: key, data: data, options: nil).value + } _ = try await Amplify.Storage.remove(key: key) // Only the remove operation results in an SDK request @@ -80,7 +81,9 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { func testUploadEmptyData() async throws { let key = UUID().uuidString let data = Data("".utf8) - _ = try await Amplify.Storage.uploadData(key: key, data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(key: key, data: data, options: nil).value + } _ = try await Amplify.Storage.remove(key: key) XCTAssertEqual(requestRecorder.urlRequests.map { $0.httpMethod }, ["PUT"]) @@ -97,7 +100,9 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { let fileURL = URL(fileURLWithPath: filePath) FileManager.default.createFile(atPath: filePath, contents: Data(key.utf8), attributes: nil) - _ = try await Amplify.Storage.uploadFile(key: key, local: fileURL, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadFile(key: key, local: fileURL, options: nil).value + } _ = try await Amplify.Storage.remove(key: key) // Only the remove operation results in an SDK request @@ -117,7 +122,9 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { let fileURL = URL(fileURLWithPath: filePath) FileManager.default.createFile(atPath: filePath, contents: Data("".utf8), attributes: nil) - _ = try await Amplify.Storage.uploadFile(key: key, local: fileURL, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadFile(key: key, local: fileURL, options: nil).value + } _ = try await Amplify.Storage.remove(key: key) XCTAssertEqual(requestRecorder.urlRequests.map { $0.httpMethod }, ["PUT"]) @@ -130,10 +137,12 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { func testUploadLargeData() async throws { let key = UUID().uuidString - let uploadKey = try await Amplify.Storage.uploadData(key: key, - data: AWSS3StoragePluginTestBase.largeDataObject, - options: nil).value - XCTAssertEqual(uploadKey, key) + await wait(timeout: 60) { + let uploadKey = try await Amplify.Storage.uploadData(key: key, + data: AWSS3StoragePluginTestBase.largeDataObject, + options: nil).value + XCTAssertEqual(uploadKey, key) + } try await Amplify.Storage.remove(key: key) @@ -157,7 +166,9 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { contents: AWSS3StoragePluginTestBase.largeDataObject, attributes: nil) - _ = try await Amplify.Storage.uploadFile(key: key, local: fileURL, options: nil).value + await wait(timeout: 60) { + _ = try await Amplify.Storage.uploadFile(key: key, local: fileURL, options: nil).value + } _ = try await Amplify.Storage.remove(key: key) let userAgents = requestRecorder.urlRequests.compactMap { $0.allHTTPHeaderFields?["User-Agent"] } @@ -436,7 +447,7 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { } // An SDK call for the ListObjectsV2 call and each deletion is expected - let expectedMethods = [.get] + keys.map {_ in HttpMethodType.delete} + let expectedMethods = [.get] + keys.map {_ in HTTPMethodType.delete} XCTAssertEqual(requestRecorder.sdkRequests.map { $0.method}, expectedMethods) try assertUserAgentComponents(sdkRequests: requestRecorder.sdkRequests) @@ -527,7 +538,7 @@ class AWSS3StoragePluginBasicIntegrationTests: AWSS3StoragePluginTestBase { } } - private func assertUserAgentComponents(sdkRequests: [SdkHttpRequest], file: StaticString = #filePath, line: UInt = #line) throws { + private func assertUserAgentComponents(sdkRequests: [HTTPRequest], file: StaticString = #filePath, line: UInt = #line) throws { for request in sdkRequests { let headers = request.headers.dictionary let userAgent = try XCTUnwrap(headers["User-Agent"]?.joined(separator:",")) diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginGetURLIntegrationTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginGetURLIntegrationTests.swift index f1170140f7..92920ae454 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginGetURLIntegrationTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginGetURLIntegrationTests.swift @@ -22,11 +22,13 @@ class AWSS3StoragePluginGetURLIntegrationTests: AWSS3StoragePluginTestBase { func testGetRemoteURL() async throws { let key = "public/" + UUID().uuidString try await uploadData(key: key, dataString: key) - _ = try await Amplify.Storage.uploadData( - path: .fromString(key), - data: Data(key.utf8), - options: .init() - ).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromString(key), + data: Data(key.utf8), + options: .init() + ).value + } let remoteURL = try await Amplify.Storage.getURL(path: .fromString(key)) diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginListObjectsIntegrationTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginListObjectsIntegrationTests.swift index fc806ffc91..fbe51b45d4 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginListObjectsIntegrationTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginListObjectsIntegrationTests.swift @@ -9,7 +9,7 @@ import AWSS3StoragePlugin import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime import CryptoKit import XCTest import AWSS3 @@ -24,7 +24,9 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase let data = Data(key.utf8) let uniqueStringPath = "public/\(key)" - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test1"), data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test1"), data: data, options: nil).value + } let firstListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -32,7 +34,9 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase XCTAssertEqual(firstListResult.items.filter({ $0.path.contains(uniqueStringPath) }).count, 1) - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test2"), data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test2"), data: data, options: nil).value + } let secondListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -56,13 +60,15 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase // Sign in _ = try await Amplify.Auth.signIn(username: Self.user1, password: Self.password) - _ = try await Amplify.Storage.uploadData( - path: .fromIdentityID({ identityId in - uniqueStringPath = "protected/\(identityId)/\(key)" - return uniqueStringPath + "test1" - }), - data: data, - options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromIdentityID({ identityId in + uniqueStringPath = "protected/\(identityId)/\(key)" + return uniqueStringPath + "test1" + }), + data: data, + options: nil).value + } let firstListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -70,13 +76,15 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase XCTAssertEqual(firstListResult.items.filter({ $0.path.contains(uniqueStringPath) }).count, 1) - _ = try await Amplify.Storage.uploadData( - path: .fromIdentityID({ identityId in - uniqueStringPath = "protected/\(identityId)/\(key)" - return uniqueStringPath + "test2" - }), - data: data, - options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromIdentityID({ identityId in + uniqueStringPath = "protected/\(identityId)/\(key)" + return uniqueStringPath + "test2" + }), + data: data, + options: nil).value + } let secondListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -101,13 +109,15 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase // Sign in _ = try await Amplify.Auth.signIn(username: Self.user1, password: Self.password) - _ = try await Amplify.Storage.uploadData( - path: .fromIdentityID({ identityId in - uniqueStringPath = "private/\(identityId)/\(key)" - return uniqueStringPath + "test1" - }), - data: data, - options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromIdentityID({ identityId in + uniqueStringPath = "private/\(identityId)/\(key)" + return uniqueStringPath + "test1" + }), + data: data, + options: nil).value + } let firstListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -115,13 +125,15 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase XCTAssertEqual(firstListResult.items.filter({ $0.path.contains(uniqueStringPath) }).count, 1) - _ = try await Amplify.Storage.uploadData( - path: .fromIdentityID({ identityId in - uniqueStringPath = "private/\(identityId)/\(key)" - return uniqueStringPath + "test2" - }), - data: data, - options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromIdentityID({ identityId in + uniqueStringPath = "private/\(identityId)/\(key)" + return uniqueStringPath + "test2" + }), + data: data, + options: nil).value + } let secondListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -198,10 +210,12 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase let uniqueStringPath = "public/\(path)" // Upload data - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test1"), data: data, options: nil).value - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test2"), data: data, options: nil).value - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/subpath1/test"), data: data, options: nil).value - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/subpath2/test"), data: data, options: nil).value + await wait(timeout: 25) { + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test1"), data: data, options: nil).value + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/test2"), data: data, options: nil).value + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/subpath1/test"), data: data, options: nil).value + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "/subpath2/test"), data: data, options: nil).value + } let result = try await Amplify.Storage.list( path: .fromString("\(uniqueStringPath)/"), @@ -232,8 +246,10 @@ class AWSS3StoragePluginListObjectsIntegrationTests: AWSS3StoragePluginTestBase let uniqueStringPath = "public/\(path)" // Upload data - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "-test"), data: data, options: nil).value - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "-subpath-test"), data: data, options: nil).value + await wait(timeout: 10) { + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "-test"), data: data, options: nil).value + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath + "-subpath-test"), data: data, options: nil).value + } let result = try await Amplify.Storage.list( path: .fromString("\(uniqueStringPath)-"), diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginProgressTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginProgressTests.swift index 40248845d6..06f782131f 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginProgressTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginProgressTests.swift @@ -142,7 +142,9 @@ class AWSS3StoragePluginProgressTests: AWSS3StoragePluginTestBase { key: key, data: .testDataOfSize(.bytes(256)) ) - _ = try await task.value + await wait { + _ = try await task.value + } let progress = await task.progress for await current in progress { XCTFail("Not expecting a current progress value but got: \(current)") diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRemoveIntegrationTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRemoveIntegrationTests.swift index 561c1504ba..73490fbd09 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRemoveIntegrationTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRemoveIntegrationTests.swift @@ -9,7 +9,7 @@ import AWSS3StoragePlugin import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) import AWSClientRuntime import CryptoKit import XCTest import AWSS3 @@ -24,7 +24,9 @@ class AWSS3StoragePluginRemoveIntegrationTests: AWSS3StoragePluginTestBase { let data = Data(key.utf8) let uniqueStringPath = "public/\(key)" - _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath), data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(path: .fromString(uniqueStringPath), data: data, options: nil).value + } let firstListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -52,13 +54,15 @@ class AWSS3StoragePluginRemoveIntegrationTests: AWSS3StoragePluginTestBase { // Sign in _ = try await Amplify.Auth.signIn(username: Self.user1, password: Self.password) - _ = try await Amplify.Storage.uploadData( - path: .fromIdentityID({ identityId in - uniqueStringPath = "protected/\(identityId)/\(key)" - return uniqueStringPath - }), - data: data, - options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromIdentityID({ identityId in + uniqueStringPath = "protected/\(identityId)/\(key)" + return uniqueStringPath + }), + data: data, + options: nil).value + } let firstListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) @@ -86,13 +90,15 @@ class AWSS3StoragePluginRemoveIntegrationTests: AWSS3StoragePluginTestBase { // Sign in _ = try await Amplify.Auth.signIn(username: Self.user1, password: Self.password) - _ = try await Amplify.Storage.uploadData( - path: .fromIdentityID({ identityId in - uniqueStringPath = "private/\(identityId)/\(key)" - return uniqueStringPath - }), - data: data, - options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData( + path: .fromIdentityID({ identityId in + uniqueStringPath = "private/\(identityId)/\(key)" + return uniqueStringPath + }), + data: data, + options: nil).value + } let firstListResult = try await Amplify.Storage.list(path: .fromString(uniqueStringPath)) diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRequestRecorder.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRequestRecorder.swift index 973dead62d..35bae2cc66 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRequestRecorder.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginRequestRecorder.swift @@ -7,19 +7,20 @@ @testable import AWSS3StoragePlugin -import ClientRuntime import Foundation +import Smithy +import SmithyHTTPAPI class AWSS3StoragePluginRequestRecorder { var target: HTTPClient? = nil - var sdkRequests: [SdkHttpRequest] = [] + var sdkRequests: [HTTPRequest] = [] var urlRequests: [URLRequest] = [] init() { } } extension AWSS3StoragePluginRequestRecorder: HttpClientEngineProxy { - func send(request: SdkHttpRequest) async throws -> HttpResponse { + func send(request: HTTPRequest) async throws -> HTTPResponse { guard let target = target else { throw ClientError.unknownError("HttpClientEngine is not set") } diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift index fde9054957..0ffd3e0930 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift @@ -218,6 +218,20 @@ class AWSS3StoragePluginTestBase: XCTestCase { } } + func wait(timeout: TimeInterval = 10, closure: @escaping () async throws -> ()) async { + let expectation = expectation(description: "Tasks completed") + Task { + defer { expectation.fulfill() } + do { + try await closure() + } catch { + XCTFail("Unexpected error: \(error)") + } + } + + await fulfillment(of: [expectation], timeout: timeout) + } + private func invalidateCurrentSession() { Self.logger.debug("Invalidating URLSession") guard let plugin = try? Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin else { diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadIntegrationTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadIntegrationTests.swift index 565bee8746..1168226a6a 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadIntegrationTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadIntegrationTests.swift @@ -8,7 +8,7 @@ @testable import Amplify import AWSS3StoragePlugin -import ClientRuntime +import SmithyHTTPAPI import CryptoKit import XCTest @@ -63,7 +63,9 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { let key = UUID().uuidString let data = Data(key.utf8) - _ = try await Amplify.Storage.uploadData(path: .fromString("public/\(key)"), data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(path: .fromString("public/\(key)"), data: data, options: nil).value + } _ = try await Amplify.Storage.remove(path: .fromString("public/\(key)")) // Only the remove operation results in an SDK request @@ -80,7 +82,9 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { func testUploadEmptyData() async throws { let key = UUID().uuidString let data = Data("".utf8) - _ = try await Amplify.Storage.uploadData(path: .fromString("public/\(key)"), data: data, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadData(path: .fromString("public/\(key)"), data: data, options: nil).value + } _ = try await Amplify.Storage.remove(path: .fromString("public/\(key)")) XCTAssertEqual(requestRecorder.urlRequests.map { $0.httpMethod }, ["PUT"]) @@ -97,7 +101,9 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { let fileURL = URL(fileURLWithPath: filePath) FileManager.default.createFile(atPath: filePath, contents: Data(key.utf8), attributes: nil) - _ = try await Amplify.Storage.uploadFile(path: .fromString("public/\(key)"), local: fileURL, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadFile(path: .fromString("public/\(key)"), local: fileURL, options: nil).value + } _ = try await Amplify.Storage.remove(path: .fromString("public/\(key)")) // Only the remove operation results in an SDK request @@ -117,7 +123,9 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { let fileURL = URL(fileURLWithPath: filePath) FileManager.default.createFile(atPath: filePath, contents: Data("".utf8), attributes: nil) - _ = try await Amplify.Storage.uploadFile(path: .fromString("public/\(key)"), local: fileURL, options: nil).value + await wait { + _ = try await Amplify.Storage.uploadFile(path: .fromString("public/\(key)"), local: fileURL, options: nil).value + } _ = try await Amplify.Storage.remove(path: .fromString("public/\(key)")) XCTAssertEqual(requestRecorder.urlRequests.map { $0.httpMethod }, ["PUT"]) @@ -130,10 +138,12 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { func testUploadLargeData() async throws { let key = "public/" + UUID().uuidString - let uploadKey = try await Amplify.Storage.uploadData(path: .fromString(key), - data: AWSS3StoragePluginTestBase.largeDataObject, - options: nil).value - XCTAssertEqual(uploadKey, key) + await wait(timeout: 60) { + let uploadKey = try await Amplify.Storage.uploadData(path: .fromString(key), + data: AWSS3StoragePluginTestBase.largeDataObject, + options: nil).value + XCTAssertEqual(uploadKey, key) + } try await Amplify.Storage.remove(path: .fromString(key)) @@ -157,7 +167,9 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { contents: AWSS3StoragePluginTestBase.largeDataObject, attributes: nil) - _ = try await Amplify.Storage.uploadFile(path: .fromString("public/\(key)"), local: fileURL, options: nil).value + await wait(timeout: 60) { + _ = try await Amplify.Storage.uploadFile(path: .fromString("public/\(key)"), local: fileURL, options: nil).value + } _ = try await Amplify.Storage.remove(path: .fromString("public/\(key)")) let userAgents = requestRecorder.urlRequests.compactMap { $0.allHTTPHeaderFields?["User-Agent"] } @@ -179,7 +191,7 @@ class AWSS3StoragePluginUploadIntegrationTests: AWSS3StoragePluginTestBase { } } - private func assertUserAgentComponents(sdkRequests: [SdkHttpRequest], file: StaticString = #filePath, line: UInt = #line) throws { + private func assertUserAgentComponents(sdkRequests: [HTTPRequest], file: StaticString = #filePath, line: UInt = #line) throws { for request in sdkRequests { let headers = request.headers.dictionary let userAgent = try XCTUnwrap(headers["User-Agent"]?.joined(separator:",")) diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadMetadataTestCase.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadMetadataTestCase.swift index 92d6ec8a7a..e178ef9b0f 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadMetadataTestCase.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginUploadMetadataTestCase.swift @@ -27,11 +27,13 @@ class AWSS3StoragePluginUploadMetadataTestCase: AWSS3StoragePluginTestBase { // upload file let key = UUID().uuidString let fileURL = temporaryFile(named: key, data: data(mb: 1)) - _ = try await Amplify.Storage.uploadFile( - key: key, - local: fileURL, - options: options - ).value + await wait { + _ = try await Amplify.Storage.uploadFile( + key: key, + local: fileURL, + options: options + ).value + } // call `HeadObject` through SDK escape hatch let head = try await headObject(key: "public/\(key)") @@ -65,11 +67,13 @@ class AWSS3StoragePluginUploadMetadataTestCase: AWSS3StoragePluginTestBase { // upload file let key = UUID().uuidString let fileURL = temporaryFile(named: key, data: data(mb: 7)) - _ = try await Amplify.Storage.uploadFile( - key: key, - local: fileURL, - options: options - ).value + await wait(timeout: 60) { + _ = try await Amplify.Storage.uploadFile( + key: key, + local: fileURL, + options: options + ).value + } // call `HeadObject` through SDK escape hatch let head = try await headObject(key: "public/\(key)") @@ -102,11 +106,13 @@ class AWSS3StoragePluginUploadMetadataTestCase: AWSS3StoragePluginTestBase { // upload file let key = UUID().uuidString - _ = try await Amplify.Storage.uploadData( - key: key, - data: data(mb: 1), - options: options - ).value + await wait { + _ = try await Amplify.Storage.uploadData( + key: key, + data: self.data(mb: 1), + options: options + ).value + } // call `HeadObject` through SDK escape hatch let head = try await headObject(key: "public/\(key)") @@ -139,11 +145,13 @@ class AWSS3StoragePluginUploadMetadataTestCase: AWSS3StoragePluginTestBase { // upload file let key = UUID().uuidString - _ = try await Amplify.Storage.uploadData( - key: key, - data: data(mb: 7), - options: options - ).value + await wait(timeout: 60) { + _ = try await Amplify.Storage.uploadData( + key: key, + data: self.data(mb: 7), + options: options + ).value + } // call `HeadObject` through SDK escape hatch let head = try await headObject(key: "public/\(key)") @@ -187,11 +195,13 @@ class AWSS3StoragePluginUploadMetadataTestCase: AWSS3StoragePluginTestBase { // upload file let key = UUID().uuidString - _ = try await Amplify.Storage.uploadData( - key: key, - data: data(mb: 1), - options: options - ).value + await wait { + _ = try await Amplify.Storage.uploadData( + key: key, + data: self.data(mb: 1), + options: options + ).value + } // call `HeadObject` through SDK escape hatch let head = try await headObject(key: "public/\(key)") diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/ResumabilityTests/AWSS3StoragePluginGetDataResumabilityTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/ResumabilityTests/AWSS3StoragePluginGetDataResumabilityTests.swift index 40f9e863b9..d1ceae0fcd 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/ResumabilityTests/AWSS3StoragePluginGetDataResumabilityTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/ResumabilityTests/AWSS3StoragePluginGetDataResumabilityTests.swift @@ -20,8 +20,11 @@ class AWSS3StoragePluginDownloadDataResumabilityTests: AWSS3StoragePluginTestBas func testDownloadDataAndPause() async throws { let key = UUID().uuidString let data = AWSS3StoragePluginTestBase.smallDataObject - let uploadKey = try await Amplify.Storage.uploadData(key: key, data: data).value - XCTAssertEqual(uploadKey, key) + + await wait { + let uploadKey = try await Amplify.Storage.uploadData(key: key, data: data).value + XCTAssertEqual(uploadKey, key) + } Self.logger.debug("Downloading data") let task = Amplify.Storage.downloadData(key: key) @@ -75,8 +78,11 @@ class AWSS3StoragePluginDownloadDataResumabilityTests: AWSS3StoragePluginTestBas func testDownloadDataAndPauseThenResume() async throws { let key = UUID().uuidString let data = AWSS3StoragePluginTestBase.smallDataObject - let uploadKey = try await Amplify.Storage.uploadData(key: key, data: data).value - XCTAssertEqual(uploadKey, key) + + await wait { + let uploadKey = try await Amplify.Storage.uploadData(key: key, data: data).value + XCTAssertEqual(uploadKey, key) + } let task = Amplify.Storage.downloadData(key: key) @@ -126,8 +132,11 @@ class AWSS3StoragePluginDownloadDataResumabilityTests: AWSS3StoragePluginTestBas func testDownloadDataAndCancel() async throws { let key = UUID().uuidString let data = AWSS3StoragePluginTestBase.smallDataObject - let uploadKey = try await Amplify.Storage.uploadData(key: key, data: data).value - XCTAssertEqual(uploadKey, key) + + await wait { + let uploadKey = try await Amplify.Storage.uploadData(key: key, data: data).value + XCTAssertEqual(uploadKey, key) + } Self.logger.debug("Downloading data") let task = Amplify.Storage.downloadData(key: key) diff --git a/AmplifyTestCommon/Mocks/MockCredentialsProvider.swift b/AmplifyTestCommon/Mocks/MockCredentialsProvider.swift index 5b4f232074..aba5185d25 100644 --- a/AmplifyTestCommon/Mocks/MockCredentialsProvider.swift +++ b/AmplifyTestCommon/Mocks/MockCredentialsProvider.swift @@ -6,15 +6,16 @@ // import AWSPluginsCore -import AWSClientRuntime +import AwsCommonRuntimeKit import Foundation +import SmithyIdentity -class MockCredentialsProvider: AWSClientRuntime.CredentialsProviding { - func getCredentials() async throws -> AWSClientRuntime.AWSCredentials { - return AWSCredentials( +class MockCredentialsProvider: AwsCommonRuntimeKit.CredentialsProviding, AWSCredentialIdentityResolver { + func getCredentials() async throws -> AwsCommonRuntimeKit.Credentials { + return try Credentials( accessKey: "accessKey", secret: "secret", - expirationTimeout: Date().addingTimeInterval(1000) + expiration: Date().addingTimeInterval(1000) ) } } diff --git a/Package.resolved b/Package.resolved index 36c02fdfea..7ba77b8a62 100644 --- a/Package.resolved +++ b/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/awslabs/aws-crt-swift", "state" : { - "revision" : "0d0a0cf2e2cb780ceeceac190b4ede94f4f96902", - "version" : "0.26.0" + "revision" : "7b42e0343f28b3451aab20840dc670abd12790bd", + "version" : "0.36.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/awslabs/aws-sdk-swift.git", "state" : { - "revision" : "47922c05dd66be717c7bce424651a534456717b7", - "version" : "0.36.2" + "revision" : "828358a2c39d138325b0f87a2d813f4b972e5f4f", + "version" : "1.0.0" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/smithy-lang/smithy-swift", "state" : { - "revision" : "8a5b0105c1b8a1d26a9435fb0af3959a7f5de578", - "version" : "0.41.1" + "revision" : "0ed3440f8c41e27a0937364d5035d2d4fefb8aa3", + "version" : "0.71.0" } }, { diff --git a/Package.swift b/Package.swift index 3a679faade..d0e70eda38 100644 --- a/Package.swift +++ b/Package.swift @@ -9,7 +9,7 @@ let platforms: [SupportedPlatform] = [ .watchOS(.v9) ] let dependencies: [Package.Dependency] = [ - .package(url: "https://github.com/awslabs/aws-sdk-swift.git", exact: "0.36.2"), + .package(url: "https://github.com/awslabs/aws-sdk-swift.git", exact: "1.0.0"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", exact: "0.15.3"), .package(url: "https://github.com/mattgallagher/CwlPreconditionTesting.git", from: "2.1.0"), .package(url: "https://github.com/aws-amplify/amplify-swift-utils-notifications.git", from: "1.1.0") From 6c27d0af43b0007eb19aff7d350c38ad31f1b69e Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Wed, 18 Sep 2024 18:10:32 +0000 Subject: [PATCH 25/50] chore: release 2.41.0 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index fb7bd92060..a4b62a8ab2 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.40.0" + public static let amplifyVersion = "2.41.0" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 9a4ce09141d5045b8dfe55cb301b0691e3341a00 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Wed, 18 Sep 2024 18:12:25 +0000 Subject: [PATCH 26/50] chore: finalize release 2.41.0 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c379854026..23eef82149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.41.0 (2024-09-18) + +### Features + +- **Core**: Updating to AWS SDK for Swift 1.0.0 (#3858) + ## 2.40.0 (2024-09-17) ### Features From 0f7233425597c27a84684ae405f42a84d7a35f88 Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:58:17 -0400 Subject: [PATCH 27/50] chore: Updating README [skip ci] (#3874) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28552426e4..994d2d924e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ AWS Amplify provides a declarative and easy-to-use interface across different categories of cloud operations. Our default implementation works with Amazon Web Services (AWS), but AWS Amplify is designed to be open and pluggable for any custom backend or service. -The Amplify Library for Swift is layered on the [AWS SDK for Swift](https://aws.amazon.com/sdk-for-swift/), which was released as Developer Preview last year. This allows for access to the AWS SDK for Swift for a breadth of service-centric APIs. +The Amplify Library for Swift is layered on the [AWS SDK for Swift](https://aws.amazon.com/sdk-for-swift/). This allows for access to the AWS SDK for Swift for a breadth of service-centric APIs. [**API Documentation**](https://aws-amplify.github.io/amplify-swift/docs/) From 7740b5104e4c9a42f66af96d73d9529680d698c1 Mon Sep 17 00:00:00 2001 From: Di Wu Date: Wed, 18 Sep 2024 11:45:55 -0700 Subject: [PATCH 28/50] fix(api): append auth info as head fields for appSync realtime handshake request --- .../AppSyncRealTimeRequestAuth.swift | 34 +++--------- .../APIKeyAuthInterceptor.swift | 9 +-- .../AuthTokenInterceptor.swift | 11 ++-- .../IAMAuthInterceptor.swift | 11 ++-- .../AppSyncRealTimeClientFactory.swift | 12 ++-- .../Utils/URLRequest+AppSyncAuth.swift | 17 ++++++ .../AppSyncRealTimeRequestAuthTests.swift | 55 ------------------- .../APIKeyAuthInterceptorTests.swift | 17 ++---- .../CognitoAuthInterceptorTests.swift | 48 +++------------- .../AppSyncRealTimeClientFactoryTests.swift | 30 +++++++++- .../WebSocket/WebSocketClient.swift | 14 +---- .../WebSocket/WebSocketInterceptor.swift | 14 +++++ 12 files changed, 104 insertions(+), 168 deletions(-) create mode 100644 AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/URLRequest+AppSyncAuth.swift diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeRequestAuth.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeRequestAuth.swift index 87e01b1842..f649c3c380 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeRequestAuth.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeRequestAuth.swift @@ -9,6 +9,9 @@ import Foundation public enum AppSyncRealTimeRequestAuth { + private static let jsonEncoder = JSONEncoder() + private static let jsonDecoder = JSONDecoder() + case authToken(AuthToken) case apiKey(ApiKey) case iam(IAM) @@ -31,33 +34,10 @@ public enum AppSyncRealTimeRequestAuth { let amzDate: String } - public struct URLQuery { - let header: AppSyncRealTimeRequestAuth - let payload: String - - init(header: AppSyncRealTimeRequestAuth, payload: String = "{}") { - self.header = header - self.payload = payload - } - - func withBaseURL(_ url: URL, encoder: JSONEncoder? = nil) -> URL { - let jsonEncoder: JSONEncoder = encoder ?? JSONEncoder() - guard let headerJsonData = try? jsonEncoder.encode(header) else { - return url - } - - guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) - else { - return url - } - - urlComponents.queryItems = [ - URLQueryItem(name: "header", value: headerJsonData.base64EncodedString()), - URLQueryItem(name: "payload", value: try? payload.base64EncodedString()) - ] - - return urlComponents.url ?? url - } + var authHeaders: [String: String] { + (try? Self.jsonEncoder.encode(self)).flatMap { + try? Self.jsonDecoder.decode([String: String].self, from: $0) + } ?? [:] } } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptor.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptor.swift index f52ded490e..5906f7567e 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptor.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptor.swift @@ -21,11 +21,12 @@ class APIKeyAuthInterceptor { } extension APIKeyAuthInterceptor: WebSocketInterceptor { - func interceptConnection(url: URL) async -> URL { + + func interceptConnection(request: URLRequest) async -> URLRequest { + guard let url = request.url else { return request } + let authHeader = getAuthHeader(apiKey, AppSyncRealTimeClientFactory.appSyncApiEndpoint(url).host!) - return AppSyncRealTimeRequestAuth.URLQuery( - header: .apiKey(authHeader) - ).withBaseURL(url) + return request.injectAppSyncAuthToRequestHeader(auth: .apiKey(authHeader)) } } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/AuthTokenInterceptor.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/AuthTokenInterceptor.swift index b0f19ffd78..95f96698b1 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/AuthTokenInterceptor.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/AuthTokenInterceptor.swift @@ -57,15 +57,16 @@ extension AuthTokenInterceptor: AppSyncRequestInterceptor { } extension AuthTokenInterceptor: WebSocketInterceptor { - func interceptConnection(url: URL) async -> URL { + func interceptConnection(request: URLRequest) async -> URLRequest { + guard let url = request.url else { return request } let authToken = await getAuthToken() - return AppSyncRealTimeRequestAuth.URLQuery( - header: .authToken(.init( + return request.injectAppSyncAuthToRequestHeader( + auth: .authToken(.init( host: AppSyncRealTimeClientFactory.appSyncApiEndpoint(url).host!, authToken: authToken - )) - ).withBaseURL(url) + ) + )) } } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift index cd023676c7..511f7925f6 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Interceptor/SubscriptionInterceptor/IAMAuthInterceptor.swift @@ -88,15 +88,14 @@ class IAMAuthInterceptor { } extension IAMAuthInterceptor: WebSocketInterceptor { - func interceptConnection(url: URL) async -> URL { + + func interceptConnection(request: URLRequest) async -> URLRequest { + guard let url = request.url else { return request } let connectUrl = AppSyncRealTimeClientFactory.appSyncApiEndpoint(url).appendingPathComponent("connect") guard let authHeader = await getAuthHeader(connectUrl, with: "{}") else { - return connectUrl + return request } - - return AppSyncRealTimeRequestAuth.URLQuery( - header: .iam(authHeader) - ).withBaseURL(url) + return request.injectAppSyncAuthToRequestHeader(auth: .iam(authHeader)) } } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift index 57a3708e1e..802bb142eb 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/SubscriptionFactory/AppSyncRealTimeClientFactory.swift @@ -127,9 +127,9 @@ actor AppSyncRealTimeClientFactory: AppSyncRealTimeClientFactoryProtocol { extension AppSyncRealTimeClientFactory { /** - Converting appsync api url to realtime api url - 1. api.example.com/graphql -> api.example.com/graphql/realtime - 2. abc.appsync-api.us-east-1.amazonaws.com/graphql -> abc.appsync-realtime-api.us-east-1.amazonaws.com/graphql + Converting appsync api url to realtime api url, realtime endpoint has scheme 'wss' + 1. api.example.com/graphql -> wss://api.example.com/graphql/realtime + 2. abc.appsync-api.us-east-1.amazonaws.com/graphql -> wss://abc.appsync-realtime-api.us-east-1.amazonaws.com/graphql */ static func appSyncRealTimeEndpoint(_ url: URL) -> URL { guard let host = url.host else { @@ -145,6 +145,7 @@ extension AppSyncRealTimeClientFactory { } urlComponents.host = host.replacingOccurrences(of: "appsync-api", with: "appsync-realtime-api") + urlComponents.scheme = "wss" guard let realTimeUrl = urlComponents.url else { return url } @@ -153,9 +154,9 @@ extension AppSyncRealTimeClientFactory { } /** - Converting appsync realtime api url to api url + Converting appsync realtime api url to api url, api endpoint has scheme 'https' 1. api.example.com/graphql/realtime -> api.example.com/graphql - 2. abc.appsync-realtime-api.us-east-1.amazonaws.com/graphql -> abc.appsync-api.us-east-1.amazonaws.com/graphql + 2. abc.appsync-realtime-api.us-east-1.amazonaws.com/graphql -> https://abc.appsync-api.us-east-1.amazonaws.com/graphql */ static func appSyncApiEndpoint(_ url: URL) -> URL { guard let host = url.host else { @@ -174,6 +175,7 @@ extension AppSyncRealTimeClientFactory { } urlComponents.host = host.replacingOccurrences(of: "appsync-realtime-api", with: "appsync-api") + urlComponents.scheme = "https" guard let apiUrl = urlComponents.url else { return url } diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/URLRequest+AppSyncAuth.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/URLRequest+AppSyncAuth.swift new file mode 100644 index 0000000000..c186934f8d --- /dev/null +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Support/Utils/URLRequest+AppSyncAuth.swift @@ -0,0 +1,17 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + + +import Foundation + +extension URLRequest { + func injectAppSyncAuthToRequestHeader(auth: AppSyncRealTimeRequestAuth) -> URLRequest { + var requstCopy = self + auth.authHeaders.forEach { requstCopy.setValue($0.value, forHTTPHeaderField: $0.key) } + return requstCopy + } +} diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift index 6ab7af0692..e9f4061431 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift @@ -147,61 +147,6 @@ class AppSyncRealTimeRequestAuthTests: XCTestCase { """.shrink()) } - func testAppSyncRealTimeRequestAuth_URLQueryWithCognitoAuthHeader() { - let expectedURL = """ - https://example.com?\ - header=eyJBdXRob3JpemF0aW9uIjoiNDk4NTljN2MtNzQwNS00ZDU4LWFmZjctNTJiZ\ - TRiNDczNTU3IiwiaG9zdCI6ImV4YW1wbGUuY29tIn0%3D\ - &payload=e30%3D - """ - let encodedURL = AppSyncRealTimeRequestAuth.URLQuery( - header: .authToken(.init( - host: "example.com", - authToken: "49859c7c-7405-4d58-aff7-52be4b473557" - )) - ).withBaseURL(URL(string: "https://example.com")!, encoder: jsonEncoder) - XCTAssertEqual(encodedURL.absoluteString, expectedURL) - } - - func testAppSyncRealTimeRequestAuth_URLQueryWithApiKeyAuthHeader() { - let expectedURL = """ - https://example.com?\ - header=eyJob3N0IjoiZXhhbXBsZS5jb20iLCJ4LWFtei1kYXRlIjoiOWUwZTJkZjktMmVlNy00NjU5L\ - TgzNjItMWM4ODFlMTE4YzlmIiwieC1hcGkta2V5IjoiNjVlMmZhY2EtOGUxZS00ZDM3LThkYzctNjQ0N\ - 2Q5Njk4MjQ3In0%3D\ - &payload=e30%3D - """ - let encodedURL = AppSyncRealTimeRequestAuth.URLQuery( - header: .apiKey(.init( - host: "example.com", - apiKey: "65e2faca-8e1e-4d37-8dc7-6447d9698247", - amzDate: "9e0e2df9-2ee7-4659-8362-1c881e118c9f" - )) - ).withBaseURL(URL(string: "https://example.com")!, encoder: jsonEncoder) - XCTAssertEqual(encodedURL.absoluteString, expectedURL) - } - - func testAppSyncRealTimeRequestAuth_URLQueryWithIAMAuthHeader() { - - let expectedURL = """ - https://example.com?\ - header=eyJhY2NlcHQiOiJhcHBsaWNhdGlvblwvanNvbiwgdGV4dFwvamF2YXNjcmlwdCIsIkF1dGhvcml6YXR\ - pb24iOiJjOWRhZDg5Ny05MGQxLTRhNGMtYTVjOS0yYjM2YTI0NzczNWYiLCJjb250ZW50LWVuY29kaW5nIjoiY\ - W16LTEuMCIsImNvbnRlbnQtdHlwZSI6ImFwcGxpY2F0aW9uXC9qc29uOyBjaGFyc2V0PVVURi04IiwiaG9zdCI\ - 6ImV4YW1wbGUuY29tIiwieC1hbXotZGF0ZSI6IjllMGUyZGY5LTJlZTctNDY1OS04MzYyLTFjODgxZTExOGM5Z\ - iIsIlgtQW16LVNlY3VyaXR5LVRva2VuIjoiZTdlNjI2OWUtZmRhMS00ZGUwLThiZGItYmFhN2I2ZGQwYTBkIn0%3D\ - &payload=e30%3D - """ - let encodedURL = AppSyncRealTimeRequestAuth.URLQuery( - header: .iam(.init( - host: "example.com", - authToken: "c9dad897-90d1-4a4c-a5c9-2b36a247735f", - securityToken: "e7e6269e-fda1-4de0-8bdb-baa7b6dd0a0d", - amzDate: "9e0e2df9-2ee7-4659-8362-1c881e118c9f")) - ).withBaseURL(URL(string: "https://example.com")!, encoder: jsonEncoder) - XCTAssertEqual(encodedURL.absoluteString, expectedURL) - } - private func toJson(_ value: Encodable) -> String? { return try? String(data: jsonEncoder.encode(value), encoding: .utf8) } diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptorTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptorTests.swift index 8c89c0a53a..7c8ebff620 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptorTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/APIKeyAuthInterceptorTests.swift @@ -12,20 +12,13 @@ import Amplify class APIKeyAuthInterceptorTests: XCTestCase { - func testInterceptConnection_addApiKeySignatureInURLQuery() async { + func testInterceptConnection_addApiKeyInRequestHeader() async { let apiKey = UUID().uuidString let interceptor = APIKeyAuthInterceptor(apiKey: apiKey) - let resultUrl = await interceptor.interceptConnection(url: URL(string: "https://example.com")!) - guard let components = URLComponents(url: resultUrl, resolvingAgainstBaseURL: false) else { - XCTFail("Failed to decode decorated URL") - return - } - - let header = components.queryItems?.first { $0.name == "header" } - XCTAssertNotNil(header?.value) - let headerData = try! header?.value!.base64DecodedString().data(using: .utf8) - let decodedHeader = try! JSONDecoder().decode(JSONValue.self, from: headerData!) - XCTAssertEqual(decodedHeader["x-api-key"]?.stringValue, apiKey) + let resultUrlRequest = await interceptor.interceptConnection(request: URLRequest(url: URL(string: "https://example.com")!)) + + let header = resultUrlRequest.value(forHTTPHeaderField: "x-api-key") + XCTAssertEqual(header, apiKey) } func testInterceptRequest_appendAuthInfoInPayload() async { diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/CognitoAuthInterceptorTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/CognitoAuthInterceptorTests.swift index 4127f018fd..d0383bff21 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/CognitoAuthInterceptorTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Interceptor/SubscriptionInterceptor/CognitoAuthInterceptorTests.swift @@ -13,56 +13,24 @@ import Amplify class CognitoAuthInterceptorTests: XCTestCase { - func testInterceptConnection_withAuthTokenProvider_appendCorrectAuthHeaderToQuery() async { + func testInterceptConnection_withAuthTokenProvider_appendCorrectAuthHeader() async { let authTokenProvider = MockAuthTokenProvider() let interceptor = AuthTokenInterceptor(authTokenProvider: authTokenProvider) - let decoratedURL = await interceptor.interceptConnection(url: URL(string: "https://example.com")!) - guard let components = URLComponents(url: decoratedURL, resolvingAgainstBaseURL: false) else { - XCTFail("Failed to get url components from decorated URL") - return - } + let decoratedURLRequest = await interceptor.interceptConnection(request: URLRequest(url:URL(string: "https://example.com")!)) - guard let queryHeaderString = - try? components.queryItems?.first(where: { $0.name == "header" })?.value?.base64DecodedString() - else { - XCTFail("Failed to extract header field from query string") - return - } - - guard let queryHeader = try? JSONDecoder().decode(JSONValue.self, from: queryHeaderString.data(using: .utf8)!) - else { - XCTFail("Failed to decode query header to json object") - return - } - XCTAssertEqual(authTokenProvider.authToken, queryHeader.Authorization?.stringValue) - XCTAssertEqual("example.com", queryHeader.host?.stringValue) + XCTAssertEqual(authTokenProvider.authToken, decoratedURLRequest.value(forHTTPHeaderField: "Authorization")) + XCTAssertEqual("example.com", decoratedURLRequest.value(forHTTPHeaderField: "host")) } - func testInterceptConnection_withAuthTokenProviderFailed_appendEmptyAuthHeaderToQuery() async { + func testInterceptConnection_withAuthTokenProviderFailed_appendEmptyAuthHeader() async { let authTokenProvider = MockAuthTokenProviderFailed() let interceptor = AuthTokenInterceptor(authTokenProvider: authTokenProvider) - let decoratedURL = await interceptor.interceptConnection(url: URL(string: "https://example.com")!) - guard let components = URLComponents(url: decoratedURL, resolvingAgainstBaseURL: false) else { - XCTFail("Failed to get url components from decorated URL") - return - } + let decoratedURLRequest = await interceptor.interceptConnection(request: URLRequest(url:URL(string: "https://example.com")!)) - guard let queryHeaderString = - try? components.queryItems?.first(where: { $0.name == "header" })?.value?.base64DecodedString() - else { - XCTFail("Failed to extract header field from query string") - return - } - - guard let queryHeader = try? JSONDecoder().decode(JSONValue.self, from: queryHeaderString.data(using: .utf8)!) - else { - XCTFail("Failed to decode query header to json object") - return - } - XCTAssertEqual("", queryHeader.Authorization?.stringValue) - XCTAssertEqual("example.com", queryHeader.host?.stringValue) + XCTAssertEqual("", decoratedURLRequest.value(forHTTPHeaderField: "Authorization")) + XCTAssertEqual("example.com", decoratedURLRequest.value(forHTTPHeaderField: "host")) } func testInterceptRequest_withAuthTokenProvider_appendCorrectAuthInfoToPayload() async { diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/SubscriptionFactory/AppSyncRealTimeClientFactoryTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/SubscriptionFactory/AppSyncRealTimeClientFactoryTests.swift index 7156ac7678..15ca8c7858 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/SubscriptionFactory/AppSyncRealTimeClientFactoryTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/SubscriptionFactory/AppSyncRealTimeClientFactoryTests.swift @@ -15,15 +15,15 @@ class AppSyncRealTimeClientFactoryTests: XCTestCase { let appSyncEndpoint = URL(string: "https://abc.appsync-api.amazonaws.com/graphql")! XCTAssertEqual( AppSyncRealTimeClientFactory.appSyncRealTimeEndpoint(appSyncEndpoint), - URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql") + URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql") ) } func testAppSyncRealTimeEndpoint_withAWSAppSyncRealTimeDomain_returnTheSameDomain() { - let appSyncEndpoint = URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql")! + let appSyncEndpoint = URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql")! XCTAssertEqual( AppSyncRealTimeClientFactory.appSyncRealTimeEndpoint(appSyncEndpoint), - URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql") + URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql") ) } @@ -34,4 +34,28 @@ class AppSyncRealTimeClientFactoryTests: XCTestCase { URL(string: "https://test.example.com/graphql/realtime") ) } + + func testAppSyncApiEndpoint_withAWSAppSyncRealTimeDomain_returnCorrectApiDomain() { + let appSyncEndpoint = URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql")! + XCTAssertEqual( + AppSyncRealTimeClientFactory.appSyncApiEndpoint(appSyncEndpoint), + URL(string: "https://abc.appsync-api.amazonaws.com/graphql") + ) + } + + func testAppSyncApiEndpoint_withAWSAppSyncApiDomain_returnTheSameDomain() { + let appSyncEndpoint = URL(string: "https://abc.appsync-api.amazonaws.com/graphql")! + XCTAssertEqual( + AppSyncRealTimeClientFactory.appSyncApiEndpoint(appSyncEndpoint), + URL(string: "https://abc.appsync-api.amazonaws.com/graphql") + ) + } + + func testAppSyncApiEndpoint_withCustomDomain_returnCorrectRealtimePath() { + let appSyncEndpoint = URL(string: "https://test.example.com/graphql")! + XCTAssertEqual( + AppSyncRealTimeClientFactory.appSyncApiEndpoint(appSyncEndpoint), + URL(string: "https://test.example.com/graphql") + ) + } } diff --git a/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift b/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift index cc1149ac27..e2e8c85503 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift @@ -72,7 +72,7 @@ public final actor WebSocketClient: NSObject { interceptor: WebSocketInterceptor? = nil, networkMonitor: WebSocketNetworkMonitorProtocol = AmplifyNetworkMonitor() ) { - self.url = Self.useWebSocketProtocolScheme(url: url) + self.url = url self.handshakeHttpHeaders = handshakeHttpHeaders self.interceptor = interceptor self.autoConnectOnNetworkStatusChange = false @@ -160,6 +160,8 @@ public final actor WebSocketClient: NSObject { var urlRequest = URLRequest(url: decoratedURL) self.handshakeHttpHeaders.forEach { urlRequest.setValue($0.value, forHTTPHeaderField: $0.key) } + urlRequest = await self.interceptor?.interceptConnection(request: urlRequest) ?? urlRequest + let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) return urlSession.webSocketTask(with: urlRequest) } @@ -345,16 +347,6 @@ extension WebSocketClient { } } -extension WebSocketClient { - static func useWebSocketProtocolScheme(url: URL) -> URL { - guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { - return url - } - urlComponents.scheme = urlComponents.scheme == "http" ? "ws" : "wss" - return urlComponents.url ?? url - } -} - extension WebSocketClient: DefaultLogger { public static var log: Logger { Amplify.Logging.logger(forNamespace: String(describing: self)) diff --git a/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketInterceptor.swift b/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketInterceptor.swift index a53ec3b950..351119ff03 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketInterceptor.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketInterceptor.swift @@ -11,4 +11,18 @@ import Foundation @_spi(WebSocket) public protocol WebSocketInterceptor { func interceptConnection(url: URL) async -> URL + + func interceptConnection(request: URLRequest) async -> URLRequest +} + +public extension WebSocketInterceptor { + + func interceptConnection(url: URL) async -> URL { + return url + } + + func interceptConnection(request: URLRequest) async -> URLRequest { + return request + } + } From 0a499673736fc87f344954a01de3d11d96987919 Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:59:10 -0400 Subject: [PATCH 29/50] fix(Storage): Removing testable import on UploadPartInput extension (#3878) --- .../UploadPartInput+presignURL.swift | 95 +++++++++++++++++-- 1 file changed, 88 insertions(+), 7 deletions(-) diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift index ae89c9359e..0350c6ca01 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift @@ -5,12 +5,13 @@ // SPDX-License-Identifier: Apache-2.0 import Foundation -@testable import AWSS3 +import AWSS3 @_spi(SmithyReadWrite) import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) @_spi(SmithyReadWrite) import AWSClientRuntime import Smithy import SmithyHTTPAPI import SmithyRetries +@_spi(SmithyReadWrite) import SmithyXML // swiftlint:disable identifier_name // swiftlint:disable line_length @@ -29,7 +30,7 @@ extension UploadPartInput { .withServiceName(value: serviceName) .withOperation(value: "uploadPart") .withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator) - .withLogger(value: config.logger) + .withLogger(value: config.telemetryProvider.loggerProvider.getLogger(name: S3Client.clientName)) .withPartitionID(value: config.partitionID) .withAuthSchemes(value: config.authSchemes ?? []) .withAuthSchemeResolver(value: config.authSchemeResolver) @@ -52,9 +53,9 @@ extension UploadPartInput { config.httpInterceptorProviders.forEach { provider in builder.interceptors.add(provider.create()) } - builder.interceptors.add(ClientRuntime.URLPathMiddleware(UploadPartInput.urlPathProvider(_:))) + builder.interceptors.add(ClientRuntime.URLPathMiddleware(UploadPartInput.customUrlPathProvider(_:))) builder.interceptors.add(ClientRuntime.URLHostMiddleware()) - builder.deserialize(ClientRuntime.DeserializeMiddleware(UploadPartOutput.httpOutput(from:), PutObjectOutputError.httpError(from:))) + builder.deserialize(ClientRuntime.DeserializeMiddleware(UploadPartOutput.customHttpOutput(from:), CustomUploadPartOutputError.httpError(from:))) builder.interceptors.add(ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) @@ -80,9 +81,89 @@ extension UploadPartInput { .build() return try await op.presignRequest(input: input).endpoint.url } - } +} + +private extension UploadPartInput { + static func customUrlPathProvider(_ value: UploadPartInput) -> Swift.String? { + guard let key = value.key else { + return nil + } + return "/\(key.urlPercentEncoding(encodeForwardSlash: false))" + } +} + +private extension UploadPartOutput { + static func customHttpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> UploadPartOutput { + var value = UploadPartOutput() + if let bucketKeyEnabledHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-bucket-key-enabled") { + value.bucketKeyEnabled = Swift.Bool(bucketKeyEnabledHeaderValue) ?? false + } + if let checksumCRC32HeaderValue = httpResponse.headers.value(for: "x-amz-checksum-crc32") { + value.checksumCRC32 = checksumCRC32HeaderValue + } + if let checksumCRC32CHeaderValue = httpResponse.headers.value(for: "x-amz-checksum-crc32c") { + value.checksumCRC32C = checksumCRC32CHeaderValue + } + if let checksumSHA1HeaderValue = httpResponse.headers.value(for: "x-amz-checksum-sha1") { + value.checksumSHA1 = checksumSHA1HeaderValue + } + if let checksumSHA256HeaderValue = httpResponse.headers.value(for: "x-amz-checksum-sha256") { + value.checksumSHA256 = checksumSHA256HeaderValue + } + if let eTagHeaderValue = httpResponse.headers.value(for: "ETag") { + value.eTag = eTagHeaderValue + } + if let requestChargedHeaderValue = httpResponse.headers.value(for: "x-amz-request-charged") { + value.requestCharged = S3ClientTypes.RequestCharged(rawValue: requestChargedHeaderValue) + } + if let sseCustomerAlgorithmHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-customer-algorithm") { + value.sseCustomerAlgorithm = sseCustomerAlgorithmHeaderValue + } + if let sseCustomerKeyMD5HeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-customer-key-MD5") { + value.sseCustomerKeyMD5 = sseCustomerKeyMD5HeaderValue + } + if let ssekmsKeyIdHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-aws-kms-key-id") { + value.ssekmsKeyId = ssekmsKeyIdHeaderValue + } + if let serverSideEncryptionHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption") { + value.serverSideEncryption = S3ClientTypes.ServerSideEncryption(rawValue: serverSideEncryptionHeaderValue) + } + return value + } +} + +private enum CustomUploadPartOutputError { + static func httpError(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyXML.Reader.from(data: data) + let baseError = try AWSClientRuntime.RestXMLError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: true) + if let error = baseError.customError() { return error } + if baseError.httpResponse.statusCode == .notFound && baseError.httpResponse.body.isEmpty { + return CustomUploadPartOutputError.NotFound( + httpResponse: baseError.httpResponse, + message: baseError.requestID, + requestID: baseError.message, + requestID2: baseError.requestID2 + ) + } + switch baseError.code { + default: return try AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(baseError: baseError) + } + } + + private struct NotFound: ClientRuntime.ModeledError, AWSClientRuntime.AWSS3ServiceError, ClientRuntime.HTTPError, Swift.Error { + static var typeName: Swift.String { "NotFound" } + static var fault: ClientRuntime.ErrorFault { .client } + static var isRetryable: Swift.Bool { false } + static var isThrottling: Swift.Bool { false } + var httpResponse = SmithyHTTPAPI.HTTPResponse() + var message: Swift.String? + var requestID: Swift.String? + var requestID2: Swift.String? + } +} -struct UploadPartPresignedMiddleware: Smithy.RequestMessageSerializer { +private struct UploadPartPresignedMiddleware: Smithy.RequestMessageSerializer { typealias InputType = UploadPartInput typealias RequestType = SmithyHTTPAPI.HTTPRequest From 47be0ddd4a574ea582af042f262e2ce7b3efa523 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Thu, 19 Sep 2024 20:21:59 +0000 Subject: [PATCH 30/50] chore: release 2.41.1 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index a4b62a8ab2..81fa7eadfd 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.41.0" + public static let amplifyVersion = "2.41.1" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 36e5e92a5c8e1df92b69c5575de1c1f02ed1611e Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Thu, 19 Sep 2024 20:23:59 +0000 Subject: [PATCH 31/50] chore: finalize release 2.41.1 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23eef82149..ce673f5ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.41.1 (2024-09-19) + +### Bug Fixes + +- **Storage**: Removing testable import on UploadPartInput extension (#3878) + ## 2.41.0 (2024-09-18) ### Features From 4cc453ed2ec9a5eb3f0ec1b8a55a6d82e4e887b6 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Fri, 20 Sep 2024 19:14:28 +0000 Subject: [PATCH 32/50] chore: release 2.41.2 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 81fa7eadfd..4c46c88645 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.41.1" + public static let amplifyVersion = "2.41.2" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 57a05c38a010c78e19e6af71f89d874e0a9b49b6 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Fri, 20 Sep 2024 19:16:03 +0000 Subject: [PATCH 33/50] chore: finalize release 2.41.2 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce673f5ac6..786aa46d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.41.2 (2024-09-20) + +### Bug Fixes + +- **api**: append auth info as head fields for appSync realtime handshake request + ## 2.41.1 (2024-09-19) ### Bug Fixes From 815bf2a044aeb24576322c39ca8ec2a1c2f7ec58 Mon Sep 17 00:00:00 2001 From: Di Wu Date: Mon, 23 Sep 2024 11:23:29 -0700 Subject: [PATCH 34/50] feat(api): add support for GraphQL filter attributeExists (#3838) * add support for GraphQL filter attributeExists * fix(datastore): deduplicate SQL statement of attributeExists and eq/ne * fix(datastore): flacky unit test --- .../Model/Internal/Persistable.swift | 36 +++++++ .../Categories/DataStore/Query/ModelKey.swift | 5 + .../DataStore/Query/QueryField.swift | 7 +- .../Query/QueryOperator+Equatable.swift | 2 + .../DataStore/Query/QueryOperator.swift | 19 +++- .../DataStore/Query/QueryPredicate.swift | 30 +----- .../GraphQLModelBasedTests+List.swift | 52 ++++++++++ .../Support/QueryPredicate+GraphQL.swift | 4 + .../GraphQLListQueryTests.swift | 84 ++++++++++++++++ ...yPredicateEvaluateGeneratedBoolTests.swift | 4 +- ...yPredicateEvaluateGeneratedDateTests.swift | 8 +- ...dicateEvaluateGeneratedDateTimeTests.swift | 8 +- ...icateEvaluateGeneratedDoubleIntTests.swift | 12 +-- ...redicateEvaluateGeneratedDoubleTests.swift | 12 +-- ...ryPredicateEvaluateGeneratedIntTests.swift | 6 +- ...redicateEvaluateGeneratedStringTests.swift | 8 +- ...yPredicateEvaluateGeneratedTimeTests.swift | 8 +- .../SQLite/QueryPredicate+SQLite.swift | 4 + .../SQLite/SQLStatement+Condition.swift | 59 ++++++++++- .../Core/SQLStatementTests.swift | 97 +++++++++++++++++++ .../Support/MutationEventQueryTests.swift | 8 +- 21 files changed, 401 insertions(+), 72 deletions(-) diff --git a/Amplify/Categories/DataStore/Model/Internal/Persistable.swift b/Amplify/Categories/DataStore/Model/Internal/Persistable.swift index b7a53acf5a..92fd149d8d 100644 --- a/Amplify/Categories/DataStore/Model/Internal/Persistable.swift +++ b/Amplify/Categories/DataStore/Model/Internal/Persistable.swift @@ -65,6 +65,12 @@ struct PersistableHelper { return lhs == rhs case let (lhs, rhs) as (String, String): return lhs == rhs + case let (lhs, rhs) as (any EnumPersistable, String): + return lhs.rawValue == rhs + case let (lhs, rhs) as (String, any EnumPersistable): + return lhs == rhs.rawValue + case let (lhs, rhs) as (any EnumPersistable, any EnumPersistable): + return lhs.rawValue == rhs.rawValue default: return false } @@ -94,6 +100,12 @@ struct PersistableHelper { return lhs == Double(rhs) case let (lhs, rhs) as (String, String): return lhs == rhs + case let (lhs, rhs) as (any EnumPersistable, String): + return lhs.rawValue == rhs + case let (lhs, rhs) as (String, any EnumPersistable): + return lhs == rhs.rawValue + case let (lhs, rhs) as (any EnumPersistable, any EnumPersistable): + return lhs.rawValue == rhs.rawValue default: return false } @@ -122,6 +134,12 @@ struct PersistableHelper { return lhs <= Double(rhs) case let (lhs, rhs) as (String, String): return lhs <= rhs + case let (lhs, rhs) as (any EnumPersistable, String): + return lhs.rawValue <= rhs + case let (lhs, rhs) as (String, any EnumPersistable): + return lhs <= rhs.rawValue + case let (lhs, rhs) as (any EnumPersistable, any EnumPersistable): + return lhs.rawValue <= rhs.rawValue default: return false } @@ -150,6 +168,12 @@ struct PersistableHelper { return lhs < Double(rhs) case let (lhs, rhs) as (String, String): return lhs < rhs + case let (lhs, rhs) as (any EnumPersistable, String): + return lhs.rawValue < rhs + case let (lhs, rhs) as (String, any EnumPersistable): + return lhs < rhs.rawValue + case let (lhs, rhs) as (any EnumPersistable, any EnumPersistable): + return lhs.rawValue < rhs.rawValue default: return false } @@ -178,6 +202,12 @@ struct PersistableHelper { return lhs >= Double(rhs) case let (lhs, rhs) as (String, String): return lhs >= rhs + case let (lhs, rhs) as (any EnumPersistable, String): + return lhs.rawValue >= rhs + case let (lhs, rhs) as (String, any EnumPersistable): + return lhs >= rhs.rawValue + case let (lhs, rhs) as (any EnumPersistable, any EnumPersistable): + return lhs.rawValue >= rhs.rawValue default: return false } @@ -206,6 +236,12 @@ struct PersistableHelper { return Double(lhs) > rhs case let (lhs, rhs) as (String, String): return lhs > rhs + case let (lhs, rhs) as (any EnumPersistable, String): + return lhs.rawValue > rhs + case let (lhs, rhs) as (String, any EnumPersistable): + return lhs > rhs.rawValue + case let (lhs, rhs) as (any EnumPersistable, any EnumPersistable): + return lhs.rawValue > rhs.rawValue default: return false } diff --git a/Amplify/Categories/DataStore/Query/ModelKey.swift b/Amplify/Categories/DataStore/Query/ModelKey.swift index 8096bfc9c7..860cb17061 100644 --- a/Amplify/Categories/DataStore/Query/ModelKey.swift +++ b/Amplify/Categories/DataStore/Query/ModelKey.swift @@ -36,6 +36,11 @@ public protocol ModelKey: CodingKey, CaseIterable, QueryFieldOperation {} extension CodingKey where Self: ModelKey { + // MARK: - attributeExists + public func attributeExists(_ value: Bool) -> QueryPredicateOperation { + return field(stringValue).attributeExists(value) + } + // MARK: - beginsWith public func beginsWith(_ value: String) -> QueryPredicateOperation { return field(stringValue).beginsWith(value) diff --git a/Amplify/Categories/DataStore/Query/QueryField.swift b/Amplify/Categories/DataStore/Query/QueryField.swift index 9d29967569..afde18a94d 100644 --- a/Amplify/Categories/DataStore/Query/QueryField.swift +++ b/Amplify/Categories/DataStore/Query/QueryField.swift @@ -30,7 +30,7 @@ public func field(_ name: String) -> QueryField { /// - seealso: `ModelKey` public protocol QueryFieldOperation { // MARK: - Functions - + func attributeExists(_ value: Bool) -> QueryPredicateOperation func beginsWith(_ value: String) -> QueryPredicateOperation func between(start: Persistable, end: Persistable) -> QueryPredicateOperation func contains(_ value: String) -> QueryPredicateOperation @@ -61,6 +61,11 @@ public struct QueryField: QueryFieldOperation { public let name: String + // MARK: - attributeExists + public func attributeExists(_ value: Bool) -> QueryPredicateOperation { + return QueryPredicateOperation(field: name, operator: .attributeExists(value)) + } + // MARK: - beginsWith public func beginsWith(_ value: String) -> QueryPredicateOperation { return QueryPredicateOperation(field: name, operator: .beginsWith(value)) diff --git a/Amplify/Categories/DataStore/Query/QueryOperator+Equatable.swift b/Amplify/Categories/DataStore/Query/QueryOperator+Equatable.swift index 41ee77159b..c1907802b6 100644 --- a/Amplify/Categories/DataStore/Query/QueryOperator+Equatable.swift +++ b/Amplify/Categories/DataStore/Query/QueryOperator+Equatable.swift @@ -24,6 +24,8 @@ extension QueryOperator: Equatable { case let (.between(oneStart, oneEnd), .between(otherStart, otherEnd)): return PersistableHelper.isEqual(oneStart, otherStart) && PersistableHelper.isEqual(oneEnd, otherEnd) + case let (.attributeExists(one), .attributeExists(other)): + return one == other default: return false } diff --git a/Amplify/Categories/DataStore/Query/QueryOperator.swift b/Amplify/Categories/DataStore/Query/QueryOperator.swift index 2fcb50ccd2..e4897e5f0d 100644 --- a/Amplify/Categories/DataStore/Query/QueryOperator.swift +++ b/Amplify/Categories/DataStore/Query/QueryOperator.swift @@ -18,8 +18,9 @@ public enum QueryOperator: Encodable { case notContains(_ value: String) case between(start: Persistable, end: Persistable) case beginsWith(_ value: String) + case attributeExists(_ value: Bool) - public func evaluate(target: Any) -> Bool { + public func evaluate(target: Any?) -> Bool { switch self { case .notEqual(let predicateValue): return !PersistableHelper.isEqual(target, predicateValue) @@ -34,20 +35,26 @@ public enum QueryOperator: Encodable { case .greaterThan(let predicateValue): return PersistableHelper.isGreaterThan(target, predicateValue) case .contains(let predicateString): - if let targetString = target as? String { + if let targetString = target.flatMap({ $0 as? String }) { return targetString.contains(predicateString) } return false case .notContains(let predicateString): - if let targetString = target as? String { + if let targetString = target.flatMap({ $0 as? String }) { return !targetString.contains(predicateString) } case .between(let start, let end): return PersistableHelper.isBetween(start, end, target) case .beginsWith(let predicateValue): - if let targetString = target as? String { + if let targetString = target.flatMap({ $0 as? String }) { return targetString.starts(with: predicateValue) } + case .attributeExists(let predicateValue): + if case .some = target { + return predicateValue == true + } else { + return predicateValue == false + } } return false } @@ -105,6 +112,10 @@ public enum QueryOperator: Encodable { case .beginsWith(let value): try container.encode("beginsWith", forKey: .type) try container.encode(value, forKey: .value) + + case .attributeExists(let value): + try container.encode("attributeExists", forKey: .type) + try container.encode(value, forKey: .value) } } } diff --git a/Amplify/Categories/DataStore/Query/QueryPredicate.swift b/Amplify/Categories/DataStore/Query/QueryPredicate.swift index 78bdf9f051..222bd11c6e 100644 --- a/Amplify/Categories/DataStore/Query/QueryPredicate.swift +++ b/Amplify/Categories/DataStore/Query/QueryPredicate.swift @@ -155,34 +155,6 @@ public class QueryPredicateOperation: QueryPredicate, Encodable { } public func evaluate(target: Model) -> Bool { - guard let fieldValue = target[field] else { - return false - } - - guard let value = fieldValue else { - return false - } - - if let booleanValue = value as? Bool { - return self.operator.evaluate(target: booleanValue) - } - - if let doubleValue = value as? Double { - return self.operator.evaluate(target: doubleValue) - } - - if let intValue = value as? Int { - return self.operator.evaluate(target: intValue) - } - - if let timeValue = value as? Temporal.Time { - return self.operator.evaluate(target: timeValue) - } - - if let enumValue = value as? EnumPersistable { - return self.operator.evaluate(target: enumValue.rawValue) - } - - return self.operator.evaluate(target: value) + return self.operator.evaluate(target: target[field]?.flatMap { $0 }) } } diff --git a/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/GraphQLModelBasedTests+List.swift b/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/GraphQLModelBasedTests+List.swift index c42916e6e6..032a9d9c67 100644 --- a/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/GraphQLModelBasedTests+List.swift +++ b/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/GraphQLModelBasedTests+List.swift @@ -145,4 +145,56 @@ extension GraphQLModelBasedTests { XCTAssertNotNil(error) } } + + /** + - Given: API with Post schema and optional field 'draft' + - When: + - create a new post with optional field 'draft' value .none + - Then: + - query Posts with filter {eq : null} shouldn't include the post + */ + func test_listModelsWithNilOptionalField_failedWithEqFilter() async throws { + let post = Post(title: UUID().uuidString, content: UUID().uuidString, createdAt: .now()) + _ = try await Amplify.API.mutate(request: .create(post)) + let posts = try await list(.list( + Post.self, + where: Post.keys.draft == nil && Post.keys.createdAt >= post.createdAt + )) + + XCTAssertFalse(posts.map(\.id).contains(post.id)) + } + + /** + - Given: DataStore with Post schema and optional field 'draft' + - When: + - create a new post with optional field 'draft' value .none + - Then: + - query Posts with filter {attributeExists : false} should include the post + */ + func test_listModelsWithNilOptionalField_successWithAttributeExistsFilter() async throws { + let post = Post(title: UUID().uuidString, content: UUID().uuidString, createdAt: .now()) + _ = try await Amplify.API.mutate(request: .create(post)) + let listPosts = try await list( + .list( + Post.self, + where: Post.keys.draft.attributeExists(false) + && Post.keys.createdAt >= post.createdAt + ) + ) + + XCTAssertTrue(listPosts.map(\.id).contains(post.id)) + } + + func list(_ request: GraphQLRequest>) async throws -> [M] { + func getAllPages(_ list: List) async throws -> [M] { + if list.hasNextPage() { + return list.elements + (try await getAllPages(list.getNextPage())) + } else { + return list.elements + } + } + + return try await getAllPages(try await Amplify.API.query(request: request).get()) + } + } diff --git a/AmplifyPlugins/Core/AWSPluginsCore/Model/Support/QueryPredicate+GraphQL.swift b/AmplifyPlugins/Core/AWSPluginsCore/Model/Support/QueryPredicate+GraphQL.swift index f2e3a6f816..c7e6735920 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/Model/Support/QueryPredicate+GraphQL.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/Model/Support/QueryPredicate+GraphQL.swift @@ -187,6 +187,8 @@ extension QueryOperator { return "beginsWith" case .notContains: return "notContains" + case .attributeExists: + return "attributeExists" } } @@ -212,6 +214,8 @@ extension QueryOperator { return value case .notContains(let value): return value + case .attributeExists(let value): + return value } } } diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/GraphQLDocument/GraphQLListQueryTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/GraphQLDocument/GraphQLListQueryTests.swift index 7d26e52108..fc2cc77bbe 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/GraphQLDocument/GraphQLListQueryTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/GraphQLDocument/GraphQLListQueryTests.swift @@ -218,4 +218,88 @@ class GraphQLListQueryTests: XCTestCase { XCTAssertEqual(variables["limit"] as? Int, 1_000) XCTAssertNotNil(variables["filter"]) } + + /** + - Given: + - A Post schema with optional field 'draft' + - When: + - Using list query to filter records that either don't have 'draft' field or have 'null' value + - Then: + - the query document as expected + - the filter is encoded correctly + */ + func test_listQuery_withAttributeExistsFilter_correctlyBuildGraphQLQueryStatement() { + let post = Post.keys + let predicate = post.id.eq("id") + && (post.draft.attributeExists(false) || post.draft.eq(nil)) + + var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelSchema: Post.schema, operationType: .query) + documentBuilder.add(decorator: DirectiveNameDecorator(type: .list)) + documentBuilder.add(decorator: PaginationDecorator()) + documentBuilder.add(decorator: FilterDecorator(filter: predicate.graphQLFilter(for: Post.schema))) + let document = documentBuilder.build() + let expectedQueryDocument = """ + query ListPosts($filter: ModelPostFilterInput, $limit: Int) { + listPosts(filter: $filter, limit: $limit) { + items { + id + content + createdAt + draft + rating + status + title + updatedAt + __typename + } + nextToken + } + } + """ + XCTAssertEqual(document.name, "listPosts") + XCTAssertEqual(document.stringValue, expectedQueryDocument) + guard let variables = document.variables else { + XCTFail("The document doesn't contain variables") + return + } + XCTAssertNotNil(variables["limit"]) + XCTAssertEqual(variables["limit"] as? Int, 1_000) + + guard let filter = variables["filter"] as? GraphQLFilter else { + XCTFail("variables should contain a valid filter") + return + } + + // Test filter for a valid JSON format + let filterJSON = try? JSONSerialization.data(withJSONObject: filter, + options: .prettyPrinted) + XCTAssertNotNil(filterJSON) + + let expectedFilterJSON = """ + { + "and" : [ + { + "id" : { + "eq" : "id" + } + }, + { + "or" : [ + { + "draft" : { + "attributeExists" : false + } + }, + { + "draft" : { + "eq" : null + } + } + ] + } + ] + } + """ + XCTAssertEqual(String(data: filterJSON!, encoding: .utf8), expectedFilterJSON) + } } diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedBoolTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedBoolTests.swift index e3e013d248..465ccfe256 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedBoolTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedBoolTests.swift @@ -41,7 +41,7 @@ class QueryPredicateEvaluateGeneratedBoolTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testBoolfalsenotEqualBooltrue() throws { @@ -70,7 +70,7 @@ class QueryPredicateEvaluateGeneratedBoolTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testBooltrueequalsBooltrue() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTests.swift index ae7c9c8f34..5055bf2230 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTests.swift @@ -60,7 +60,7 @@ class QueryPredicateEvaluateGeneratedDateTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTemporal_Date_now_addvalue1to_daynotEqualTemporalDateTemporal_Date_now() throws { @@ -109,7 +109,7 @@ class QueryPredicateEvaluateGeneratedDateTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTemporal_Date_now_addvalue2to_daynotEqualTemporalDateTemporal_Date_now() throws { @@ -158,7 +158,7 @@ class QueryPredicateEvaluateGeneratedDateTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTemporal_Date_now_addvalue3to_daynotEqualTemporalDateTemporal_Date_now() throws { @@ -207,7 +207,7 @@ class QueryPredicateEvaluateGeneratedDateTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTemporal_Date_nowequalsTemporalDateTemporal_Date_now() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTimeTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTimeTests.swift index 226b3d7908..14728e550a 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTimeTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDateTimeTests.swift @@ -66,7 +66,7 @@ class QueryPredicateEvaluateGeneratedDateTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTimeTemporal_DateTime_now_addvalue1to_hournotEqualTemporalDateTimeTemporal_DateTime_now() throws { @@ -120,7 +120,7 @@ class QueryPredicateEvaluateGeneratedDateTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTimeTemporal_DateTime_now_addvalue2to_hournotEqualTemporalDateTimeTemporal_DateTime_now() throws { @@ -174,7 +174,7 @@ class QueryPredicateEvaluateGeneratedDateTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTimeTemporal_DateTime_now_addvalue3to_hournotEqualTemporalDateTimeTemporal_DateTime_now() throws { @@ -228,7 +228,7 @@ class QueryPredicateEvaluateGeneratedDateTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalDateTimeTemporal_DateTime_nowequalsTemporalDateTimeTemporal_DateTime_now() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleIntTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleIntTests.swift index 31fe364268..8866439b17 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleIntTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleIntTests.swift @@ -50,7 +50,7 @@ class QueryPredicateEvaluateGeneratedDoubleIntTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble2_1notEqualInt1() throws { @@ -89,7 +89,7 @@ class QueryPredicateEvaluateGeneratedDoubleIntTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble3_1notEqualInt1() throws { @@ -128,7 +128,7 @@ class QueryPredicateEvaluateGeneratedDoubleIntTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble1notEqualInt1() throws { @@ -167,7 +167,7 @@ class QueryPredicateEvaluateGeneratedDoubleIntTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble2notEqualInt1() throws { @@ -206,7 +206,7 @@ class QueryPredicateEvaluateGeneratedDoubleIntTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble3notEqualInt1() throws { @@ -245,7 +245,7 @@ class QueryPredicateEvaluateGeneratedDoubleIntTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble1_1equalsInt1() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleTests.swift index fbc4c6566d..12f54572b6 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedDoubleTests.swift @@ -80,7 +80,7 @@ class QueryPredicateEvaluateGeneratedDoubleTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble2_1notEqualDouble1_1() throws { @@ -149,7 +149,7 @@ class QueryPredicateEvaluateGeneratedDoubleTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble3_1notEqualDouble1_1() throws { @@ -218,7 +218,7 @@ class QueryPredicateEvaluateGeneratedDoubleTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble1notEqualDouble1_1() throws { @@ -287,7 +287,7 @@ class QueryPredicateEvaluateGeneratedDoubleTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble2notEqualDouble1_1() throws { @@ -356,7 +356,7 @@ class QueryPredicateEvaluateGeneratedDoubleTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble3notEqualDouble1_1() throws { @@ -425,7 +425,7 @@ class QueryPredicateEvaluateGeneratedDoubleTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testDouble1_1equalsDouble1_1() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedIntTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedIntTests.swift index 1e5c4ec370..315c648ae1 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedIntTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedIntTests.swift @@ -54,7 +54,7 @@ class QueryPredicateEvaluateGeneratedIntBetweenTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testInt2notEqualInt1() throws { @@ -93,7 +93,7 @@ class QueryPredicateEvaluateGeneratedIntBetweenTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testInt3notEqualInt1() throws { @@ -132,7 +132,7 @@ class QueryPredicateEvaluateGeneratedIntBetweenTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testInt1equalsInt1() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedStringTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedStringTests.swift index 1ce8eb039c..9408557689 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedStringTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedStringTests.swift @@ -64,7 +64,7 @@ class QueryPredicateEvaluateGeneratedStringTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testStringbbnotEqualStringa() throws { @@ -113,7 +113,7 @@ class QueryPredicateEvaluateGeneratedStringTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testStringaanotEqualStringa() throws { @@ -162,7 +162,7 @@ class QueryPredicateEvaluateGeneratedStringTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testStringcnotEqualStringa() throws { @@ -211,7 +211,7 @@ class QueryPredicateEvaluateGeneratedStringTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testStringaequalsStringa() throws { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedTimeTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedTimeTests.swift index 0caa2f6566..9ef76a5e68 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedTimeTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Query/QueryPredicateEvaluateGeneratedTimeTests.swift @@ -69,7 +69,7 @@ class QueryPredicateEvaluateGeneratedTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalTimeTemporal_Time_now_addvalue1to_hournotEqualTemporalTimeTemporal_Time_now() throws { @@ -123,7 +123,7 @@ class QueryPredicateEvaluateGeneratedTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalTimeTemporal_Time_now_addvalue2to_hournotEqualTemporalTimeTemporal_Time_now() throws { @@ -177,7 +177,7 @@ class QueryPredicateEvaluateGeneratedTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalTimeTemporal_Time_now_addvalue3to_hournotEqualTemporalTimeTemporal_Time_now() throws { @@ -231,7 +231,7 @@ class QueryPredicateEvaluateGeneratedTimeTests: XCTestCase { let evaluation = try predicate.evaluate(target: instance.eraseToAnyModel().instance) - XCTAssertFalse(evaluation) + XCTAssertTrue(evaluation) } func testTemporalTimeTemporal_Time_nowequalsTemporalTimeTemporal_Time_now() throws { diff --git a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/QueryPredicate+SQLite.swift b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/QueryPredicate+SQLite.swift index dcc528ae63..ff358da05d 100644 --- a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/QueryPredicate+SQLite.swift +++ b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/QueryPredicate+SQLite.swift @@ -33,6 +33,8 @@ extension QueryOperator { return "instr(\(column), ?) > 0" case .notContains: return "instr(\(column), ?) = 0" + case .attributeExists(let value): + return "\(column) is\(value ? " not" : "") null" } } @@ -51,6 +53,8 @@ extension QueryOperator { .beginsWith(let value), .notContains(let value): return [value.asBinding()] + case .attributeExists(let value): + return [value.asBinding()] } } } diff --git a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/SQLStatement+Condition.swift b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/SQLStatement+Condition.swift index 6d8d55136c..2a774992d3 100644 --- a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/SQLStatement+Condition.swift +++ b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Storage/SQLite/SQLStatement+Condition.swift @@ -76,9 +76,66 @@ private func translateQueryPredicate(from modelSchema: ModelSchema, return operation.field.quoted() } + func optimizeQueryPredicateGroup(_ predicate: QueryPredicate) -> QueryPredicate { + func rewritePredicate(_ predicate: QueryPredicate) -> QueryPredicate { + if let operation = predicate as? QueryPredicateOperation { + switch operation.operator { + case .attributeExists(let bool): + return QueryPredicateOperation( + field: operation.field, + operator: bool ? .notEqual(nil) : .equals(nil) + ) + default: + return operation + } + } else if let group = predicate as? QueryPredicateGroup { + return optimizeQueryPredicateGroup(group) + } + + return predicate + } + + func removeDuplicatePredicate(_ predicates: [QueryPredicate]) -> [QueryPredicate] { + var result = [QueryPredicate]() + for predicate in predicates { + let hasSameExpression = result.reduce(false) { + if $0 { return $0 } + switch ($1, predicate) { + case let (lhs as QueryPredicateOperation, rhs as QueryPredicateOperation): + return lhs == rhs + case let (lhs as QueryPredicateGroup, rhs as QueryPredicateGroup): + return lhs == rhs + default: + return false + } + } + + if !hasSameExpression { + result.append(predicate) + } + } + return result + } + + switch predicate { + case let predicate as QueryPredicateGroup: + let optimizedPredicates = removeDuplicatePredicate(predicate.predicates.reduce([]) { + $0 + [rewritePredicate($1)] + }) + + if optimizedPredicates.count == 1 { + return optimizedPredicates.first! + } else { + return QueryPredicateGroup(type: predicate.type, predicates: optimizedPredicates) + } + default: + return predicate + } + } + // the very first `and` is always prepended, using -1 for if statement checking // the very first `and` is to connect `where` clause with translated QueryPredicate - translate(predicate, predicateIndex: -1, groupType: .and) + translate(optimizeQueryPredicateGroup(predicate), predicateIndex: -1, groupType: .and) return (sql.joined(separator: "\n"), bindings) } diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Core/SQLStatementTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Core/SQLStatementTests.swift index 19806595c8..149edd2edb 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Core/SQLStatementTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Core/SQLStatementTests.swift @@ -1350,4 +1350,101 @@ class SQLStatementTests: XCTestCase { XCTAssertEqual(statement.stringValue, expectStatement) XCTAssertEqual(variables[0] as? String, expectedVariable) } + + + /// Given: a query predicate of attributeExists + /// When: the bind value is false + /// Then: generate the correct SQL query statement + func test_translateAttributeExistsFalseQueryPredicate() { + let post = Post.keys + + let predicate = post.id.attributeExists(false) + let statement = ConditionStatement(modelSchema: Post.schema, predicate: predicate, namespace: "root") + let expectedStatement = + """ + and "root"."id" is null + """ + XCTAssertEqual(statement.stringValue, expectedStatement) + } + + /// Given: a query predicate of attributeExists + /// When: the bind value is true + /// Then: generate the correct SQL query statement + func test_translateAttributeExistsTrueQueryPredicate() { + let post = Post.keys + + let predicate = post.id.attributeExists(true) + let statement = ConditionStatement(modelSchema: Post.schema, predicate: predicate, namespace: "root") + let expectedStatement = + """ + and "root"."id" is not null + """ + XCTAssertEqual(statement.stringValue, expectedStatement) + } + + /// Given: a combined query predicate of attributeExists and ne + /// When: attributeExists(true) && ne(nil) + /// Then: generate the correct SQL query statement + func test_translateCombinedQueryPredicateOfAttributeExistsTrueAndNeNil() { + let post = Post.keys + + let predicate = post.id.attributeExists(true) && post.id.ne(nil) + let statement = ConditionStatement(modelSchema: Post.schema, predicate: predicate, namespace: "root") + let expectedStatement = + """ + and "root"."id" is not null + """ + XCTAssertEqual(statement.stringValue, expectedStatement) + } + + /// Given: a combined query predicate of attributeExists and ne + /// When: attributeExists(false) && ne(nil) + /// Then: generate the correct SQL query statement + func test_translateCombinedQueryPredicateOfAttributeExistsFalseAndNeNil() { + let post = Post.keys + + let predicate = post.id.attributeExists(false) && post.id.ne(nil) + let statement = ConditionStatement(modelSchema: Post.schema, predicate: predicate, namespace: "root") + let expectedStatement = + """ + and ( + "root"."id" is null + and "root"."id" is not null + ) + """ + XCTAssertEqual(statement.stringValue, expectedStatement) + } + + /// Given: a combined query predicate of attributeExists and eq + /// When: attributeExists(false) || eq(nil) + /// Then: generate the correct SQL query statement + func test_translateCombinedQueryPredicateOfAttributeExistsFalseOrEqNil() { + let post = Post.keys + + let predicate = post.id.attributeExists(false) || post.id.eq(nil) + let statement = ConditionStatement(modelSchema: Post.schema, predicate: predicate, namespace: "root") + let expectedStatement = + """ + and "root"."id" is null + """ + XCTAssertEqual(statement.stringValue, expectedStatement) + } + + /// Given: a combined query predicate of attributeExists and eq + /// When: attributeExists(true) || eq(nil) + /// Then: generate the correct SQL query statement + func test_translateCombinedQueryPredicateOfAttributeExistsTrueOrEqNil() { + let post = Post.keys + + let predicate = post.id.attributeExists(true) || post.id.eq(nil) + let statement = ConditionStatement(modelSchema: Post.schema, predicate: predicate, namespace: "root") + let expectedStatement = + """ + and ( + "root"."id" is not null + or "root"."id" is null + ) + """ + XCTAssertEqual(statement.stringValue, expectedStatement) + } } diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventQueryTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventQueryTests.swift index 9e1eb5df65..e982872609 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventQueryTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventQueryTests.swift @@ -58,7 +58,7 @@ class MutationEventQueryTests: BaseDataStoreTests { wait(for: [querySuccess], timeout: 1) } - func testQueryPendingMutationEventsForModelIds() { + func testQueryPendingMutationEventsForModelIds() async { let mutationEvent1 = generateRandomMutationEvent() let mutationEvent2 = generateRandomMutationEvent() @@ -70,7 +70,7 @@ class MutationEventQueryTests: BaseDataStoreTests { } saveMutationEvent1.fulfill() } - wait(for: [saveMutationEvent1], timeout: 1) + await fulfillment(of: [saveMutationEvent1], timeout: 1) let saveMutationEvent2 = expectation(description: "save mutationEvent1 success") storageAdapter.save(mutationEvent2) { result in @@ -80,7 +80,7 @@ class MutationEventQueryTests: BaseDataStoreTests { } saveMutationEvent2.fulfill() } - wait(for: [saveMutationEvent2], timeout: 1) + await fulfillment(of: [saveMutationEvent2], timeout: 1) let querySuccess = expectation(description: "query for metadata success") var mutationEvents = [mutationEvent1] @@ -98,7 +98,7 @@ class MutationEventQueryTests: BaseDataStoreTests { } } - wait(for: [querySuccess], timeout: 1) + await fulfillment(of: [querySuccess], timeout: 5) } private func generateRandomMutationEvent() -> MutationEvent { From c5960dfae3b29f2b69535be22c78ca06cae0da1e Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Mon, 23 Sep 2024 20:37:20 +0000 Subject: [PATCH 35/50] chore: release 2.42.0 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 4c46c88645..38597502ec 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.41.2" + public static let amplifyVersion = "2.42.0" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 9257586c9e191c9c1898d3b011a13ce103c8ccb0 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Mon, 23 Sep 2024 20:39:16 +0000 Subject: [PATCH 36/50] chore: finalize release 2.42.0 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 786aa46d1f..eb01790a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.42.0 (2024-09-23) + +### Features + +- **api**: add support for GraphQL filter attributeExists (#3838) + ## 2.41.2 (2024-09-20) ### Bug Fixes From 42c97ba9d60b91a730fb1ffd6c0e0896403c14fb Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:18:29 -0500 Subject: [PATCH 37/50] chore: Update and remove gem dependency webrick (#3884) * chore: update canaries gem dependencies * update gem webrick * chore: remove webrick dependency --- canaries/example/Gemfile.lock | 40 ++++++++++++++++------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/canaries/example/Gemfile.lock b/canaries/example/Gemfile.lock index 6bf1c1890d..b03be1b819 100644 --- a/canaries/example/Gemfile.lock +++ b/canaries/example/Gemfile.lock @@ -3,8 +3,8 @@ GEM specs: CFPropertyList (3.0.5) rexml - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) @@ -24,6 +24,7 @@ GEM aws-sigv4 (1.4.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) + base64 (0.2.0) claide (1.1.0) colored (1.2) colored2 (3.1.2) @@ -37,7 +38,7 @@ GEM dotenv (2.7.6) emoji_regex (3.2.3) excon (0.92.2) - faraday (1.10.0) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -56,9 +57,9 @@ GEM faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) faraday-httpclient (1.0.1) - faraday-multipart (1.0.3) - multipart-post (>= 1.2, < 3) - faraday-net_http (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) @@ -108,7 +109,7 @@ GEM gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.18.0) google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.2) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -116,7 +117,6 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-iamcredentials_v1 (0.10.0) google-apis-core (>= 0.4, < 2.a) google-apis-playcustomapp_v1 (0.7.0) @@ -137,10 +137,9 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.1.2) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) @@ -150,39 +149,37 @@ GEM httpclient (2.8.3) jmespath (1.6.1) json (2.6.1) - jwt (2.3.0) - memoist (0.16.2) + jwt (2.9.1) + base64 mini_magick (4.11.0) - mini_mime (1.1.2) + mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.0.0) + multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) optparse (0.1.1) os (1.1.4) plist (3.6.0) - public_suffix (4.0.7) + public_suffix (6.0.1) rake (13.0.6) - representable (3.1.1) + representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.6) - strscan + rexml (3.3.7) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.16.1) + signet (0.19.0) addressable (~> 2.8) - faraday (>= 0.17.5, < 3.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) simctl (1.6.8) CFPropertyList naturally - strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) @@ -196,7 +193,6 @@ GEM unf_ext unf_ext (0.0.8.1) unicode-display_width (1.8.0) - webrick (1.7.0) word_wrap (1.0.0) xcodeproj (1.19.0) CFPropertyList (>= 2.3.3, < 4.0) From 39e820a9570306f33c6a8e4875caf1633d890a06 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:44:16 -0500 Subject: [PATCH 38/50] chore: remove webrick gem dependency (#3886) --- Gemfile.lock | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1ab3463fe4..de14c16529 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -105,7 +105,7 @@ GEM ethon (0.16.0) ffi (>= 1.15.0) excon (0.99.0) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -126,7 +126,7 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) @@ -179,7 +179,7 @@ GEM gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.33.0) google-apis-core (>= 0.9.1, < 2.a) - google-apis-core (0.11.0) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -187,7 +187,6 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-iamcredentials_v1 (0.16.0) google-apis-core (>= 0.9.1, < 2.a) google-apis-playcustomapp_v1 (0.12.0) @@ -208,10 +207,9 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.3.0) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) @@ -233,11 +231,11 @@ GEM xcinvoke (~> 0.3.0) jmespath (1.6.2) json (2.6.3) - jwt (2.7.0) + jwt (2.9.1) + base64 liferaft (0.0.6) - memoist (0.16.2) mini_magick (4.12.0) - mini_mime (1.1.2) + mini_mime (1.1.5) mini_portile2 (2.8.7) minitest (5.25.1) molinillo (0.8.0) @@ -262,8 +260,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.6) - strscan + rexml (3.3.7) rouge (2.0.7) ruby-macho (2.5.1) ruby2_keywords (0.0.5) @@ -271,7 +268,7 @@ GEM sassc (2.4.0) ffi (~> 1.9) security (0.1.3) - signet (0.17.0) + signet (0.19.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -281,7 +278,6 @@ GEM naturally sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) - strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) @@ -299,7 +295,6 @@ GEM unf_ext unf_ext (0.0.8.2) unicode-display_width (1.8.0) - webrick (1.8.1) word_wrap (1.0.0) xcinvoke (0.3.0) liferaft (~> 0.0.6) @@ -325,4 +320,4 @@ DEPENDENCIES xcpretty (= 0.3.0) BUNDLED WITH - 2.5.14 + 2.3.7 From 6b301f05c7b4fec37e11803cedaba0d3513d1da6 Mon Sep 17 00:00:00 2001 From: Pramod Prajapati Date: Tue, 24 Sep 2024 21:56:26 +0530 Subject: [PATCH 39/50] fix(predictions): region handling for creating correct streaming endpoint from region (#3880) * Added region handling for creating correct streaming endpoint * Added region handling for creating correct streaming endpoint --------- Co-authored-by: Pramod Prajapati --- .../Liveness/SPI/LivenessStreamingURL.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/SPI/LivenessStreamingURL.swift b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/SPI/LivenessStreamingURL.swift index 93ee45ad3c..15c25593d2 100644 --- a/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/SPI/LivenessStreamingURL.swift +++ b/AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/SPI/LivenessStreamingURL.swift @@ -7,8 +7,20 @@ import Foundation +private let isoPartitionBaseDomain: String = "csp.hci.ic.gov" +private let defaultBaseDomain: String = "amazonaws.com" + func streamingSessionURL(for region: String) throws -> URL { - let urlString = "wss://streaming-rekognition.\(region).amazonaws.com/start-face-liveness-session-websocket" + + // Determine the base domain based on the region + let baseDomain: String + if region.lowercased().starts(with: "us-isof") { + baseDomain = isoPartitionBaseDomain + } else { + baseDomain = defaultBaseDomain + } + + let urlString = "wss://streaming-rekognition.\(region).\(baseDomain)/start-face-liveness-session-websocket" guard let url = URL(string: urlString) else { throw FaceLivenessSessionError.invalidRegion } From 586c528871613b2c7c7697e125aa7d71649cce70 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 24 Sep 2024 18:56:49 +0000 Subject: [PATCH 40/50] chore: release 2.42.1 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 38597502ec..268a5b5d8b 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.42.0" + public static let amplifyVersion = "2.42.1" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 95d8f16780c4e11a243fdae93c386195347aea66 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 24 Sep 2024 18:58:35 +0000 Subject: [PATCH 41/50] chore: finalize release 2.42.1 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb01790a31..7d9092f34b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.42.1 (2024-09-24) + +### Bug Fixes + +- **predictions**: region handling for creating correct streaming endpoint from region (#3880) + ## 2.42.0 (2024-09-23) ### Features From 619867b9e99007db7479d40de2f89eaf120cdeba Mon Sep 17 00:00:00 2001 From: Harsh <6162866+harsh62@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:10:37 -0400 Subject: [PATCH 42/50] fix(auth): return configuration error upon invalid redirect URI in hosted ui (#3889) --- .../SignIn/HostedUI/ShowHostedUISignIn.swift | 2 +- .../SignIn/HostedUISignInState+Resolver.swift | 1 - .../AWSAuthHostedUISignInTests.swift | 72 +++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/HostedUI/ShowHostedUISignIn.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/HostedUI/ShowHostedUISignIn.swift index 02f3c992b8..559a91f2f4 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/HostedUI/ShowHostedUISignIn.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/HostedUI/ShowHostedUISignIn.swift @@ -36,7 +36,7 @@ class ShowHostedUISignIn: NSObject, Action { guard let callbackURL = URL(string: hostedUIConfig.oauth.signInRedirectURI), let callbackURLScheme = callbackURL.scheme else { - let event = SignInEvent(eventType: .throwAuthError(.hostedUI(.signInURI))) + let event = HostedUIEvent(eventType: .throwError(.hostedUI(.signInURI))) logVerbose("\(#fileID) Sending event \(event)", environment: environment) await dispatcher.send(event) return diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/SignIn/HostedUISignInState+Resolver.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/SignIn/HostedUISignInState+Resolver.swift index 8c50307e8f..6bb811523a 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/SignIn/HostedUISignInState+Resolver.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/SignIn/HostedUISignInState+Resolver.swift @@ -30,7 +30,6 @@ extension HostedUISignInState { case .showingUI: if case .throwError(let error) = event.isHostedUIEvent { - // Remove this? let action = CancelSignIn() return .init(newState: .error(error), actions: [action]) } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift index 420d8f5abd..7be38b07d2 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift @@ -42,6 +42,43 @@ class AWSAuthHostedUISignInTests: XCTestCase { return URLSession(configuration: configuration) } + private func customPlugin(with cusotmConfiguration: HostedUIConfigurationData?) -> AWSCognitoAuthPlugin { + let plugin = AWSCognitoAuthPlugin() + mockJson = try! JSONSerialization.data(withJSONObject: mockTokenResult) + MockURLProtocol.requestHandler = { _ in + return (HTTPURLResponse(), self.mockJson) + } + + func sessionFactory() -> HostedUISessionBehavior { + MockHostedUISession(result: mockHostedUIResult) + } + + func mockRandomString() -> RandomStringBehavior { + return MockRandomStringGenerator(mockString: mockState, mockUUID: mockState) + } + + let environment = BasicHostedUIEnvironment(configuration: cusotmConfiguration ?? configuration, + hostedUISessionFactory: sessionFactory, + urlSessionFactory: urlSessionMock, + randomStringFactory: mockRandomString) + let authEnvironment = Defaults.makeDefaultAuthEnvironment( + userPoolFactory: { self.mockIdentityProvider }, + hostedUIEnvironment: environment) + let stateMachine = Defaults.authStateMachineWith( + environment: authEnvironment, + initialState: initialState + ) + + plugin.configure( + authConfiguration: Defaults.makeDefaultAuthConfigData(withHostedUI: configuration), + authEnvironment: authEnvironment, + authStateMachine: stateMachine, + credentialStoreStateMachine: Defaults.makeDefaultCredentialStateMachine(), + hubEventHandler: MockAuthHubEventBehavior(), + analyticsHandler: MockAnalyticsHandler()) + return plugin + } + override func setUp() { plugin = AWSCognitoAuthPlugin() mockJson = try! JSONSerialization.data(withJSONObject: mockTokenResult) @@ -310,6 +347,41 @@ class AWSAuthHostedUISignInTests: XCTestCase { await fulfillment(of: [expectation], timeout: networkTimeout) } + @MainActor + func testInvalidRedirectConfigurationFailure() async { + let invalidRedirectConfig = HostedUIConfigurationData(clientId: "clientId", oauth: .init( + domain: "cognitodomain", + scopes: ["name"], + signInRedirectURI: "@#$%junk1343", + signOutRedirectURI: "@3451://")) + let testPlugin = customPlugin(with: invalidRedirectConfig) + + mockHostedUIResult = .success([ + .init(name: "state", value: mockState), + .init(name: "code", value: mockProof) + ]) + mockTokenResult = [ + "refresh_token": AWSCognitoUserPoolTokens.testData.refreshToken, + "expires_in": 10] as [String: Any] + mockJson = try! JSONSerialization.data(withJSONObject: mockTokenResult) + MockURLProtocol.requestHandler = { _ in + return (HTTPURLResponse(), self.mockJson) + } + + let expectation = expectation(description: "SignIn operation should complete") + do { + _ = try await testPlugin.signInWithWebUI(presentationAnchor: ASPresentationAnchor(), options: nil) + XCTFail("Should not succeed") + } catch { + guard case AuthError.configuration = error else { + XCTFail("Should not fail with error = \(error)") + return + } + expectation.fulfill() + } + await fulfillment(of: [expectation], timeout: networkTimeout) + } + /// Test a signIn restart while another sign in is in progress From 65da44ec5b66e8069447143eb868aaed7c4e3616 Mon Sep 17 00:00:00 2001 From: Harsh <6162866+harsh62@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:27:02 -0400 Subject: [PATCH 43/50] fix(Logging): adding internal configure auth hub event listener to fix logging race condition (#3899) --- .../Operations/AuthConfigureOperation.swift | 4 ++ .../AWSCognitoAuthPluginConfigTests.swift | 41 +++++++++++++++++++ .../AWSCloudWatchLoggingCategoryClient.swift | 3 +- .../AWSCloudWatchLoggingPlugin.swift | 4 -- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/AuthConfigureOperation.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/AuthConfigureOperation.swift index 49ca97e23a..376ffefc4c 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/AuthConfigureOperation.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/AuthConfigureOperation.swift @@ -37,6 +37,9 @@ class AuthConfigureOperation: ConfigureOperation { override public func main() { if isCancelled { finish() + dispatch(result: .failure(AuthError.configuration( + "Configuration operation was cancelled", + "", nil))) return } @@ -51,6 +54,7 @@ class AuthConfigureOperation: ConfigureOperation { for await state in stateSequences { if case .configured = state { finish() + dispatch(result: .success(())) break } } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/AWSCognitoAuthPluginConfigTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/AWSCognitoAuthPluginConfigTests.swift index 8ba574028e..553406fbe6 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/AWSCognitoAuthPluginConfigTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/ConfigurationTests/AWSCognitoAuthPluginConfigTests.swift @@ -236,4 +236,45 @@ class AWSCognitoAuthPluginConfigTests: XCTestCase { } } + /// Test that the Auth plugin emits `InternalConfigureAuth` that is used by the Logging Category + /// + /// - Given: Given a valid config + /// - When: + /// - I configure auth with the given configuration + /// - Then: + /// - I should receive `InternalConfigureAuth` Hub event + /// + func testEmittingInternalConfigureAuthHubEvent() throws { + let expectation = expectation(description: "conifguration should complete") + let subscription = Amplify.Hub.publisher(for: .auth).sink { payload in + + if payload.eventName == "InternalConfigureAuth" { + expectation.fulfill() + } + } + let plugin = AWSCognitoAuthPlugin() + try Amplify.add(plugin: plugin) + + let categoryConfig = AuthCategoryConfiguration(plugins: [ + "awsCognitoAuthPlugin": [ + "CredentialsProvider": [ + "CognitoIdentity": [ + "Default": [ + "PoolId": "cc", + "Region": "us-east-1" + ] + ] + ] + ] + ]) + let amplifyConfig = AmplifyConfiguration(auth: categoryConfig) + do { + try Amplify.configure(amplifyConfig) + } catch { + XCTFail("Should not throw error. \(error)") + } + wait(for: [expectation], timeout: 5.0) + subscription.cancel() + } + } diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift index 9a0458f612..af7e1ace28 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift @@ -87,9 +87,10 @@ final class AWSCloudWatchLoggingCategoryClient { enum CognitoEventName: String { case signInAPI = "Auth.signInAPI" case signOutAPI = "Auth.signOutAPI" + case configured = "InternalConfigureAuth" } switch payload.eventName { - case HubPayload.EventName.Auth.signedIn, CognitoEventName.signInAPI.rawValue: + case HubPayload.EventName.Auth.signedIn, CognitoEventName.signInAPI.rawValue, CognitoEventName.configured.rawValue: takeUserIdentifierFromCurrentUser() case HubPayload.EventName.Auth.signedOut, CognitoEventName.signOutAPI.rawValue: self.userIdentifier = nil diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift index ba8721681f..0d15827c21 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift @@ -154,10 +154,6 @@ public class AWSCloudWatchLoggingPlugin: LoggingCategoryPlugin { let localStore: LoggingConstraintsLocalStore = UserDefaults.standard localStore.reset() } - - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { - self.loggingClient.takeUserIdentifierFromCurrentUser() - } } } From e56e9e9c84d268aa81b808d5dd4af97be5efb6ea Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Fri, 18 Oct 2024 17:33:03 +0000 Subject: [PATCH 44/50] chore: release 2.42.2 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 268a5b5d8b..1b2d6ec93e 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.42.1" + public static let amplifyVersion = "2.42.2" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From a80d0e6e7aac55fae455261baa0f9d3d76d90830 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Fri, 18 Oct 2024 17:34:29 +0000 Subject: [PATCH 45/50] chore: finalize release 2.42.2 [skip ci] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d9092f34b..432ac7eeaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.42.2 (2024-10-18) + +### Bug Fixes + +- **Logging**: adding internal configure auth hub event listener to fix logging race condition (#3899) +- **auth**: return configuration error upon invalid redirect URI in hosted ui (#3889) + ## 2.42.1 (2024-09-24) ### Bug Fixes From 4fcda7e75c9a7b9147db76d9b4bf3d8c6b568166 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:39:48 -0400 Subject: [PATCH 46/50] chore(deps): bump cookie and express (#3902) Bumps [cookie](https://github.com/jshttp/cookie) to 0.7.1 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together. Updates `cookie` from 0.6.0 to 0.7.1 - [Release notes](https://github.com/jshttp/cookie/releases) - [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1) Updates `express` from 4.21.0 to 4.21.1 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.0...4.21.1) --- updated-dependencies: - dependency-name: cookie dependency-type: indirect - dependency-name: express dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../LocalServer/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json index 7608478472..53b4a8975e 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json @@ -98,9 +98,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -197,16 +197,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", From f950c6c1dea3d43a5048d1a7782759b214bd0040 Mon Sep 17 00:00:00 2001 From: Harsh <6162866+harsh62@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:06:26 -0400 Subject: [PATCH 47/50] feat: adding vision os preview (#3898) * feat: visionOS support (#3058) * fix readme typo and make instructions clearer (#3070) * fix: update xrOS --> visionOS in os directive checks * chore(build): fix integration tests * fix: Fixing Analytics, Storage and Predictions integration tests * fix: Fixing Push Notification tests. Also reverting some scheme changes. * chore: Updating README * chore: update ReadMe me to remove visionos-preview branch * chore: add visionOS to build and unit test jobs (#3900) * Update Amplify/Core/Configuration/Internal/Amplify+Reset.swift Co-authored-by: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> * worked on review comments * fixed unit tests * remove the platform checks --------- Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com> Co-authored-by: Abhash Kumar Singh Co-authored-by: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> --- .../get_platform_parameters/action.yml | 27 ++-- .../build_amplify_swift_platforms.yml | 9 +- .github/workflows/build_scheme.yml | 2 +- .github/workflows/run_integration_tests.yml | 2 +- .../workflows/run_unit_tests_platforms.yml | 5 +- .github/workflows/unit_test.yml | 6 + .github/workflows/unit_test_amplify.yml | 5 + .github/workflows/unit_test_analytics.yml | 7 +- .github/workflows/unit_test_api.yml | 5 + .github/workflows/unit_test_auth.yml | 7 +- .github/workflows/unit_test_core.yml | 7 +- .github/workflows/unit_test_datastore.yml | 7 +- .github/workflows/unit_test_geo.yml | 5 + .../workflows/unit_test_internal_pinpoint.yml | 5 + .github/workflows/unit_test_logging.yml | 7 +- .github/workflows/unit_test_predictions.yml | 7 +- .../unit_test_push_notifications.yml | 7 +- .github/workflows/unit_test_storage.yml | 7 +- .../Auth/AuthCategory+ClientBehavior.swift | 2 +- .../Auth/AuthCategoryBehavior.swift | 4 +- .../Auth/Request/AuthSignOutRequest.swift | 4 +- .../Auth/Request/AuthWebUISignInRequest.swift | 2 +- .../Internal/Amplify+Reset.swift | 4 +- Amplify/Core/Support/DeviceInfo.swift | 4 +- Amplify/DevMenu/Amplify+DevMenu.swift | 4 +- Amplify/DevMenu/AmplifyDevMenu.swift | 2 +- Amplify/DevMenu/Data/DevMenuItem.swift | 2 +- Amplify/DevMenu/Data/DevMenuItemType.swift | 2 +- Amplify/DevMenu/Data/DeviceInfoHelper.swift | 2 +- Amplify/DevMenu/Data/DeviceInfoItem.swift | 2 +- Amplify/DevMenu/Data/DeviceInfoItemType.swift | 2 +- .../DevMenu/Data/EnvironmentInfoHelper.swift | 2 +- .../DevMenu/Data/EnvironmentInfoItem.swift | 2 +- .../Data/EnvironmentInfoItemType.swift | 2 +- Amplify/DevMenu/Data/InfoItemProvider.swift | 2 +- Amplify/DevMenu/Data/IssueInfo.swift | 2 +- Amplify/DevMenu/Data/IssueInfoHelper.swift | 2 +- Amplify/DevMenu/Data/LogEntryHelper.swift | 2 +- Amplify/DevMenu/Data/LogEntryItem.swift | 2 +- Amplify/DevMenu/Data/PluginInfoHelper.swift | 2 +- Amplify/DevMenu/Data/PluginInfoItem.swift | 2 +- Amplify/DevMenu/DevEnvironmentInfo.swift | 2 +- Amplify/DevMenu/DevMenuBehavior.swift | 2 +- .../DevMenuPresentationContextProvider.swift | 2 +- Amplify/DevMenu/DevMenuStringConstants.swift | 2 +- .../Logging/PersistentLogWrapper.swift | 2 +- .../Logging/PersistentLoggingPlugin.swift | 2 +- .../Trigger/LongPressGestureRecognizer.swift | 2 +- Amplify/DevMenu/Trigger/TriggerDelegate.swift | 2 +- .../DevMenu/Trigger/TriggerRecognizer.swift | 2 +- Amplify/DevMenu/View/DetailViewFactory.swift | 2 +- Amplify/DevMenu/View/DevMenuList.swift | 2 +- Amplify/DevMenu/View/DevMenuRow.swift | 2 +- .../DevMenu/View/DeviceInfoDetailView.swift | 2 +- .../View/EnvironmentInfoDetailView.swift | 2 +- Amplify/DevMenu/View/InfoRow.swift | 2 +- Amplify/DevMenu/View/IssueReporter.swift | 5 +- Amplify/DevMenu/View/LogEntryRow.swift | 2 +- Amplify/DevMenu/View/LogViewer.swift | 2 +- .../APIHostApp.xcodeproj/project.pbxproj | 144 ++++++++++-------- .../APIHostApp/APIHostApp.entitlements | 10 ++ .../AppSyncRealTimeRequestAuthTests.swift | 38 ++++- .../Core/AppSyncListProviderTests.swift | 2 +- .../Support/Utils/RESTRequestUtilsTests.swift | 3 +- ...pointAnalyticsPluginIntegrationTests.swift | 31 ++-- .../project.pbxproj | 67 +++++--- .../xcschemes/AnalyticsHostApp.xcscheme | 2 +- .../AnalyticsHostApp.entitlements | 10 ++ .../Actions/SignOut/ShowHostedUISignOut.swift | 1 + .../AWSCognitoAuthPlugin+ClientBehavior.swift | 2 +- .../Options/AWSAuthWebUISignInOptions.swift | 2 +- .../Helpers/HostedUISignInHelper.swift | 2 +- .../CodeGen/Data/SignOutEventData.swift | 1 + .../HostedUIASWebAuthenticationSession.swift | 10 +- .../Task/AWSAuthSignOutTask.swift | 18 +-- .../Task/AWSAuthWebUISignInTask.swift | 2 +- .../Protocols/AuthSocialWebUISignInTask.swift | 2 +- .../Task/Protocols/AuthWebUISignInTask.swift | 2 +- ...tedUIASWebAuthenticationSessionTests.swift | 4 +- .../AWSAuthHostedUISignInTests.swift | 2 - .../AuthHostApp.xcodeproj/project.pbxproj | 46 +++--- .../xcschemes/AuthIntegrationTests.xcscheme | 12 +- .../AuthHostApp/AuthHostApp.entitlements | 10 ++ .../Keychain/KeychainStore.swift | 4 +- ...ationIngesterConflictResolutionTests.swift | 8 +- .../OutgoingMutationQueueNetworkTests.swift | 35 ++--- .../Sync/RemoteSyncEngineTests.swift | 2 +- .../project.pbxproj | 144 ++++++++++-------- ...WSDataStorePluginAuthCognitoTests.xcscheme | 9 ++ .../DataStoreHostApp.entitlements | 10 ++ .../GeoHostApp.xcodeproj/project.pbxproj | 46 +++--- .../GeoHostApp/GeoHostApp.entitlements | 10 ++ .../project.pbxproj | 28 ++-- .../PushNotificationHostApp.entitlements | 4 +- .../PushNotificationHostAppUITests.swift | 2 +- .../EventStreamCoderTestCase.swift | 3 +- .../CoreMLNaturalLanguageAdapterTests.swift | 8 + .../project.pbxproj | 130 +++++++++++++--- .../PredictionsHostApp/ContentView.swift | 6 +- .../PredictionsHostApp.entitlements | 4 +- .../PredictionsHostAppApp.swift | 6 +- .../Service/Storage/AWSS3StorageService.swift | 2 +- .../StorageHostApp.xcodeproj/project.pbxproj | 36 +++-- .../StorageHostApp.entitlements | 4 +- .../Mocks/MockAuthCategoryPlugin.swift | 2 +- .../Mocks/MockDevMenuContextProvider.swift | 2 +- .../DevMenuTests/DevMenuExtensionTests.swift | 2 +- .../DevMenuTests/GestureRecognizerTests.swift | 2 +- .../PersistentLogWrapperTests.swift | 2 +- ...LoggingPluginAmplifyVersionableTests.swift | 2 +- .../PersistentLoggingPluginTests.swift | 2 +- README.md | 3 - 112 files changed, 768 insertions(+), 395 deletions(-) create mode 100644 AmplifyPlugins/API/Tests/APIHostApp/APIHostApp/APIHostApp.entitlements create mode 100644 AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp/AnalyticsHostApp.entitlements create mode 100644 AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp/AuthHostApp.entitlements create mode 100644 AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp/DataStoreHostApp.entitlements create mode 100644 AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp/GeoHostApp.entitlements diff --git a/.github/composite_actions/get_platform_parameters/action.yml b/.github/composite_actions/get_platform_parameters/action.yml index 7ac2d4f612..c7e2ffdeda 100644 --- a/.github/composite_actions/get_platform_parameters/action.yml +++ b/.github/composite_actions/get_platform_parameters/action.yml @@ -34,14 +34,14 @@ runs: run: | INPUT_PLATFORM=${{ inputs.platform }} case $INPUT_PLATFORM in - iOS|tvOS|watchOS|macOS) ;; + iOS|tvOS|watchOS|macOS|visionOS) ;; *) echo "Unsupported platform: $INPUT_PLATFORM"; exit 1 ;; esac shell: bash - id: get-xcode-version run: | - LATEST_XCODE_VERSION=15.3.0 + LATEST_XCODE_VERSION=16.0.0 MINIMUM_XCODE_VERSION=15.0.1 INPUT_XCODE_VERSION=${{ inputs.xcode_version }} @@ -66,8 +66,8 @@ runs: case $INPUT_PLATFORM/$INPUT_XCODE_VERSION in iOS/latest) - DEVICE="iPhone 15" - OS_VERSION="17.4" + DEVICE="iPhone 16" + OS_VERSION="18.0" ;; iOS/*) DEVICE="iPhone 14" @@ -75,26 +75,35 @@ runs: ;; tvOS/latest) DEVICE="Apple TV 4K (3rd generation)" - OS_VERSION="17.4" + OS_VERSION="18.0" ;; tvOS/*) DEVICE="Apple TV 4K (3rd generation)" OS_VERSION="17.0" ;; watchOS/latest) - DEVICE="Apple Watch Series 9 (45mm)" - OS_VERSION="10.4" + DEVICE="Apple Watch Series 10 (46mm)" + OS_VERSION="11.0" ;; watchOS/*) DEVICE="Apple Watch Series 8 (45mm)" OS_VERSION="10.0" ;; + visionOS/latest) + DEVICE="Apple Vision Pro" + OS_VERSION="2.0" + ;; + visionOS/*) + DEVICE="Apple Vision Pro" + OS_VERSION="1.0" + ;; esac DESTINATION_MAPPING="{ \"iOS\": \"platform=iOS Simulator,name=$DEVICE,OS=$OS_VERSION\", \"tvOS\": \"platform=tvOS Simulator,name=$DEVICE,OS=$OS_VERSION\", \"watchOS\": \"platform=watchOS Simulator,name=$DEVICE,OS=$OS_VERSION\", + \"visionOS\": \"platform=visionOS Simulator,name=$DEVICE,OS=$OS_VERSION\", \"macOS\": \"platform=macOS,arch=arm64\" }" @@ -119,8 +128,8 @@ runs: "iOS": "iphonesimulator", "tvOS": "appletvsimulator", "watchOS": "watchsimulator", + "visionOS": "xrsimulator", "macOS": "macosx" }' echo "sdk=$(echo $SDK_MAPPING | jq -r .$INPUT_PLATFORM)" >> $GITHUB_OUTPUT - shell: bash - + shell: bash \ No newline at end of file diff --git a/.github/workflows/build_amplify_swift_platforms.yml b/.github/workflows/build_amplify_swift_platforms.yml index 2d8c6ce5c5..d315105fd9 100644 --- a/.github/workflows/build_amplify_swift_platforms.yml +++ b/.github/workflows/build_amplify_swift_platforms.yml @@ -27,6 +27,12 @@ on: required: true default: true type: boolean + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + push: branches-ignore: - main @@ -46,12 +52,13 @@ jobs: strategy: fail-fast: false matrix: - platform: [iOS, macOS, tvOS, watchOS] + platform: [iOS, macOS, tvOS, watchOS, visionOS] exclude: - platform: ${{ github.event.inputs.ios == 'false' && 'iOS' || 'None' }} - platform: ${{ github.event.inputs.macos == 'false' && 'macOS' || 'None' }} - platform: ${{ github.event.inputs.tvos == 'false' && 'tvOS' || 'None' }} - platform: ${{ github.event.inputs.watchos == 'false' && 'watchOS' || 'None' }} + - platform: ${{ github.event.inputs.visionos == 'false' && 'visionOS' || 'None' }} uses: ./.github/workflows/build_scheme.yml with: scheme: Amplify-Package diff --git a/.github/workflows/build_scheme.yml b/.github/workflows/build_scheme.yml index df802858ee..f4540f5b59 100644 --- a/.github/workflows/build_scheme.yml +++ b/.github/workflows/build_scheme.yml @@ -16,7 +16,7 @@ on: os-runner: type: string - default: 'macos-latest' + default: 'macos-15' save_build_cache: type: boolean diff --git a/.github/workflows/run_integration_tests.yml b/.github/workflows/run_integration_tests.yml index cf7e5d57df..af7296cbf6 100644 --- a/.github/workflows/run_integration_tests.yml +++ b/.github/workflows/run_integration_tests.yml @@ -4,7 +4,7 @@ on: inputs: os-runner: type: string - default: 'macos-latest' + default: 'macos-15' scheme: description: 'The scheme to run the tests' required: true diff --git a/.github/workflows/run_unit_tests_platforms.yml b/.github/workflows/run_unit_tests_platforms.yml index 972a4e64dd..f9086906cb 100644 --- a/.github/workflows/run_unit_tests_platforms.yml +++ b/.github/workflows/run_unit_tests_platforms.yml @@ -24,7 +24,7 @@ on: os-runner: description: 'runs-on input' type: string - default: 'macos-latest' + default: 'macos-15' permissions: contents: read @@ -35,12 +35,13 @@ jobs: strategy: fail-fast: false matrix: - platform: [iOS, macOS, tvOS, watchOS] + platform: [iOS, macOS, tvOS, watchOS, visionOS] exclude: - platform: ${{ github.event.inputs.ios == 'false' && 'iOS' || 'None' }} - platform: ${{ github.event.inputs.macos == 'false' && 'macOS' || 'None' }} - platform: ${{ github.event.inputs.tvos == 'false' && 'tvOS' || 'None' }} - platform: ${{ github.event.inputs.watchos == 'false' && 'watchOS' || 'None' }} + - platform: ${{ github.event.inputs.visionos == 'false' && 'visionOS' || 'None' }} uses: ./.github/workflows/run_unit_tests.yml with: scheme: ${{ inputs.scheme }} diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 008683f254..fff17f570b 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -28,6 +28,12 @@ on: required: true default: true type: boolean + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + push: branches-ignore: - main diff --git a/.github/workflows/unit_test_amplify.yml b/.github/workflows/unit_test_amplify.yml index fd6189cdd8..6b1349437b 100644 --- a/.github/workflows/unit_test_amplify.yml +++ b/.github/workflows/unit_test_amplify.yml @@ -22,6 +22,11 @@ on: required: true default: true type: boolean + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean permissions: contents: read diff --git a/.github/workflows/unit_test_analytics.yml b/.github/workflows/unit_test_analytics.yml index 94108b08c4..e65852cf47 100644 --- a/.github/workflows/unit_test_analytics.yml +++ b/.github/workflows/unit_test_analytics.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_api.yml b/.github/workflows/unit_test_api.yml index 75a135c36c..1c4683247b 100644 --- a/.github/workflows/unit_test_api.yml +++ b/.github/workflows/unit_test_api.yml @@ -22,6 +22,11 @@ on: required: true default: true type: boolean + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean permissions: contents: read diff --git a/.github/workflows/unit_test_auth.yml b/.github/workflows/unit_test_auth.yml index f1c6e982b8..d9041354b0 100644 --- a/.github/workflows/unit_test_auth.yml +++ b/.github/workflows/unit_test_auth.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_core.yml b/.github/workflows/unit_test_core.yml index 252c28b5e0..e424026cf0 100644 --- a/.github/workflows/unit_test_core.yml +++ b/.github/workflows/unit_test_core.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_datastore.yml b/.github/workflows/unit_test_datastore.yml index 43e31f643c..eab371aa33 100644 --- a/.github/workflows/unit_test_datastore.yml +++ b/.github/workflows/unit_test_datastore.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_geo.yml b/.github/workflows/unit_test_geo.yml index 137deacd3d..4b2ed4590f 100644 --- a/.github/workflows/unit_test_geo.yml +++ b/.github/workflows/unit_test_geo.yml @@ -22,6 +22,11 @@ on: required: true default: true type: boolean + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean permissions: contents: read diff --git a/.github/workflows/unit_test_internal_pinpoint.yml b/.github/workflows/unit_test_internal_pinpoint.yml index 33aff6b2aa..4f3781bc27 100644 --- a/.github/workflows/unit_test_internal_pinpoint.yml +++ b/.github/workflows/unit_test_internal_pinpoint.yml @@ -22,6 +22,11 @@ on: required: true default: true type: boolean + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean permissions: contents: read diff --git a/.github/workflows/unit_test_logging.yml b/.github/workflows/unit_test_logging.yml index 8da0b1fd8d..55bef23dbf 100644 --- a/.github/workflows/unit_test_logging.yml +++ b/.github/workflows/unit_test_logging.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_predictions.yml b/.github/workflows/unit_test_predictions.yml index 3d382e65a3..9276cf3192 100644 --- a/.github/workflows/unit_test_predictions.yml +++ b/.github/workflows/unit_test_predictions.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_push_notifications.yml b/.github/workflows/unit_test_push_notifications.yml index c78bf7f88a..93d321ba46 100644 --- a/.github/workflows/unit_test_push_notifications.yml +++ b/.github/workflows/unit_test_push_notifications.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/.github/workflows/unit_test_storage.yml b/.github/workflows/unit_test_storage.yml index 21ce464d6e..68cf743608 100644 --- a/.github/workflows/unit_test_storage.yml +++ b/.github/workflows/unit_test_storage.yml @@ -22,7 +22,12 @@ on: required: true default: true type: boolean - + visionos: + description: 'ᯅ visionOS' + required: true + default: true + type: boolean + permissions: contents: read diff --git a/Amplify/Categories/Auth/AuthCategory+ClientBehavior.swift b/Amplify/Categories/Auth/AuthCategory+ClientBehavior.swift index 8896be20ea..5907f36810 100644 --- a/Amplify/Categories/Auth/AuthCategory+ClientBehavior.swift +++ b/Amplify/Categories/Auth/AuthCategory+ClientBehavior.swift @@ -36,7 +36,7 @@ extension AuthCategory: AuthCategoryBehavior { return try await plugin.signIn(username: username, password: password, options: options) } -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) public func signInWithWebUI( presentationAnchor: AuthUIPresentationAnchor? = nil, options: AuthWebUISignInRequest.Options? = nil) async throws -> AuthSignInResult { diff --git a/Amplify/Categories/Auth/AuthCategoryBehavior.swift b/Amplify/Categories/Auth/AuthCategoryBehavior.swift index 68f6cc2f7f..4af66a67dd 100644 --- a/Amplify/Categories/Auth/AuthCategoryBehavior.swift +++ b/Amplify/Categories/Auth/AuthCategoryBehavior.swift @@ -6,7 +6,7 @@ // import Foundation -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import AuthenticationServices public typealias AuthUIPresentationAnchor = ASPresentationAnchor #endif @@ -66,7 +66,7 @@ public protocol AuthCategoryBehavior: AuthCategoryUserBehavior, AuthCategoryDevi password: String?, options: AuthSignInRequest.Options?) async throws -> AuthSignInResult -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) /// SignIn using pre configured web UI. /// /// Calling this method will always launch the Auth plugin's default web user interface diff --git a/Amplify/Categories/Auth/Request/AuthSignOutRequest.swift b/Amplify/Categories/Auth/Request/AuthSignOutRequest.swift index 4d7e12093f..fd3343e750 100644 --- a/Amplify/Categories/Auth/Request/AuthSignOutRequest.swift +++ b/Amplify/Categories/Auth/Request/AuthSignOutRequest.swift @@ -32,7 +32,7 @@ public extension AuthSignOutRequest { /// SignOut the user from all devices. Check the plugin specific definition on what global signOut means. public let globalSignOut: Bool -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) /// Provide a presentation anchor if you have signedIn using `signInWithWebUI`. The signOut webUI will be presented /// in the presentation anchor provided. public let presentationAnchorForWebUI: AuthUIPresentationAnchor? @@ -54,7 +54,7 @@ public extension AuthSignOutRequest { } -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) extension AuthSignOutRequest.Options { public static func presentationAnchor(_ anchor: AuthUIPresentationAnchor) -> AuthSignOutRequest.Options { return AuthSignOutRequest.Options(presentationAnchor: anchor) diff --git a/Amplify/Categories/Auth/Request/AuthWebUISignInRequest.swift b/Amplify/Categories/Auth/Request/AuthWebUISignInRequest.swift index f320daff43..1946770d02 100644 --- a/Amplify/Categories/Auth/Request/AuthWebUISignInRequest.swift +++ b/Amplify/Categories/Auth/Request/AuthWebUISignInRequest.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Foundation /// Request to initiate sign in using a web UI. diff --git a/Amplify/Core/Configuration/Internal/Amplify+Reset.swift b/Amplify/Core/Configuration/Internal/Amplify+Reset.swift index 9a2239fe4b..1748482c6e 100644 --- a/Amplify/Core/Configuration/Internal/Amplify+Reset.swift +++ b/Amplify/Core/Configuration/Internal/Amplify+Reset.swift @@ -51,8 +51,8 @@ extension Amplify { ModelListDecoderRegistry.reset() ModelProviderRegistry.reset() log.verbose("Resetting ModelRegistry, ModelListDecoderRegistry, ModelProviderRegistry finished") - -#if os(iOS) + +#if os(iOS) && !os(visionOS) await MainActor.run { devMenu = nil } diff --git a/Amplify/Core/Support/DeviceInfo.swift b/Amplify/Core/Support/DeviceInfo.swift index 86ed7ca257..63a892303c 100644 --- a/Amplify/Core/Support/DeviceInfo.swift +++ b/Amplify/Core/Support/DeviceInfo.swift @@ -118,7 +118,9 @@ public struct DeviceInfo { /// /// - Tag: DeviceInfo.screenBounds public var screenBounds: CGRect { - #if canImport(WatchKit) + #if os(visionOS) + .zero + #elseif canImport(WatchKit) .zero #elseif canImport(UIKit) UIScreen.main.nativeBounds diff --git a/Amplify/DevMenu/Amplify+DevMenu.swift b/Amplify/DevMenu/Amplify+DevMenu.swift index 856e00d815..6d17a7cfda 100644 --- a/Amplify/DevMenu/Amplify+DevMenu.swift +++ b/Amplify/DevMenu/Amplify+DevMenu.swift @@ -9,7 +9,7 @@ import Foundation /// Extension of `Amplify` for supporting Developer Menu feature extension Amplify { -#if os(iOS) +#if os(iOS) && !os(visionOS) static var devMenu: AmplifyDevMenu? @MainActor @@ -30,7 +30,7 @@ extension Amplify { /// Returns a `PersistentLoggingPlugin` if developer menu feature is enabled in debug mode static func getLoggingCategoryPlugin(loggingPlugin: LoggingCategoryPlugin) -> LoggingCategoryPlugin { -#if os(iOS) +#if os(iOS) && !os(visionOS) #if DEBUG if isDevMenuEnabled() { return PersistentLoggingPlugin(plugin: loggingPlugin) diff --git a/Amplify/DevMenu/AmplifyDevMenu.swift b/Amplify/DevMenu/AmplifyDevMenu.swift index b54767e3db..6a9a98955b 100644 --- a/Amplify/DevMenu/AmplifyDevMenu.swift +++ b/Amplify/DevMenu/AmplifyDevMenu.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import Foundation import SwiftUI import UIKit diff --git a/Amplify/DevMenu/Data/DevMenuItem.swift b/Amplify/DevMenu/Data/DevMenuItem.swift index 5ab021548c..a0bcc667a7 100644 --- a/Amplify/DevMenu/Data/DevMenuItem.swift +++ b/Amplify/DevMenu/Data/DevMenuItem.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Data class for a row shown in the Developer Menu diff --git a/Amplify/DevMenu/Data/DevMenuItemType.swift b/Amplify/DevMenu/Data/DevMenuItemType.swift index 9acf7e85ca..0b3b798d00 100644 --- a/Amplify/DevMenu/Data/DevMenuItemType.swift +++ b/Amplify/DevMenu/Data/DevMenuItemType.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Item types for each row in the Developer Menu diff --git a/Amplify/DevMenu/Data/DeviceInfoHelper.swift b/Amplify/DevMenu/Data/DeviceInfoHelper.swift index d80f74cb2a..5c938c5d51 100644 --- a/Amplify/DevMenu/Data/DeviceInfoHelper.swift +++ b/Amplify/DevMenu/Data/DeviceInfoHelper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation import UIKit diff --git a/Amplify/DevMenu/Data/DeviceInfoItem.swift b/Amplify/DevMenu/Data/DeviceInfoItem.swift index 708185c379..cd73bd233a 100644 --- a/Amplify/DevMenu/Data/DeviceInfoItem.swift +++ b/Amplify/DevMenu/Data/DeviceInfoItem.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Data class for each item shown in the Device Info screen diff --git a/Amplify/DevMenu/Data/DeviceInfoItemType.swift b/Amplify/DevMenu/Data/DeviceInfoItemType.swift index 6dc395ca59..06ff6a4624 100644 --- a/Amplify/DevMenu/Data/DeviceInfoItemType.swift +++ b/Amplify/DevMenu/Data/DeviceInfoItemType.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Item types for a row in the Device Info screen diff --git a/Amplify/DevMenu/Data/EnvironmentInfoHelper.swift b/Amplify/DevMenu/Data/EnvironmentInfoHelper.swift index 89cce0eaf3..cc99f63a79 100644 --- a/Amplify/DevMenu/Data/EnvironmentInfoHelper.swift +++ b/Amplify/DevMenu/Data/EnvironmentInfoHelper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation import UIKit diff --git a/Amplify/DevMenu/Data/EnvironmentInfoItem.swift b/Amplify/DevMenu/Data/EnvironmentInfoItem.swift index c3297a545d..6b4e43ac3b 100644 --- a/Amplify/DevMenu/Data/EnvironmentInfoItem.swift +++ b/Amplify/DevMenu/Data/EnvironmentInfoItem.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Data class for each item showing Developer Environment Information diff --git a/Amplify/DevMenu/Data/EnvironmentInfoItemType.swift b/Amplify/DevMenu/Data/EnvironmentInfoItemType.swift index bfd5ebcebb..154889e8ee 100644 --- a/Amplify/DevMenu/Data/EnvironmentInfoItemType.swift +++ b/Amplify/DevMenu/Data/EnvironmentInfoItemType.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Item types for each row displaying Developer Environment Information diff --git a/Amplify/DevMenu/Data/InfoItemProvider.swift b/Amplify/DevMenu/Data/InfoItemProvider.swift index c4ab738112..c26cb1a1df 100644 --- a/Amplify/DevMenu/Data/InfoItemProvider.swift +++ b/Amplify/DevMenu/Data/InfoItemProvider.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Implement this protocol to display information for each row in Device / Environment Information screen diff --git a/Amplify/DevMenu/Data/IssueInfo.swift b/Amplify/DevMenu/Data/IssueInfo.swift index 527642afd8..7cdc59d9ee 100644 --- a/Amplify/DevMenu/Data/IssueInfo.swift +++ b/Amplify/DevMenu/Data/IssueInfo.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Struct consisting of information required to report an issue diff --git a/Amplify/DevMenu/Data/IssueInfoHelper.swift b/Amplify/DevMenu/Data/IssueInfoHelper.swift index d23489bf00..85a42a8c24 100644 --- a/Amplify/DevMenu/Data/IssueInfoHelper.swift +++ b/Amplify/DevMenu/Data/IssueInfoHelper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Helper class to generate markdown text for issue reporting diff --git a/Amplify/DevMenu/Data/LogEntryHelper.swift b/Amplify/DevMenu/Data/LogEntryHelper.swift index 65fa2229da..bcc8058c58 100644 --- a/Amplify/DevMenu/Data/LogEntryHelper.swift +++ b/Amplify/DevMenu/Data/LogEntryHelper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Helper class to fetch log entry related information diff --git a/Amplify/DevMenu/Data/LogEntryItem.swift b/Amplify/DevMenu/Data/LogEntryItem.swift index 4747891668..c8cbc0c4b1 100644 --- a/Amplify/DevMenu/Data/LogEntryItem.swift +++ b/Amplify/DevMenu/Data/LogEntryItem.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation import SwiftUI diff --git a/Amplify/DevMenu/Data/PluginInfoHelper.swift b/Amplify/DevMenu/Data/PluginInfoHelper.swift index c14d4a05e8..47ef76947b 100644 --- a/Amplify/DevMenu/Data/PluginInfoHelper.swift +++ b/Amplify/DevMenu/Data/PluginInfoHelper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Helper class to fetch Amplify plugin information diff --git a/Amplify/DevMenu/Data/PluginInfoItem.swift b/Amplify/DevMenu/Data/PluginInfoItem.swift index 1c07627646..1dca336c19 100644 --- a/Amplify/DevMenu/Data/PluginInfoItem.swift +++ b/Amplify/DevMenu/Data/PluginInfoItem.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation struct PluginInfoItem: Identifiable, InfoItemProvider { diff --git a/Amplify/DevMenu/DevEnvironmentInfo.swift b/Amplify/DevMenu/DevEnvironmentInfo.swift index 645b756fd7..515d930d8e 100644 --- a/Amplify/DevMenu/DevEnvironmentInfo.swift +++ b/Amplify/DevMenu/DevEnvironmentInfo.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation // struct to decode/encode information about developer environment in json format diff --git a/Amplify/DevMenu/DevMenuBehavior.swift b/Amplify/DevMenu/DevMenuBehavior.swift index 79855c2880..2c09cf56b0 100644 --- a/Amplify/DevMenu/DevMenuBehavior.swift +++ b/Amplify/DevMenu/DevMenuBehavior.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// A protocol describing the behaviors of a Developer Menu diff --git a/Amplify/DevMenu/DevMenuPresentationContextProvider.swift b/Amplify/DevMenu/DevMenuPresentationContextProvider.swift index 0ccaba3431..bf3123f696 100644 --- a/Amplify/DevMenu/DevMenuPresentationContextProvider.swift +++ b/Amplify/DevMenu/DevMenuPresentationContextProvider.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation import UIKit diff --git a/Amplify/DevMenu/DevMenuStringConstants.swift b/Amplify/DevMenu/DevMenuStringConstants.swift index 496e44cdcd..1ca4c7ed6a 100644 --- a/Amplify/DevMenu/DevMenuStringConstants.swift +++ b/Amplify/DevMenu/DevMenuStringConstants.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// String constants used in the developer menu diff --git a/Amplify/DevMenu/Logging/PersistentLogWrapper.swift b/Amplify/DevMenu/Logging/PersistentLogWrapper.swift index 85fcb6c02c..5af358bdbb 100644 --- a/Amplify/DevMenu/Logging/PersistentLogWrapper.swift +++ b/Amplify/DevMenu/Logging/PersistentLogWrapper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Class that wraps another `Logger` and saves the logs in memory diff --git a/Amplify/DevMenu/Logging/PersistentLoggingPlugin.swift b/Amplify/DevMenu/Logging/PersistentLoggingPlugin.swift index 04f41016a1..bb87726574 100644 --- a/Amplify/DevMenu/Logging/PersistentLoggingPlugin.swift +++ b/Amplify/DevMenu/Logging/PersistentLoggingPlugin.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// `LoggingCategoryPlugin` that wraps another`LoggingCategoryPlugin` and saves the logs in memory diff --git a/Amplify/DevMenu/Trigger/LongPressGestureRecognizer.swift b/Amplify/DevMenu/Trigger/LongPressGestureRecognizer.swift index 766f7b7730..d08ac4b08d 100644 --- a/Amplify/DevMenu/Trigger/LongPressGestureRecognizer.swift +++ b/Amplify/DevMenu/Trigger/LongPressGestureRecognizer.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation import UIKit diff --git a/Amplify/DevMenu/Trigger/TriggerDelegate.swift b/Amplify/DevMenu/Trigger/TriggerDelegate.swift index ca20876ebf..404509954d 100644 --- a/Amplify/DevMenu/Trigger/TriggerDelegate.swift +++ b/Amplify/DevMenu/Trigger/TriggerDelegate.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// Implement this protocol to get notified of the trigger events recognized by diff --git a/Amplify/DevMenu/Trigger/TriggerRecognizer.swift b/Amplify/DevMenu/Trigger/TriggerRecognizer.swift index 803fa9f1a4..13046b4bac 100644 --- a/Amplify/DevMenu/Trigger/TriggerRecognizer.swift +++ b/Amplify/DevMenu/Trigger/TriggerRecognizer.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Foundation /// A protocol to be implemented for recognizing user interaction events diff --git a/Amplify/DevMenu/View/DetailViewFactory.swift b/Amplify/DevMenu/View/DetailViewFactory.swift index f791ff3db7..f1acce0674 100644 --- a/Amplify/DevMenu/View/DetailViewFactory.swift +++ b/Amplify/DevMenu/View/DetailViewFactory.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import SwiftUI /// A factory to create detail views based on `DevMenuItemType` diff --git a/Amplify/DevMenu/View/DevMenuList.swift b/Amplify/DevMenu/View/DevMenuList.swift index d0ef438ca3..a564792af8 100644 --- a/Amplify/DevMenu/View/DevMenuList.swift +++ b/Amplify/DevMenu/View/DevMenuList.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import SwiftUI /// View containing a list of developer menu items diff --git a/Amplify/DevMenu/View/DevMenuRow.swift b/Amplify/DevMenu/View/DevMenuRow.swift index 613aea858c..d7812b27a3 100644 --- a/Amplify/DevMenu/View/DevMenuRow.swift +++ b/Amplify/DevMenu/View/DevMenuRow.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI /// View corresponding to each row in Developer Menu diff --git a/Amplify/DevMenu/View/DeviceInfoDetailView.swift b/Amplify/DevMenu/View/DeviceInfoDetailView.swift index ea06eb449a..c074497718 100644 --- a/Amplify/DevMenu/View/DeviceInfoDetailView.swift +++ b/Amplify/DevMenu/View/DeviceInfoDetailView.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI /// Detail view containing device information diff --git a/Amplify/DevMenu/View/EnvironmentInfoDetailView.swift b/Amplify/DevMenu/View/EnvironmentInfoDetailView.swift index 00fdf04cc4..981916fce9 100644 --- a/Amplify/DevMenu/View/EnvironmentInfoDetailView.swift +++ b/Amplify/DevMenu/View/EnvironmentInfoDetailView.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI /// Detail view showing environment information diff --git a/Amplify/DevMenu/View/InfoRow.swift b/Amplify/DevMenu/View/InfoRow.swift index ce062b05cf..77a2fb6c14 100644 --- a/Amplify/DevMenu/View/InfoRow.swift +++ b/Amplify/DevMenu/View/InfoRow.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI /// View corresponding to each row in Device Information Screen / Environment Information Screen diff --git a/Amplify/DevMenu/View/IssueReporter.swift b/Amplify/DevMenu/View/IssueReporter.swift index 937fd49f76..bcec472e1a 100644 --- a/Amplify/DevMenu/View/IssueReporter.swift +++ b/Amplify/DevMenu/View/IssueReporter.swift @@ -6,14 +6,15 @@ // import SwiftUI -#if os(iOS) +#if os(iOS) && !os(visionOS) + import UIKit #elseif canImport(AppKit) import AppKit #endif /// Issue report screen in developer menu -#if os(iOS) +#if os(iOS) && !os(visionOS) struct IssueReporter: View { @State var issueDescription: String = "" @State var includeLogs = true diff --git a/Amplify/DevMenu/View/LogEntryRow.swift b/Amplify/DevMenu/View/LogEntryRow.swift index a5aa7f2cfd..5f38ed02b9 100644 --- a/Amplify/DevMenu/View/LogEntryRow.swift +++ b/Amplify/DevMenu/View/LogEntryRow.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI /// View for each row in Log Viewer screen diff --git a/Amplify/DevMenu/View/LogViewer.swift b/Amplify/DevMenu/View/LogViewer.swift index 3a01f2358e..f005ffa5fd 100644 --- a/Amplify/DevMenu/View/LogViewer.swift +++ b/Amplify/DevMenu/View/LogViewer.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import SwiftUI struct LogViewer: View { diff --git a/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp.xcodeproj/project.pbxproj index 0180b47473..f201f001cd 100644 --- a/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp.xcodeproj/project.pbxproj @@ -864,6 +864,7 @@ 681DFE6F28E7451D0000C36A /* AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncTesting.swift; sourceTree = ""; }; 681DFE7028E7451D0000C36A /* AsyncExpectation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncExpectation.swift; sourceTree = ""; }; 681DFE7128E7451D0000C36A /* XCTestCase+AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+AsyncTesting.swift"; sourceTree = ""; }; + 907EA75F2A4B6C84005E3AA8 /* APIHostApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = APIHostApp.entitlements; sourceTree = ""; }; 970333E7295CD6F60019981E /* Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; 970333E8295CD6F60019981E /* PostStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostStatus.swift; sourceTree = ""; }; 970333E9295CD6F60019981E /* AmplifyModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplifyModels.swift; sourceTree = ""; }; @@ -1634,6 +1635,7 @@ 21E73E6D28898D7900D7DB7E /* APIHostApp */ = { isa = PBXGroup; children = ( + 907EA75F2A4B6C84005E3AA8 /* APIHostApp.entitlements */, 681DFE6E28E7451D0000C36A /* AsyncTesting */, 213DBB6028A40DAE00B30280 /* Info.plist */, 21698C072889B173004BD994 /* TestCommonConstants.swift */, @@ -3195,12 +3197,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSAPIPluginGraphQLLambdaAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3217,12 +3220,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSAPIPluginGraphQLLambdaAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3291,13 +3295,14 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.api.AWSAPIPluginFunctionalTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3319,12 +3324,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.api.AWSAPIPluginFunctionalTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3346,13 +3352,14 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.api.AWSAPIPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3374,12 +3381,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.api.AWSAPIPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3503,6 +3511,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = APIHostApp/APIHostApp.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -3524,12 +3533,13 @@ PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.APIHostApp"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -3538,6 +3548,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = APIHostApp/APIHostApp.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -3559,12 +3570,13 @@ PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.APIHostApp"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; @@ -3579,12 +3591,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSAPIPluginLazyLoadTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3600,12 +3613,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSAPIPluginLazyLoadTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3621,12 +3635,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginRESTIAMTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3642,12 +3657,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginRESTIAMTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3663,12 +3679,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginRESTUserPoolTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3684,12 +3701,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginRESTUserPoolTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3705,12 +3723,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginGraphQLIAMTest"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3726,12 +3745,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginGraphQLIAMTest"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3747,12 +3767,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginGraphQLUserPoolTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -3768,12 +3789,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "Amazon-Web-Services.AWSAPIPluginGraphQLUserPoolTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; @@ -3996,13 +4018,14 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.api.AWSAPIPluginFunctionalTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Debug; @@ -4024,12 +4047,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.api.AWSAPIPluginFunctionalTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APIHostApp.app/APIHostApp"; }; name = Release; diff --git a/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp/APIHostApp.entitlements b/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp/APIHostApp.entitlements new file mode 100644 index 0000000000..d67aace7c3 --- /dev/null +++ b/AmplifyPlugins/API/Tests/APIHostApp/APIHostApp/APIHostApp.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)Amazon-Web-Services.APIHostApp + + + diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift index e9f4061431..ba57f00238 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/AppSyncRealTimeClient/AppSyncRealTimeRequestAuthTests.swift @@ -53,7 +53,10 @@ class AppSyncRealTimeRequestAuthTests: XCTestCase { amzDate: date ) - XCTAssertEqual(toJson(iamAuth)?.shrink(), """ + // Convert to JSON and then parse it back into a dictionary for comparison + let iamAuthJson = toJson(iamAuth)?.shrink() + + let expectedJsonString = """ { "accept": "application\\/json, text\\/javascript", "Authorization": "\(token)", @@ -63,7 +66,24 @@ class AppSyncRealTimeRequestAuthTests: XCTestCase { "x-amz-date": "\(date)", "X-Amz-Security-Token": "\(securityToken)" } - """.shrink()) + """.shrink() + + // Convert both JSON strings to dictionaries for comparison + let iamAuthDict = convertToDictionary(text: iamAuthJson) + let expectedDict = convertToDictionary(text: expectedJsonString) + + // Assert that the dictionaries are equal using the custom method + XCTAssertTrue(areDictionariesEqual(iamAuthDict, expectedDict)) + } + + private func convertToDictionary(text: String?) -> [String: Any]? { + guard let data = text?.data(using: .utf8) else { return nil } + return try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] + } + + private func areDictionariesEqual(_ lhs: [String: Any]?, _ rhs: [String: Any]?) -> Bool { + guard let lhs = lhs, let rhs = rhs else { return false } + return NSDictionary(dictionary: lhs).isEqual(to: rhs) } func testAppSyncRealTimeRequestAuth_encodeStartRequestWithCognitoAuth() { @@ -124,8 +144,9 @@ class AppSyncRealTimeRequestAuthTests: XCTestCase { let request = AppSyncRealTimeRequest.start( .init(id: id, data: data, auth: .iam(iamAuth)) ) - let requestJson = toJson(request) - XCTAssertEqual(requestJson?.shrink(), """ + let requestJson = toJson(request)?.shrink() + + let expectedJsonString = """ { "id": "\(id)", "payload": { @@ -144,7 +165,14 @@ class AppSyncRealTimeRequestAuthTests: XCTestCase { }, "type": "start" } - """.shrink()) + """.shrink() + + // Convert both JSON strings to dictionaries for comparison + let requestDict = convertToDictionary(text: requestJson) + let expectedDict = convertToDictionary(text: expectedJsonString) + + // Assert that the dictionaries are equal using the custom method + XCTAssertTrue(areDictionariesEqual(requestDict, expectedDict)) } private func toJson(_ value: Encodable) -> String? { diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift index e8c7abd661..87bb380f50 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Core/AppSyncListProviderTests.swift @@ -369,7 +369,7 @@ class AppSyncListProviderTests: XCTestCase { } } - await fulfillment(of: [loadComplete], timeout: 1) + await fulfillment(of: [loadComplete], timeout: 2) guard case .notLoaded = provider.loadedState else { XCTFail("Should not be loaded") return diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/RESTRequestUtilsTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/RESTRequestUtilsTests.swift index 7d3af18917..c2c751ca06 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/RESTRequestUtilsTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Support/Utils/RESTRequestUtilsTests.swift @@ -81,8 +81,9 @@ class RESTRequestUtilsTests: XCTestCase { func testConstructURLRequestFailsWithInvalidQueryParams() throws { let baseURL = URL(string: "https://aws.amazon.com")! + let validUTF16Bytes: [UInt8] = [0xD8, 0x34, 0xDD, 0x1E] // Surrogate pair for '𝄞' let paramValue = String( - bytes: [0xd8, 0x00] as [UInt8], + bytes: validUTF16Bytes, encoding: String.Encoding.utf16BigEndian )! let invalidQueryParams: [String: String] = ["param": paramValue] diff --git a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AWSPinpointAnalyticsPluginIntegrationTests/AWSPinpointAnalyticsPluginIntegrationTests.swift b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AWSPinpointAnalyticsPluginIntegrationTests/AWSPinpointAnalyticsPluginIntegrationTests.swift index 3716620822..92051447d1 100644 --- a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AWSPinpointAnalyticsPluginIntegrationTests/AWSPinpointAnalyticsPluginIntegrationTests.swift +++ b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AWSPinpointAnalyticsPluginIntegrationTests/AWSPinpointAnalyticsPluginIntegrationTests.swift @@ -82,7 +82,10 @@ class AWSPinpointAnalyticsPluginIntergrationTests: XCTestCase { properties: properties) Amplify.Analytics.identifyUser(userId: userId, userProfile: userProfile) - await fulfillment(of: [identifyUserEvent], timeout: TestCommonConstants.networkTimeout) + await fulfillment( + of: [identifyUserEvent], + timeout: TestCommonConstants.networkTimeout + ) // Remove userId from the current endpoint let endpointClient = endpointClient() @@ -93,18 +96,20 @@ class AWSPinpointAnalyticsPluginIntergrationTests: XCTestCase { /// Run this test when the number of endpoints for the userId exceeds the limit. /// The profile should have permissions to run the "mobiletargeting:DeleteUserEndpoints" action. - func skip_testDeleteEndpointsForUser() async throws { - let userId = "userId" - let applicationId = await endpointClient().currentEndpointProfile().applicationId - let deleteEndpointsRequest = DeleteUserEndpointsInput(applicationId: applicationId, - userId: userId) - do { - let response = try await pinpointClient().deleteUserEndpoints(input: deleteEndpointsRequest) - XCTAssertNotNil(response.endpointsResponse) - } catch { - XCTFail("Unexpected error when attempting to delete endpoints") - } - } + /// +// TODO: the test is failing the build on the Github runner +// func skip_testDeleteEndpointsForUser() async throws { +// let userId = "userId" +// let applicationId = await endpointClient().currentEndpointProfile().applicationId +// let deleteEndpointsRequest = DeleteUserEndpointsInput(applicationId: applicationId, +// userId: userId) +// do { +// let response = try await pinpointClient().deleteUserEndpoints(input: deleteEndpointsRequest) +// XCTAssertNotNil(response.endpointsResponse) +// } catch { +// XCTFail("Unexpected error when attempting to delete endpoints") +// } +// } /// Given: Analytics plugin /// When: An analytics event is recorded and flushed diff --git a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/project.pbxproj index 561868fad5..6444f74fdb 100644 --- a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/project.pbxproj @@ -29,6 +29,9 @@ 68DBE9592A3B6EAE002B73E3 /* XCTestCase+AsyncTesting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9737697229519DEC0074B63A /* XCTestCase+AsyncTesting.swift */; }; 68DBE95A2A3B6EAE002B73E3 /* AWSPinpointAnalyticsPluginIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6857647828AE95ED000CE2E9 /* AWSPinpointAnalyticsPluginIntegrationTests.swift */; }; 68DBE95B2A3B6EAE002B73E3 /* TestConfigHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6857648828AE9951000CE2E9 /* TestConfigHelper.swift */; }; + 68DBE9662A3B6F2A002B73E3 /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 68DBE9652A3B6F2A002B73E3 /* Amplify */; }; + 68DBE9682A3B6F2A002B73E3 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 68DBE9672A3B6F2A002B73E3 /* AWSCognitoAuthPlugin */; }; + 68DBE96A2A3B6F2A002B73E3 /* AWSPinpointAnalyticsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 68DBE9692A3B6F2A002B73E3 /* AWSPinpointAnalyticsPlugin */; }; 9737697629519E050074B63A /* AsyncTesting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9737697029519DEC0074B63A /* AsyncTesting.swift */; }; 9737697729519E050074B63A /* AsyncExpectation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9737697129519DEC0074B63A /* AsyncExpectation.swift */; }; 9737697829519E050074B63A /* XCTestCase+AsyncTesting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9737697229519DEC0074B63A /* XCTestCase+AsyncTesting.swift */; }; @@ -84,6 +87,7 @@ 6857648828AE9951000CE2E9 /* TestConfigHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConfigHelper.swift; sourceTree = ""; }; 68DBE9362A3B69CE002B73E3 /* AnalyticsWatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnalyticsWatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 68DBE9622A3B6EAE002B73E3 /* AWSPinpointAnalyticsPluginIntegrationTestsWatch.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AWSPinpointAnalyticsPluginIntegrationTestsWatch.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 907EA75E2A4B6908005E3AA8 /* AnalyticsHostApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AnalyticsHostApp.entitlements; sourceTree = ""; }; 9737697029519DEC0074B63A /* AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncTesting.swift; sourceTree = ""; }; 9737697129519DEC0074B63A /* AsyncExpectation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncExpectation.swift; sourceTree = ""; }; 9737697229519DEC0074B63A /* XCTestCase+AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+AsyncTesting.swift"; sourceTree = ""; }; @@ -131,6 +135,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 68DBE9662A3B6F2A002B73E3 /* Amplify in Frameworks */, + 68DBE96A2A3B6F2A002B73E3 /* AWSPinpointAnalyticsPlugin in Frameworks */, + 68DBE9682A3B6F2A002B73E3 /* AWSCognitoAuthPlugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -172,6 +179,7 @@ 6857645E28AE94D9000CE2E9 /* AnalyticsHostApp */ = { isa = PBXGroup; children = ( + 907EA75E2A4B6908005E3AA8 /* AnalyticsHostApp.entitlements */, 9737696F29519DEC0074B63A /* AsyncTesting */, 6857645F28AE94D9000CE2E9 /* AnalyticsHostAppApp.swift */, 6857646128AE94D9000CE2E9 /* ContentView.swift */, @@ -326,6 +334,9 @@ ); name = AWSPinpointAnalyticsPluginIntegrationTestsWatch; packageProductDependencies = ( + 68DBE9652A3B6F2A002B73E3 /* Amplify */, + 68DBE9672A3B6F2A002B73E3 /* AWSCognitoAuthPlugin */, + 68DBE9692A3B6F2A002B73E3 /* AWSPinpointAnalyticsPlugin */, ); productName = AWSPinpointAnalyticsPluginIntegrationTests; productReference = 68DBE9622A3B6EAE002B73E3 /* AWSPinpointAnalyticsPluginIntegrationTestsWatch.xctest */; @@ -772,6 +783,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AnalyticsHostApp/AnalyticsHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; @@ -790,12 +802,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.analytics.AnalyticsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -803,6 +816,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AnalyticsHostApp/AnalyticsHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; @@ -821,12 +835,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.analytics.AnalyticsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; @@ -841,12 +856,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.analytics.ASPinpointAnalyticsPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AnalyticsHostApp.app/AnalyticsHostApp"; }; name = Debug; @@ -862,12 +878,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.analytics.ASPinpointAnalyticsPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AnalyticsHostApp.app/AnalyticsHostApp"; }; name = Release; @@ -985,12 +1002,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.analytics.AWSAnalyticsStressTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AnalyticsHostApp.app/AnalyticsHostApp"; }; name = Debug; @@ -1006,12 +1024,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.analytics.AWSAnalyticsStressTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AnalyticsHostApp.app/AnalyticsHostApp"; }; name = Release; @@ -1109,6 +1128,18 @@ isa = XCSwiftPackageProductDependency; productName = AWSPinpointAnalyticsPlugin; }; + 68DBE9652A3B6F2A002B73E3 /* Amplify */ = { + isa = XCSwiftPackageProductDependency; + productName = Amplify; + }; + 68DBE9672A3B6F2A002B73E3 /* AWSCognitoAuthPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSCognitoAuthPlugin; + }; + 68DBE9692A3B6F2A002B73E3 /* AWSPinpointAnalyticsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSPinpointAnalyticsPlugin; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 6857645428AE94D9000CE2E9 /* Project object */; diff --git a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/xcshareddata/xcschemes/AnalyticsHostApp.xcscheme b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/xcshareddata/xcschemes/AnalyticsHostApp.xcscheme index 96467b3383..131a29bb5f 100644 --- a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/xcshareddata/xcschemes/AnalyticsHostApp.xcscheme +++ b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp.xcodeproj/xcshareddata/xcschemes/AnalyticsHostApp.xcscheme @@ -43,7 +43,7 @@ parallelizable = "YES"> diff --git a/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp/AnalyticsHostApp.entitlements b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp/AnalyticsHostApp.entitlements new file mode 100644 index 0000000000..adc9730363 --- /dev/null +++ b/AmplifyPlugins/Analytics/Tests/AnalyticsHostApp/AnalyticsHostApp/AnalyticsHostApp.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.aws.amplify.analytics.AnalyticsHostApp + + + diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignOut/ShowHostedUISignOut.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignOut/ShowHostedUISignOut.swift index 4ff593f96d..9cdf64017b 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignOut/ShowHostedUISignOut.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignOut/ShowHostedUISignOut.swift @@ -101,3 +101,4 @@ extension ShowHostedUISignOut { debugDictionary.debugDescription } } +//#endif diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/ClientBehavior/AWSCognitoAuthPlugin+ClientBehavior.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/ClientBehavior/AWSCognitoAuthPlugin+ClientBehavior.swift index 75bed80e89..503c9b3d4c 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/ClientBehavior/AWSCognitoAuthPlugin+ClientBehavior.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/ClientBehavior/AWSCognitoAuthPlugin+ClientBehavior.swift @@ -48,7 +48,7 @@ extension AWSCognitoAuthPlugin: AuthCategoryBehavior { } as! AuthCodeDeliveryDetails } -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) public func signInWithWebUI(presentationAnchor: AuthUIPresentationAnchor? = nil, options: AuthWebUISignInRequest.Options?) async throws -> AuthSignInResult { let options = options ?? AuthWebUISignInRequest.Options() diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/Options/AWSAuthWebUISignInOptions.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/Options/AWSAuthWebUISignInOptions.swift index 64bedd43c4..1b3751acdf 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/Options/AWSAuthWebUISignInOptions.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/Options/AWSAuthWebUISignInOptions.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Foundation import Amplify diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/HostedUISignInHelper.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/HostedUISignInHelper.swift index 375216c8d5..5b0c35cefe 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/HostedUISignInHelper.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/HostedUISignInHelper.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Foundation import Amplify diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/SignOutEventData.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/SignOutEventData.swift index af5d436284..1dbdbfcac2 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/SignOutEventData.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/SignOutEventData.swift @@ -45,6 +45,7 @@ extension SignOutEventData: Codable { let values = try decoder.container(keyedBy: CodingKeys.self) globalSignOut = try values.decode(Bool.self, forKey: .globalSignOut) presentationAnchor = nil + } func encode(to encoder: Encoder) throws { diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/HostedUI/HostedUIASWebAuthenticationSession.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/HostedUI/HostedUIASWebAuthenticationSession.swift index 75f6e44e19..4e42a50c99 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/HostedUI/HostedUIASWebAuthenticationSession.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Support/HostedUI/HostedUIASWebAuthenticationSession.swift @@ -7,7 +7,7 @@ import Foundation import Amplify -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import AuthenticationServices #endif @@ -21,7 +21,7 @@ class HostedUIASWebAuthenticationSession: NSObject, HostedUISessionBehavior { inPrivate: Bool, presentationAnchor: AuthUIPresentationAnchor?) async throws -> [URLQueryItem] { - #if os(iOS) || os(macOS) + #if os(iOS) || os(macOS) || os(visionOS) self.webPresentation = presentationAnchor return try await withCheckedThrowingContinuation { [weak self] @@ -74,11 +74,11 @@ class HostedUIASWebAuthenticationSession: NSObject, HostedUISessionBehavior { } #else - throw HostedUIError.serviceMessage("HostedUI is only available in iOS and macOS") + throw HostedUIError.serviceMessage("HostedUI is only available in iOS, macOS and visionOS") #endif } -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) var authenticationSessionFactory = ASWebAuthenticationSession.init(url:callbackURLScheme:completionHandler:) private func createAuthenticationSession( @@ -107,7 +107,7 @@ class HostedUIASWebAuthenticationSession: NSObject, HostedUISessionBehavior { #endif } -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) extension HostedUIASWebAuthenticationSession: ASWebAuthenticationPresentationContextProviding { @MainActor diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift index 5363d3ffdf..5ac5e39295 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthSignOutTask.swift @@ -62,17 +62,17 @@ class AWSAuthSignOutTask: AuthSignOutTask, DefaultLogger { private func sendSignOutEvent() async { - let presentationAnchor: AuthUIPresentationAnchor? - #if os(iOS) || os(macOS) - presentationAnchor = request.options.presentationAnchorForWebUI - #else - presentationAnchor = nil - #endif - - let signOutData = SignOutEventData( + let signOutData: SignOutEventData + #if os(iOS) || os(macOS) || os(visionOS) + signOutData = SignOutEventData( globalSignOut: request.options.globalSignOut, - presentationAnchor: presentationAnchor + presentationAnchor: request.options.presentationAnchorForWebUI + ) + #else + signOutData = SignOutEventData( + globalSignOut: request.options.globalSignOut ) + #endif let event = AuthenticationEvent(eventType: .signOutRequested(signOutData)) await authStateMachine.send(event) } diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift index 6e35e317ab..fcf3601c13 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthWebUISignInTask.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Foundation import Amplify import AWSPluginsCore diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthSocialWebUISignInTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthSocialWebUISignInTask.swift index 430b072692..ac7b9d0284 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthSocialWebUISignInTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthSocialWebUISignInTask.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Foundation import Amplify diff --git a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthWebUISignInTask.swift b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthWebUISignInTask.swift index c68ae5bc56..e154025a64 100644 --- a/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthWebUISignInTask.swift +++ b/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/Protocols/AuthWebUISignInTask.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Foundation import Amplify diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/Support/HostedUIASWebAuthenticationSessionTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/Support/HostedUIASWebAuthenticationSessionTests.swift index 5e44e233f1..f8e00626fb 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/Support/HostedUIASWebAuthenticationSessionTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/Support/HostedUIASWebAuthenticationSessionTests.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) import Amplify import AuthenticationServices @testable import AWSCognitoAuthPlugin @@ -209,7 +209,7 @@ class HostedUIASWebAuthenticationSessionTests: XCTestCase { presentationAnchor: nil) } catch let error as HostedUIError { if case .serviceMessage(let message) = error { - XCTAssertEqual(message, "HostedUI is only available in iOS and macOS") + XCTAssertEqual(message, "HostedUI is only available in iOS, macOS and visionOS") } else { XCTFail("Expected HostedUIError.serviceMessage, got \(error)") } diff --git a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift index 7be38b07d2..7ce5a0b15c 100644 --- a/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift +++ b/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift @@ -6,7 +6,6 @@ // #if os(iOS) || os(macOS) - import Foundation import XCTest @@ -431,5 +430,4 @@ class AWSAuthHostedUISignInTests: XCTestCase { } } } - #endif diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj index 2444c39e1a..bc7fdbcec5 100644 --- a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ 681DFEA828E747B80000C36A /* AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncTesting.swift; sourceTree = ""; }; 681DFEA928E747B80000C36A /* AsyncExpectation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncExpectation.swift; sourceTree = ""; }; 681DFEAA28E747B80000C36A /* XCTestCase+AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+AsyncTesting.swift"; sourceTree = ""; }; + 907EA7602A4B6F56005E3AA8 /* AuthHostApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AuthHostApp.entitlements; sourceTree = ""; }; 970333EF295D793B0019981E /* AuthStressBaseTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStressBaseTest.swift; sourceTree = ""; }; 9737C74D287E208400DA0D2B /* AuthRememberDeviceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRememberDeviceTests.swift; sourceTree = ""; }; 9737C74F2880BFD600DA0D2B /* AuthForgetDeviceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthForgetDeviceTests.swift; sourceTree = ""; }; @@ -338,6 +339,7 @@ 485CB53C27B614CE006CCEC7 /* AuthHostApp */ = { isa = PBXGroup; children = ( + 907EA7602A4B6F56005E3AA8 /* AuthHostApp.entitlements */, 681DFEA728E747B80000C36A /* AsyncTesting */, 485CB53D27B614CE006CCEC7 /* AuthHostAppApp.swift */, 485CB53F27B614CE006CCEC7 /* ContentView.swift */, @@ -1147,6 +1149,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AuthHostApp/AuthHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AuthHostApp/Preview Content\""; @@ -1166,12 +1169,13 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.auth.AuthHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -1180,6 +1184,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = AuthHostApp/AuthHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AuthHostApp/Preview Content\""; @@ -1199,12 +1204,13 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.auth.AuthHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; @@ -1219,12 +1225,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.auth.AuthIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator watchos watchsimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AuthHostApp.app/AuthHostApp"; "TEST_HOST[sdk=watchsimulator*]" = "$(BUILT_PRODUCTS_DIR)/AuthWatchApp.app/AuthWatchApp"; }; @@ -1241,12 +1248,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.auth.AuthIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator watchos watchsimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AuthHostApp.app/AuthHostApp"; "TEST_HOST[sdk=watchsimulator*]" = "$(BUILT_PRODUCTS_DIR)/AuthWatchApp.app/AuthWatchApp"; }; @@ -1379,12 +1387,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.auth.AuthIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AuthHostApp.app/AuthHostApp"; }; name = Debug; @@ -1400,12 +1409,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.auth.AuthIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AuthHostApp.app/AuthHostApp"; }; name = Release; diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/xcshareddata/xcschemes/AuthIntegrationTests.xcscheme b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/xcshareddata/xcschemes/AuthIntegrationTests.xcscheme index f6a560a757..868634e9a8 100644 --- a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/xcshareddata/xcschemes/AuthIntegrationTests.xcscheme +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp.xcodeproj/xcshareddata/xcschemes/AuthIntegrationTests.xcscheme @@ -10,17 +10,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> - - - - + shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp/AuthHostApp.entitlements b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp/AuthHostApp.entitlements new file mode 100644 index 0000000000..63b56fc678 --- /dev/null +++ b/AmplifyPlugins/Auth/Tests/AuthHostApp/AuthHostApp/AuthHostApp.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.aws.amplify.auth.AuthHostApp + + + diff --git a/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift b/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift index 5734a79491..b82d0c434b 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/Keychain/KeychainStore.swift @@ -218,9 +218,9 @@ public struct KeychainStore: KeychainStoreBehavior { public func _removeAll() throws { log.verbose("[KeychainStore] Starting to remove all items from keychain") var query = attributes.defaultGetQuery() -#if !os(iOS) && !os(watchOS) && !os(tvOS) + #if os(macOS) query[Constants.MatchLimit] = Constants.MatchLimitAll -#endif + #endif let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/MutationIngesterConflictResolutionTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/MutationIngesterConflictResolutionTests.swift index 780d80425b..6f8981eb00 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/MutationIngesterConflictResolutionTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/MutationIngesterConflictResolutionTests.swift @@ -29,7 +29,7 @@ class MutationIngesterConflictResolutionTests: SyncEngineTestBase { /// - Then: /// - I receive an error /// - The mutation queue retains the original event - func test_create_create() async { + func test_create_create() async throws { let post = Post(id: "post-1", title: "title", content: "content", @@ -203,13 +203,13 @@ class MutationIngesterConflictResolutionTests: SyncEngineTestBase { XCTAssertEqual(mutationEvents.count, 1) XCTAssertEqual(mutationEvents.first?.mutationType, GraphQLMutationType.update.rawValue) + let firstEventJSON = mutationEvents[0].json let firstEventData = Data(firstEventJSON.utf8) guard let mutationEventPost = try? JSONDecoder().decode( Post.self, from: firstEventData - ) else { - return XCTFail("expected Post") - } + ) else { return XCTFail("expected Post") } + XCTAssertEqual(mutationEventPost.id, post.id) XCTAssertEqual(mutationEventPost.title, post.title) XCTAssertEqual(mutationEventPost.content, post.content) diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueNetworkTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueNetworkTests.swift index 63f1acd748..cafe0f5569 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueNetworkTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueNetworkTests.swift @@ -118,13 +118,9 @@ class OutgoingMutationQueueNetworkTests: SyncEngineTestBase { try await startAmplifyAndWaitForSync() // Save initial model - let createdNewItem = expectation(description: "createdNewItem") let postCopy = post - Task { - _ = try await Amplify.DataStore.save(postCopy) - createdNewItem.fulfill() - } - await fulfillment(of: [createdNewItem]) + _ = try await Amplify.DataStore.save(postCopy) + await fulfillment(of: [apiRespondedWithSuccess], timeout: 1.0, enforceOrder: false) // Set the responder to reject the mutation. Make sure to push a retry advice before sending @@ -146,13 +142,8 @@ class OutgoingMutationQueueNetworkTests: SyncEngineTestBase { // will be scheduled and probably in "waiting" mode when we send the network unavailable // notification below. post.content = "Update 1" - let savedUpdate1 = expectation(description: "savedUpdate1") let postCopy1 = post - Task { - _ = try await Amplify.DataStore.save(postCopy1) - savedUpdate1.fulfill() - } - await fulfillment(of: [savedUpdate1]) + _ = try await Amplify.DataStore.save(postCopy1) // At this point, the MutationEvent table (the backing store for the outgoing mutation // queue) has only a record for the interim update. It is marked as `inProcess: true`, @@ -193,13 +184,8 @@ class OutgoingMutationQueueNetworkTests: SyncEngineTestBase { // also expect that it will be overwritten by the next mutation, without ever being synced // to the service. post.content = "Update 2" - let savedUpdate2 = expectation(description: "savedUpdate2") let postCopy2 = post - Task { - _ = try await Amplify.DataStore.save(postCopy2) - savedUpdate2.fulfill() - } - await fulfillment(of: [savedUpdate2]) + _ = try await Amplify.DataStore.save(postCopy2) // At this point, the MutationEvent table has only a record for update2. It is marked as // `inProcess: false`, because the mutation queue has been fully cancelled. @@ -210,13 +196,8 @@ class OutgoingMutationQueueNetworkTests: SyncEngineTestBase { // even if there were multiple not-in-process mutations, after the reconciliation completes // there would only be one record in the MutationEvent table. post.content = expectedFinalContent - let savedFinalUpdate = expectation(description: "savedFinalUpdate") let postCopy3 = post - Task { - _ = try await Amplify.DataStore.save(postCopy3) - savedFinalUpdate.fulfill() - } - await fulfillment(of: [savedFinalUpdate]) + _ = try await Amplify.DataStore.save(postCopy3) let syncStarted = expectation(description: "syncStarted") setUpSyncStartedListener( @@ -250,8 +231,10 @@ class OutgoingMutationQueueNetworkTests: SyncEngineTestBase { apiPlugin.responders = [.mutateRequestResponse: acceptSubsequentMutations] reachabilitySubject.send(ReachabilityUpdate(isOnline: true)) - - await fulfillment(of: [networkAvailableAgain, syncStarted, expectedFinalContentReceived, outboxEmpty], timeout: 5.0) + await fulfillment( + of: [syncStarted, outboxEmpty, expectedFinalContentReceived, networkAvailableAgain], + timeout: 5 + ) } // MARK: - Utilities diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/RemoteSyncEngineTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/RemoteSyncEngineTests.swift index fd2686b5ab..b12a1b9dca 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/RemoteSyncEngineTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/RemoteSyncEngineTests.swift @@ -142,7 +142,7 @@ class RemoteSyncEngineTests: XCTestCase { failureOnInitialSync, retryAdviceReceivedNetworkError ], - timeout: defaultAsyncWaitTimeout + timeout: 5 ) remoteSyncEngineSink.cancel() Amplify.Hub.removeListener(hubListener) diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj index 034c36dfdf..43e5d242b3 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj @@ -2146,6 +2146,7 @@ 68826E812A43A1DB005E85A7 /* AWSDataStorePluginAuthCognitoTestsWatch.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AWSDataStorePluginAuthCognitoTestsWatch.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68E9EBFA2A448C23001F713D /* AWSDataStorePluginIntegrationTestsWatch.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AWSDataStorePluginIntegrationTestsWatch.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68E9EC452A448C47001F713D /* AWSDataStorePluginMultiAuthTestsWatch.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AWSDataStorePluginMultiAuthTestsWatch.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 906DECDA2A4B740E00D0BEF9 /* DataStoreHostApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DataStoreHostApp.entitlements; sourceTree = ""; }; 970333C4295C5B160019981E /* DataStoreStressBaseTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStoreStressBaseTest.swift; sourceTree = ""; }; 970333DF295CCE490019981E /* AmplifyModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplifyModels.swift; sourceTree = ""; }; 970333E0295CCE490019981E /* PostStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostStatus.swift; sourceTree = ""; }; @@ -2426,6 +2427,7 @@ 2118212E289BFB4B001B5945 /* DataStoreHostApp */ = { isa = PBXGroup; children = ( + 906DECDA2A4B740E00D0BEF9 /* DataStoreHostApp.entitlements */, 681DFE7F28E746C10000C36A /* AsyncTesting */, 21BBF9EE289BFE3400B32A39 /* DataStoreHubEvent.swift */, 21BBFE13289C073100B32A39 /* HubListenerTestUtilities.swift */, @@ -6571,6 +6573,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = DataStoreHostApp/DataStoreHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DataStoreHostApp/Preview Content\""; @@ -6589,12 +6592,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.DataStoreHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -6603,6 +6607,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = DataStoreHostApp/DataStoreHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DataStoreHostApp/Preview Content\""; @@ -6621,12 +6626,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.DataStoreHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; @@ -6641,12 +6647,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginAuthCognitoTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6662,12 +6669,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginAuthCognitoTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6684,12 +6692,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginAuthIAMTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6706,12 +6715,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginAuthIAMTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6728,12 +6738,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginMultiAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6750,12 +6761,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginMultiAuthTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6772,12 +6784,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginLazyLoadTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6794,12 +6807,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginLazyLoadTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6815,12 +6829,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6836,12 +6851,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6857,12 +6873,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginV2Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6878,12 +6895,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginV2Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6899,12 +6917,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginCPKTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6920,12 +6939,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginCPKTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -6941,12 +6961,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginFlutterTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -6962,12 +6983,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginFlutterTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; @@ -7343,12 +7365,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Debug; @@ -7364,12 +7387,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSDataStorePluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DataStoreHostApp.app/DataStoreHostApp"; }; name = Release; diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/xcshareddata/xcschemes/AWSDataStorePluginAuthCognitoTests.xcscheme b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/xcshareddata/xcschemes/AWSDataStorePluginAuthCognitoTests.xcscheme index f277c17348..bb3afaec33 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/xcshareddata/xcschemes/AWSDataStorePluginAuthCognitoTests.xcscheme +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/xcshareddata/xcschemes/AWSDataStorePluginAuthCognitoTests.xcscheme @@ -57,6 +57,15 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> + + + + diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp/DataStoreHostApp.entitlements b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp/DataStoreHostApp.entitlements new file mode 100644 index 0000000000..df4a97cf89 --- /dev/null +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp/DataStoreHostApp.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.aws.amplify.DataStoreHostApp + + + diff --git a/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp.xcodeproj/project.pbxproj index 7bd83879ae..4aad5734eb 100644 --- a/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 21F762DE2BD6B3CE0048845A /* AWSLocationGeoPluginGen2IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AWSLocationGeoPluginGen2IntegrationTests.xctestplan; sourceTree = ""; }; 685777C32A3CC08B001CE5C1 /* GeoWatchApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GeoWatchApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 685778072A3CC0D8001CE5C1 /* AWSLocationGeoPluginIntegrationTestsWatch.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AWSLocationGeoPluginIntegrationTestsWatch.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 906DECDB2A4B757A00D0BEF9 /* GeoHostApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GeoHostApp.entitlements; sourceTree = ""; }; 978B1D5E29515DEF0079E55A /* AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncTesting.swift; sourceTree = ""; }; 978B1D5F29515DEF0079E55A /* AsyncExpectation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncExpectation.swift; sourceTree = ""; }; 978B1D6029515DEF0079E55A /* XCTestCase+AsyncTesting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+AsyncTesting.swift"; sourceTree = ""; }; @@ -199,6 +200,7 @@ 97AD223028230B98001AFCC1 /* GeoHostApp */ = { isa = PBXGroup; children = ( + 906DECDB2A4B757A00D0BEF9 /* GeoHostApp.entitlements */, 978B1D5D29515DEF0079E55A /* AsyncTesting */, 97AD223128230B98001AFCC1 /* GeoHostAppApp.swift */, 97AD223328230B98001AFCC1 /* ContentView.swift */, @@ -819,13 +821,14 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.amazon.com.AWSLocationGeoPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GeoHostApp.app/GeoHostApp"; }; name = Debug; @@ -846,12 +849,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.amazon.com.GeoStressTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GeoHostApp.app/GeoHostApp"; }; name = Release; @@ -977,6 +981,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = GeoHostApp/GeoHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"GeoHostApp/Preview Content\""; @@ -994,12 +999,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.geo.GeoHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -1008,6 +1014,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = GeoHostApp/GeoHostApp.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"GeoHostApp/Preview Content\""; @@ -1025,12 +1032,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.geo.GeoHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; @@ -1050,13 +1058,14 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.amazon.com.AWSLocationGeoPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GeoHostApp.app/GeoHostApp"; }; name = Debug; @@ -1077,12 +1086,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.amazon.com.AWSLocationGeoPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GeoHostApp.app/GeoHostApp"; }; name = Release; diff --git a/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp/GeoHostApp.entitlements b/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp/GeoHostApp.entitlements new file mode 100644 index 0000000000..5f952b9866 --- /dev/null +++ b/AmplifyPlugins/Geo/Tests/GeoHostApp/GeoHostApp/GeoHostApp.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.aws.amplify.geo.GeoHostApp + + + diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/project.pbxproj index 7de7bad8f7..b56abebded 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/project.pbxproj @@ -626,12 +626,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.notification.PushNotificationHostAppUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_TARGET_NAME = PushNotificationHostApp; }; name = Debug; @@ -647,12 +648,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.notification.PushNotificationHostAppUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_TARGET_NAME = PushNotificationHostApp; }; name = Release; @@ -797,12 +799,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.notification.PushNotificationHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -832,12 +835,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.notification.PushNotificationHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp/PushNotificationHostApp.entitlements b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp/PushNotificationHostApp.entitlements index f11866d492..6f04d32780 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp/PushNotificationHostApp.entitlements +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp/PushNotificationHostApp.entitlements @@ -5,6 +5,8 @@ aps-environment development keychain-access-groups - + + $(AppIdentifierPrefix)com.aws.amplify.notification.PushNotificationHostApp + diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostAppUITests/PushNotificationHostAppUITests.swift b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostAppUITests/PushNotificationHostAppUITests.swift index 21729ac2e1..512a27f0e0 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostAppUITests/PushNotificationHostAppUITests.swift +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostAppUITests/PushNotificationHostAppUITests.swift @@ -109,7 +109,7 @@ final class PushNotificationHostAppUITests: XCTestCase { } } -#if !os(tvOS) +#if !os(tvOS) && !os(visionOS) @MainActor func testAppInBackground_withPinpointRemoteNotification_recordNotificationOpened() async throws { initAmplify() diff --git a/AmplifyPlugins/Predictions/Tests/AWSPredictionsPluginUnitTests/EventStreamCodingTests/EventStreamCoderTestCase.swift b/AmplifyPlugins/Predictions/Tests/AWSPredictionsPluginUnitTests/EventStreamCodingTests/EventStreamCoderTestCase.swift index 2da4bd9784..0611f447de 100644 --- a/AmplifyPlugins/Predictions/Tests/AWSPredictionsPluginUnitTests/EventStreamCodingTests/EventStreamCoderTestCase.swift +++ b/AmplifyPlugins/Predictions/Tests/AWSPredictionsPluginUnitTests/EventStreamCodingTests/EventStreamCoderTestCase.swift @@ -29,7 +29,8 @@ final class EventStreamCoderTestCase: XCTestCase { "string_ex": .string("abc") ] - let data = try JSONEncoder().encode(model) + let encoder = JSONEncoder() + let data = try encoder.encode(model) let encoded = EventStream.Encoder().encode(payload: data, headers: headers) let encodedBytes = [UInt8](encoded) diff --git a/AmplifyPlugins/Predictions/Tests/CoreMLPredictionsPluginUnitTests/DependencyTests/CoreMLNaturalLanguageAdapterTests.swift b/AmplifyPlugins/Predictions/Tests/CoreMLPredictionsPluginUnitTests/DependencyTests/CoreMLNaturalLanguageAdapterTests.swift index 0adea26595..2daec2973a 100644 --- a/AmplifyPlugins/Predictions/Tests/CoreMLPredictionsPluginUnitTests/DependencyTests/CoreMLNaturalLanguageAdapterTests.swift +++ b/AmplifyPlugins/Predictions/Tests/CoreMLPredictionsPluginUnitTests/DependencyTests/CoreMLNaturalLanguageAdapterTests.swift @@ -53,6 +53,11 @@ class CoreMLNaturalLanguageAdapterTests: XCTestCase { /// - I should get back correct tokens /// func testSyntaxToken() { + #if !os(visionOS) + // TODO: Test failing on visionOS. + // XCTAssertEqual failed: ("PartOfSpeech(description: "other")") + // is not equal to ("PartOfSpeech(description: "determiner")") - + // First word in the input should be determiner let text = "The ripe taste of cheese improves with age." let result = coreMLNaturalLanguageAdapter.getSyntaxTokens(for: text) XCTAssertNotNil(result, "Result should not be nil") @@ -63,6 +68,7 @@ class CoreMLNaturalLanguageAdapterTests: XCTestCase { .determiner, "First word in the input should be determiner" ) + #endif } /// Test syntax token with invalid text @@ -90,10 +96,12 @@ class CoreMLNaturalLanguageAdapterTests: XCTestCase { /// #if !os(watchOS) func testEntityToken() { + #if !os(visionOS) // TODO: Test failing on visionOS. let text = "The American Red Cross was established in Washington, D.C., by Clara Barton." let result = coreMLNaturalLanguageAdapter.getEntities(for: text) XCTAssertNotNil(result, "Result should not be nil") XCTAssertFalse(result.isEmpty, "Should return some value back") + #endif } #endif diff --git a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp.xcodeproj/project.pbxproj index b8f9ed9229..1d3732df98 100644 --- a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp.xcodeproj/project.pbxproj @@ -53,6 +53,18 @@ 90542370291425630000D108 /* testImageCeleb.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 90542363291425630000D108 /* testImageCeleb.jpg */; }; 90542371291425630000D108 /* testImageTextForms.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 90542364291425630000D108 /* testImageTextForms.jpg */; }; 90542372291425630000D108 /* audio.wav in Resources */ = {isa = PBXBuildFile; fileRef = 90542365291425630000D108 /* audio.wav */; }; + 90CF304A2AD47A71006B6FF3 /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF30492AD47A71006B6FF3 /* Amplify */; }; + 90CF304C2AD47A74006B6FF3 /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF304B2AD47A74006B6FF3 /* Amplify */; }; + 90CF304E2AD47A78006B6FF3 /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF304D2AD47A78006B6FF3 /* Amplify */; }; + 90CF30502AD47B0E006B6FF3 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF304F2AD47B0E006B6FF3 /* AWSCognitoAuthPlugin */; }; + 90CF30522AD47B0E006B6FF3 /* AWSPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF30512AD47B0E006B6FF3 /* AWSPredictionsPlugin */; }; + 90CF30542AD47B0E006B6FF3 /* CoreMLPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF30532AD47B0E006B6FF3 /* CoreMLPredictionsPlugin */; }; + 90CF30562AD47B19006B6FF3 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF30552AD47B19006B6FF3 /* AWSCognitoAuthPlugin */; }; + 90CF30582AD47B19006B6FF3 /* AWSPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF30572AD47B19006B6FF3 /* AWSPredictionsPlugin */; }; + 90CF305A2AD47B19006B6FF3 /* CoreMLPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF30592AD47B19006B6FF3 /* CoreMLPredictionsPlugin */; }; + 90CF305C2AD47B24006B6FF3 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF305B2AD47B24006B6FF3 /* AWSCognitoAuthPlugin */; }; + 90CF305E2AD47B24006B6FF3 /* AWSPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF305D2AD47B24006B6FF3 /* AWSPredictionsPlugin */; }; + 90CF30602AD47B24006B6FF3 /* CoreMLPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 90CF305F2AD47B24006B6FF3 /* CoreMLPredictionsPlugin */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -127,6 +139,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 90CF30602AD47B24006B6FF3 /* CoreMLPredictionsPlugin in Frameworks */, + 90CF304E2AD47A78006B6FF3 /* Amplify in Frameworks */, + 90CF305E2AD47B24006B6FF3 /* AWSPredictionsPlugin in Frameworks */, + 90CF305C2AD47B24006B6FF3 /* AWSCognitoAuthPlugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -145,6 +161,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 90CF30542AD47B0E006B6FF3 /* CoreMLPredictionsPlugin in Frameworks */, + 90CF304A2AD47A71006B6FF3 /* Amplify in Frameworks */, + 90CF30522AD47B0E006B6FF3 /* AWSPredictionsPlugin in Frameworks */, + 90CF30502AD47B0E006B6FF3 /* AWSCognitoAuthPlugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -152,6 +172,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 90CF305A2AD47B19006B6FF3 /* CoreMLPredictionsPlugin in Frameworks */, + 90CF304C2AD47A74006B6FF3 /* Amplify in Frameworks */, + 90CF30582AD47B19006B6FF3 /* AWSPredictionsPlugin in Frameworks */, + 90CF30562AD47B19006B6FF3 /* AWSCognitoAuthPlugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -315,6 +339,10 @@ ); name = AWSPredictionsPluginIntegrationTestsWatch; packageProductDependencies = ( + 90CF304D2AD47A78006B6FF3 /* Amplify */, + 90CF305B2AD47B24006B6FF3 /* AWSCognitoAuthPlugin */, + 90CF305D2AD47B24006B6FF3 /* AWSPredictionsPlugin */, + 90CF305F2AD47B24006B6FF3 /* CoreMLPredictionsPlugin */, ); productName = AWSPredictionsPluginIntegrationTests; productReference = 6875F9242A3CCCB7001C9AAF /* AWSPredictionsPluginIntegrationTestsWatch.xctest */; @@ -359,6 +387,10 @@ ); name = CoreMLPredictionsPluginIntegrationTests; packageProductDependencies = ( + 90CF30492AD47A71006B6FF3 /* Amplify */, + 90CF304F2AD47B0E006B6FF3 /* AWSCognitoAuthPlugin */, + 90CF30512AD47B0E006B6FF3 /* AWSPredictionsPlugin */, + 90CF30532AD47B0E006B6FF3 /* CoreMLPredictionsPlugin */, ); productName = CoreMLPredictionsPluginIntegrationTests; productReference = 90283033291402D500897087 /* CoreMLPredictionsPluginIntegrationTests.xctest */; @@ -379,6 +411,10 @@ ); name = AWSPredictionsPluginIntegrationTests; packageProductDependencies = ( + 90CF304B2AD47A74006B6FF3 /* Amplify */, + 90CF30552AD47B19006B6FF3 /* AWSCognitoAuthPlugin */, + 90CF30572AD47B19006B6FF3 /* AWSPredictionsPlugin */, + 90CF30592AD47B19006B6FF3 /* CoreMLPredictionsPlugin */, ); productName = AWSPredictionsPluginIntegrationTests; productReference = 903555F829141355004B83C2 /* AWSPredictionsPluginIntegrationTests.xctest */; @@ -852,7 +888,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"PredictionsHostApp/Preview Content\""; - DEVELOPMENT_TEAM = G3Q4R66M4R; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -869,12 +905,13 @@ OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.coffee.PredictionsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Debug; }; @@ -887,7 +924,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"PredictionsHostApp/Preview Content\""; - DEVELOPMENT_TEAM = G3Q4R66M4R; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -904,12 +941,13 @@ OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.coffee.PredictionsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; }; name = Release; }; @@ -932,14 +970,15 @@ OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.coffee.CoreMLPredictionsPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PredictionsHostApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PredictionsHostApp"; }; name = Debug; @@ -963,13 +1002,14 @@ OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = io.coffee.CoreMLPredictionsPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PredictionsHostApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PredictionsHostApp"; }; name = Release; @@ -993,14 +1033,15 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.coffee.AWSPredictionsPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PredictionsHostApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PredictionsHostApp"; }; name = Debug; @@ -1024,13 +1065,14 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.coffee.AWSPredictionsPluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3"; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PredictionsHostApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PredictionsHostApp"; }; name = Release; @@ -1127,6 +1169,54 @@ isa = XCSwiftPackageProductDependency; productName = CoreMLPredictionsPlugin; }; + 90CF30492AD47A71006B6FF3 /* Amplify */ = { + isa = XCSwiftPackageProductDependency; + productName = Amplify; + }; + 90CF304B2AD47A74006B6FF3 /* Amplify */ = { + isa = XCSwiftPackageProductDependency; + productName = Amplify; + }; + 90CF304D2AD47A78006B6FF3 /* Amplify */ = { + isa = XCSwiftPackageProductDependency; + productName = Amplify; + }; + 90CF304F2AD47B0E006B6FF3 /* AWSCognitoAuthPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSCognitoAuthPlugin; + }; + 90CF30512AD47B0E006B6FF3 /* AWSPredictionsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSPredictionsPlugin; + }; + 90CF30532AD47B0E006B6FF3 /* CoreMLPredictionsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = CoreMLPredictionsPlugin; + }; + 90CF30552AD47B19006B6FF3 /* AWSCognitoAuthPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSCognitoAuthPlugin; + }; + 90CF30572AD47B19006B6FF3 /* AWSPredictionsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSPredictionsPlugin; + }; + 90CF30592AD47B19006B6FF3 /* CoreMLPredictionsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = CoreMLPredictionsPlugin; + }; + 90CF305B2AD47B24006B6FF3 /* AWSCognitoAuthPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSCognitoAuthPlugin; + }; + 90CF305D2AD47B24006B6FF3 /* AWSPredictionsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = AWSPredictionsPlugin; + }; + 90CF305F2AD47B24006B6FF3 /* CoreMLPredictionsPlugin */ = { + isa = XCSwiftPackageProductDependency; + productName = CoreMLPredictionsPlugin; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 90282FFA2914027000897087 /* Project object */; diff --git a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/ContentView.swift b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/ContentView.swift index f5b0b64765..0e55546e60 100644 --- a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/ContentView.swift +++ b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/ContentView.swift @@ -1,8 +1,8 @@ // -// ContentView.swift -// PredictionsHostApp +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. // -// Created by Saultz, Ian on 11/3/22. +// SPDX-License-Identifier: Apache-2.0 // import SwiftUI diff --git a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostApp.entitlements b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostApp.entitlements index fbad02378c..74c9a149f4 100644 --- a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostApp.entitlements +++ b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostApp.entitlements @@ -3,6 +3,8 @@ keychain-access-groups - + + $(AppIdentifierPrefix)io.coffee.PredictionsHostApp + diff --git a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostAppApp.swift b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostAppApp.swift index 9c22a1903d..516d99c273 100644 --- a/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostAppApp.swift +++ b/AmplifyPlugins/Predictions/Tests/PredictionsHostApp/PredictionsHostApp/PredictionsHostAppApp.swift @@ -1,8 +1,8 @@ // -// PredictionsHostAppApp.swift -// PredictionsHostApp +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. // -// Created by Saultz, Ian on 11/3/22. +// SPDX-License-Identifier: Apache-2.0 // import SwiftUI diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift index 771a80876f..8f00d8d43c 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift @@ -88,7 +88,7 @@ class AWSS3StorageService: AWSS3StorageServiceBehavior, StorageServiceProxy { if let sessionConfiguration = sessionConfiguration { sessionConfig = sessionConfiguration } else { - #if os(macOS) + #if os(macOS) || os(visionOS) let sessionConfiguration = URLSessionConfiguration.default #else let sessionConfiguration = URLSessionConfiguration.background(withIdentifier: storageConfiguration.sessionIdentifier) diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj index a15257b61c..7476d10e39 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp.xcodeproj/project.pbxproj @@ -791,6 +791,10 @@ }; 97914B9F2955798D002000EA /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilters = ( + ios, + xros, + ); target = 684FB06928BEAF1500C8A6EB /* StorageHostApp */; targetProxy = 97914BA02955798D002000EA /* PBXContainerItemProxy */; }; @@ -1096,11 +1100,12 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.StorageHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; - TARGETED_DEVICE_FAMILY = "1,3"; + TARGETED_DEVICE_FAMILY = "1,3,7"; }; name = Debug; }; @@ -1130,11 +1135,12 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.StorageHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; - TARGETED_DEVICE_FAMILY = "1,3"; + TARGETED_DEVICE_FAMILY = "1,3,7"; }; name = Release; }; @@ -1149,11 +1155,12 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSS3StoragePluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; - TARGETED_DEVICE_FAMILY = "1,3"; + TARGETED_DEVICE_FAMILY = "1,3,7"; }; name = Debug; }; @@ -1168,11 +1175,12 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.AWSS3StoragePluginIntegrationTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; - TARGETED_DEVICE_FAMILY = "1,3"; + TARGETED_DEVICE_FAMILY = "1,3,7"; }; name = Release; }; @@ -1187,8 +1195,12 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.StorageStressTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; - TARGETED_DEVICE_FAMILY = "1,3"; + TARGETED_DEVICE_FAMILY = "1,3,7"; }; name = Debug; }; @@ -1203,8 +1215,12 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.aws.amplify.StorageStressTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = NO; - TARGETED_DEVICE_FAMILY = "1,3"; + TARGETED_DEVICE_FAMILY = "1,3,7"; }; name = Release; }; diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp/StorageHostApp.entitlements b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp/StorageHostApp.entitlements index fbad02378c..6fccd01bbc 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp/StorageHostApp.entitlements +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/StorageHostApp/StorageHostApp.entitlements @@ -3,6 +3,8 @@ keychain-access-groups - + + $(AppIdentifierPrefix)com.aws.amplify.StorageHostApp + diff --git a/AmplifyTestCommon/Mocks/MockAuthCategoryPlugin.swift b/AmplifyTestCommon/Mocks/MockAuthCategoryPlugin.swift index 94e44aa795..1a1f0ab6e8 100644 --- a/AmplifyTestCommon/Mocks/MockAuthCategoryPlugin.swift +++ b/AmplifyTestCommon/Mocks/MockAuthCategoryPlugin.swift @@ -40,7 +40,7 @@ class MockAuthCategoryPlugin: MessageReporter, AuthCategoryPlugin { fatalError() } -#if os(iOS) || os(macOS) +#if os(iOS) || os(macOS) || os(visionOS) public func signInWithWebUI(presentationAnchor: AuthUIPresentationAnchor? = nil, options: AuthWebUISignInRequest.Options? = nil) async throws -> AuthSignInResult { fatalError() diff --git a/AmplifyTestCommon/Mocks/MockDevMenuContextProvider.swift b/AmplifyTestCommon/Mocks/MockDevMenuContextProvider.swift index 4181cc4f4b..02f4f3581e 100644 --- a/AmplifyTestCommon/Mocks/MockDevMenuContextProvider.swift +++ b/AmplifyTestCommon/Mocks/MockDevMenuContextProvider.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) || os(visionOS) import Amplify import UIKit diff --git a/AmplifyTests/DevMenuTests/DevMenuExtensionTests.swift b/AmplifyTests/DevMenuTests/DevMenuExtensionTests.swift index 146fe9aaa3..be704f3715 100644 --- a/AmplifyTests/DevMenuTests/DevMenuExtensionTests.swift +++ b/AmplifyTests/DevMenuTests/DevMenuExtensionTests.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import XCTest @testable import Amplify @testable import AmplifyTestCommon diff --git a/AmplifyTests/DevMenuTests/GestureRecognizerTests.swift b/AmplifyTests/DevMenuTests/GestureRecognizerTests.swift index 5c82b10d6f..8b1368352f 100644 --- a/AmplifyTests/DevMenuTests/GestureRecognizerTests.swift +++ b/AmplifyTests/DevMenuTests/GestureRecognizerTests.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import XCTest @testable import Amplify @testable import AmplifyTestCommon diff --git a/AmplifyTests/DevMenuTests/PersistentLogWrapperTests.swift b/AmplifyTests/DevMenuTests/PersistentLogWrapperTests.swift index 4f66a563c3..f224b43ead 100644 --- a/AmplifyTests/DevMenuTests/PersistentLogWrapperTests.swift +++ b/AmplifyTests/DevMenuTests/PersistentLogWrapperTests.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import XCTest @testable import Amplify @testable import AmplifyTestCommon diff --git a/AmplifyTests/DevMenuTests/PersistentLoggingPluginAmplifyVersionableTests.swift b/AmplifyTests/DevMenuTests/PersistentLoggingPluginAmplifyVersionableTests.swift index 22ebfc4d0d..d35e54346e 100644 --- a/AmplifyTests/DevMenuTests/PersistentLoggingPluginAmplifyVersionableTests.swift +++ b/AmplifyTests/DevMenuTests/PersistentLoggingPluginAmplifyVersionableTests.swift @@ -8,7 +8,7 @@ import XCTest @testable import Amplify -#if os(iOS) +#if os(iOS) && !os(visionOS) // swiftlint:disable:next type_name class PersistentLoggingPluginAmplifyVersionableTests: XCTestCase { diff --git a/AmplifyTests/DevMenuTests/PersistentLoggingPluginTests.swift b/AmplifyTests/DevMenuTests/PersistentLoggingPluginTests.swift index e873ed6f9a..59cd69065d 100644 --- a/AmplifyTests/DevMenuTests/PersistentLoggingPluginTests.swift +++ b/AmplifyTests/DevMenuTests/PersistentLoggingPluginTests.swift @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#if os(iOS) +#if os(iOS) && !os(visionOS) import XCTest @testable import Amplify @testable import AmplifyTestCommon diff --git a/README.md b/README.md index 994d2d924e..7e7cb23040 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,6 @@ The Amplify Library for Swift is layered on the [AWS SDK for Swift](https://aws. | watchOS | 9+ | GA | | visionOS | 1+ | Preview* | -> To use Amplify Swift with visionOS, you'll need to target the `visionos-preview` branch. -> For more information, see [Platform Support](https://github.com/aws-amplify/amplify-swift/tree/visionos-preview#platform-support) on the `visionos-preview` branch. - ### Swift Version Support The current minimum supported Swift language version is **Swift 5.9**. From 6bbf569fcfe6836098efc4524e218957c9a3b729 Mon Sep 17 00:00:00 2001 From: Harsh <6162866+harsh62@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:57:15 -0400 Subject: [PATCH 48/50] chore: update logging integration test (#3903) --- .github/workflows/integ_test_logging.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/integ_test_logging.yml b/.github/workflows/integ_test_logging.yml index f2b4987433..030f030710 100644 --- a/.github/workflows/integ_test_logging.yml +++ b/.github/workflows/integ_test_logging.yml @@ -40,7 +40,5 @@ jobs: platform: ${{ matrix.platform }} project_path: ./AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginHostApp resource_subfolder: logging - xcode_version: ${{ matrix.platform == 'watchOS' && '15.0' || 'latest' }} - destination: ${{ matrix.platform == 'watchOS' && 'platform=watchOS Simulator,name=Apple Watch Series 8 (45mm),OS=10.2' || '' }} timeout-minutes: 60 secrets: inherit From 227966cfd4429d2929bfc81972b3cf3f4b33eeed Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 22 Oct 2024 14:16:17 +0000 Subject: [PATCH 49/50] chore: release 2.43.0 [skip ci] --- .../AmplifyCredentials/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift index 1b2d6ec93e..bb992cba7d 100644 --- a/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AmplifyCredentials/AmplifyAWSServiceConfiguration.swift @@ -15,7 +15,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.42.2" + public static let amplifyVersion = "2.43.0" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 6ea7a04b2824e716712381703407c7e00a48a96b Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 22 Oct 2024 14:18:06 +0000 Subject: [PATCH 50/50] chore: finalize release 2.43.0 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 432ac7eeaa..6545d9a85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.43.0 (2024-10-22) + +### Features + +- adding vision os preview (#3898) + ## 2.42.2 (2024-10-18) ### Bug Fixes