refactor: use default server from network service, simplify recoverable requests

This commit is contained in:
Ivan Smolin 2022-03-04 20:07:41 +03:00
parent b52dd87f55
commit b42fcd596f
6 changed files with 30 additions and 103 deletions

View File

@ -35,11 +35,14 @@ open class DefaultJsonNetworkService {
public var jsonDecoder: JSONDecoder
public var jsonEncoder: JSONEncoder
public var defaultServer: Server
public var plugins: [PluginType] = []
public init(session: Session,
jsonDecoder: JSONDecoder,
jsonEncoder: JSONEncoder,
defaultServer: Server,
serializationQueue: DispatchQueue = .global(qos: .default),
callbackQueue: DispatchQueue = .main) {
@ -48,6 +51,7 @@ open class DefaultJsonNetworkService {
self.callbackQueue = callbackQueue
self.jsonDecoder = jsonDecoder
self.jsonEncoder = jsonEncoder
self.defaultServer = defaultServer
}
open func createProvider() -> MoyaProvider<SerializedRequest> {
@ -101,14 +105,15 @@ open class DefaultJsonNetworkService {
mapMoyaError: @escaping Closure<MoyaError, R>,
completion: @escaping ParameterClosure<R>) -> Cancellable {
ScopeCancellable { [jsonEncoder, serializationQueue, callbackQueue] scope in
ScopeCancellable { [jsonEncoder, serializationQueue, callbackQueue, defaultServer] scope in
let workItem = DispatchWorkItem {
guard !scope.isCancelled else {
return
}
do {
let serializedRequest = try request.serialize(using: ApplicationJsonBodySerializer(jsonEncoder: jsonEncoder))
let serializedRequest = try request.serialize(using: ApplicationJsonBodySerializer(jsonEncoder: jsonEncoder),
defaultServer: defaultServer)
scope.add(cancellable: self.process(request: serializedRequest,
decodableSuccessStatusCodes: decodableSuccessStatusCodes,

View File

@ -30,41 +30,33 @@ open class DefaultRecoverableJsonNetworkService<ApiError: Decodable & Error>: De
private var defaultErrorHandlers: [ErrorHandler] = []
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 {
public func process<B: Encodable, S: Decodable>(request: EndpointRequest<B, S>,
prependErrorHandlers: [ErrorHandler] = [],
appendErrorHandlers: [ErrorHandler] = [],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<S, ApiError> {
await process(recoverableRequest: recoverableRequest,
await process(request: request,
errorHandlers: prependErrorHandlers + defaultErrorHandlers + appendErrorHandlers,
mapMoyaError: mapMoyaError)
}
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 {
public func process<B: Encodable, S: Decodable>(request: EndpointRequest<B, S>,
errorHandlers: [ErrorHandler] = [],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<S, ApiError> {
switch recoverableRequest.create() {
case let .success(endpointRequest):
let result = await process(request: endpointRequest, mapMoyaError: mapMoyaError) as Result<RF.SuccessResponse, ApiError>
let result = await process(request: request, mapMoyaError: mapMoyaError)
switch result {
case let .failure(errorResponse):
let chain = AsyncEventHandlingChain(handlers: errorHandlers)
if case let .failure(errorResponse) = result {
let chain = AsyncEventHandlingChain(handlers: errorHandlers)
if await chain.handle(errorResponse) {
return await process(recoverableRequest: recoverableRequest,
errorHandlers: errorHandlers,
mapMoyaError: mapMoyaError)
} else {
return result
}
default:
return result
if await chain.handle(errorResponse) {
return await process(request: request,
errorHandlers: errorHandlers,
mapMoyaError: mapMoyaError)
}
case let .failure(error):
return .failure(error)
}
return result
}
public func register<ErrorHandler: AsyncErrorHandler>(defaultErrorHandler: ErrorHandler) where ErrorHandler.EventType == ApiError {

View File

@ -1,31 +0,0 @@
//
// 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.
//
import TINetworking
public protocol RequestFactory {
associatedtype Body
associatedtype SuccessResponse
associatedtype CreateFailure: Error
func create() -> Result<EndpointRequest<Body, SuccessResponse>, CreateFailure>
}

View File

@ -1,41 +0,0 @@
//
// 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.
//
import TINetworking
public struct StaticRequestFactory<Body, SuccessResponse, CreateFailure: Error>: RequestFactory {
let request: EndpointRequest<Body, SuccessResponse>
public init(request: EndpointRequest<Body, SuccessResponse>) {
self.request = request
}
public func create() -> Result<EndpointRequest<Body, SuccessResponse>, CreateFailure> {
.success(request)
}
}
public extension EndpointRequest {
func staticRequestFactory<CreateFailure: Error>() -> StaticRequestFactory<Body, SuccessResponse, CreateFailure> {
.init(request: self)
}
}

View File

@ -24,8 +24,10 @@ import Alamofire
import Foundation
public extension EndpointRequest {
func serialize<Serializer: BodySerializer>(using serializer: Serializer) throws -> SerializedRequest where Serializer.Body == Body {
let baseUrl = try server.url(using: customServerVariables)
func serialize<Serializer: BodySerializer>(using serializer: Serializer,
defaultServer: Server) throws -> SerializedRequest where Serializer.Body == Body {
let baseUrl = try (server ?? defaultServer).url(using: customServerVariables)
let path = PathParameterEncoding(templateUrl: templatePath).encode(parameters: pathParameters)
let (contentType, bodyData) = try serializer.serialize(body: body)
let queryParameters = QueryStringParameterEncoding().encode(parameters: queryParameters)

View File

@ -31,7 +31,7 @@ public struct EndpointRequest<Body, SuccessResponse> {
public var headerParameters: HTTPHeaders?
public var cookieParameters: [String: Parameter<LocationCookie>]
public var acceptableStatusCodes: Set<Int>
public var server: Server
public var server: Server?
public var customServerVariables: [KeyValueTuple<String, String>]
public init(templatePath: String,
@ -42,7 +42,7 @@ public struct EndpointRequest<Body, SuccessResponse> {
headerParameters: HTTPHeaders? = nil,
cookieParameters: [String: Parameter<LocationCookie>] = [:],
acceptableStatusCodes: Set<Int> = HTTPCodes.success.asSet(),
server: Server,
server: Server? = nil,
customServerVariables: [KeyValueTuple<String, String>] = []) {
self.templatePath = templatePath