fix: preserve merge error context

This commit is contained in:
Piotr Durlej 2026-05-13 19:00:40 +02:00
parent 2ab5808c34
commit 2a9128c130
3 changed files with 39 additions and 4 deletions

View file

@ -488,7 +488,11 @@ public enum ServiceError: LocalizedError, Sendable {
case .invalidResponse:
"Invalid response from server"
case let .httpError(statusCode, message):
message.isEmpty ? "HTTP error: \(statusCode)" : "HTTP \(statusCode): \(message)"
if let message = Self.normalizedHTTPMessage(message) {
"HTTP \(statusCode): \(message)"
} else {
"HTTP error: \(statusCode)"
}
case .notMergeable:
"This pull request cannot be merged"
case .mergeConflict:
@ -497,4 +501,20 @@ public enum ServiceError: LocalizedError, Sendable {
"Decoding failed: \(detail)"
}
}
private static func normalizedHTTPMessage(_ message: String) -> String? {
let trimmed = message.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else {
return nil
}
guard
let data = trimmed.data(using: .utf8),
let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let jsonMessage = object["message"] as? String
else {
return trimmed
}
let trimmedJSONMessage = jsonMessage.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmedJSONMessage.isEmpty ? trimmed : trimmedJSONMessage
}
}

View file

@ -173,11 +173,11 @@ public final class PullRequestService: Sendable {
do {
_ = try await client.performRequestNoContent(url: url, method: "POST", body: jsonData, validateStatus: true)
} catch let error as ServiceError {
if case let .httpError(statusCode, _) = error {
if case let .httpError(statusCode, message) = error {
switch statusCode {
case 405:
case 405 where message.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty:
throw ServiceError.notMergeable
case 409:
case 409 where message.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty:
throw ServiceError.mergeConflict
default:
throw error

View file

@ -75,6 +75,21 @@ struct NotificationTests {
#expect(ServiceError.decodingFailed(detail: "missing key").errorDescription == "Decoding failed: missing key")
}
@Test func httpErrorDescriptionUsesForgejoMessageBody() {
let error = ServiceError.httpError(
statusCode: 405,
message: #"{"message":"This pull request has failing status checks"}"#,
)
#expect(error.errorDescription == "HTTP 405: This pull request has failing status checks")
}
@Test func httpErrorDescriptionFallsBackToRawBody() {
let error = ServiceError.httpError(statusCode: 500, message: "database unavailable")
#expect(error.errorDescription == "HTTP 500: database unavailable")
}
// MARK: - Full API response decoding
@Test func decodesNotificationArray() throws {