Skip to content

fix: handle missing user theme_preference on sign in #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions Coder Desktop/CoderSDK/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public struct Client {
throw .network(error)
}
guard let httpResponse = resp as? HTTPURLResponse else {
throw .unexpectedResponse(data)
throw .unexpectedResponse(String(data: data, encoding: .utf8) ?? "<non-utf8 data>")
}
return HTTPResponse(resp: httpResponse, data: data, req: req)
}
Expand Down Expand Up @@ -72,7 +72,7 @@ public struct Client {

func responseAsError(_ resp: HTTPResponse) -> ClientError {
do {
let body = try Client.decoder.decode(Response.self, from: resp.data)
let body = try decode(Response.self, from: resp.data)
let out = APIError(
response: body,
statusCode: resp.resp.statusCode,
Expand All @@ -81,7 +81,24 @@ public struct Client {
)
return .api(out)
} catch {
return .unexpectedResponse(resp.data.prefix(1024))
return .unexpectedResponse(String(data: resp.data, encoding: .utf8) ?? "<non-utf8 data>")
}
}

// Wrapper around JSONDecoder.decode that displays useful error messages from `DecodingError`.
func decode<T>(_: T.Type, from data: Data) throws(ClientError) -> T where T: Decodable {
do {
return try Client.decoder.decode(T.self, from: data)
} catch let DecodingError.keyNotFound(_, context) {
throw .unexpectedResponse("Key not found: \(context.debugDescription)")
} catch let DecodingError.valueNotFound(_, context) {
throw .unexpectedResponse("Value not found: \(context.debugDescription)")
} catch let DecodingError.typeMismatch(_, context) {
throw .unexpectedResponse("Type mismatch: \(context.debugDescription)")
} catch let DecodingError.dataCorrupted(context) {
throw .unexpectedResponse("Data corrupted: \(context.debugDescription)")
} catch {
throw .unexpectedResponse(String(data: data.prefix(1024), encoding: .utf8) ?? "<non-utf8 data>")
}
}
}
Expand Down Expand Up @@ -119,7 +136,7 @@ public struct FieldValidation: Decodable, Sendable {
public enum ClientError: Error {
case api(APIError)
case network(any Error)
case unexpectedResponse(Data)
case unexpectedResponse(String)
case encodeFailure(any Error)

public var description: String {
Expand All @@ -129,7 +146,7 @@ public enum ClientError: Error {
case let .network(error):
error.localizedDescription
case let .unexpectedResponse(data):
"Unexpected or non HTTP response: \(data)"
"Unexpected response: \(data)"
case let .encodeFailure(error):
"Failed to encode body: \(error.localizedDescription)"
}
Expand Down
6 changes: 1 addition & 5 deletions Coder Desktop/CoderSDK/Deployment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ public extension Client {
guard res.resp.statusCode == 200 else {
throw responseAsError(res)
}
do {
return try Client.decoder.decode(BuildInfoResponse.self, from: res.data)
} catch {
throw .unexpectedResponse(res.data.prefix(1024))
}
return try decode(BuildInfoResponse.self, from: res.data)
}
}

Expand Down
41 changes: 2 additions & 39 deletions Coder Desktop/CoderSDK/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,20 @@ public extension Client {
guard res.resp.statusCode == 200 else {
throw responseAsError(res)
}
do {
return try Client.decoder.decode(User.self, from: res.data)
} catch {
throw .unexpectedResponse(res.data.prefix(1024))
}
return try decode(User.self, from: res.data)
}
}

public struct User: Encodable, Decodable, Equatable, Sendable {
public let id: UUID
public let username: String
public let avatar_url: String
public let name: String
public let email: String
public let created_at: Date
public let updated_at: Date
public let last_seen_at: Date
public let status: String
public let login_type: String
public let theme_preference: String
public let organization_ids: [UUID]
public let roles: [Role]

public init(
id: UUID,
username: String,
avatar_url: String,
name: String,
email: String,
created_at: Date,
updated_at: Date,
last_seen_at: Date,
status: String,
login_type: String,
theme_preference: String,
organization_ids: [UUID],
roles: [Role]
username: String
) {
self.id = id
self.username = username
self.avatar_url = avatar_url
self.name = name
self.email = email
self.created_at = created_at
self.updated_at = updated_at
self.last_seen_at = last_seen_at
self.status = status
self.login_type = login_type
self.theme_preference = theme_preference
self.organization_ids = organization_ids
self.roles = roles
}
}

Expand Down
Loading