Skip to content

Commit 531349f

Browse files
authored
fix(api): interceptors refactoring (#1247)
* feat(datastore): InterceptorsConfig init * feat(datastore): consume interceptors from plugin * feat(datastore): interceptors from endpointconfig * feat(datastore): AWSAuthorizationConfiguration factory * chore(api): rename AWSConfiguration factory method, add docs * chore(api): fix outdated integration tests * chore(api): add tests for EndpointInterceptorsConfig * chore(api): add tests for AWSAPICategoryPluginConfiguration * chore(api): address PR comments * chore(api): rename APIEndpointInterceptorsConfig
1 parent 0df2a96 commit 531349f

15 files changed

+556
-168
lines changed

AmplifyPlugins/API/APICategoryPlugin.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@
137137
6BD462082538102500906831 /* AuthTokenProviderWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BD462072538102500906831 /* AuthTokenProviderWrapper.swift */; };
138138
76148E0925D896EA007F3F21 /* URLComponents+sigv4Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76148E0825D896EA007F3F21 /* URLComponents+sigv4Encoding.swift */; };
139139
7632AD8A252E1E10009B5BC9 /* AppSyncJSONValue+toJSONValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7632AD89252E1E10009B5BC9 /* AppSyncJSONValue+toJSONValue.swift */; };
140+
76A240762665566C0001B523 /* AWSAPIEndpointInterceptorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76A240752665566C0001B523 /* AWSAPIEndpointInterceptorsTests.swift */; };
141+
76DCCD2E265EDDDD00BD7964 /* AWSAPIEndpointInterceptors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76DCCD2D265EDDDD00BD7964 /* AWSAPIEndpointInterceptors.swift */; };
140142
9B13EA5E48896E8B38883633 /* Pods_HostApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 930DD773E0FB4047393CA2AD /* Pods_HostApp.framework */; };
141143
A04815BCD5F9181C8AEDEF43 /* Pods_AWSAPICategoryPlugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 881AB4B98B48235DEC7754C2 /* Pods_AWSAPICategoryPlugin.framework */; };
142144
B1F5048F35638D3D142C4F1F /* Pods_AWSAPICategoryPlugin_AWSAPICategoryPluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B13CFC866A30622EDD91AF4 /* Pods_AWSAPICategoryPlugin_AWSAPICategoryPluginTests.framework */; };
@@ -474,6 +476,8 @@
474476
74EDB7008F5342ED4B38C9CA /* Pods_HostApp_AWSAPICategoryPluginIntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HostApp_AWSAPICategoryPluginIntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
475477
76148E0825D896EA007F3F21 /* URLComponents+sigv4Encoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLComponents+sigv4Encoding.swift"; sourceTree = "<group>"; };
476478
7632AD89252E1E10009B5BC9 /* AppSyncJSONValue+toJSONValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppSyncJSONValue+toJSONValue.swift"; sourceTree = "<group>"; };
479+
76A240752665566C0001B523 /* AWSAPIEndpointInterceptorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAPIEndpointInterceptorsTests.swift; sourceTree = "<group>"; };
480+
76DCCD2D265EDDDD00BD7964 /* AWSAPIEndpointInterceptors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAPIEndpointInterceptors.swift; sourceTree = "<group>"; };
477481
77792DD821FC754D857FC63C /* Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithIAMIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithIAMIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithIAMIntegrationTests/Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithIAMIntegrationTests.release.xcconfig"; sourceTree = "<group>"; };
478482
7866FCFB5807C2D20219CEBE /* Pods-HostApp-AWSAPICategoryPluginTestCommon-RESTWithIAMIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp-AWSAPICategoryPluginTestCommon-RESTWithIAMIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-HostApp-AWSAPICategoryPluginTestCommon-RESTWithIAMIntegrationTests/Pods-HostApp-AWSAPICategoryPluginTestCommon-RESTWithIAMIntegrationTests.release.xcconfig"; sourceTree = "<group>"; };
479483
7A255F655FE0AE43E68F2972 /* Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithUserPoolIntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithUserPoolIntegrationTests.debug.xcconfig"; path = "Target Support Files/Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithUserPoolIntegrationTests/Pods-HostApp-AWSAPICategoryPluginTestCommon-GraphQLWithUserPoolIntegrationTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -923,6 +927,7 @@
923927
children = (
924928
21D7A094237B54D90057D00D /* AWSAPICategoryPluginConfiguration.swift */,
925929
21D7A095237B54D90057D00D /* AWSAPICategoryPluginConfiguration+EndpointConfig.swift */,
930+
76DCCD2D265EDDDD00BD7964 /* AWSAPIEndpointInterceptors.swift */,
926931
217856B62381F19300A30D19 /* AWSAPICategoryPluginEndpointType.swift */,
927932
);
928933
path = Configuration;
@@ -1176,6 +1181,7 @@
11761181
children = (
11771182
B4DFA5C7237A611D0013E17B /* AWSAPICategoryPluginConfigurationEndpointConfigTests.swift */,
11781183
B4DFA5C8237A611D0013E17B /* AWSAPICategoryPluginConfigurationTests.swift */,
1184+
76A240752665566C0001B523 /* AWSAPIEndpointInterceptorsTests.swift */,
11791185
);
11801186
path = Configuration;
11811187
sourceTree = "<group>";
@@ -2363,6 +2369,7 @@
23632369
21D7A119237B54D90057D00D /* IAMURLRequestInterceptor.swift in Sources */,
23642370
21D7A114237B54D90057D00D /* GraphQLOperationRequest+Validate.swift in Sources */,
23652371
21D7A0E3237B54D90057D00D /* AWSGraphQLOperation+APIOperation.swift in Sources */,
2372+
76DCCD2E265EDDDD00BD7964 /* AWSAPIEndpointInterceptors.swift in Sources */,
23662373
21D7A0E0237B54D90057D00D /* AWSGraphQLSubscriptionOperation.swift in Sources */,
23672374
21D7A10B237B54D90057D00D /* AWSAppSyncGraphQLResponse.swift in Sources */,
23682375
2129BE3E239486D2006363A1 /* AnyModel+JSONInit.swift in Sources */,
@@ -2420,6 +2427,7 @@
24202427
B4DFA5F5237A611D0013E17B /* APIKeyURLRequestInterceptorTests.swift in Sources */,
24212428
B4DFA5EA237A611D0013E17B /* AWSAPICategoryPlugin+GraphQLBehaviorTests.swift in Sources */,
24222429
21A4F44125A6390B00E1047D /* AppSyncModelMetadataTests.swift in Sources */,
2430+
76A240762665566C0001B523 /* AWSAPIEndpointInterceptorsTests.swift in Sources */,
24232431
FA249EE524C5F8CC009B3CE8 /* GraphQLSubscribeCombineTests.swift in Sources */,
24242432
21A4F98F25A7BF9E00E1047D /* AppSyncListResponseTests.swift in Sources */,
24252433
21A4EF34259E39A200E1047D /* AppSyncListDecoderTests.swift in Sources */,

