Skip to content
This repository was archived by the owner on Apr 17, 2024. It is now read-only.

Commit fa3c3b5

Browse files
committed
test combine
1 parent 5ff290e commit fa3c3b5

File tree

1,605 files changed

+79817
-108056
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,605 files changed

+79817
-108056
lines changed

.openapi-generator/FILES

+548-1,062
Large diffs are not rendered by default.

.openapi-generator/VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.2.0
1+
7.1.0

Cartfile

-1
This file was deleted.

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ build:
1313
docker run \
1414
--rm -v ${PWD}:/local \
1515
--user ${UID}:${GID} \
16-
docker.io/openapitools/openapi-generator-cli:v6.2.0 generate \
16+
docker.io/openapitools/openapi-generator-cli:v7.1.0 generate \
1717
-i /local/schema.yml \
18-
-g swift5 \
18+
-g swift-combine \
1919
-o /local \
2020
-c /local/config.yaml
2121
rm -rf ./test

OpenAPITransport/Package.swift

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// swift-tools-version:5.1
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "OpenAPITransport",
7+
platforms: [
8+
.iOS(.v13),
9+
.macOS(.v10_15)
10+
],
11+
products: [
12+
.library(
13+
name: "OpenAPITransport",
14+
targets: ["OpenAPITransport"]
15+
),
16+
],
17+
dependencies: [],
18+
targets: [
19+
.target(
20+
name: "OpenAPITransport",
21+
dependencies: [],
22+
path: "Sources"
23+
),
24+
]
25+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
// OpenAPITransport.swift
2+
//
3+
// Generated by openapi-generator
4+
// https://openapi-generator.tech
5+
6+
import Foundation
7+
import Combine
8+
9+
// MARK: - OpenAPITransport
10+
11+
public protocol OpenAPITransport: AnyObject {
12+
var baseURL: URL? { get }
13+
14+
func send(request: URLRequest) -> AnyPublisher<OpenAPITransportResponse, OpenAPITransportError>
15+
16+
func cancelAll()
17+
}
18+
19+
public struct OpenAPITransportResponse {
20+
public let data: Data
21+
public let statusCode: Int
22+
23+
public init(data: Data, statusCode: Int) {
24+
self.data = data
25+
self.statusCode = statusCode
26+
}
27+
}
28+
29+
public struct OpenAPITransportError: Error, CustomStringConvertible, LocalizedError {
30+
public let statusCode: Int
31+
public let description: String
32+
public let errorDescription: String?
33+
/// It might be source network error
34+
public let nestedError: Error?
35+
/// Data may contain additional reason info (like json payload)
36+
public let data: Data
37+
38+
public init(
39+
statusCode: Int,
40+
description: String? = nil,
41+
errorDescription: String? = nil,
42+
nestedError: Error? = nil,
43+
data: Data = Data()
44+
) {
45+
self.statusCode = statusCode
46+
self.errorDescription = errorDescription
47+
self.nestedError = nestedError
48+
self.data = data
49+
if let description = description {
50+
self.description = description
51+
} else {
52+
var summary = "OpenAPITransportError with status \(statusCode)"
53+
if let nestedError = nestedError {
54+
summary.append(contentsOf: ", \(nestedError.localizedDescription)")
55+
}
56+
self.description = summary
57+
}
58+
}
59+
}
60+
61+
// MARK: - Policy
62+
63+
/// Policy to define whether response is successful or requires authentication
64+
public protocol ResponsePolicy {
65+
func defineState(for request: URLRequest, output: URLSession.DataTaskPublisher.Output) -> AnyPublisher<ResponseState, Never>
66+
}
67+
68+
public enum ResponseState {
69+
/// Return success to client
70+
case success
71+
/// Return error to client
72+
case failure
73+
/// Repeat request
74+
case retry
75+
}
76+
77+
// MARK: - Interceptor
78+
79+
/// Define how to customize URL request before network call
80+
public protocol Interceptor {
81+
/// Customize request before performing. Add headers or encrypt body for example.
82+
func intercept(request: URLRequest) -> AnyPublisher<URLRequest, OpenAPITransportError>
83+
84+
/// Customize response before handling. Decrypt body for example.
85+
func intercept(output: URLSession.DataTaskPublisher.Output) -> AnyPublisher<URLSession.DataTaskPublisher.Output, OpenAPITransportError>
86+
}
87+
88+
// MARK: - Transport delegate
89+
90+
public protocol OpenAPITransportDelegate: AnyObject {
91+
func willStart(request: URLRequest)
92+
93+
func didFinish(request: URLRequest, response: HTTPURLResponse?, data: Data)
94+
95+
func didFinish(request: URLRequest, error: Error)
96+
}
97+
98+
// MARK: - Implementation
99+
100+
open class URLSessionOpenAPITransport: OpenAPITransport {
101+
public struct Config {
102+
public var baseURL: URL?
103+
public var session: URLSession
104+
public var processor: Interceptor
105+
public var policy: ResponsePolicy
106+
public weak var delegate: OpenAPITransportDelegate?
107+
108+
public init(
109+
baseURL: URL? = nil,
110+
session: URLSession = .shared,
111+
processor: Interceptor = DefaultInterceptor(),
112+
policy: ResponsePolicy = DefaultResponsePolicy(),
113+
delegate: OpenAPITransportDelegate? = nil
114+
) {
115+
self.baseURL = baseURL
116+
self.session = session
117+
self.processor = processor
118+
self.policy = policy
119+
self.delegate = delegate
120+
}
121+
}
122+
123+
private var cancellable = Set<AnyCancellable>()
124+
public var config: Config
125+
public var baseURL: URL? { config.baseURL }
126+
127+
public init(config: Config = .init()) {
128+
self.config = config
129+
}
130+
131+
open func send(request: URLRequest) -> AnyPublisher<OpenAPITransportResponse, OpenAPITransportError> {
132+
config.processor
133+
// Add custom headers or refresh token if needed
134+
.intercept(request: request)
135+
.flatMap { request -> AnyPublisher<OpenAPITransportResponse, OpenAPITransportError> in
136+
self.config.delegate?.willStart(request: request)
137+
// Perform network call
138+
return self.config.session.dataTaskPublisher(for: request)
139+
.mapError {
140+
self.config.delegate?.didFinish(request: request, error: $0)
141+
return OpenAPITransportError(statusCode: $0.code.rawValue, description: "Network call finished fails")
142+
}
143+
.flatMap { output in
144+
self.config.processor.intercept(output: output)
145+
}
146+
.flatMap { output -> AnyPublisher<OpenAPITransportResponse, OpenAPITransportError> in
147+
let response = output.response as? HTTPURLResponse
148+
self.config.delegate?.didFinish(request: request, response: response, data: output.data)
149+
return self.config.policy.defineState(for: request, output: output)
150+
.setFailureType(to: OpenAPITransportError.self)
151+
.flatMap { state -> AnyPublisher<OpenAPITransportResponse, OpenAPITransportError> in
152+
switch state {
153+
case .success:
154+
let transportResponse = OpenAPITransportResponse(data: output.data, statusCode: 200)
155+
return Result.success(transportResponse).publisher.eraseToAnyPublisher()
156+
case .retry:
157+
return Fail(error: OpenAPITransportError.retryError).eraseToAnyPublisher()
158+
case .failure:
159+
let code = response?.statusCode ?? OpenAPITransportError.noResponseCode
160+
let transportError = OpenAPITransportError(statusCode: code, data: output.data)
161+
return Fail(error: transportError).eraseToAnyPublisher()
162+
}
163+
}.eraseToAnyPublisher()
164+
}
165+
.eraseToAnyPublisher()
166+
}
167+
.retry(times: 2) { error -> Bool in
168+
return error.statusCode == OpenAPITransportError.retryError.statusCode
169+
}.eraseToAnyPublisher()
170+
}
171+
172+
open func cancelAll() {
173+
cancellable.removeAll()
174+
}
175+
}
176+
177+
public final class DefaultInterceptor: Interceptor {
178+
public init() {}
179+
180+
public func intercept(request: URLRequest) -> AnyPublisher<URLRequest, OpenAPITransportError> {
181+
Just(request)
182+
.setFailureType(to: OpenAPITransportError.self)
183+
.eraseToAnyPublisher()
184+
}
185+
186+
public func intercept(output: URLSession.DataTaskPublisher.Output) -> AnyPublisher<URLSession.DataTaskPublisher.Output, OpenAPITransportError> {
187+
Just(output)
188+
.setFailureType(to: OpenAPITransportError.self)
189+
.eraseToAnyPublisher()
190+
}
191+
}
192+
193+
public final class DefaultResponsePolicy: ResponsePolicy {
194+
public init() {}
195+
196+
public func defineState(for request: URLRequest, output: URLSession.DataTaskPublisher.Output) -> AnyPublisher<ResponseState, Never> {
197+
let state: ResponseState
198+
switch (output.response as? HTTPURLResponse)?.statusCode {
199+
case .some(200...299): state = .success
200+
default: state = .failure
201+
}
202+
return Just(state).eraseToAnyPublisher()
203+
}
204+
}
205+
206+
/// Custom transport errors. It begins with 6.. not to conflict with HTTP codes
207+
public extension OpenAPITransportError {
208+
static let incorrectAuthenticationCode = 600
209+
static func incorrectAuthenticationError(_ nestedError: Error? = nil) -> OpenAPITransportError {
210+
OpenAPITransportError(
211+
statusCode: OpenAPITransportError.incorrectAuthenticationCode,
212+
description: "Impossible to add authentication headers to request",
213+
errorDescription: NSLocalizedString(
214+
"Impossible to add authentication headers to request",
215+
comment: "Incorrect authentication"
216+
),
217+
nestedError: nestedError
218+
)
219+
}
220+
221+
static let failedAuthenticationRefreshCode = 601
222+
static func failedAuthenticationRefreshError(_ nestedError: Error? = nil) -> OpenAPITransportError {
223+
OpenAPITransportError(
224+
statusCode: OpenAPITransportError.failedAuthenticationRefreshCode,
225+
description: "Error while refreshing authentication",
226+
errorDescription: NSLocalizedString(
227+
"Error while refreshing authentication",
228+
comment: "Failed authentication refresh"
229+
),
230+
nestedError: nestedError
231+
)
232+
}
233+
234+
static let noResponseCode = 603
235+
static func noResponseError(_ nestedError: Error? = nil) -> OpenAPITransportError {
236+
OpenAPITransportError(
237+
statusCode: OpenAPITransportError.noResponseCode,
238+
description: "There is no HTTP URL response",
239+
errorDescription: NSLocalizedString(
240+
"There is no HTTP URL response",
241+
comment: "No response"
242+
),
243+
nestedError: nestedError
244+
)
245+
}
246+
247+
static let badURLCode = 604
248+
static func badURLError(_ nestedError: Error? = nil) -> OpenAPITransportError {
249+
OpenAPITransportError(
250+
statusCode: OpenAPITransportError.badURLCode,
251+
description: "Request URL cannot be created with given parameters",
252+
errorDescription: NSLocalizedString(
253+
"Request URL cannot be created with given parameters",
254+
comment: "Bad URL"
255+
),
256+
nestedError: nestedError
257+
)
258+
}
259+
260+
static let invalidResponseMappingCode = 605
261+
static func invalidResponseMappingError(data: Data) -> OpenAPITransportError {
262+
OpenAPITransportError(
263+
statusCode: OpenAPITransportError.invalidResponseMappingCode,
264+
description: "Response data cannot be expected object scheme",
265+
errorDescription: NSLocalizedString(
266+
"Response data cannot be expected object scheme",
267+
comment: "Invalid response mapping"
268+
),
269+
data: data
270+
)
271+
}
272+
273+
static let retryErrorCode = 606
274+
static let retryError = OpenAPITransportError(statusCode: OpenAPITransportError.retryErrorCode)
275+
}
276+
277+
// MARK: - Private
278+
279+
private extension Publishers {
280+
struct RetryIf<P: Publisher>: Publisher {
281+
typealias Output = P.Output
282+
typealias Failure = P.Failure
283+
284+
let publisher: P
285+
let times: Int
286+
let condition: (P.Failure) -> Bool
287+
288+
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
289+
guard times > 0 else { return publisher.receive(subscriber: subscriber) }
290+
291+
publisher.catch { (error: P.Failure) -> AnyPublisher<Output, Failure> in
292+
if condition(error) {
293+
return RetryIf(publisher: publisher, times: times - 1, condition: condition).eraseToAnyPublisher()
294+
} else {
295+
return Fail(error: error).eraseToAnyPublisher()
296+
}
297+
}.receive(subscriber: subscriber)
298+
}
299+
}
300+
}
301+
302+
private extension Publisher {
303+
func retry(times: Int, if condition: @escaping (Failure) -> Bool) -> Publishers.RetryIf<Self> {
304+
Publishers.RetryIf(publisher: self, times: times, condition: condition)
305+
}
306+
}

Package.swift

-33
This file was deleted.

0 commit comments

Comments
 (0)