Skip to content

Surface Cloudlog/Wavelog upload rejections to the operator - #923

Open
patrickrb wants to merge 1 commit into
devfrom
fix/cloudlog-upload-reason
Open

patrickrb wants to merge 1 commit into
devfrom
fix/cloudlog-upload-reason

Conversation

@patrickrb

Copy link
Copy Markdown
Owner

Why

A Wavelog user reported "the API link tests OK but no contacts are accepted". Their debug.log had the answer on every upload:

Cloudlog: POST .../index.php/api/qso -> HTTP 401: {"status":"failed","reason":"station id does not belong to the API key owner."}

The Station ID they entered belongs to a different Wavelog user than the API key. Test Connection only hits api/auth, so it passes, and the app then showed "Cloudlog / Wavelog / Nextlog: 0 accepted" with no reason. This PR makes the server's own explanation visible everywhere an upload result reaches the screen.

What changed

  • Catch-up sync dialog (Logbook ☁ button): after the run, shows each service's rejection reason with the HTTP/JSON wrapper stripped, and for Cloudlog-family servers a one-line hint naming the setting to fix (Station ID or API key).
  • Post-QSO upload (MainViewModel.doAfterTransmit) and background auto-sync (QsoAutoSync): toast the same reason + hint instead of failing silently.
  • Cloudlog settings dialog: after Test Connection passes, warns in red when the entered Station ID is blank or is not in the station list the server returned for this key (that list is scoped to the key's owner, so a mismatch is exactly the 401 above).
  • debug.log: every rejected upload now also logs Cloudlog: upload rejected with station_profile_id=<id> so the next report is a one-line diagnosis.

New pure helpers so the logic is unit-testable: sync/CloudlogFailureHint.kt (cloudlogServerReason, classifyCloudlogFailure, cloudlogHintResId, cloudlogFailureMessage) and ui/settings/StationIdCheck.kt (checkStationId). Wavelog's exact reason strings were taken from its Api.php.

Tests

  • CloudlogFailureHintTest (8), StationIdCheckTest (5), ThirdPartyServiceFailureReasonTest (+1 for describeStationId), QsoAutoSyncSummaryTest — all green.
  • ktlint clean on the new/edited lines (the remaining hits in these files are pre-existing composable-naming ones).
  • Emulator check against a mock Wavelog (auth + station_info OK, every api/qso POST → the 401 above): the settings dialog shows Pass plus "Station ID 7 is not one of this API key's stations. Pick one from the list."; the sync dialog shows "rejected uploads: station id does not belong to the API key owner." followed by the Station ID hint; debug.log has the new station_profile_id=7 line.
  • Not exercised on hardware: the post-QSO toast (needs a real logged QSO) and the auto-sync toast; both use the same cloudlogFailureMessage path as the dialog.

🤖 Generated with Claude Code

A Wavelog user whose Station ID belongs to a different user than the API
key passes Test Connection and then has every QSO rejected with
HTTP 401 "station id does not belong to the API key owner." The app showed
"0 accepted" and nothing else; the reason only lived in debug.log.

- Catch-up sync dialog: show each service's rejection reason (JSON wrapper
  stripped) and, for Cloudlog-family servers, a hint naming the setting to
  fix (Station ID / API key).
- Post-QSO upload and background auto-sync: toast the same reason + hint.
- Cloudlog settings dialog: after Test Connection, warn when the entered
  Station ID is blank or not in the station list the server returned for
  this key.
- debug.log: log the station_profile_id on every rejected upload.

New pure helpers (CloudlogFailureHint.kt, StationIdCheck.kt) with unit
tests; verified on the emulator against a mock Wavelog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 40.00000% with 51 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.51%. Comparing base (0677e40) to head (ba01fab).

Files with missing lines Patch % Lines
...lin/radio/ks3ckc/ft8af/ui/logbook/LogbookScreen.kt 0.00% 26 Missing ⚠️
.../radio/ks3ckc/ft8af/ui/settings/LoggingSettings.kt 0.00% 13 Missing ⚠️
...lin/radio/ks3ckc/ft8af/sync/CloudlogFailureHint.kt 76.47% 6 Missing and 2 partials ⚠️
...main/kotlin/radio/ks3ckc/ft8af/sync/QsoAutoSync.kt 0.00% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #923      +/-   ##
============================================
- Coverage     41.51%   41.51%   -0.01%     
- Complexity      252      273      +21     
============================================
  Files           280      282       +2     
  Lines         33993    34078      +85     
  Branches       4034     4060      +26     