AmplifyPlugins/API/AWSAPICategoryPlugin/AWSAPIPlugin+InterceptorBehavior.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ public extension AWSAPIPlugin {
1414
"")
1515
}
1616

17-
pluginConfig.endpoints[apiName]?.addInterceptor(interceptor: interceptor)
17+
pluginConfig.addInterceptor(interceptor, toEndpoint: apiName)
1818
}
1919
}

AmplifyPlugins/API/AWSAPICategoryPlugin/Configuration/AWSAPICategoryPluginConfiguration+EndpointConfig.swift

Lines changed: 68 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@ import AWSPluginsCore
1111
import AWSCore
1212

1313
public extension AWSAPICategoryPluginConfiguration {
14-
1514
struct EndpointConfig {
16-
15+
// API name
1716
let name: String
17+
1818
let baseURL: URL
1919
let region: AWSRegionType?
20+
21+
// default authorization type
2022
let authorizationType: AWSAuthorizationType
23+
24+
// default authorization configuration
2125
let authorizationConfiguration: AWSAuthorizationConfiguration
26+
2227
let endpointType: AWSAPICategoryPluginEndpointType
23-
// TODO: Refactor into an "Intercepting connection configuration" or similar --
24-
// EndpointConfig shouldn't be holding onto interceptors; it should just be a data holder.
25-
// https://github.com/aws-amplify/amplify-ios/issues/73
26-
var interceptors = [URLRequestInterceptor]()
28+
29+
var apiKey: String?
2730

2831
public init(name: String,
2932
jsonValue: JSONValue,
@@ -42,12 +45,17 @@ public extension AWSAPICategoryPluginConfiguration {
4245
)
4346
}
4447

48+
var apiKeyValue: String?
49+
if case .string(let apiKey) = endpointJSON["apiKey"] {
50+
apiKeyValue = apiKey
51+
}
52+
4553
try self.init(name: name,
4654
baseURL: EndpointConfig.getBaseURL(from: endpointJSON),
47-
region: EndpointConfig.getRegion(from: endpointJSON),
48-
authorizationType: EndpointConfig.getAuthorizationType(from: endpointJSON),
49-
authorizationConfiguration: EndpointConfig.getAuthorizationConfiguration(from: endpointJSON),
55+
region: AWSRegionType.region(from: endpointJSON),
56+
authorizationType: AWSAuthorizationType.from(endpointJSON: endpointJSON),
5057
endpointType: EndpointConfig.getEndpointType(from: endpointJSON),
58+
apiKey: apiKeyValue,
5159
apiAuthProviderFactory: apiAuthProviderFactory,
5260
authService: authService)
5361
}
@@ -56,62 +64,25 @@ public extension AWSAPICategoryPluginConfiguration {
5664
baseURL: URL,
5765
region: AWSRegionType?,
5866
authorizationType: AWSAuthorizationType,
59-
authorizationConfiguration: AWSAuthorizationConfiguration,
6067
endpointType: AWSAPICategoryPluginEndpointType,
68+
apiKey: String? = nil,
6169
apiAuthProviderFactory: APIAuthProviderFactory,
6270
authService: AWSAuthServiceBehavior? = nil) throws {
6371
self.name = name
6472
self.baseURL = baseURL
6573
self.region = region
6674
self.authorizationType = authorizationType
67-
self.authorizationConfiguration = authorizationConfiguration
75+
self.authorizationConfiguration = try AWSAuthorizationConfiguration.makeConfiguration(authType: authorizationType,
76+
region: region,
77+
apiKey: apiKey)
6878
self.endpointType = endpointType
69-
try addInterceptors(authService: authService, apiAuthProviderFactory: apiAuthProviderFactory)
79+
self.apiKey = apiKey
7080
}
7181

72-
public mutating func addInterceptor(interceptor: URLRequestInterceptor) {
73-
interceptors.append(interceptor)
74-
}
75-
76-
// MARK: Private
77-
78-
/// Adds auto-discovered interceptors. Currently only works for authorization interceptors
79-
private mutating func addInterceptors(authService: AWSAuthServiceBehavior? = nil,
80-
apiAuthProviderFactory: APIAuthProviderFactory) throws {
81-
switch authorizationConfiguration {
82-
case .none:
83-
// No interceptors needed
84-
break
85-
case .apiKey(let apiKeyConfig):
86-
let provider = BasicAPIKeyProvider(apiKey: apiKeyConfig.apiKey)
87-
let interceptor = APIKeyURLRequestInterceptor(apiKeyProvider: provider)
88-
addInterceptor(interceptor: interceptor)
89-
case .awsIAM(let iamConfig):
90-
guard let authService = authService else {
91-
throw PluginError.pluginConfigurationError("AuthService is not set for IAM",
92-
"")
93-
}
94-
let provider = BasicIAMCredentialsProvider(authService: authService)
95-
let interceptor = IAMURLRequestInterceptor(iamCredentialsProvider: provider,
96-
region: iamConfig.region,
97-
endpointType: endpointType)
98-
addInterceptor(interceptor: interceptor)
99-
case .amazonCognitoUserPools:
100-
guard let authService = authService else {
101-
throw PluginError.pluginConfigurationError("AuthService not set for cognito user pools",
102-
"")
103-
}
104-
let provider = BasicUserPoolTokenProvider(authService: authService)
105-
let interceptor = UserPoolURLRequestInterceptor(userPoolTokenProvider: provider)
106-
addInterceptor(interceptor: interceptor)
107-
case .openIDConnect:
108-
guard let oidcAuthProvider = apiAuthProviderFactory.oidcAuthProvider() else {
109-
return
110-
}
111-
let wrappedAuthProvider = AuthTokenProviderWrapper(oidcAuthProvider: oidcAuthProvider)
112-
let interceptor = UserPoolURLRequestInterceptor(userPoolTokenProvider: wrappedAuthProvider)
113-
addInterceptor(interceptor: interceptor)
114-
}
82+
public func authorizationConfigurationFor(authType: AWSAuthorizationType) throws -> AWSAuthorizationConfiguration {
83+
return try AWSAuthorizationConfiguration.makeConfiguration(authType: authType,
84+
region: region,
85+
apiKey: apiKey)
11586
}
11687

11788
// MARK: - Configuration file helpers
@@ -142,23 +113,6 @@ public extension AWSAPICategoryPluginConfiguration {
142113
return baseURL
143114
}
144115

145-
private static func getRegion(from endpointJSON: [String: JSONValue]) throws -> AWSRegionType? {
146-
let region: AWSRegionType?
147-
148-
if case .string(let endpointRegion) = endpointJSON["region"] {
149-
let regionType = endpointRegion.aws_regionTypeValue()
150-
guard regionType != AWSRegionType.Unknown else {
151-
return nil
152-
}
153-
154-
region = regionType
155-
} else {
156-
region = nil
157-
}
158-
159-
return region
160-
}
161-
162116
private static func getEndpointType(from endpointJSON: [String: JSONValue]) throws ->
163117
AWSAPICategoryPluginEndpointType {
164118

@@ -187,99 +141,64 @@ public extension AWSAPICategoryPluginConfiguration {
187141

188142
return endpointType
189143
}
144+
}
145+
}
190146

191-
private static func getAuthorizationType(
192-
from endpointJSON: [String: JSONValue]
193-
) throws -> AWSAuthorizationType {
194-
guard case .string(let authorizationTypeString) = endpointJSON["authorizationType"] else {
195-
throw PluginError.pluginConfigurationError(
196-
"Could not get `AuthorizationType` from plugin configuration",
197-
"""
198-
The specified configuration does not have a string with the key `AuthorizationType`. Review the \
199-
configuration and ensure it contains the expected values:
200-
\(endpointJSON)
201-
"""
202-
)
203-
}
204-
205-
guard let authorizationType = AWSAuthorizationType(rawValue: authorizationTypeString) else {
206-
let authTypes = AWSAuthorizationType.allCases.map { $0.rawValue }.joined(separator: ", ")
207-
throw PluginError.pluginConfigurationError(
208-
"Could not convert `\(authorizationTypeString)` to an AWSAuthorizationType",
209-
"""
210-
The "authorizationType" value in the specified configuration cannot be converted to an \
211-
AWSAuthorizationType. Review the configuration and ensure it contains a valid value \
212-
(\(authTypes)):
213-
\(endpointJSON)
214-
"""
215-
)
216-
}
147+
// MARK: - AWSRegionType + fromEndpointJSON
217148

218-
return authorizationType
219-
}
149+
private extension AWSRegionType {
150+
static func region(from endpointJSON: [String: JSONValue]) throws -> AWSRegionType? {
151+
let region: AWSRegionType?
220152

221-
// TODO: Refactor auth configuration creation into separate files--this file is for endpoint configs
222-
// https://github.com/aws-amplify/amplify-ios/issues/73
223-
private static func getAuthorizationConfiguration(from endpointJSON: [String: JSONValue])
224-
throws -> AWSAuthorizationConfiguration {
225-
let authType = try getAuthorizationType(from: endpointJSON)
226-
227-
switch authType {
228-
case .none:
229-
return .none
230-
case .apiKey:
231-
return try apiKeyAuthorizationConfiguration(from: endpointJSON)
232-
case .awsIAM:
233-
return try awsIAMAuthorizationConfiguration(from: endpointJSON)
234-
case .openIDConnect:
235-
return try oidcAuthorizationConfiguration(from: endpointJSON)
236-
case .amazonCognitoUserPools:
237-
return try userPoolsAuthorizationConfiguration(from: endpointJSON)
153+
if case .string(let endpointRegion) = endpointJSON["region"] {
154+
let regionType = endpointRegion.aws_regionTypeValue()
155+
guard regionType != AWSRegionType.Unknown else {
156+
return nil
238157
}
239158

159+
region = regionType
160+
} else {
161+
region = nil
240162
}
241163

242-
private static func apiKeyAuthorizationConfiguration(from endpointJSON: [String: JSONValue])
243-
throws -> AWSAuthorizationConfiguration {
244-
245-
guard case .string(let apiKey) = endpointJSON["apiKey"] else {
246-
throw PluginError.pluginConfigurationError(
247-
"Could not get `ApiKey` from plugin configuration",
248-
"""
249-
The specified configuration does not have a string with the key `ApiKey`. Review the \
250-
configuration and ensure it contains the expected values:
251-
\(endpointJSON)
252-
"""
253-
)
254-
}
255-
256-
let config = APIKeyConfiguration(apiKey: apiKey)
257-
return .apiKey(config)
258-
}
164+
return region
165+
}
166+
}
259167

260-
static func awsIAMAuthorizationConfiguration(from endpointJSON: [String: JSONValue])
261-
throws -> AWSAuthorizationConfiguration {
262-
let regionOptional = try EndpointConfig.getRegion(from: endpointJSON)
263-
guard let region = regionOptional else {
264-
throw PluginError.pluginConfigurationError("Region is not set for IAM",
265-
"Set the region")
266-
}
267-
return .awsIAM(AWSIAMConfiguration(region: region))
268-
}
168+
// MARK: - AWSAuthorizationType + fromEndpointJSON
269169

270-
static func oidcAuthorizationConfiguration(from endpointJSON: [String: JSONValue])
271-
throws -> AWSAuthorizationConfiguration {
272-
return .openIDConnect(OIDCConfiguration())
170+
private extension AWSAuthorizationType {
171+
static func from(endpointJSON: [String: JSONValue]) throws -> AWSAuthorizationType {
172+
guard case .string(let authorizationTypeString) = endpointJSON["authorizationType"] else {
173+
throw PluginError.pluginConfigurationError(
174+
"Could not get `AuthorizationType` from plugin configuration",
175+
"""
176+
The specified configuration does not have a string with the key `AuthorizationType`. Review the \
177+
configuration and ensure it contains the expected values:
178+
\(endpointJSON)
179+
"""
180+
)
273181
}
274182

275-
static func userPoolsAuthorizationConfiguration(from endpointJSON: [String: JSONValue])
276-
throws -> AWSAuthorizationConfiguration {
277-
return .amazonCognitoUserPools(CognitoUserPoolsConfiguration())
183+
guard let authorizationType = AWSAuthorizationType(rawValue: authorizationTypeString) else {
184+
let authTypes = AWSAuthorizationType.allCases.map { $0.rawValue }.joined(separator: ", ")
185+
throw PluginError.pluginConfigurationError(
186+
"Could not convert `\(authorizationTypeString)` to an AWSAuthorizationType",
187+
"""
188+
The "authorizationType" value in the specified configuration cannot be converted to an \
189+
AWSAuthorizationType. Review the configuration and ensure it contains a valid value \
190+
(\(authTypes)):
191+
\(endpointJSON)
192+
"""
193+
)
278194
}
279195

196+
return authorizationType
280197
}
281198
}
282199

200+
// MARK: - Dictionary + AWSAPICategoryPluginConfiguration.EndpointConfig
201+
283202
extension Dictionary where Key == String, Value == AWSAPICategoryPluginConfiguration.EndpointConfig {
284203

285204
/// Getting the `EndpointConfig` resolves to the following rules:

0 commit comments

Comments
 (0)