Skip to content

Commit 56bc2a7

Browse files
authored
feat(datastore): multi-auth support (#1260)
* feat(datastore): InterceptorsConfig init * feat(datastore): consume interceptors from plugin * chore(api): add tests for EndpointInterceptorsConfig * chore(api): address PR comments * chore(api): rename APIEndpointInterceptorsConfig * chore(api): fix outdated integration tests * feat: multi-auth support WIP * feat(datastore): multi-auth, support for different strategies * feat(datastore): multi-auth, retryable operation * feat(datastore): multi-auth strategy initial implmentation * feat(datastore): provide multi-auth strategy * feat(datastore): pass authtype to AWSGraphQLSubscriptionOperation * fix(api): refactor AWSAPICategoryPluginConfiguration+EndpointConfig * feat(datastore): retryable graphqlsubscription operation wip * feat(datastore): generic retryable graphql operation * feat(datastore): retryable initialsync operation * feat(datastore): get interceptors forgraphql operation * feat(datastore): multiauth strategy, use factory instead of static map * chore(datastore): AWSSubscriptionConnectionFactory mapper naming * chore(datastore): update tests * chore(datastore): add public initializer to AuthModeStrategy * feat(datastore): multiauth mutation support WIP * feat(datastore): AuthorizationTypeProvider * feat(datastore): multiauth mutation support * feat(datastore): retryable operation max retries * move RetryableGraphQLOperation * feat(datastore): retryable operation refactoring * feat(datastore): retryable operation refactoring * rebase on main * chore: update tests * use auth type based on provider * use auth type based on provider or default * intercept 401 errors in mutation response * feat(datastore): retry if operation failed with auth error * feat(datastore): check user status to determine auth * update unit tests * chore: remove unnecessary import * chore: RetryableGraphQLOperation tests * chore: update docs * chore: AuthModeStrategy tests * chore: AuthModeStrategy priority tests * chore: AuthModeStrategy order/operation tests * chore: AuthModeStrategyDelegate tests * chore: AuthModeStrategy priority tests * chore: update docs * remove custom strategy, move strategy resolution * AuthRuleProvider priority * use AWSPluginOptions to store auth type on requests * add missing docs, update imports * update tests * update tests * use AWSPluginOptions to store auth type on requests * update tests after rebase * update usage of GraphQLRequest.Options * update usage of GraphQLOperationRequest.Options * update usage of GraphQLOperationRequest.Options * fix isUserLoggedIn when OIDC is used * fix isUserLoggedIn when OIDC is used * clean up isUserLoggedIn delegate method
1 parent 5bca202 commit 56bc2a7

File tree

51 files changed

+1302
-208
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1302
-208
lines changed

Amplify.xcodeproj/project.pbxproj

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@
242242
6BEE081D2533CCFA00133961 /* OGCScenarioBPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BEE081B2533CCFA00133961 /* OGCScenarioBPost.swift */; };
243243
6BEE08242533D30800133961 /* OGCScenarioBMGroupPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BEE08222533D30800133961 /* OGCScenarioBMGroupPost.swift */; };
244244
6BEE08252533D30800133961 /* OGCScenarioBMGroupPost+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BEE08232533D30800133961 /* OGCScenarioBMGroupPost+Schema.swift */; };
245+
7608EE14268142F300FEC9CD /* AWSPluginOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7608EE13268142F300FEC9CD /* AWSPluginOptions.swift */; };
245246
762167CC261542F70033FCD2 /* Record.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762167CB261542F70033FCD2 /* Record.swift */; };
246247
762167D52615435C0033FCD2 /* Record+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762167D42615435C0033FCD2 /* Record+Schema.swift */; };
247248
762C978526210F6400798FA3 /* RecordCover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762C978426210F6400798FA3 /* RecordCover.swift */; };
@@ -251,6 +252,11 @@
251252
7678B38526017D5300B4917F /* AppSyncErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7678B38326017D5300B4917F /* AppSyncErrorTypeTests.swift */; };
252253
767F85FC2649FF540076D633 /* CustomerOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767F85FB2649FF540076D633 /* CustomerOrder.swift */; };
253254
767F85FE2649FFCC0076D633 /* CustomerOrder+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767F85FD2649FFCC0076D633 /* CustomerOrder+Schema.swift */; };
255+
769CF2242669B1B9007843A0 /* RetryableGraphQLOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 769CF2232669B1B9007843A0 /* RetryableGraphQLOperation.swift */; };
256+
76A4023C264B1E3A009D4127 /* AWSAuthModeStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76A4023B264B1E3A009D4127 /* AWSAuthModeStrategy.swift */; };
257+
76B17DD8264C4D7D00735B30 /* ModelSchema+AuthRulesByOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B17DD7264C4D7D00735B30 /* ModelSchema+AuthRulesByOperation.swift */; };
258+
76C0F4EC26797F4500F0E6AB /* AuthModeStrategyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76C0F4EA26797F3C00F0E6AB /* AuthModeStrategyTests.swift */; };
259+
76FFFC91267966D9001EC92B /* RetryableGraphQLOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76FFFC90267966D9001EC92B /* RetryableGraphQLOperationTests.swift */; };
254260
7D5ED6C78E25246DDAF2F2EC /* Pods_Amplify.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F3A76FB68CEFA45F4BB1BB /* Pods_Amplify.framework */; platformFilter = ios; };
255261
7F27B1DCE59C1E674172CCD6 /* Pods_Amplify_AmplifyTestConfigs_AmplifyTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 976D972EC2BBCAAD023694EB /* Pods_Amplify_AmplifyTestConfigs_AmplifyTests.framework */; };
256262
881246F5DCC59436DC932469 /* Pods_Amplify_AWSPluginsCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35D92182B8445C8F9B0FAE94 /* Pods_Amplify_AWSPluginsCore.framework */; };
@@ -1129,6 +1135,7 @@
11291135
71B6F506E5F3F00B0743A9BF /* Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsTestCommon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsTestCommon.debug.xcconfig"; path = "Target Support Files/Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsTestCommon/Pods-Amplify-AWSPluginsCore-AWSPluginsTestConfigs-AWSPluginsTestCommon.debug.xcconfig"; sourceTree = "<group>"; };
11301136
73C2E5FA55C85539AD9E39EE /* Pods_Amplify_AWSPluginsCore_CoreMLPredictionsPlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Amplify_AWSPluginsCore_CoreMLPredictionsPlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; };
11311137
7503342A8C13588E2B0DDBDE /* Pods-AmplifyFunctionalTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AmplifyFunctionalTests.debug.xcconfig"; path = "Target Support Files/Pods-AmplifyFunctionalTests/Pods-AmplifyFunctionalTests.debug.xcconfig"; sourceTree = "<group>"; };
1138+
7608EE13268142F300FEC9CD /* AWSPluginOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSPluginOptions.swift; sourceTree = "<group>"; };
11321139
762167CB261542F70033FCD2 /* Record.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Record.swift; sourceTree = "<group>"; };
11331140
762167D42615435C0033FCD2 /* Record+Schema.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Record+Schema.swift"; sourceTree = "<group>"; };
11341141
762C978426210F6400798FA3 /* RecordCover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordCover.swift; sourceTree = "<group>"; };
@@ -1137,6 +1144,11 @@
11371144
7678B38326017D5300B4917F /* AppSyncErrorTypeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncErrorTypeTests.swift; sourceTree = "<group>"; };
11381145
767F85FB2649FF540076D633 /* CustomerOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomerOrder.swift; sourceTree = "<group>"; };
11391146
767F85FD2649FFCC0076D633 /* CustomerOrder+Schema.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CustomerOrder+Schema.swift"; sourceTree = "<group>"; };
1147+
769CF2232669B1B9007843A0 /* RetryableGraphQLOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryableGraphQLOperation.swift; sourceTree = "<group>"; };
1148+
76A4023B264B1E3A009D4127 /* AWSAuthModeStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAuthModeStrategy.swift; sourceTree = "<group>"; };
1149+
76B17DD7264C4D7D00735B30 /* ModelSchema+AuthRulesByOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ModelSchema+AuthRulesByOperation.swift"; sourceTree = "<group>"; };
1150+
76C0F4EA26797F3C00F0E6AB /* AuthModeStrategyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthModeStrategyTests.swift; sourceTree = "<group>"; };
1151+
76FFFC90267966D9001EC92B /* RetryableGraphQLOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryableGraphQLOperationTests.swift; sourceTree = "<group>"; };
11401152
77A2D125114EA0FFA11A7EFD /* Pods-AmplifyTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AmplifyTests.debug.xcconfig"; path = "Target Support Files/Pods-AmplifyTests/Pods-AmplifyTests.debug.xcconfig"; sourceTree = "<group>"; };
11411153
7A238096BAB328655B5E388F /* Pods-Amplify-AWSPluginsCore-CoreMLPredictionsPlugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Amplify-AWSPluginsCore-CoreMLPredictionsPlugin.debug.xcconfig"; path = "Target Support Files/Pods-Amplify-AWSPluginsCore-CoreMLPredictionsPlugin/Pods-Amplify-AWSPluginsCore-CoreMLPredictionsPlugin.debug.xcconfig"; sourceTree = "<group>"; };
11421154
7A7FD9B3CFF63FD16788A990 /* Pods-Amplify-AmplifyAWSPlugins-AWSPluginsCore-AWSPinpointAnalyticsPlugin-AWSPinpointAnalyticsPluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Amplify-AmplifyAWSPlugins-AWSPluginsCore-AWSPinpointAnalyticsPlugin-AWSPinpointAnalyticsPluginTests.release.xcconfig"; path = "Target Support Files/Pods-Amplify-AmplifyAWSPlugins-AWSPluginsCore-AWSPinpointAnalyticsPlugin-AWSPinpointAnalyticsPluginTests/Pods-Amplify-AmplifyAWSPlugins-AWSPluginsCore-AWSPinpointAnalyticsPlugin-AWSPinpointAnalyticsPluginTests.release.xcconfig"; sourceTree = "<group>"; };
@@ -2493,6 +2505,7 @@
24932505
isa = PBXGroup;
24942506
children = (
24952507
6BEE0816253114FD00133961 /* AWSAuthServiceTests.swift */,
2508+
76C0F4EA26797F3C00F0E6AB /* AuthModeStrategyTests.swift */,
24962509
);
24972510
path = Auth;
24982511
sourceTree = "<group>";
@@ -2921,13 +2934,14 @@
29212934
B937FBFC23FB4ED300081639 /* Schema */ = {
29222935
isa = PBXGroup;
29232936
children = (
2937+
210B3E33245CB73400F43848 /* AuthRule.swift */,
29242938
B92E03AB2367CE7A006CEB8D /* Model+Schema.swift */,
29252939
B9FAA10A23878122009414B4 /* ModelField+Association.swift */,
29262940
B92E03A82367CE7A006CEB8D /* ModelSchema.swift */,
29272941
FAA64FC22397294500B9C3C6 /* ModelSchema+Attributes.swift */,
2942+
76B17DD7264C4D7D00735B30 /* ModelSchema+AuthRulesByOperation.swift */,
29282943
B92E03AA2367CE7A006CEB8D /* ModelSchema+Definition.swift */,
29292944
B9B64A9E23FCBF7E00730B68 /* ModelValueConverter.swift */,
2930-
210B3E33245CB73400F43848 /* AuthRule.swift */,
29312945
);
29322946
path = Schema;
29332947
sourceTree = "<group>";
@@ -2942,7 +2956,6 @@
29422956
B9521830237E21B900F53237 /* Comment.swift */,
29432957
B952182E237E21B900F53237 /* Comment+Schema.swift */,
29442958
767F85FB2649FF540076D633 /* CustomerOrder.swift */,
2945-
767F85FD2649FFCC0076D633 /* CustomerOrder+Schema.swift */,
29462959
21AD4255249BFFDF0016FE95 /* DeprecatedTodo.swift */,
29472960
FAA2E8BB239FFC7700E420EA /* MockModels.swift */,
29482961
6BEE08222533D30800133961 /* OGCScenarioBMGroupPost.swift */,
@@ -3179,6 +3192,7 @@
31793192
children = (
31803193
FA131AAC2360FE070008381C /* AWSPluginsCore.h */,
31813194
FA131AAD2360FE070008381C /* Info.plist */,
3195+
7608EE13268142F300FEC9CD /* AWSPluginOptions.swift */,
31823196
21C395B4245729F100597EA2 /* API */,
31833197
FA131ACB2360FE470008381C /* Auth */,
31843198
2129BE0223947FA3006363A1 /* Model */,
@@ -3208,6 +3222,7 @@
32083222
21420A8D237222A900FA140C /* AWSAuthorizationType.swift */,
32093223
21420A89237222A800FA140C /* AWSAuthService.swift */,
32103224
21420A81237222A700FA140C /* AWSAuthServiceBehavior.swift */,
3225+
76A4023B264B1E3A009D4127 /* AWSAuthModeStrategy.swift */,
32113226
B42B3D39246AE159007211E0 /* AuthPluginBehavior */,
32123227
21420A7A237222A700FA140C /* Configuration */,
32133228
21420A83237222A800FA140C /* Provider */,
@@ -3325,6 +3340,7 @@
33253340
isa = PBXGroup;
33263341
children = (
33273342
FA6BC871235F5BD60001A882 /* GraphQLOperation.swift */,
3343+
769CF2232669B1B9007843A0 /* RetryableGraphQLOperation.swift */,
33283344
FAC428A5235F83770000F221 /* RESTOperation.swift */,
33293345
FA58456624D9D6960028D65A /* AmplifyOperation+APIPublishers.swift */,
33303346
);
@@ -3625,6 +3641,7 @@
36253641
FAA9FC3D236210840012638A /* APICategoryClientInterceptorTests.swift */,
36263642
FA6BC868235F52740001A882 /* APICategoryClientGraphQLTests.swift */,
36273643
FAC23558227A056600424678 /* APICategoryConfigurationTests.swift */,
3644+
76FFFC90267966D9001EC92B /* RetryableGraphQLOperationTests.swift */,
36283645
);
36293646
path = API;
36303647
sourceTree = "<group>";
@@ -4754,8 +4771,10 @@
47544771
212CE70D23E9E991007D8E71 /* FilterDecorator.swift in Sources */,
47554772
219A888123EB629800BBC5F2 /* ModelBasedGraphQLDocumentDecorator.swift in Sources */,
47564773
212CE70423E9E967007D8E71 /* GraphQLSubscription.swift in Sources */,
4774+
76A4023C264B1E3A009D4127 /* AWSAuthModeStrategy.swift in Sources */,
47574775
21420A99237222A900FA140C /* APIKeyProvider.swift in Sources */,
47584776
21A3FDB224630D9F00E76120 /* AuthRuleDecorator.swift in Sources */,
4777+
7608EE14268142F300FEC9CD /* AWSPluginOptions.swift in Sources */,
47594778
6BBECD7123ADA7E100C8DFBE /* AmplifyAWSServiceConfiguration.swift in Sources */,
47604779
21AD424F249BF0EC0016FE95 /* Model+AnyModel.swift in Sources */,
47614780
B4EBEB682462050B00D06375 /* AuthCognitoIdentityProvider.swift in Sources */,
@@ -4791,6 +4810,7 @@
47914810
21A3FDB62464590600E76120 /* ModelMultipleOwnerAuthRuleTests.swift in Sources */,
47924811
6B5087BD2565E5AD000AB673 /* QueryPredicateEvaluateGeneratedDoubleTests.swift in Sources */,
47934812
6B5087C5256632D3000AB673 /* QueryPredicateEvaluateGeneratedStringTests.swift in Sources */,
4813+
76C0F4EC26797F4500F0E6AB /* AuthModeStrategyTests.swift in Sources */,
47944814
2129BE3A2394828B006363A1 /* QueryPredicateGraphQLTests.swift in Sources */,
47954815
2129BE48239489AC006363A1 /* MutationSyncMetadataTests.swift in Sources */,
47964816
216E45F1248E971E0035E3CE /* GraphQLRequestEmbeddableTypeTests.swift in Sources */,
@@ -5201,6 +5221,7 @@
52015221
95DAAB36237E63370028544F /* Selection.swift in Sources */,
52025222
B4251A0B242504FA007F59EF /* AuthChangePasswordOperation.swift in Sources */,
52035223
FAC0A2AD22B43FE700B50912 /* HubCategory+ClientBehavior.swift in Sources */,
5224+
769CF2242669B1B9007843A0 /* RetryableGraphQLOperation.swift in Sources */,
52045225
B4251A0124250369007F59EF /* AuthConfirmResetPasswordRequest.swift in Sources */,
52055226
FAAFAF2F23904B14002CF932 /* AtomicValue+Bool.swift in Sources */,
52065227
B9B50DC823DA15890086F1E1 /* DataStoreError+Temporal.swift in Sources */,
@@ -5222,6 +5243,7 @@
52225243
FAC23522227A053D00424678 /* LoggingCategory.swift in Sources */,
52235244
FAC2358F227A4A6E00424678 /* HubCategory.swift in Sources */,
52245245
B4B5CC872457B2470019C783 /* AuthUpdateUserAttributeRequest.swift in Sources */,
5246+
76B17DD8264C4D7D00735B30 /* ModelSchema+AuthRulesByOperation.swift in Sources */,
52255247
B98E9D0E2372236300934B51 /* ModelKey.swift in Sources */,
52265248
B91A879E23D12B730049A12F /* Time.swift in Sources */,
52275249
B9FAA13C238BBE67009414B4 /* DataStoreCallback+Combine.swift in Sources */,
@@ -5300,6 +5322,7 @@
53005322
B4BD6B3323708C0000A1F0A7 /* PredictionsCategoryClientAPITests.swift in Sources */,
53015323
FA47B8382350C58B0031A0E3 /* AutoUnsubscribeHubListenToOperationTests.swift in Sources */,
53025324
FAC23574227A056B00424678 /* ConfigurationTests.swift in Sources */,
5325+
76FFFC91267966D9001EC92B /* RetryableGraphQLOperationTests.swift in Sources */,
53035326
B9DCA263240F217C00075E22 /* AnyEncodableTests.swift in Sources */,
53045327
975751B224D2129E00FA0A6E /* DevMenuExtensionTests.swift in Sources */,
53055328
FAA64FC72397347B00B9C3C6 /* AtomicValue+RangeReplaceableCollectionTests.swift in Sources */,
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
10+
/// Convenience protocol to handle any kind of GraphQLOperation
11+
public protocol AnyGraphQLOperation {
12+
associatedtype Success
13+
associatedtype Failure: Error
14+
typealias ResultListener = (Result<Success, Failure>) -> Void
15+
}
16+
17+
/// Abastraction for a retryable GraphQLOperation.
18+
public protocol RetryableGraphQLOperationBehavior: Operation, DefaultLogger {
19+
associatedtype Payload: Decodable
20+
21+
/// GraphQLOperation concrete type
22+
associatedtype OperationType: AnyGraphQLOperation
23+
24+
typealias RequestFactory = () -> GraphQLRequest<Payload>
25+
typealias OperationFactory = (GraphQLRequest<Payload>, @escaping OperationResultListener) -> OperationType
26+
typealias OperationResultListener = OperationType.ResultListener
27+
28+
/// Operation unique identifier
29+
var id: UUID { get }
30+
31+
/// Number of attempts (min 1)
32+
var attempts: Int { get set }
33+
34+
/// Underlying GraphQL operation instantiated by `operationFactory`
35+
var underlyingOperation: OperationType? { get set }
36+
37+
/// Maximum number of allowed retries
38+
var maxRetries: Int { get }
39+
40+
/// GraphQLRequest factory, invoked to create a new operation
41+
var requestFactory: RequestFactory { get }
42+
43+
/// GraphQL operation factory, invoked with a newly created GraphQL request
44+
/// and a wrapped result listener.
45+
var operationFactory: OperationFactory { get }
46+
47+
var resultListener: OperationResultListener { get }
48+
49+
init(requestFactory: @escaping RequestFactory,
50+
maxRetries: Int,
51+
resultListener: @escaping OperationResultListener,
52+
_ operationFactory: @escaping OperationFactory)
53+
54+
func start(request: GraphQLRequest<Payload>)
55+
56+
func shouldRetry(error: APIError?) -> Bool
57+
}
58+
59+
// MARK: RetryableGraphQLOperationBehavior + default implementation
60+
extension RetryableGraphQLOperationBehavior {
61+
public func start(request: GraphQLRequest<Payload>) {
62+
attempts += 1
63+
log.debug("[\(id)] - Try [\(attempts)/\(maxRetries)]")
64+
let wrappedResultListener: OperationResultListener = { result in
65+
if case let .failure(error) = result, self.shouldRetry(error: error as? APIError) {
66+
self.log.debug("\(error)")
67+
self.start(request: self.requestFactory())
68+
return
69+
}
70+
71+
if case let .failure(error) = result {
72+
self.log.debug("\(error)")
73+
self.log.debug("[\(self.id)] - Failed")
74+
}
75+
76+
if case .success = result {
77+
self.log.debug("[Operation \(self.id)] - Success")
78+
}
79+
self.resultListener(result)
80+
}
81+
underlyingOperation = operationFactory(request, wrappedResultListener)
82+
}
83+
}
84+
85+
// MARK: - RetryableGraphQLOperation
86+
public final class RetryableGraphQLOperation<Payload: Decodable>: Operation, RetryableGraphQLOperationBehavior {
87+
public typealias Payload = Payload
88+
public typealias OperationType = GraphQLOperation<Payload>
89+
90+
public var id: UUID
91+
public var maxRetries: Int
92+
public var attempts: Int = 0
93+
public var requestFactory: RequestFactory
94+
public var underlyingOperation: GraphQLOperation<Payload>?
95+
public var resultListener: OperationResultListener
96+
public var operationFactory: OperationFactory
97+
98+
public init(requestFactory: @escaping () -> GraphQLRequest<Payload>,
99+
maxRetries: Int,
100+
resultListener: @escaping OperationResultListener,
101+
_ operationFactory: @escaping OperationFactory) {
102+
self.id = UUID()
103+
self.maxRetries = max(1, maxRetries)
104+
self.requestFactory = requestFactory
105+
self.operationFactory = operationFactory
106+
self.resultListener = resultListener
107+
}
108+
public override func main() {
109+
start(request: requestFactory())
110+
}
111+
112+
public override func cancel() {
113+
underlyingOperation?.cancel()
114+
}
115+
116+
public func shouldRetry(error: APIError?) -> Bool {
117+
guard case let .operationError(_, _, underlyingError) = error,
118+
let authError = underlyingError as? AuthError else {
119+
return false
120+
}
121+
122+
switch authError {
123+
case .signedOut, .notAuthorized:
124+
return attempts < maxRetries
125+
default:
126+
return false
127+
}
128+
}
129+
}
130+
131+
// MARK: - RetryableGraphQLSubscriptionOperation
132+
public final class RetryableGraphQLSubscriptionOperation<Payload: Decodable>: Operation,
133+
RetryableGraphQLOperationBehavior {
134+
public typealias OperationType = GraphQLSubscriptionOperation<Payload>
135+
136+
public typealias Payload = Payload
137+
138+
public var id: UUID
139+
public var maxRetries: Int
140+
public var attempts: Int = 0
141+
public var underlyingOperation: GraphQLSubscriptionOperation<Payload>?
142+
public var requestFactory: RequestFactory
143+
public var resultListener: OperationResultListener
144+
public var operationFactory: OperationFactory
145+
146+
public init(requestFactory: @escaping RequestFactory,
147+
maxRetries: Int,
148+
resultListener: @escaping OperationResultListener,
149+
_ operationFactory: @escaping OperationFactory) {
150+
self.id = UUID()
151+
self.maxRetries = max(1, maxRetries)
152+
self.requestFactory = requestFactory
153+
self.operationFactory = operationFactory
154+
self.resultListener = resultListener
155+
}
156+
public override func main() {
157+
start(request: requestFactory())
158+
}
159+
160+
public override func cancel() {
161+
underlyingOperation?.cancel()
162+
}
163+
164+
public func shouldRetry(error: APIError?) -> Bool {
165+
return attempts < maxRetries
166+
}
167+
168+
}
169+
170+
// MARK: GraphQLOperation - GraphQLSubscriptionOperation + AnyGraphQLOperation
171+
extension GraphQLOperation: AnyGraphQLOperation {}
172+
extension GraphQLSubscriptionOperation: AnyGraphQLOperation {}

0 commit comments

Comments
 (0)