feat: add SuccessResponse generic argument to EndpointRequest

This commit is contained in:
Ivan Smolin 2022-03-02 11:57:59 +03:00
parent 6ba07b25ad
commit aa2dc3880e
6 changed files with 73 additions and 25 deletions

View File

@ -27,13 +27,15 @@ import TISwiftUtils
import Foundation
open class DefaultJsonNetworkService {
var session: Session
public var session: Session
var serializationQueue: DispatchQueue
var callbackQueue: DispatchQueue
public var serializationQueue: DispatchQueue
public var callbackQueue: DispatchQueue
var jsonDecoder: JSONDecoder
var jsonEncoder: JSONEncoder
public var jsonDecoder: JSONDecoder
public var jsonEncoder: JSONEncoder
public var plugins: [PluginType] = []
public init(session: Session,
jsonDecoder: JSONDecoder,
@ -53,7 +55,7 @@ open class DefaultJsonNetworkService {
}
@available(iOS 13.0.0, *)
public func process<B: Encodable, S: Decodable, F: Decodable>(request: EndpointRequest<B>,
public func process<B: Encodable, S: Decodable, F: Decodable>(request: EndpointRequest<B, S>,
mapMoyaError: @escaping Closure<MoyaError, F>) async -> Result<S, F> {
await process(request: request,
mapSuccess: Result.success,
@ -62,7 +64,7 @@ open class DefaultJsonNetworkService {
}
@available(iOS 13.0.0, *)
public func process<B: Encodable, S: Decodable, F: Decodable, R>(request: EndpointRequest<B>,
public func process<B: Encodable, S: Decodable, F: Decodable, R>(request: EndpointRequest<B, S>,
decodableSuccessStatusCodes: Set<Int>? = nil,
decodableFailureStatusCodes: Set<Int>? = nil,
mapSuccess: @escaping Closure<S, R>,
@ -89,7 +91,7 @@ open class DefaultJsonNetworkService {
})
}
public func process<B: Encodable, S: Decodable, F: Decodable, R>(request: EndpointRequest<B>,
public func process<B: Encodable, S: Decodable, F: Decodable, R>(request: EndpointRequest<B, S>,
decodableSuccessStatusCodes: Set<Int>? = nil,
decodableFailureStatusCodes: Set<Int>? = nil,
mapSuccess: @escaping Closure<S, R>,
@ -156,7 +158,7 @@ open class DefaultJsonNetworkService {
failureStatusCodes = request.acceptableStatusCodes.subtracting(successCodes)
default:
successStatusCodes = [200]
successStatusCodes = HTTPCodes.success.asSet() // default success status codes if nothing was passed
failureStatusCodes = request.acceptableStatusCodes.subtracting(successStatusCodes)
}

View File

@ -30,23 +30,23 @@ open class DefaultRecoverableJsonNetworkService<ApiError: Decodable & Error>: De
private var defaultErrorHandlers: [ErrorHandler] = []
public func process<B: Encodable, S: Decodable, RF: RequestFactory>(recoverableRequest: RF,
prependErrorHandlers: [ErrorHandler] = [],
appendErrorHandlers: [ErrorHandler] = [],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<S, ApiError> where RF.Body == B, RF.CreateFailure == ApiError {
public func process<RF: RequestFactory>(recoverableRequest: RF,
prependErrorHandlers: [ErrorHandler] = [],
appendErrorHandlers: [ErrorHandler] = [],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<RF.SuccessResponse, ApiError> where RF.Body: Encodable, RF.SuccessResponse: Decodable, RF.CreateFailure == ApiError {
await process(recoverableRequest: recoverableRequest,
errorHandlers: prependErrorHandlers + defaultErrorHandlers + appendErrorHandlers,
mapMoyaError: mapMoyaError)
}
public func process<B: Encodable, S: Decodable, RF: RequestFactory>(recoverableRequest: RF,
errorHandlers: [ErrorHandler] = [],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<S, ApiError> where RF.Body == B, RF.CreateFailure == ApiError {
public func process<RF: RequestFactory>(recoverableRequest: RF,
errorHandlers: [ErrorHandler] = [],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<RF.SuccessResponse, ApiError> where RF.Body: Encodable, RF.SuccessResponse: Decodable, RF.CreateFailure == ApiError {
switch recoverableRequest.create() {
case let .success(endpointRequest):
let result = await process(request: endpointRequest, mapMoyaError: mapMoyaError) as Result<S, ApiError>
let result = await process(request: endpointRequest, mapMoyaError: mapMoyaError) as Result<RF.SuccessResponse, ApiError>
switch result {
case let .failure(errorResponse):

View File

@ -24,7 +24,8 @@ import TINetworking
public protocol RequestFactory {
associatedtype Body
associatedtype SuccessResponse
associatedtype CreateFailure: Error
func create() -> Result<EndpointRequest<Body>, CreateFailure>
func create() -> Result<EndpointRequest<Body, SuccessResponse>, CreateFailure>
}

View File

@ -22,20 +22,20 @@
import TINetworking
public struct StaticRequestFactory<Body, CreateFailure: Error>: RequestFactory {
let request: EndpointRequest<Body>
public struct StaticRequestFactory<Body, SuccessResponse, CreateFailure: Error>: RequestFactory {
let request: EndpointRequest<Body, SuccessResponse>
public init(request: EndpointRequest<Body>) {
public init(request: EndpointRequest<Body, SuccessResponse>) {
self.request = request
}
public func create() -> Result<EndpointRequest<Body>, CreateFailure> {
public func create() -> Result<EndpointRequest<Body, SuccessResponse>, CreateFailure> {
.success(request)
}
}
public extension EndpointRequest {
func staticRequestFactory<CreateFailure: Error>() -> StaticRequestFactory<Body, CreateFailure> {
func staticRequestFactory<CreateFailure: Error>() -> StaticRequestFactory<Body, SuccessResponse, CreateFailure> {
.init(request: self)
}
}

View File

@ -0,0 +1,45 @@
//
// Copyright (c) 2022 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
public enum HTTPCodes {
case informational
case success
case redirection
case clientError
case serverError
public func asSet() -> Set<Int> {
switch self {
case .informational:
return Set(100...199)
case .success:
return Set(200...299)
case .redirection:
return Set(300...399)
case .clientError:
return Set(400...499)
case .serverError:
return Set(500...599)
}
}
}

View File

@ -22,7 +22,7 @@
import Alamofire
public struct EndpointRequest<Body> {
public struct EndpointRequest<Body, SuccessResponse> {
public var templatePath: String
public var method: HTTPMethod
public var body: Body
@ -41,7 +41,7 @@ public struct EndpointRequest<Body> {
pathParameters: [String: Parameter<LocationPath>] = [:],
headerParameters: HTTPHeaders? = nil,
cookieParameters: [String: Parameter<LocationCookie>] = [:],
acceptableStatusCodes: Set<Int> = [200],
acceptableStatusCodes: Set<Int> = HTTPCodes.success.asSet(),
server: Server,
customServerVariables: [KeyValueTuple<String, String>] = []) {