Skip to content

Commit ff5b5c0

Browse files
committed
fix: handle missing user theme_preference on sign in
1 parent ae51d0e commit ff5b5c0

File tree

3 files changed

+25
-49
lines changed

3 files changed

+25
-49
lines changed

Coder Desktop/CoderSDK/Client.swift

+22-5
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct Client {
4444
throw .network(error)
4545
}
4646
guard let httpResponse = resp as? HTTPURLResponse else {
47-
throw .unexpectedResponse(data)
47+
throw .unexpectedResponse(String(data: data, encoding: .utf8) ?? "<non-utf8 data>")
4848
}
4949
return HTTPResponse(resp: httpResponse, data: data, req: req)
5050
}
@@ -72,7 +72,7 @@ public struct Client {
7272

7373
func responseAsError(_ resp: HTTPResponse) -> ClientError {
7474
do {
75-
let body = try Client.decoder.decode(Response.self, from: resp.data)
75+
let body = try decode(Response.self, from: resp.data)
7676
let out = APIError(
7777
response: body,
7878
statusCode: resp.resp.statusCode,
@@ -81,7 +81,24 @@ public struct Client {
8181
)
8282
return .api(out)
8383
} catch {
84-
return .unexpectedResponse(resp.data.prefix(1024))
84+
return .unexpectedResponse(String(data: resp.data, encoding: .utf8) ?? "<non-utf8 data>")
85+
}
86+
}
87+
88+
// Wrapper around JSONDecoder.decode that displays useful error messages from `DecodingError`.
89+
func decode<T>(_: T.Type, from data: Data) throws(ClientError) -> T where T: Decodable {
90+
do {
91+
return try Client.decoder.decode(T.self, from: data)
92+
} catch let DecodingError.keyNotFound(_, context) {
93+
throw .unexpectedResponse("Key not found: \(context.debugDescription)")
94+
} catch let DecodingError.valueNotFound(_, context) {
95+
throw .unexpectedResponse("Value not found: \(context.debugDescription)")
96+
} catch let DecodingError.typeMismatch(_, context) {
97+
throw .unexpectedResponse("Type mismatch: \(context.debugDescription)")
98+
} catch let DecodingError.dataCorrupted(context) {
99+
throw .unexpectedResponse("Data corrupted: \(context.debugDescription)")
100+
} catch {
101+
throw .unexpectedResponse(String(data: data.prefix(1024), encoding: .utf8) ?? "<non-utf8 data>")
85102
}
86103
}
87104
}
@@ -119,7 +136,7 @@ public struct FieldValidation: Decodable, Sendable {
119136
public enum ClientError: Error {
120137
case api(APIError)
121138
case network(any Error)
122-
case unexpectedResponse(Data)
139+
case unexpectedResponse(String)
123140
case encodeFailure(any Error)
124141

125142
public var description: String {
@@ -129,7 +146,7 @@ public enum ClientError: Error {
129146
case let .network(error):
130147
error.localizedDescription
131148
case let .unexpectedResponse(data):
132-
"Unexpected or non HTTP response: \(data)"
149+
"Unexpected response: \(data)"
133150
case let .encodeFailure(error):
134151
"Failed to encode body: \(error.localizedDescription)"
135152
}

Coder Desktop/CoderSDK/Deployment.swift

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ public extension Client {
66
guard res.resp.statusCode == 200 else {
77
throw responseAsError(res)
88
}
9-
do {
10-
return try Client.decoder.decode(BuildInfoResponse.self, from: res.data)
11-
} catch {
12-
throw .unexpectedResponse(res.data.prefix(1024))
13-
}
9+
return try decode(BuildInfoResponse.self, from: res.data)
1410
}
1511
}
1612

Coder Desktop/CoderSDK/User.swift

+2-39
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,20 @@ public extension Client {
66
guard res.resp.statusCode == 200 else {
77
throw responseAsError(res)
88
}
9-
do {
10-
return try Client.decoder.decode(User.self, from: res.data)
11-
} catch {
12-
throw .unexpectedResponse(res.data.prefix(1024))
13-
}
9+
return try decode(User.self, from: res.data)
1410
}
1511
}
1612

1713
public struct User: Encodable, Decodable, Equatable, Sendable {
1814
public let id: UUID
1915
public let username: String
20-
public let avatar_url: String
21-
public let name: String
22-
public let email: String
23-
public let created_at: Date
24-
public let updated_at: Date
25-
public let last_seen_at: Date
26-
public let status: String
27-
public let login_type: String
28-
public let theme_preference: String
29-
public let organization_ids: [UUID]
30-
public let roles: [Role]
3116

3217
public init(
3318
id: UUID,
34-
username: String,
35-
avatar_url: String,
36-
name: String,
37-
email: String,
38-
created_at: Date,
39-
updated_at: Date,
40-
last_seen_at: Date,
41-
status: String,
42-
login_type: String,
43-
theme_preference: String,
44-
organization_ids: [UUID],
45-
roles: [Role]
19+
username: String
4620
) {
4721
self.id = id
4822
self.username = username
49-
self.avatar_url = avatar_url
50-
self.name = name
51-
self.email = email
52-
self.created_at = created_at
53-
self.updated_at = updated_at
54-
self.last_seen_at = last_seen_at
55-
self.status = status
56-
self.login_type = login_type
57-
self.theme_preference = theme_preference
58-
self.organization_ids = organization_ids
59-
self.roles = roles
6023
}
6124
}
6225

0 commit comments

Comments
 (0)