From 068bd3c5fc06c7ffe9363fa3d2b56f86f7df7bd1 Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:45:25 -0400 Subject: [PATCH 1/2] fix(Authenticator): Allowing to only override the desired errors when invoking the errorMap functions (#93) --- CHANGELOG.md | 5 +++++ Sources/Authenticator/Authenticator.swift | 2 +- .../Constants/ComponentInformation.swift | 2 +- .../States/AuthenticatorBaseState.swift | 6 +++--- .../Views/ConfirmResetPasswordView.swift | 2 +- .../ConfirmSignInWithCustomChallengeView.swift | 2 +- .../Views/ConfirmSignInWithMFACodeView.swift | 2 +- .../Views/ConfirmSignInWithNewPasswordView.swift | 2 +- .../Views/ConfirmSignInWithTOTPCodeView.swift | 2 +- .../Authenticator/Views/ConfirmSignUpView.swift | 2 +- .../Views/ConfirmVerifyUserView.swift | 2 +- .../ContinueSignInWithMFASelectionView.swift | 2 +- .../Views/ContinueSignInWithTOTPSetupView.swift | 2 +- .../Internal/ConfirmSignInWithCodeView.swift | 2 +- .../Authenticator/Views/ResetPasswordView.swift | 2 +- Sources/Authenticator/Views/SignInView.swift | 2 +- Sources/Authenticator/Views/SignUpView.swift | 2 +- Sources/Authenticator/Views/VerifyUserView.swift | 2 +- .../States/AuthenticatorBaseStateTests.swift | 16 ++++++++++++++++ 19 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6cd0a3..e61d6bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.1.7 (2024-09-13) + +### Bug Fixes +- **Authenticator**: Allowing to only override the desired errors when invoking the errorMap functions (#93) + ## 1.1.6 (2024-08-13) ### Bug Fixes diff --git a/Sources/Authenticator/Authenticator.swift b/Sources/Authenticator/Authenticator.swift index b0d1fbb..f1e5e7e 100644 --- a/Sources/Authenticator/Authenticator.swift +++ b/Sources/Authenticator/Authenticator.swift @@ -290,7 +290,7 @@ public struct Authenticator AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { for contentState in contentStates.allObjects { contentState.errorTransform = errorTransform } diff --git a/Sources/Authenticator/Constants/ComponentInformation.swift b/Sources/Authenticator/Constants/ComponentInformation.swift index a999414..e64239e 100644 --- a/Sources/Authenticator/Constants/ComponentInformation.swift +++ b/Sources/Authenticator/Constants/ComponentInformation.swift @@ -8,6 +8,6 @@ import Foundation public class ComponentInformation { - public static let version = "1.1.6" + public static let version = "1.1.7" public static let name = "amplify-ui-swift-authenticator" } diff --git a/Sources/Authenticator/States/AuthenticatorBaseState.swift b/Sources/Authenticator/States/AuthenticatorBaseState.swift index 32f82d1..e109f04 100644 --- a/Sources/Authenticator/States/AuthenticatorBaseState.swift +++ b/Sources/Authenticator/States/AuthenticatorBaseState.swift @@ -23,7 +23,7 @@ public class AuthenticatorBaseState: ObservableObject { @ObservedObject var credentials: Credentials - var errorTransform: ((AuthError) -> AuthenticatorError)? = nil + var errorTransform: ((AuthError) -> AuthenticatorError?)? = nil private(set) var authenticatorState: AuthenticatorStateProtocol = .empty init(credentials: Credentials) { @@ -183,8 +183,8 @@ public class AuthenticatorBaseState: ObservableObject { return .unknown(from: error) } - if let errorTransform = errorTransform { - return errorTransform(authError) + if let customError = errorTransform?(authError) { + return customError } if let localizedMessage = localizedMessage(for: authError) { diff --git a/Sources/Authenticator/Views/ConfirmResetPasswordView.swift b/Sources/Authenticator/Views/ConfirmResetPasswordView.swift index 8d7a909..978aa23 100644 --- a/Sources/Authenticator/Views/ConfirmResetPasswordView.swift +++ b/Sources/Authenticator/Views/ConfirmResetPasswordView.swift @@ -135,7 +135,7 @@ public struct ConfirmResetPasswordView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ConfirmSignInWithCustomChallengeView.swift b/Sources/Authenticator/Views/ConfirmSignInWithCustomChallengeView.swift index a491c29..a56daf9 100644 --- a/Sources/Authenticator/Views/ConfirmSignInWithCustomChallengeView.swift +++ b/Sources/Authenticator/Views/ConfirmSignInWithCustomChallengeView.swift @@ -42,7 +42,7 @@ public struct ConfirmSignInWithCustomChallengeView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ConfirmSignInWithMFACodeView.swift b/Sources/Authenticator/Views/ConfirmSignInWithMFACodeView.swift index 2acb8c4..8a74fb8 100644 --- a/Sources/Authenticator/Views/ConfirmSignInWithMFACodeView.swift +++ b/Sources/Authenticator/Views/ConfirmSignInWithMFACodeView.swift @@ -48,7 +48,7 @@ public struct ConfirmSignInWithMFACodeView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ConfirmSignInWithNewPasswordView.swift b/Sources/Authenticator/Views/ConfirmSignInWithNewPasswordView.swift index 2838aff..aa57ef9 100644 --- a/Sources/Authenticator/Views/ConfirmSignInWithNewPasswordView.swift +++ b/Sources/Authenticator/Views/ConfirmSignInWithNewPasswordView.swift @@ -111,7 +111,7 @@ public struct ConfirmSignInWithNewPasswordView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ConfirmSignInWithTOTPCodeView.swift b/Sources/Authenticator/Views/ConfirmSignInWithTOTPCodeView.swift index 9afc20a..dd7111c 100644 --- a/Sources/Authenticator/Views/ConfirmSignInWithTOTPCodeView.swift +++ b/Sources/Authenticator/Views/ConfirmSignInWithTOTPCodeView.swift @@ -43,7 +43,7 @@ public struct ConfirmSignInWithTOTPView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ConfirmSignUpView.swift b/Sources/Authenticator/Views/ConfirmSignUpView.swift index 1261a64..94e8a2d 100644 --- a/Sources/Authenticator/Views/ConfirmSignUpView.swift +++ b/Sources/Authenticator/Views/ConfirmSignUpView.swift @@ -98,7 +98,7 @@ public struct ConfirmSignUpView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ConfirmVerifyUserView.swift b/Sources/Authenticator/Views/ConfirmVerifyUserView.swift index 2cc3a55..d10de31 100644 --- a/Sources/Authenticator/Views/ConfirmVerifyUserView.swift +++ b/Sources/Authenticator/Views/ConfirmVerifyUserView.swift @@ -82,7 +82,7 @@ public struct ConfirmVerifyUserView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ContinueSignInWithMFASelectionView.swift b/Sources/Authenticator/Views/ContinueSignInWithMFASelectionView.swift index b2cd7f6..fc47880 100644 --- a/Sources/Authenticator/Views/ContinueSignInWithMFASelectionView.swift +++ b/Sources/Authenticator/Views/ContinueSignInWithMFASelectionView.swift @@ -89,7 +89,7 @@ public struct ContinueSignInWithMFASelectionView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/ContinueSignInWithTOTPSetupView.swift b/Sources/Authenticator/Views/ContinueSignInWithTOTPSetupView.swift index 28e76b7..62347e0 100644 --- a/Sources/Authenticator/Views/ContinueSignInWithTOTPSetupView.swift +++ b/Sources/Authenticator/Views/ContinueSignInWithTOTPSetupView.swift @@ -120,7 +120,7 @@ public struct ContinueSignInWithTOTPSetupView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/Internal/ConfirmSignInWithCodeView.swift b/Sources/Authenticator/Views/Internal/ConfirmSignInWithCodeView.swift index 4362cc7..4a40737 100644 --- a/Sources/Authenticator/Views/Internal/ConfirmSignInWithCodeView.swift +++ b/Sources/Authenticator/Views/Internal/ConfirmSignInWithCodeView.swift @@ -25,7 +25,7 @@ struct ConfirmSignInWithCodeView Footer = { EmptyView() }, - errorTransform: ((AuthError) -> AuthenticatorError)? = nil, + errorTransform: ((AuthError) -> AuthenticatorError?)? = nil, mfaType: AuthenticatorMFAType ) { self.state = state diff --git a/Sources/Authenticator/Views/ResetPasswordView.swift b/Sources/Authenticator/Views/ResetPasswordView.swift index 100d18a..f55e207 100644 --- a/Sources/Authenticator/Views/ResetPasswordView.swift +++ b/Sources/Authenticator/Views/ResetPasswordView.swift @@ -113,7 +113,7 @@ public struct ResetPasswordView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/SignInView.swift b/Sources/Authenticator/Views/SignInView.swift index 304fbed..319ebfe 100644 --- a/Sources/Authenticator/Views/SignInView.swift +++ b/Sources/Authenticator/Views/SignInView.swift @@ -127,7 +127,7 @@ public struct SignInView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/SignUpView.swift b/Sources/Authenticator/Views/SignUpView.swift index 201402f..e322fe4 100644 --- a/Sources/Authenticator/Views/SignUpView.swift +++ b/Sources/Authenticator/Views/SignUpView.swift @@ -91,7 +91,7 @@ public struct SignUpView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Sources/Authenticator/Views/VerifyUserView.swift b/Sources/Authenticator/Views/VerifyUserView.swift index 6a21aec..9cecebd 100644 --- a/Sources/Authenticator/Views/VerifyUserView.swift +++ b/Sources/Authenticator/Views/VerifyUserView.swift @@ -78,7 +78,7 @@ public struct VerifyUserView AuthenticatorError) -> Self { + public func errorMap(_ errorTransform: @escaping (AuthError) -> AuthenticatorError?) -> Self { state.errorTransform = errorTransform return self } diff --git a/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift b/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift index bc2a5cc..2e5a841 100644 --- a/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift +++ b/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift @@ -239,6 +239,22 @@ class AuthenticatorBaseStateTests: XCTestCase { XCTAssertEqual(authenticatorError.content, "A custom error") } + func testError_withCustomErrorTransformThatReturnsNil_shouldReturnDefaultError() { + var closureCount = 0 + state.errorTransform = { error in + closureCount = 1 + if case .service = error { + return .error(message: "A service error") + } + return nil + } + let authenticatorError = state.error(for: AuthError.notAuthorized("description", "recovery", nil)) + XCTAssertEqual(closureCount, 1) + XCTAssertEqual(authenticatorError.style, .error) + let expectedMessage = "authenticator.authError.incorrectCredentials".localized() + XCTAssertEqual(authenticatorError.content, expectedMessage) + } + func testError_withLocalizedCognitoError_shouldReturnLocalizedError() { let cognitoError = AWSCognitoAuthError.userNotFound let authenticatorError = state.error(for: AuthError.service("description", "recovery", cognitoError)) From 1ec95fb644b61f5e385d3c83ffd4fd5a32a49b7a Mon Sep 17 00:00:00 2001 From: Sebastian Villena <97059974+ruisebas@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:02:59 -0400 Subject: [PATCH 2/2] fix(Authenticator): Adding new error localizations for limits exceeded (#96) --- CHANGELOG.md | 5 +++++ .../Authenticator/Constants/ComponentInformation.swift | 2 +- .../Resources/en.lproj/Localizable.strings | 2 ++ .../Authenticator/States/AuthenticatorBaseState.swift | 9 +++++++-- .../States/AuthenticatorBaseStateTests.swift | 10 ++++++++-- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e61d6bb..62660ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.1.8 (2024-09-20) + +### Bug Fixes +- **Authenticator**: Adding new error localizations for limits exceeded (#96) + ## 1.1.7 (2024-09-13) ### Bug Fixes diff --git a/Sources/Authenticator/Constants/ComponentInformation.swift b/Sources/Authenticator/Constants/ComponentInformation.swift index e64239e..3968ef6 100644 --- a/Sources/Authenticator/Constants/ComponentInformation.swift +++ b/Sources/Authenticator/Constants/ComponentInformation.swift @@ -8,6 +8,6 @@ import Foundation public class ComponentInformation { - public static let version = "1.1.7" + public static let version = "1.1.8" public static let name = "amplify-ui-swift-authenticator" } diff --git a/Sources/Authenticator/Resources/en.lproj/Localizable.strings b/Sources/Authenticator/Resources/en.lproj/Localizable.strings index 1d91dd9..d16ead3 100644 --- a/Sources/Authenticator/Resources/en.lproj/Localizable.strings +++ b/Sources/Authenticator/Resources/en.lproj/Localizable.strings @@ -164,6 +164,7 @@ /* Authenticator Errors */ "authenticator.authError.incorrectCredentials" = "Incorrect username or password"; +"authenticator.authError.passwordAttemptsExceeded" = "You've reached the password attempts limit. Please try again later"; "authenticator.authError.continueSignInWithMFASelection.noSelectionError" = "Please select an MFA method to continue"; "authenticator.unknownError" = "Sorry, something went wrong"; @@ -174,6 +175,7 @@ "authenticator.cognitoError.network" = "Please check your connectivity"; "authenticator.cognitoError.usernameExists" = "Username already exists"; "authenticator.cognitoError.userNotFound" = "User not found"; +"authenticator.cognitoError.limitExceeded" = "You've reached the request limit. Please try again later"; /* Toolbar displayed on top of the keyboard */ "authenticator.keyboardToolbar.Done" = "Done"; diff --git a/Sources/Authenticator/States/AuthenticatorBaseState.swift b/Sources/Authenticator/States/AuthenticatorBaseState.swift index e109f04..4337987 100644 --- a/Sources/Authenticator/States/AuthenticatorBaseState.swift +++ b/Sources/Authenticator/States/AuthenticatorBaseState.swift @@ -197,8 +197,13 @@ public class AuthenticatorBaseState: ObservableObject { } private func localizedMessage(for error: AuthError) -> String? { - if case .notAuthorized(_, _, _) = error { - return "authenticator.authError.incorrectCredentials".localized() + if case .notAuthorized(let description, _, _) = error { + switch description { + case "Password attempts exceeded": + return "authenticator.authError.passwordAttemptsExceeded".localized() + default: + return "authenticator.authError.incorrectCredentials".localized() + } } if case .validation(let field, _, _, _) = error { diff --git a/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift b/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift index 2e5a841..51b6181 100644 --- a/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift +++ b/Tests/AuthenticatorTests/States/AuthenticatorBaseStateTests.swift @@ -221,8 +221,14 @@ class AuthenticatorBaseStateTests: XCTestCase { XCTAssertEqual(authenticatorError.content, "authenticator.unknownError".localized()) } - func testError_withNotAuthorizedError_shouldReturnLocalizedError() { - let authenticatorError = state.error(for: AuthError.notAuthorized("description", "recovery", nil)) + func testError_withNotAuthorizedError_withPasswordAttemptsExceededDescription_shouldReturnLocalizedError() { + let authenticatorError = state.error(for: AuthError.notAuthorized("Password attempts exceeded", "recovery", nil)) + XCTAssertEqual(authenticatorError.style, .error) + XCTAssertEqual(authenticatorError.content, "authenticator.authError.passwordAttemptsExceeded".localized()) + } + + func testError_withNotAuthorizedError_withAnotherDescription_shouldReturnLocalizedError() { + let authenticatorError = state.error(for: AuthError.notAuthorized("Unable to sign in", "recovery", nil)) XCTAssertEqual(authenticatorError.style, .error) XCTAssertEqual(authenticatorError.content, "authenticator.authError.incorrectCredentials".localized()) }