Skip to content

Commit 9a0fc59

Browse files
authored
[swift6] urlsession interceptor (#19797)
* [swift6] alamofire interceptor * [swift6] alamofire interceptor * [swift6] urlsession interceptor * [swift6] urlsession interceptor * [swift6] urlsession interceptor
1 parent 4c81563 commit 9a0fc59

File tree

30 files changed

+867
-323
lines changed

30 files changed

+867
-323
lines changed

modules/openapi-generator/src/main/resources/swift6/APIs.mustache

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ import Alamofire{{/useAlamofire}}
3030
/// Configures the range of HTTP status codes that will result in a successful response
3131
///
3232
/// If a HTTP status code is outside of this range the response will be interpreted as failed.
33-
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var successfulStatusCodeRange: Range<Int>{{#useAlamofire}}
33+
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var successfulStatusCodeRange: Range<Int>{{#useURLSession}}
34+
35+
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var interceptor: OpenAPIInterceptor{{/useURLSession}}{{#useAlamofire}}
36+
3437
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var interceptor: RequestInterceptor?
38+
3539
/// ResponseSerializer that will be used by the generator for `Data` responses
3640
///
3741
/// If unchanged, Alamofires default `DataResponseSerializer` will be used.
@@ -52,7 +56,8 @@ import Alamofire{{/useAlamofire}}
5256
requestBuilderFactory: RequestBuilderFactory = {{#useAlamofire}}AlamofireRequestBuilderFactory(){{/useAlamofire}}{{#useURLSession}}URLSessionRequestBuilderFactory(){{/useURLSession}},
5357
apiResponseQueue: DispatchQueue = .main,
5458
codableHelper: CodableHelper = CodableHelper(),
55-
successfulStatusCodeRange: Range<Int> = 200..<300{{#useAlamofire}},
59+
successfulStatusCodeRange: Range<Int> = 200..<300{{#useURLSession}},
60+
interceptor: OpenAPIInterceptor = DefaultOpenAPIInterceptor(){{/useURLSession}}{{#useAlamofire}},
5661
interceptor: RequestInterceptor? = nil,
5762
dataResponseSerializer: AnyResponseSerializer<Data> = AnyResponseSerializer(DataResponseSerializer()),
5863
stringResponseSerializer: AnyResponseSerializer<String> = AnyResponseSerializer(StringResponseSerializer()){{/useAlamofire}}{{/useVapor}}
@@ -67,7 +72,8 @@ import Alamofire{{/useAlamofire}}
6772
self.requestBuilderFactory = requestBuilderFactory
6873
self.apiResponseQueue = apiResponseQueue
6974
self.codableHelper = codableHelper
70-
self.successfulStatusCodeRange = successfulStatusCodeRange{{#useAlamofire}}
75+
self.successfulStatusCodeRange = successfulStatusCodeRange{{#useURLSession}}
76+
self.interceptor = interceptor{{/useURLSession}}{{#useAlamofire}}
7177
self.interceptor = interceptor
7278
self.dataResponseSerializer = dataResponseSerializer
7379
self.stringResponseSerializer = stringResponseSerializer{{/useAlamofire}}{{/useVapor}}

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

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import UniformTypeIdentifiers
2525
}
2626

2727
// Protocol allowing implementations to alter what is returned or to test their implementations.
28-
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol URLSessionProtocol {
28+
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol URLSessionProtocol: Sendable {
2929
// Task which performs the network fetch. Expected to be from URLSession.dataTask(with:completionHandler:) such that a network request
3030
// is sent off when `.resume()` is called.
3131
func dataTaskFromProtocol(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, (any Error)?) -> Void) -> URLSessionDataTaskProtocol
@@ -160,21 +160,46 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
160160
do {
161161
let request = try createURLRequest(urlSession: urlSession, method: xMethod, encoding: encoding, headers: headers)
162162
163-
let dataTask = urlSession.dataTaskFromProtocol(with: request) { data, response, error in
164-
self.openAPIClient.apiResponseQueue.async {
165-
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
163+
openAPIClient.interceptor.intercept(urlRequest: request, urlSession: urlSession, openAPIClient: openAPIClient) { result in
164+
165+
switch result {
166+
case .success(let modifiedRequest):
167+
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
166168
self.cleanupRequest()
167-
}
168-
}
169+
if let response, let error {
170+
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, openAPIClient: self.openAPIClient, data: data, response: response, error: error) { retry in
171+
switch retry {
172+
case .retry:
173+
self.execute(completion: completion)
174+
175+
case .dontRetry:
176+
self.openAPIClient.apiResponseQueue.async {
177+
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
178+
}
179+
}
180+
}
181+
} else {
182+
self.openAPIClient.apiResponseQueue.async {
183+
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
184+
}
185+
}
186+
}
187+
188+
self.onProgressReady?(dataTask.progress)
169189

170-
onProgressReady?(dataTask.progress)
190+
URLSessionRequestBuilderConfiguration.shared.challengeHandlerStore[dataTask.taskIdentifier] = self.taskDidReceiveChallenge
191+
URLSessionRequestBuilderConfiguration.shared.credentialStore[dataTask.taskIdentifier] = self.credential
171192

172-
URLSessionRequestBuilderConfiguration.shared.challengeHandlerStore[dataTask.taskIdentifier] = taskDidReceiveChallenge
173-
URLSessionRequestBuilderConfiguration.shared.credentialStore[dataTask.taskIdentifier] = credential
193+
self.requestTask.set(task: dataTask)
174194

175-
requestTask.set(task: dataTask)
195+
dataTask.resume()
176196

177-
dataTask.resume()
197+
case .failure(let error):
198+
self.openAPIClient.apiResponseQueue.async {
199+
completion(.failure(ErrorResponse.error(415, nil, nil, error)))
200+
}
201+
}
202+
}
178203
} catch {
179204
self.openAPIClient.apiResponseQueue.async {
180205
completion(.failure(ErrorResponse.error(415, nil, nil, error)))
@@ -676,3 +701,26 @@ private extension Optional where Wrapped == Data {
676701
}
677702

678703
extension JSONDataEncoding: ParameterEncoding {}
704+
705+
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} enum OpenAPIInterceptorRetry {
706+
case retry
707+
case dontRetry
708+
}
709+
710+
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol OpenAPIInterceptor {
711+
func intercept(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, completion: @escaping (Result<URLRequest, Error>) -> Void)
712+
713+
func retry(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
714+
}
715+
716+
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
717+
public init() {}
718+
719+
public func intercept(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, completion: @escaping (Result<URLRequest, any Error>) -> Void) {
720+
completion(.success(urlRequest))
721+
}
722+
723+
public func retry(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
724+
completion(.dontRetry)
725+
}
726+
}

samples/client/petstore/swift6/alamofireLibrary/Sources/PetstoreClient/Infrastructure/APIs.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ open class OpenAPIClient: @unchecked Sendable {
2222
///
2323
/// If a HTTP status code is outside of this range the response will be interpreted as failed.
2424
public var successfulStatusCodeRange: Range<Int>
25+
2526
public var interceptor: RequestInterceptor?
27+
2628
/// ResponseSerializer that will be used by the generator for `Data` responses
2729
///
2830
/// If unchanged, Alamofires default `DataResponseSerializer` will be used.

samples/client/petstore/swift6/apiNonStaticMethod/Sources/PetstoreClient/Infrastructure/APIs.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ open class OpenAPIClient: @unchecked Sendable {
2222
///
2323
/// If a HTTP status code is outside of this range the response will be interpreted as failed.
2424
public var successfulStatusCodeRange: Range<Int>
25+
2526
public var interceptor: RequestInterceptor?
27+
2628
/// ResponseSerializer that will be used by the generator for `Data` responses
2729
///
2830
/// If unchanged, Alamofires default `DataResponseSerializer` will be used.

samples/client/petstore/swift6/asyncAwaitLibrary/Sources/PetstoreClient/Infrastructure/APIs.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ open class OpenAPIClient: @unchecked Sendable {
2222
/// If a HTTP status code is outside of this range the response will be interpreted as failed.
2323
public var successfulStatusCodeRange: Range<Int>
2424

25+
public var interceptor: OpenAPIInterceptor
26+
2527
public init(
2628
basePath: String = "http://petstore.swagger.io:80/v2",
2729
customHeaders: [String: String] = [:],
2830
credential: URLCredential? = nil,
2931
requestBuilderFactory: RequestBuilderFactory = URLSessionRequestBuilderFactory(),
3032
apiResponseQueue: DispatchQueue = .main,
3133
codableHelper: CodableHelper = CodableHelper(),
32-
successfulStatusCodeRange: Range<Int> = 200..<300
34+
successfulStatusCodeRange: Range<Int> = 200..<300,
35+
interceptor: OpenAPIInterceptor = DefaultOpenAPIInterceptor()
3336
) {
3437
self.basePath = basePath
3538
self.customHeaders = customHeaders
@@ -38,6 +41,7 @@ open class OpenAPIClient: @unchecked Sendable {
3841
self.apiResponseQueue = apiResponseQueue
3942
self.codableHelper = codableHelper
4043
self.successfulStatusCodeRange = successfulStatusCodeRange
44+
self.interceptor = interceptor
4145
}
4246

4347
public static let shared = OpenAPIClient()

samples/client/petstore/swift6/asyncAwaitLibrary/Sources/PetstoreClient/Infrastructure/URLSessionImplementations.swift

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public protocol URLSessionDataTaskProtocol {
2525
}
2626

2727
// Protocol allowing implementations to alter what is returned or to test their implementations.
28-
public protocol URLSessionProtocol {
28+
public protocol URLSessionProtocol: Sendable {
2929
// Task which performs the network fetch. Expected to be from URLSession.dataTask(with:completionHandler:) such that a network request
3030
// is sent off when `.resume()` is called.
3131
func dataTaskFromProtocol(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, (any Error)?) -> Void) -> URLSessionDataTaskProtocol
@@ -160,21 +160,46 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
160160
do {
161161
let request = try createURLRequest(urlSession: urlSession, method: xMethod, encoding: encoding, headers: headers)
162162

163-
let dataTask = urlSession.dataTaskFromProtocol(with: request) { data, response, error in
164-
self.openAPIClient.apiResponseQueue.async {
165-
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
163+
openAPIClient.interceptor.intercept(urlRequest: request, urlSession: urlSession, openAPIClient: openAPIClient) { result in
164+
165+
switch result {
166+
case .success(let modifiedRequest):
167+
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
166168
self.cleanupRequest()
167-
}
168-
}
169+
if let response, let error {
170+
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, openAPIClient: self.openAPIClient, data: data, response: response, error: error) { retry in
171+
switch retry {
172+
case .retry:
173+
self.execute(completion: completion)
174+
175+
case .dontRetry:
176+
self.openAPIClient.apiResponseQueue.async {
177+
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
178+
}
179+
}
180+
}
181+
} else {
182+
self.openAPIClient.apiResponseQueue.async {
183+
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
184+
}
185+
}
186+
}
187+
188+
self.onProgressReady?(dataTask.progress)
169189

170-
onProgressReady?(dataTask.progress)
190+
URLSessionRequestBuilderConfiguration.shared.challengeHandlerStore[dataTask.taskIdentifier] = self.taskDidReceiveChallenge
191+
URLSessionRequestBuilderConfiguration.shared.credentialStore[dataTask.taskIdentifier] = self.credential
171192

172-
URLSessionRequestBuilderConfiguration.shared.challengeHandlerStore[dataTask.taskIdentifier] = taskDidReceiveChallenge
173-
URLSessionRequestBuilderConfiguration.shared.credentialStore[dataTask.taskIdentifier] = credential
193+
self.requestTask.set(task: dataTask)
174194

175-
requestTask.set(task: dataTask)
195+
dataTask.resume()
176196

177-
dataTask.resume()
197+
case .failure(let error):
198+
self.openAPIClient.apiResponseQueue.async {
199+
completion(.failure(ErrorResponse.error(415, nil, nil, error)))
200+
}
201+
}
202+
}
178203
} catch {
179204
self.openAPIClient.apiResponseQueue.async {
180205
completion(.failure(ErrorResponse.error(415, nil, nil, error)))
@@ -676,3 +701,26 @@ private extension Optional where Wrapped == Data {
676701
}
677702

678703
extension JSONDataEncoding: ParameterEncoding {}
704+
705+
public enum OpenAPIInterceptorRetry {
706+
case retry
707+
case dontRetry
708+
}
709+
710+
public protocol OpenAPIInterceptor {
711+
func intercept(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, completion: @escaping (Result<URLRequest, Error>) -> Void)
712+
713+
func retry(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
714+
}
715+
716+
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
717+
public init() {}
718+
719+
public func intercept(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, completion: @escaping (Result<URLRequest, any Error>) -> Void) {
720+
completion(.success(urlRequest))
721+
}
722+
723+
public func retry(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
724+
completion(.dontRetry)
725+
}
726+
}

samples/client/petstore/swift6/combineDeferredLibrary/PetstoreClient/Classes/OpenAPIs/Infrastructure/APIs.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ open class OpenAPIClient: @unchecked Sendable {
2222
/// If a HTTP status code is outside of this range the response will be interpreted as failed.
2323
public var successfulStatusCodeRange: Range<Int>
2424

25+
public var interceptor: OpenAPIInterceptor
26+
2527
public init(
2628
basePath: String = "http://petstore.swagger.io:80/v2",
2729
customHeaders: [String: String] = [:],
2830
credential: URLCredential? = nil,
2931
requestBuilderFactory: RequestBuilderFactory = URLSessionRequestBuilderFactory(),
3032
apiResponseQueue: DispatchQueue = .main,
3133
codableHelper: CodableHelper = CodableHelper(),
32-
successfulStatusCodeRange: Range<Int> = 200..<300
34+
successfulStatusCodeRange: Range<Int> = 200..<300,
35+
interceptor: OpenAPIInterceptor = DefaultOpenAPIInterceptor()
3336
) {
3437
self.basePath = basePath
3538
self.customHeaders = customHeaders
@@ -38,6 +41,7 @@ open class OpenAPIClient: @unchecked Sendable {
3841
self.apiResponseQueue = apiResponseQueue
3942
self.codableHelper = codableHelper
4043
self.successfulStatusCodeRange = successfulStatusCodeRange
44+
self.interceptor = interceptor
4145
}
4246

4347
public static let shared = OpenAPIClient()

0 commit comments

Comments
 (0)