forked from loafle/openapi-generator-original
[swift6] improve retry interceptor (#19988)
* [swift6] improve retry interceptor * [swift6] improve retry interceptor * [swift6] improve retry interceptor
This commit is contained in:
parent
9452873b99
commit
e9ea12f25a
@ -350,7 +350,14 @@ First you implement the `OpenAPIInterceptor` protocol.
|
|||||||
public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
||||||
public func intercept(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, completion: @escaping (Result<URLRequest, any Error>) -> Void) {
|
public func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, any Error>) -> Void) {
|
||||||
|
|
||||||
|
guard requestBuilder.requiresAuthentication else {
|
||||||
|
// no authentication required
|
||||||
|
completion(.success(urlRequest))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
refreshTokenIfDoesntExist { token in
|
refreshTokenIfDoesntExist { token in
|
||||||
|
|
||||||
// Change the current url request
|
// Change the current url request
|
||||||
@ -358,13 +365,13 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
newUrlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
newUrlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
||||||
|
|
||||||
// Change the global headers
|
// Change the global headers
|
||||||
openAPIClient.customHeaders["Authorization"] = "Bearer \(token)"
|
requestBuilder.openAPIClient.customHeaders["Authorization"] = "Bearer \(token)"
|
||||||
|
|
||||||
completion(.success(newUrlRequest))
|
completion(.success(newUrlRequest))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry(urlRequest: URLRequest, urlSession: URLSessionProtocol, openAPIClient: OpenAPIClient, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
// 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
|
// 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
|
||||||
refreshTokenIfUnauthorizedRequestResponse(
|
refreshTokenIfUnauthorizedRequestResponse(
|
||||||
data: data,
|
data: data,
|
||||||
@ -375,7 +382,7 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
if wasTokenRefreshed, let newToken = newToken {
|
if wasTokenRefreshed, let newToken = newToken {
|
||||||
|
|
||||||
// Change the global headers
|
// Change the global headers
|
||||||
openAPIClient.customHeaders["Authorization"] = "Bearer \(newToken)"
|
requestBuilder.openAPIClient.customHeaders["Authorization"] = "Bearer \(newToken)"
|
||||||
|
|
||||||
completion(.retry)
|
completion(.retry)
|
||||||
} else {
|
} else {
|
||||||
@ -397,7 +404,7 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse, error: Error, completionHandler: @escaping (Bool, String?) -> Void) {
|
func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error, completionHandler: @escaping (Bool, String?) -> Void) {
|
||||||
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
||||||
startRefreshingToken { token in
|
startRefreshingToken { token in
|
||||||
completionHandler(true, token)
|
completionHandler(true, token)
|
||||||
|
@ -156,23 +156,47 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ fileprivate class URLSessionRequestBuilderConfiguration: @unchecked Sendable {
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ extension JSONDataEncoding: ParameterEncoding {}
|
|||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol OpenAPIInterceptor {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ extension JSONDataEncoding: ParameterEncoding {}
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ internal class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendab
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ internal class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ internal class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendab
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
internal class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ internal class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionReques
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ internal class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionReques
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ internal class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionReques
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ internal enum OpenAPIInterceptorRetry {
|
|||||||
internal protocol OpenAPIInterceptor {
|
internal protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
internal class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ internal class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
// 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
|
// 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
|
||||||
refreshTokenIfUnauthorizedRequestResponse(
|
refreshTokenIfUnauthorizedRequestResponse(
|
||||||
data: data,
|
data: data,
|
||||||
@ -66,7 +66,7 @@ public class BearerOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse, error: Error, completionHandler: @escaping (Bool, String?) -> Void) {
|
func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error, completionHandler: @escaping (Bool, String?) -> Void) {
|
||||||
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
||||||
startRefreshingToken { token in
|
startRefreshingToken { token in
|
||||||
completionHandler(true, token)
|
completionHandler(true, token)
|
||||||
|
@ -156,23 +156,47 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
case .success(let modifiedRequest):
|
case .success(let modifiedRequest):
|
||||||
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
let dataTask = urlSession.dataTaskFromProtocol(with: modifiedRequest) { data, response, error in
|
||||||
self.cleanupRequest()
|
self.cleanupRequest()
|
||||||
if let response, let error {
|
|
||||||
self.openAPIClient.interceptor.retry(urlRequest: modifiedRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
|
||||||
switch retry {
|
|
||||||
case .retry:
|
|
||||||
self.execute(completion: completion)
|
|
||||||
|
|
||||||
case .dontRetry:
|
if let error = error {
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.retryRequest(
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -1,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: error,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: -2,
|
||||||
|
data: data,
|
||||||
|
response: response,
|
||||||
|
error: DecodableRequestBuilderError.nilHTTPResponse,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard self.openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
||||||
|
self.retryRequest(
|
||||||
|
urlRequest: modifiedRequest,
|
||||||
|
urlSession: urlSession,
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
data: data,
|
||||||
|
response: httpResponse,
|
||||||
|
error: DecodableRequestBuilderError.unsuccessfulHTTPStatusCode,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.openAPIClient.apiResponseQueue.async {
|
self.processRequestResponse(urlRequest: request, data: data, httpResponse: httpResponse, error: error, completion: completion)
|
||||||
self.processRequestResponse(urlRequest: request, data: data, response: response, error: error, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onProgressReady?(dataTask.progress)
|
self.onProgressReady?(dataTask.progress)
|
||||||
@ -204,22 +228,21 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
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) {
|
||||||
|
self.openAPIClient.interceptor.retry(urlRequest: urlRequest, urlSession: urlSession, requestBuilder: self, data: data, response: response, error: error) { retry in
|
||||||
|
switch retry {
|
||||||
|
case .retry:
|
||||||
|
self.execute(completion: completion)
|
||||||
|
|
||||||
if let error = error {
|
case .dontRetry:
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
self.openAPIClient.apiResponseQueue.async {
|
||||||
return
|
completion(.failure(ErrorResponse.error(statusCode, data, response, error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -297,22 +320,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T>, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBuilder<T>, @unchecked Sendable {
|
||||||
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, response: URLResponse?, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
override fileprivate func processRequestResponse(urlRequest: URLRequest, data: Data?, httpResponse: HTTPURLResponse, error: Error?, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
completion(.failure(ErrorResponse.error(-1, data, response, error)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(.failure(ErrorResponse.error(-2, data, response, DecodableRequestBuilderError.nilHTTPResponse)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard openAPIClient.successfulStatusCodeRange.contains(httpResponse.statusCode) else {
|
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, data, response, DecodableRequestBuilderError.unsuccessfulHTTPStatusCode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch T.self {
|
switch T.self {
|
||||||
case is String.Type:
|
case is String.Type:
|
||||||
@ -353,9 +361,9 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: filePath as! T, bodyData: data)))
|
||||||
|
|
||||||
} catch let requestParserError as DownloadException {
|
} catch let requestParserError as DownloadException {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, requestParserError)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, requestParserError)))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(ErrorResponse.error(400, data, response, error)))
|
completion(.failure(ErrorResponse.error(400, data, httpResponse, error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case is Void.Type:
|
case is Void.Type:
|
||||||
@ -372,7 +380,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
if let expressibleByNilLiteralType = T.self as? ExpressibleByNilLiteral.Type {
|
||||||
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
completion(.success(Response(response: httpResponse, body: expressibleByNilLiteralType.init(nilLiteral: ()) as! T, bodyData: data)))
|
||||||
} else {
|
} else {
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, response, DecodableRequestBuilderError.emptyDataResponse)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, nil, httpResponse, DecodableRequestBuilderError.emptyDataResponse)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -383,7 +391,7 @@ open class URLSessionDecodableRequestBuilder<T: Decodable>: URLSessionRequestBui
|
|||||||
case let .success(decodableObj):
|
case let .success(decodableObj):
|
||||||
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
completion(.success(Response(response: httpResponse, body: decodableObj, bodyData: unwrappedData)))
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, response, error)))
|
completion(.failure(ErrorResponse.error(httpResponse.statusCode, unwrappedData, httpResponse, error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +702,7 @@ public enum OpenAPIInterceptorRetry {
|
|||||||
public protocol OpenAPIInterceptor {
|
public protocol OpenAPIInterceptor {
|
||||||
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
func intercept<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
||||||
|
|
||||||
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
||||||
@ -704,7 +712,7 @@ public class DefaultOpenAPIInterceptor: OpenAPIInterceptor {
|
|||||||
completion(.success(urlRequest))
|
completion(.success(urlRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
public func retry<T>(urlRequest: URLRequest, urlSession: URLSessionProtocol, requestBuilder: RequestBuilder<T>, data: Data?, response: URLResponse?, error: Error, completion: @escaping (OpenAPIInterceptorRetry) -> Void) {
|
||||||
completion(.dontRetry)
|
completion(.dontRetry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user