Skip to content

Commit 636c94d

Browse files
committed
listener
1 parent 1f13ca1 commit 636c94d

File tree

3 files changed

+118
-33
lines changed

3 files changed

+118
-33
lines changed

Sources/SwiftAPIClient/APIClientCaller.swift

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ public extension APIClient {
229229
try configs.requestValidator.validate(request, configs.with(\.requestValidator, .alwaysSuccess))
230230
if !caller.logRequestByItSelf {
231231
configs.logRequest(request, uuid: uuid)
232+
configs.listener.onRequestStarted(id: uuid, request: request, configs: configs)
232233
}
233234

234235
return try caller.call(uuid: uuid, request: request, configs: configs) { response, validate in
@@ -240,6 +241,7 @@ public extension APIClient {
240241
updateTotalResponseMetrics(for: request, successful: true)
241242
}
242243
#endif
244+
configs.listener.onResponseSerialized(id: uuid, response: result, configs: configs)
243245
return result
244246
} catch {
245247
#if canImport(Metrics)
@@ -253,37 +255,49 @@ public extension APIClient {
253255
response: response as? Data,
254256
fileIDLine: fileIDLine
255257
)
256-
if let data = response as? Data, let failure = configs.errorDecoder.decodeError(data, configs) {
257-
try configs.errorHandler(failure, configs, context)
258-
throw failure
258+
do {
259+
if let data = response as? Data, let failure = configs.errorDecoder.decodeError(data, configs) {
260+
try configs.errorHandler(failure, configs, context)
261+
throw failure
262+
}
263+
try configs.errorHandler(error, configs, context)
264+
throw error
265+
} catch {
266+
configs.listener.onError(id: uuid, error: error, configs: configs)
267+
throw error
259268
}
260-
try configs.errorHandler(error, configs, context)
261-
throw error
262269
}
263270
}
264271
}
265272
} catch {
266-
try withConfigs { configs in
267-
let fileIDLine = configs.fileIDLine ?? FileIDLine(fileID: fileID, line: line)
268-
let configs = configs.with(\.fileIDLine, fileIDLine)
269-
if !configs._errorLoggingComponents.isEmpty {
270-
let message = configs._errorLoggingComponents.errorMessage(
271-
uuid: uuid,
272-
error: error,
273-
maskedHeaders: configs.logMaskedHeaders,
274-
fileIDLine: fileIDLine
275-
)
276-
configs.logger.log(level: configs._errorLogLevel, "\(message)")
273+
do {
274+
try withConfigs { configs in
275+
let fileIDLine = configs.fileIDLine ?? FileIDLine(fileID: fileID, line: line)
276+
let configs = configs.with(\.fileIDLine, fileIDLine)
277+
if !configs._errorLoggingComponents.isEmpty {
278+
let message = configs._errorLoggingComponents.errorMessage(
279+
uuid: uuid,
280+
error: error,
281+
maskedHeaders: configs.logMaskedHeaders,
282+
fileIDLine: fileIDLine
283+
)
284+
configs.logger.log(level: configs._errorLogLevel, "\(message)")
285+
}
286+
#if canImport(Metrics)
287+
if configs.reportMetrics {
288+
updateTotalErrorsMetrics(for: nil)
289+
}
290+
#endif
291+
let context = APIErrorContext(fileIDLine: fileIDLine)
292+
try configs.errorHandler(error, configs, context)
277293
}
278-
#if canImport(Metrics)
279-
if configs.reportMetrics {
280-
updateTotalErrorsMetrics(for: nil)
294+
throw error
295+
} catch {
296+
withConfigs { configs in
297+
configs.listener.onError(id: uuid, error: error, configs: configs)
281298
}
282-
#endif
283-
let context = APIErrorContext(fileIDLine: fileIDLine)
284-
try configs.errorHandler(error, configs, context)
299+
throw error
285300
}
286-
throw error
287301
}
288302
}
289303
}

Sources/SwiftAPIClient/Clients/HTTPClient.swift

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,10 @@ extension APIClientCaller where Result == AsyncThrowingValue<(Value, HTTPRespons
122122
do {
123123
(value, response) = try await configs.httpClientMiddleware.execute(request: request, configs: configs) { request, configs in
124124
configs.logRequest(request, uuid: uuid)
125+
configs.listener.onRequestStarted(id: uuid, request: request, configs: configs)
125126
await requestWrapper.set(request)
126127
let result = try await task(request, configs)
128+
configs.listener.onResponseReceived(id: uuid, response: result, configs: configs)
127129
await responseWrapper.set(result)
128130
return result
129131
}
@@ -147,17 +149,22 @@ extension APIClientCaller where Result == AsyncThrowingValue<(Value, HTTPRespons
147149
updateHTTPMetrics(for: request, status: response?.1.status, duration: duration, successful: false)
148150
}
149151
#endif
150-
try configs.errorHandler(
151-
error,
152-
configs,
153-
APIErrorContext(
154-
request: request,
155-
response: response.flatMap { data($0.0) },
156-
status: response?.1.status,
157-
fileIDLine: configs.fileIDLine ?? FileIDLine()
152+
do {
153+
try configs.errorHandler(
154+
error,
155+
configs,
156+
APIErrorContext(
157+
request: request,
158+
response: response.flatMap { data($0.0) },
159+
status: response?.1.status,
160+
fileIDLine: configs.fileIDLine ?? FileIDLine()
161+
)
158162
)
159-
)
160-
throw error
163+
throw error
164+
} catch {
165+
configs.listener.onError(id: uuid, error: error, configs: configs)
166+
throw error
167+
}
161168
}
162169
let request = await requestWrapper.value
163170
let duration = Date().timeIntervalSince(start)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import Foundation
2+
3+
public protocol APIClientListener {
4+
5+
func onRequestStarted(id: UUID, request: HTTPRequestComponents, configs: APIClient.Configs)
6+
func onResponseReceived<R>(id: UUID, response: R, configs: APIClient.Configs)
7+
func onResponseSerialized<T>(id: UUID, response: T, configs: APIClient.Configs)
8+
func onError(id: UUID, error: Error, configs: APIClient.Configs)
9+
}
10+
11+
extension APIClientListener {
12+
13+
func onRequestStarted(id: UUID, request: HTTPRequestComponents, configs: APIClient.Configs) {}
14+
func onResponseReceived<R>(id: UUID, response: R, configs: APIClient.Configs) {}
15+
func onResponseSerialized<T>(id: UUID, response: T, configs: APIClient.Configs) {}
16+
func onError(id: UUID, error: Error, configs: APIClient.Configs) {}
17+
}
18+
19+
public struct MultiplexAPIClientListener: APIClientListener {
20+
21+
public var listeners: [APIClientListener]
22+
23+
public init(_ listeners: [APIClientListener]) {
24+
self.listeners = listeners
25+
}
26+
27+
public func onRequestStarted(id: UUID, request: HTTPRequestComponents, configs: APIClient.Configs) {
28+
listeners.forEach { $0.onRequestStarted(id: id, request: request, configs: configs) }
29+
}
30+
31+
public func onResponseReceived<R>(id: UUID, response: R, configs: APIClient.Configs) {
32+
listeners.forEach { $0.onResponseReceived(id: id, response: response, configs: configs) }
33+
}
34+
35+
public func onResponseSerialized<T>(id: UUID, response: T, configs: APIClient.Configs) {
36+
listeners.forEach { $0.onResponseSerialized(id: id, response: response, configs: configs) }
37+
}
38+
39+
public func onError(id: UUID, error: Error, configs: APIClient.Configs) {
40+
listeners.forEach { $0.onError(id: id, error: error, configs: configs) }
41+
}
42+
}
43+
44+
extension APIClient.Configs {
45+
46+
public var listener: APIClientListener {
47+
get { self[\.listener] ?? MultiplexAPIClientListener([]) }
48+
set { self[\.listener] = newValue }
49+
}
50+
}
51+
52+
extension APIClient {
53+
54+
public func listener(_ listener: APIClientListener) -> APIClient {
55+
configs { configs in
56+
if var existingListener = configs.listener as? MultiplexAPIClientListener {
57+
existingListener.listeners.append(listener)
58+
configs.listener = existingListener
59+
} else {
60+
configs.listener = MultiplexAPIClientListener([configs.listener, listener])
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)