Skip to content

Commit e9ea12f

Browse files
authored
[swift6] improve retry interceptor (#19988)
* [swift6] improve retry interceptor * [swift6] improve retry interceptor * [swift6] improve retry interceptor
1 parent 9452873 commit e9ea12f

File tree

14 files changed

+734
-631
lines changed

14 files changed

+734
-631
lines changed

docs/faq-generators.md

+12-5
Original file line numberDiff line numberDiff line change
@@ -350,21 +350,28 @@ First you implement the `OpenAPIInterceptor` protocol.
350350
public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
351351
public init() {}
352352
353-
public func intercept(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, completion: @escaping (Result<URLRequest, any Error>) -> Void) {
353+
public func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, any Error>) -> Void) {
354+
355+
guard requestBuilder.requiresAuthentication else {
356+
// no authentication required
357+
completion(.success(urlRequest))
358+
return
359+
}
360+
354361
refreshTokenIfDoesntExist { token in
355362
356363
// Change the current url request
357364
var newUrlRequest = urlRequest
358365
newUrlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
359366
360367
// Change the global headers
361-
openAPIClient.customHeaders["Authorization"] = "Bearer \(token)"
368+
requestBuilder.openAPIClient.customHeaders["Authorization"] = "Bearer \(token)"
362369
363370
completion(.success(newUrlRequest))
364371
}
365372
}
366373
367-
public func retry(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
374+
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
368375
// We will analyse the response to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
369376
refreshTokenIfUnauthorizedRequestResponse(
370377
data: data,
@@ -375,7 +382,7 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
375382
if wasTokenRefreshed, let newToken = newToken {
376383
377384
// Change the global headers
378-
openAPIClient.customHeaders["Authorization"] = "Bearer \(newToken)"
385+
requestBuilder.openAPIClient.customHeaders["Authorization"] = "Bearer \(newToken)"
379386
380387
completion(.retry)
381388
} else {
@@ -397,7 +404,7 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
397404
}
398405
}
399406
400-
func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse, error: Error, completionHandler: @escaping (Bool, String?) -> Void) {
407+
func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error, completionHandler: @escaping (Bool, String?) -> Void) {
401408
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
402409
startRefreshingToken { token in
403410
completionHandler(true, token)

modules/openapi-generator/src/main/resources/swift6/libraries/urlsession/URLSessionImplementations.mustache

+60-52
Original file line numberDiff line numberDiff line change
@@ -155,24 +155,48 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
155155
switch result {
156156
case .success(let modifiedRequest):
157157
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
158-
self.cleanupRequest()
159-
if let response, let error {
160-
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
161-
switch retry {
162-
case .retry:
163-
self.execute(completion: completion)
164-
165-
case .dontRetry:
166-
self.openAPIClient.apiResponseQueue.async {
167-
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
168-
}
169-
}
170-
}
171-
} else {
172-
self.openAPIClient.apiResponseQueue.async {
173-
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
174-
}
158+
self.cleanupRequest()
159+
160+
if let error = error {
161+
self.retryRequest(
162+
urlRequest: modifiedRequest,
163+
urlSession: urlSession,
164+
statusCode: -1,
165+
data: data,
166+
response: response,
167+
error: error,
168+
completion: completion
169+
)
170+
return
175171
}
172+
173+
guard let httpResponse = response as? HTTPURLResponse else {
174+
self.retryRequest(
175+
urlRequest: modifiedRequest,
176+
urlSession: urlSession,
177+
statusCode: -2,
178+
data: data,
179+
response: response,
180+
error: DecodableRequestBuilderError.nilHTTPResponse,
181+
completion: completion
182+
)
183+
return
184+
}
185+
186+
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
187+
self.retryRequest(
188+
urlRequest: modifiedRequest,
189+
urlSession: urlSession,
190+
statusCode: httpResponse.statusCode,
191+
data: data,
192+
response: httpResponse,
193+
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
194+
completion: completion
195+
)
196+
return
197+
}
198+
199+
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
176200
}
177201

178202
self.onProgressReady?(dataTask.progress)
@@ -204,22 +228,21 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
204228
}
205229
}
206230

207-
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
231+
private func retryRequest(urlRequest: URLRequest, urlSession: URLSessionProtocol, statusCode: Int, data: Data?, response: URLResponse?, error: Error, completion: @Sendable @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
232+
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
233+
switch retry {
234+
case .retry:
235+
self.execute(completion: completion)
208236
209-
if let error = error {
210-
completion(.failure(ErrorResponse.error(-1, data, response, error)))
211-
return
212-
}
213-
214-
guard let httpResponse = response as? HTTPURLResponse else {
215-
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
216-
return
237+
case .dontRetry:
238+
self.openAPIClient.apiResponseQueue.async {
239+
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
240+
}
241+
}
217242
}
243+
}
218244

219-
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
220-
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
221-
return
222-
}
245+
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
223246
224247
switch T.self {
225248
case is Void.Type:
@@ -297,22 +320,7 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
297320
}
298321

299322
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
300-
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
301-
302-
if let error = error {
303-
completion(.failure(ErrorResponse.error(-1, data, response, error)))
304-
return
305-
}
306-
307-
guard let httpResponse = response as? HTTPURLResponse else {
308-
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
309-
return
310-
}
311-
312-
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
313-
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
314-
return
315-
}
323+
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
316324
317325
switch T.self {
318326
case is String.Type:
@@ -353,9 +361,9 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
353361
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
354362

355363
} catch let requestParserError as DownloadException {
356-
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
364+
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
357365
} catch {
358-
completion(.failure(ErrorResponse.error(400, data, response, error)))
366+
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
359367
}
360368

361369
case is Void.Type:
@@ -372,7 +380,7 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
372380
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
373381
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
374382
} else {
375-
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
383+
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
376384
}
377385
return
378386
}
@@ -383,7 +391,7 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
383391
case let .success(decodableObj):
384392
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
385393
case let .failure(error):
386-
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
394+
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
387395
}
388396
}
389397
}
@@ -694,7 +702,7 @@ extension JSONDataEncoding: ParameterEncoding {}
694702
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol OpenAPIInterceptor {
695703
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
696704
697-
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
705+
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
698706
}
699707

700708
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
@@ -704,7 +712,7 @@ extension JSONDataEncoding: ParameterEncoding {}
704712
completion(.success(urlRequest))
705713
}
706714

707-
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
715+
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
708716
completion(.dontRetry)
709717
}
710718
}

0 commit comments

Comments
 (0)