@@ -3,7 +3,7 @@ import Foundation
3
3
4
4
protocol Client {
5
5
init ( url: URL , token: String ? )
6
- func user( _ ident: String ) async throws -> User
6
+ func user( _ ident: String ) async throws ( ClientError ) -> User
7
7
}
8
8
9
9
struct CoderClient : Client {
@@ -25,38 +25,117 @@ struct CoderClient: Client {
25
25
func request< T: Encodable > (
26
26
_ path: String ,
27
27
method: HTTPMethod ,
28
- body: T
29
- ) async -> DataResponse < Data , AFError > {
28
+ body: T ? = nil
29
+ ) async throws ( ClientError ) -> HTTPResponse {
30
30
let url = self . url. appendingPathComponent ( path)
31
- let headers : HTTPHeaders = [ Headers . sessionToken: token ?? " " ]
32
- return await AF . request (
31
+ let headers : HTTPHeaders ? = token . map { [ Headers . sessionToken: $0 ] }
32
+ let out = await AF . request (
33
33
url,
34
34
method: method,
35
35
parameters: body,
36
- encoder: JSONParameterEncoder . default,
37
36
headers: headers
38
37
) . serializingData ( ) . response
38
+ switch out. result {
39
+ case . success( let data) :
40
+ return HTTPResponse ( resp: out. response!, data: data, req: out. request)
41
+ case . failure( let error) :
42
+ throw ClientError . reqError ( error)
43
+ }
39
44
}
40
45
41
46
func request(
42
47
_ path: String ,
43
48
method: HTTPMethod
44
- ) async -> DataResponse < Data , AFError > {
49
+ ) async throws ( ClientError ) -> HTTPResponse {
45
50
let url = self . url. appendingPathComponent ( path)
46
- let headers : HTTPHeaders = [ Headers . sessionToken: token ?? " " ]
47
- return await AF . request (
51
+ let headers : HTTPHeaders ? = token . map { [ Headers . sessionToken: $0 ] }
52
+ let out = await AF . request (
48
53
url,
49
54
method: method,
50
55
headers: headers
51
56
) . serializingData ( ) . response
57
+ switch out. result {
58
+ case . success( let data) :
59
+ return HTTPResponse ( resp: out. response!, data: data, req: out. request)
60
+ case . failure( let error) :
61
+ throw ClientError . reqError ( error)
62
+ }
52
63
}
64
+
65
+ func responseAsError( _ resp: HTTPResponse ) -> ClientError {
66
+ do {
67
+ let body = try CoderClient . decoder. decode ( Response . self, from: resp. data)
68
+ let out = APIError (
69
+ response: body,
70
+ statusCode: resp. resp. statusCode,
71
+ method: resp. req? . httpMethod,
72
+ url: resp. req? . url
73
+ )
74
+ return ClientError . apiError ( out)
75
+ } catch {
76
+ return ClientError . unexpectedResponse ( resp. data [ ... 1024 ] )
77
+ }
78
+ }
79
+
80
+ enum Headers {
81
+ static let sessionToken = " Coder-Session-Token "
82
+ }
83
+
53
84
}
54
85
55
- enum ClientError : Error {
56
- case unexpectedStatusCode
57
- case badResponse
86
+ struct HTTPResponse {
87
+ let resp : HTTPURLResponse
88
+ let data : Data
89
+ let req : URLRequest ?
58
90
}
59
91
60
- enum Headers {
61
- static let sessionToken = " Coder-Session-Token "
92
+ struct APIError : Decodable {
93
+ let response : Response
94
+ let statusCode : Int
95
+ let method : String ?
96
+ let url : URL ?
97
+
98
+ var description : String {
99
+ var components : [ String ] = [ ]
100
+ if let method = method, let url = url {
101
+ components. append ( " \( method) \( url. absoluteString) " )
102
+ }
103
+ components. append ( " Unexpected status code \( statusCode) : \n \( response. message) " )
104
+ if let detail = response. detail {
105
+ components. append ( " \t Error: \( detail) " )
106
+ }
107
+ if let validations = response. validations, !validations. isEmpty {
108
+ let validationMessages = validations. map { " \t \( $0. field) : \( $0. detail) " }
109
+ components. append ( contentsOf: validationMessages)
110
+ }
111
+ return components. joined ( separator: " \n " )
112
+ }
113
+ }
114
+
115
+ struct Response : Decodable {
116
+ let message : String
117
+ let detail : String ?
118
+ let validations : [ ValidationError ] ?
119
+ }
120
+
121
+ struct ValidationError : Decodable {
122
+ let field : String
123
+ let detail : String
124
+ }
125
+
126
+ enum ClientError : Error {
127
+ case apiError( APIError )
128
+ case reqError( AFError )
129
+ case unexpectedResponse( Data )
130
+
131
+ var description : String {
132
+ switch self {
133
+ case . apiError( let error) :
134
+ return error. description
135
+ case . reqError( let error) :
136
+ return error. localizedDescription
137
+ case . unexpectedResponse( let data) :
138
+ return " Unexpected response: \( data) "
139
+ }
140
+ }
62
141
}
0 commit comments