============================================
+ Hits          14112    14146      +34     
- Misses        19592    19641      +49     
- Partials        289      291       +2     
Flag Coverage Δ
android 17.73% <40.00%> (+0.09%) ⬆️
native 9.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...n/radio/ks3ckc/ft8af/ui/settings/StationIdCheck.kt 100.00% <100.00%> (ø)
...main/kotlin/radio/ks3ckc/ft8af/sync/QsoAutoSync.kt 21.42% <0.00%> (-1.30%) ⬇️
...lin/radio/ks3ckc/ft8af/sync/CloudlogFailureHint.kt 76.47% <76.47%> (ø)
.../radio/ks3ckc/ft8af/ui/settings/LoggingSettings.kt 1.12% <0.00%> (-0.02%) ⬇️
...lin/radio/ks3ckc/ft8af/ui/logbook/LogbookScreen.kt 6.66% <0.00%> (-0.12%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Automatic-upload rejection notifications do not reach the Compose UI, leaving a central reporting path ineffective.

4 open issues
What changed in this PR

Adds upload-rejection explanations to FT8AF’s logging workflows so operators can diagnose Cloudlog-family configuration problems.

Changes:

  • Adds server-reason formatting and settings hints to upload results.
  • Adds Station ID warnings and rejection diagnostics.
  • Adds tests for failure parsing and station validation.
File Description
ft8af/​app/​src/​test/​kotlin/​radio/​ks3ckc/​ft8af/​ui/​settings/​StationIdCheckTest.kt Tests station validation.
ft8af/​app/​src/​test/​kotlin/​radio/​ks3ckc/​ft8af/​sync/​CloudlogFailureHintTest.kt Tests rejection parsing and hints.
ft8af/​app/​src/​test/​java/​com/​k1af/​ft8af/​log/​ThirdPartyServiceFailureReasonTest.java Tests diagnostic Station ID formatting.
ft8af/​app/​src/​main/​res/​values/​strings_compose.xml Adds rejection and warning text.
ft8af/​app/​src/​main/​kotlin/​radio/​ks3ckc/​ft8af/​ui/​settings/​StationIdCheck.kt Adds station-list validation.
ft8af/​app/​src/​main/​kotlin/​radio/​ks3ckc/​ft8af/​ui/​settings/​LoggingSettings.kt Displays Station ID warnings.
ft8af/​app/​src/​main/​kotlin/​radio/​ks3ckc/​ft8af/​ui/​logbook/​LogbookScreen.kt Displays catch-up rejection details.
ft8af/​app/​src/​main/​kotlin/​radio/​ks3ckc/​ft8af/​sync/​QsoAutoSync.kt Adds automatic-sync failure notifications.
ft8af/​app/​src/​main/​kotlin/​radio/​ks3ckc/​ft8af/​sync/​CloudlogFailureHint.kt Formats rejection reasons and hints.
ft8af/​app/​src/​main/​java/​com/​k1af/​ft8af/​MainViewModel.java Adds post-QSO rejection notifications.
ft8af/​app/​src/​main/​java/​com/​k1af/​ft8af/​log/​ThirdPartyService.java Propagates failures and logs Station IDs.

Review effort: Balanced

Comment on lines +1137 to +1139
ToastMessage.show(String.format(
getStringFromResource(R.string.cloudlog_upload_rejected),
CloudlogFailureHint.cloudlogFailureMessage(why.toString())));
Comment on lines +35 to +39
private val MESSAGES = Regex("\"messages\"\\s*:\\s*\\[(.*?)]")
private val QUOTED = Regex("\"((?:[^\"\\\\]|\\\\.)*)\"")

private fun unescapeJson(s: String): String {
return s.replace("\\\"", "\"").replace("\\/", "/").replace("\\\\", "\\")
fun classifyCloudlogFailure(failure: String?): CloudlogFailureKind {
val reason = cloudlogServerReason(failure).lowercase()
if (reason.isEmpty()) return CloudlogFailureKind.OTHER
if (reason.contains("station")) return CloudlogFailureKind.STATION_ID
<string name="settings_station_id">Station ID</string>
<string name="settings_station_id_hint">e.g. 1</string>
<string name="settings_station_id_missing">No Station ID selected. Pick one from the list or every upload will be rejected.</string>
<string name="settings_station_id_not_on_server">Station ID %1$s is not one of this API key\'s stations. Pick one from the list.</string>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants