Skip to content

Commit a5a5e07

Browse files
authored
feat(interceptor): Make public rescue API async (#22)
1 parent 79e1655 commit a5a5e07

File tree

5 files changed

+51
-40
lines changed

5 files changed

+51
-40
lines changed

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ excluded:
99
disabled_rules:
1010
- statement_position
1111
- type_name
12+
- trailing_newline
1213

1314
opt_in_rules:
1415
- closure_end_indentation

Sources/SimpleHTTP/Interceptor/CompositeInterceptor.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,12 @@ extension CompositeInterceptor: Interceptor {
2121
}
2222
}
2323

24-
public func rescueRequest<Output>(_ request: Request<Output>, error: Error) -> AnyPublisher<Void, Error>? {
25-
let publishers = compactMap { $0.rescueRequest(request, error: error) }
26-
27-
guard !publishers.isEmpty else {
28-
return nil
24+
public func shouldRescueRequest<Output>(_ request: Request<Output>, error: Error) async throws -> Bool {
25+
for interceptor in interceptors where try await interceptor.shouldRescueRequest(request, error: error) {
26+
return true
2927
}
3028

31-
return Publishers.MergeMany(publishers).eraseToAnyPublisher()
29+
return false
3230
}
3331

3432
public func adaptOutput<Output>(_ response: Output, for request: Request<Output>) throws -> Output {

Sources/SimpleHTTP/Interceptor/Interceptor.swift

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public protocol RequestInterceptor {
1111
/// catch and retry a failed request
1212
/// - Returns: nil if the request should not be retried. Otherwise a publisher that will be executed before
1313
/// retrying the request
14-
func rescueRequest<Output>(_ request: Request<Output>, error: Error) -> AnyPublisher<Void, Error>?
14+
func shouldRescueRequest<Output>(_ request: Request<Output>, error: Error) async throws -> Bool
1515
}
1616

1717
/// a protocol intercepting a session response
@@ -25,32 +25,3 @@ public protocol ResponseInterceptor {
2525
/// - Parameter request: the request that was sent to the server
2626
func receivedResponse<Output>(_ result: Result<Output, Error>, for request: Request<Output>)
2727
}
28-
29-
extension RequestInterceptor {
30-
func shouldRescueRequest<Output>(_ request: Request<Output>, error: Error) async throws -> Bool {
31-
var cancellable: Set<AnyCancellable> = []
32-
let onCancel = { cancellable.removeAll() }
33-
34-
guard let rescuePublisher = rescueRequest(request, error: error) else {
35-
return false
36-
}
37-
38-
return try await withTaskCancellationHandler(
39-
handler: { onCancel() },
40-
operation: {
41-
try await withCheckedThrowingContinuation { continuation in
42-
rescuePublisher
43-
.sink(
44-
receiveCompletion: {
45-
if case let .failure(error) = $0 {
46-
return continuation.resume(throwing: error)
47-
}
48-
},
49-
receiveValue: { _ in
50-
continuation.resume(returning: true)
51-
})
52-
.store(in: &cancellable)
53-
}
54-
})
55-
}
56-
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import XCTest
2+
import SimpleHTTP
3+
4+
class CompositeInterceptorTests: XCTestCase {
5+
func test_shouldRescue_moreThanOneInterceptorRescue_callFirstOneOnly() async throws {
6+
let interceptors: CompositeInterceptor = [
7+
InterceptorStub(shouldRequestMock: { _ in false }),
8+
InterceptorStub(shouldRequestMock: { _ in true }),
9+
InterceptorStub(shouldRequestMock: { _ in
10+
XCTFail("should not be called because request was already rescued")
11+
return true
12+
})
13+
]
14+
15+
let result = try await interceptors
16+
.shouldRescueRequest(Request<Void>.get("/test"), error: HTTPError(statusCode: 888))
17+
18+
XCTAssertTrue(result)
19+
}
20+
}
21+
22+
private struct InterceptorStub: Interceptor {
23+
var shouldRequestMock: (Error) throws -> Bool = { _ in false }
24+
25+
func shouldRescueRequest<Output>(_ request: Request<Output>, error: Error) async throws -> Bool {
26+
try shouldRequestMock(error)
27+
}
28+
29+
func adaptRequest<Output>(_ request: Request<Output>) -> Request<Output> {
30+
request
31+
}
32+
33+
func adaptOutput<Output>(_ output: Output, for request: Request<Output>) throws -> Output {
34+
output
35+
}
36+
37+
func receivedResponse<Output>(_ result: Result<Output, Error>, for request: Request<Output>) {
38+
39+
}
40+
41+
}

Tests/SimpleHTTPTests/Session/SessionTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class SessionAsyncTests: XCTestCase {
4747

4848
interceptor.rescueRequestErrorMock = { _ in
4949
isRescued.toggle()
50-
return Just(()).setFailureType(to: Error.self).eraseToAnyPublisher()
50+
return true
5151
}
5252

5353
_ = try await session.response(for: .void())
@@ -120,16 +120,16 @@ private extension Request {
120120
}
121121

122122
private class InterceptorStub: Interceptor {
123-
var rescueRequestErrorMock: ((Error) -> AnyPublisher<Void, Error>?)?
123+
var rescueRequestErrorMock: (Error) throws -> Bool = { _ in false }
124124
var receivedResponseMock: ((Any, Any) -> Void)?
125125
var adaptResponseMock: ((Any, Any) throws -> Any)?
126126

127127
func adaptRequest<Output>(_ request: Request<Output>) -> Request<Output> {
128128
request
129129
}
130130

131-
func rescueRequest<Output>(_ request: Request<Output>, error: Error) -> AnyPublisher<Void, Error>? {
132-
rescueRequestErrorMock?(error)
131+
func shouldRescueRequest<Output>(_ request: Request<Output>, error: Error) async throws -> Bool {
132+
try rescueRequestErrorMock(error)
133133
}
134134

135135
func adaptOutput<Output>(_ output: Output, for request: Request<Output>) throws -> Output {

0 commit comments

Comments
 (0)