diff --git a/Sources/Services/Feishu/FeishuAuthService.swift b/Sources/Services/Feishu/FeishuAuthService.swift index f8e6e47..e79194e 100644 --- a/Sources/Services/Feishu/FeishuAuthService.swift +++ b/Sources/Services/Feishu/FeishuAuthService.swift @@ -50,11 +50,14 @@ final class FeishuAuthService { request.httpMethod = "POST" request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization") - // Use manual JSON to rule out JSONEncoder issues let jsonBody = "{\"grant_type\":\"authorization_code\",\"code\":\"\(code)\"}" request.httpBody = jsonBody.data(using: .utf8) print("[BUGGER] exchangeCode: sending to \(url.absoluteString)...") - let tokenData: OAuthTokenData = try await send(request) + // OAuth token response is wrapped in {code, message, data: {...}} + let response: FeishuAPIResponse = try await send(request) + guard response.code == 0, let tokenData = response.data else { + throw FeishuError.apiError(code: response.code, message: response.msg) + } print("[BUGGER] exchangeCode: SUCCESS access=\(tokenData.accessToken.prefix(8))... refresh=\(tokenData.refreshToken?.prefix(8) ?? "nil")...") return tokenData } @@ -66,12 +69,13 @@ final class FeishuAuthService { request.httpMethod = "POST" request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") request.setValue("Bearer \(tenantToken)", forHTTPHeaderField: "Authorization") - request.httpBody = try JSONEncoder().encode([ - "grant_type": "refresh_token", - "refresh_token": refreshToken, - ] as [String: String]) - // Token endpoints return data at top level, not wrapped in {code, msg, data} - let tokenData: OAuthTokenData = try await send(request) + let jsonBody = "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"\(refreshToken)\"}" + request.httpBody = jsonBody.data(using: .utf8) + // Uses same {code, message, data: {...}} wrapper as exchangeCode + let response: FeishuAPIResponse = try await send(request) + guard response.code == 0, let tokenData = response.data else { + throw FeishuError.apiError(code: response.code, message: response.msg) + } return tokenData } diff --git a/Sources/Services/Feishu/FeishuModels.swift b/Sources/Services/Feishu/FeishuModels.swift index 586f8cf..c6c38bd 100644 --- a/Sources/Services/Feishu/FeishuModels.swift +++ b/Sources/Services/Feishu/FeishuModels.swift @@ -4,6 +4,22 @@ struct FeishuAPIResponse: Decodable { let code: Int let msg: String let data: T? + + enum CodingKeys: String, CodingKey { + case code, msg, message, data + } + + init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + code = try c.decode(Int.self, forKey: .code) + // Some endpoints use "msg", others use "message" + if let m = try c.decodeIfPresent(String.self, forKey: .msg) { + msg = m + } else { + msg = try c.decode(String.self, forKey: .message) + } + data = try c.decodeIfPresent(T.self, forKey: .data) + } } struct RecordListData: Decodable {