mirror of
https://codeberg.org/secana/ForgejoKit.git
synced 2026-06-16 05:13:53 -07:00
fix: preserve merge error context (#3)
Problem - Merge failures currently collapse 405 and 409 responses to generic ServiceError cases, even when Forgejo returns a useful response body. Change - Keeps the existing notMergeable and mergeConflict fallbacks for empty responses. - Preserves non-empty Forgejo response bodies as ServiceError.httpError for merge failures. - Extracts the common JSON message field for clearer LocalizedError descriptions. Tests - DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift test Co-authored-by: Piotr Durlej <pdurlej@users.noreply.github.com> Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/3
This commit is contained in:
parent
2ab5808c34
commit
81d8bc5b4a
3 changed files with 39 additions and 4 deletions
|
|
@ -488,7 +488,11 @@ public enum ServiceError: LocalizedError, Sendable {
|
||||||
case .invalidResponse:
|
case .invalidResponse:
|
||||||
"Invalid response from server"
|
"Invalid response from server"
|
||||||
case let .httpError(statusCode, message):
|
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:
|
case .notMergeable:
|
||||||
"This pull request cannot be merged"
|
"This pull request cannot be merged"
|
||||||
case .mergeConflict:
|
case .mergeConflict:
|
||||||
|
|
@ -497,4 +501,20 @@ public enum ServiceError: LocalizedError, Sendable {
|
||||||
"Decoding failed: \(detail)"
|
"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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,11 @@ public final class PullRequestService: Sendable {
|
||||||
do {
|
do {
|
||||||
_ = try await client.performRequestNoContent(url: url, method: "POST", body: jsonData, validateStatus: true)
|
_ = try await client.performRequestNoContent(url: url, method: "POST", body: jsonData, validateStatus: true)
|
||||||
} catch let error as ServiceError {
|
} catch let error as ServiceError {
|
||||||
if case let .httpError(statusCode, _) = error {
|
if case let .httpError(statusCode, message) = error {
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case 405:
|
case 405 where message.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty:
|
||||||
throw ServiceError.notMergeable
|
throw ServiceError.notMergeable
|
||||||
case 409:
|
case 409 where message.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty:
|
||||||
throw ServiceError.mergeConflict
|
throw ServiceError.mergeConflict
|
||||||
default:
|
default:
|
||||||
throw error
|
throw error
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,21 @@ struct NotificationTests {
|
||||||
#expect(ServiceError.decodingFailed(detail: "missing key").errorDescription == "Decoding failed: missing key")
|
#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
|
// MARK: - Full API response decoding
|
||||||
|
|
||||||
@Test func decodesNotificationArray() throws {
|
@Test func decodesNotificationArray() throws {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue