git.iko.soy/parseable-macos
on master help sign in
Fix live tail 400 error by widening query time window (#6)
9f30a7fiko
ParseableViewer/Services/ParseableClient.swift+6−2
@@ -28,8 +28,12 @@ enum ParseableError: LocalizedError {
28 return "Authentication failed. Check your username and password."
28 return "Authentication failed. Check your username and password."
29 case .invalidURL:
29 case .invalidURL:
30 return "The server URL is invalid. Check the format (e.g. https://host:port)."
30 return "The server URL is invalid. Check the format (e.g. https://host:port)."
31− case .serverError(let code, _):
31+ case .serverError(let code, let message):
32+ let trimmed = message.trimmingCharacters(in: .whitespacesAndNewlines)
33+ if trimmed.isEmpty {
32− return "Server returned error \(code). Check that the URL points to a Parseable instance."
34+ return "Server returned error \(code)."
35+ }
36+ return "Server error (\(code)): \(trimmed)"
33 default:
37 default:
34 return parseableError.localizedDescription
38 return parseableError.localizedDescription
35 }
39 }
ParseableViewer/ViewModels/LiveTailViewModel.swift+8−3
@@ -110,9 +110,14 @@ final class LiveTailViewModel {
110
110
111 private func poll(client: ParseableClient, stream: String) async {
111 private func poll(client: ParseableClient, stream: String) async {
112 let now = Date()
112 let now = Date()
113+ // The Parseable server truncates query time-ranges to minute
114+ // boundaries, so a narrow window (< 60 s) can collapse to a
115+ // zero-width range and return HTTP 400. Always look back at
116+ // least 90 seconds; fingerprint deduplication prevents showing
117+ // duplicate entries.
113− let queryStart = lastTimestamp
118+ let idealStart = lastTimestamp ?? now.addingTimeInterval(-30)
114− ?? Calendar.current.date(byAdding: .second, value: -30, to: now)
115− ?? now.addingTimeInterval(-30)
119+ let safeStart = now.addingTimeInterval(-90)
120+ let queryStart = min(idealStart, safeStart)
116 let sql = "SELECT * FROM \(QueryViewModel.escapeSQLIdentifier(stream)) ORDER BY p_timestamp DESC LIMIT 200"
121 let sql = "SELECT * FROM \(QueryViewModel.escapeSQLIdentifier(stream)) ORDER BY p_timestamp DESC LIMIT 200"
117
122
118 do {
123 do {
ParseableViewerTests/ParseableClientTests.swift+16−1
@@ -242,7 +242,22 @@ final class ParseableClientTests: XCTestCase {
242 func testUserFriendlyMessageForServerError() {
242 func testUserFriendlyMessageForServerError() {
243 let msg = ParseableError.userFriendlyMessage(for: ParseableError.serverError(500, "fail"))
243 let msg = ParseableError.userFriendlyMessage(for: ParseableError.serverError(500, "fail"))
244 XCTAssertTrue(msg.contains("500"))
244 XCTAssertTrue(msg.contains("500"))
245− XCTAssertTrue(msg.contains("Parseable"))
245+ XCTAssertTrue(msg.contains("fail"))
246+ }
247+
248+ func testUserFriendlyMessageForServerErrorIncludesDetail() {
249+ let msg = ParseableError.userFriendlyMessage(for: ParseableError.serverError(400, "Stream not found"))
250+ XCTAssertEqual(msg, "Server error (400): Stream not found")
251+ }
252+
253+ func testUserFriendlyMessageForServerErrorEmptyBody() {
254+ let msg = ParseableError.userFriendlyMessage(for: ParseableError.serverError(502, ""))
255+ XCTAssertEqual(msg, "Server returned error 502.")
256+ }
257+
258+ func testUserFriendlyMessageForServerErrorTrimsWhitespace() {
259+ let msg = ParseableError.userFriendlyMessage(for: ParseableError.serverError(400, " bad query \n"))
260+ XCTAssertEqual(msg, "Server error (400): bad query")
246 }
261 }
247
262
248 func testUserFriendlyMessageForInvalidURL() {
263 func testUserFriendlyMessageForInvalidURL() {