diff --git a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/InitialSync/InitialSyncOrchestrator.swift b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/InitialSync/InitialSyncOrchestrator.swift index 872fb56fc3..3c2dec737d 100644 --- a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/InitialSync/InitialSyncOrchestrator.swift +++ b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/InitialSync/InitialSyncOrchestrator.swift @@ -164,6 +164,7 @@ final class AWSInitialSyncOrchestrator: InitialSyncOrchestrator { allMessages.joined(separator: "\n"), underlyingError ) + return .failure(syncError) } @@ -208,7 +209,7 @@ extension AWSInitialSyncOrchestrator { return errorTypeValue } - private func isUnauthorizedError(_ error: DataStoreError) -> Bool { + func isUnauthorizedError(_ error: DataStoreError) -> Bool { guard case let .sync(_, _, underlyingError) = error, let datastoreError = underlyingError as? DataStoreError else { @@ -245,6 +246,22 @@ extension AWSInitialSyncOrchestrator { case .unauthorized = AppSyncErrorType(errorTypeValue) { return true } + + // Check is API error is of unauthorized type + if case let .api(amplifyError, _) = datastoreError, + let apiError = amplifyError as? APIError { + if case .operationError(let errorDescription, _, _) = apiError, + errorDescription.range(of: "Unauthorized", + options: .caseInsensitive) != nil { + return true + } + + if case .httpStatusError(let statusCode, _) = apiError, + (statusCode == 401 || statusCode == 403) { + return true + } + } + return false } diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/InitialSync/InitialSyncOrchestratorTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/InitialSync/InitialSyncOrchestratorTests.swift index e106b30d10..07ea9b3984 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/InitialSync/InitialSyncOrchestratorTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/InitialSync/InitialSyncOrchestratorTests.swift @@ -6,6 +6,7 @@ // import XCTest +import Foundation @testable import Amplify @testable import AmplifyTestCommon @@ -101,7 +102,7 @@ class InitialSyncOrchestratorTests: XCTestCase { Amplify.Hub.removeListener(hubListener) sink.cancel() } - + /// - Given: An InitialSyncOrchestrator with a model dependency graph, API is expected to return an error for certain models /// - When: /// - The orchestrator starts up @@ -200,7 +201,7 @@ class InitialSyncOrchestratorTests: XCTestCase { Amplify.Hub.removeListener(hubListener) sink.cancel() } - + /// - Given: An InitialSyncOrchestrator with a model dependency graph containing no associations /// - When: /// - The orchestrator starts up @@ -409,4 +410,47 @@ class InitialSyncOrchestratorTests: XCTestCase { sink.cancel() } + /// - Given: + /// An InitialSyncOrchestrator with a model dependency graph + /// - When: + /// isUnauthorized() is called with an API Error with status code 401, 403 or "Unauthorized" description + /// and return false for other cases + /// - Then: + /// - It should return true for unauthorized cases and false for other cases + func testIsUnauthorized() { + let apiPlugin = MockAPICategoryPlugin() + let storageAdapter = MockSQLiteStorageEngineAdapter() + let reconciliationQueue = MockReconciliationQueue() + + let orchestrator = + AWSInitialSyncOrchestrator(dataStoreConfiguration: .default, + authModeStrategy: AWSDefaultAuthModeStrategy(), + api: apiPlugin, + reconciliationQueue: reconciliationQueue, + storageAdapter: storageAdapter) + + let error1 = DataStoreError.api(APIError.httpStatusError(401, HTTPURLResponse(url: URL(string: "https://aws.amazon.com")!, + statusCode: 401, + httpVersion: nil, + headerFields: nil)!)) + XCTAssertTrue(orchestrator.isUnauthorizedError(DataStoreError.sync("", "", error1))) + + let error2 = DataStoreError.api(APIError.httpStatusError(403, HTTPURLResponse(url: URL(string: "https://aws.amazon.com")!, + statusCode: 403, + httpVersion: nil, + headerFields: nil)!)) + XCTAssertTrue(orchestrator.isUnauthorizedError(DataStoreError.sync("", "", error2))) + + let error3 = DataStoreError.api(APIError.httpStatusError(404, HTTPURLResponse(url: URL(string: "https://aws.amazon.com")!, + statusCode: 404, + httpVersion: nil, + headerFields: nil)!)) + XCTAssertFalse(orchestrator.isUnauthorizedError(DataStoreError.sync("", "", error3))) + + let error4 = DataStoreError.api(APIError.operationError("Unauthorized error", "", nil)) + XCTAssertTrue(orchestrator.isUnauthorizedError(DataStoreError.sync("", "", error4))) + + let error5 = DataStoreError.api(APIError.operationError("An error occurred", "", nil)) + XCTAssertFalse(orchestrator.isUnauthorizedError(DataStoreError.sync("", "", error5))) + } }