fix: validate weather callsign route segment #24

Merged
graham merged 2 commits from fix/w7-weather-callsign-validation into main 2026-09-15 16:33:28 -05:00
Owner

Fixes W7 from bugs.md.

WeatherLive.CallsignView now gates on Callsign.valid?/1 before subscribing to the per-callsign topic or running the latest-packet and 500-row history queries, copying the mount_valid/mount_invalid split from InfoLive.Show. Invalid segments render the same "no recent weather data" state as an unknown-but-valid callsign, so the response reveals nothing.

Verification: focused weather tests passed (19) including four new validation cases asserting zero database queries via telemetry, full suite passed (2239), mix credo --strict, and commit hooks including Dialyzer passed.

Fixes W7 from bugs.md. `WeatherLive.CallsignView` now gates on `Callsign.valid?/1` before subscribing to the per-callsign topic or running the latest-packet and 500-row history queries, copying the `mount_valid`/`mount_invalid` split from `InfoLive.Show`. Invalid segments render the same "no recent weather data" state as an unknown-but-valid callsign, so the response reveals nothing. Verification: focused weather tests passed (19) including four new validation cases asserting zero database queries via telemetry, full suite passed (2239), `mix credo --strict`, and commit hooks including Dialyzer passed.
fix: validate weather callsign route segment
All checks were successful
Elixir CI / Dialyzer (pull_request) Successful in 39s
Elixir CI / Build and test (pull_request) Successful in 1m40s
Elixir CI / Build and Push Docker Image (pull_request) Has been skipped
skippy-bot/review Skippy review: clean — 1 non-blocking note open
0f656dce8e
skippy-bot left a comment

🤖 Skippy PR review

1 finding — none blocking.

Severity Location Issue
🔵 Suggestion lib/aprsme_web/live/weather_live/callsign_view.ex:90 Invalid segment still distinguishable from an absent station, with a dangling heading

Reviewed 0f656dce8eb4. Comment skippy review to re-run.

### 🤖 Skippy PR review **1 finding** — none blocking. | Severity | Location | Issue | | --- | --- | --- | | 🔵 Suggestion | `lib/aprsme_web/live/weather_live/callsign_view.ex:90` | Invalid segment still distinguishable from an absent station, with a dangling heading | <sub>Reviewed `0f656dce8eb4`. Comment `skippy review` to re-run.</sub> <!-- skippy-pr-review -->
@ -73,0 +87,4 @@
socket =
socket
|> assign(:callsign, "")
First-time contributor

🔵 Suggestion — Invalid segment still distinguishable from an absent station, with a dangling heading

The two assigns here are exactly what the empty state renders from, so the mount_invalid page is not byte-similar to the unknown-but-valid one: callsign_view.html.heex:352 prints Weather for {@callsign} and layouts/root.html.heex:35 puts @page_title straight into <title>. Post-fix, /weather/.* returns Weather for / <title>Weather</title> while /weather/Z9ZZZ-9 returns Weather for Z9ZZZ-9 / <title>Weather for Z9ZZZ-9</title> — the invalid-vs-absent distinction the comment above says is not revealed (anyone diffing the two responses still gets it), plus a visibly broken heading with a trailing "for". No data leaks either way, so this is cosmetic, not a hole: if you want them indistinguishable, make the heading/title conditional on a non-blank @callsign (render plain gettext("Weather") when it is empty) instead of asserting the states match; if you keep the current shape, drop that clause from the comment.

**🔵 Suggestion — Invalid segment still distinguishable from an absent station, with a dangling heading** The two assigns here are exactly what the empty state renders from, so the `mount_invalid` page is not byte-similar to the unknown-but-valid one: `callsign_view.html.heex:352` prints `Weather for {@callsign}` and `layouts/root.html.heex:35` puts `@page_title` straight into `<title>`. Post-fix, `/weather/.*` returns `Weather for ` / `<title>Weather</title>` while `/weather/Z9ZZZ-9` returns `Weather for Z9ZZZ-9` / `<title>Weather for Z9ZZZ-9</title>` — the invalid-vs-absent distinction the comment above says is not revealed (anyone diffing the two responses still gets it), plus a visibly broken heading with a trailing "for". No data leaks either way, so this is cosmetic, not a hole: if you want them indistinguishable, make the heading/title conditional on a non-blank `@callsign` (render plain `gettext("Weather")` when it is empty) instead of asserting the states match; if you keep the current shape, drop that clause from the comment.
skippy-bot marked this conversation as resolved
fix: render a standalone Weather heading for empty callsign
All checks were successful
skippy-bot/review Skippy review: clean — no open findings
Elixir CI / Build and test (pull_request) Successful in 59s
Elixir CI / Dialyzer (pull_request) Successful in 2m49s
Elixir CI / Build and Push Docker Image (pull_request) Has been skipped
2e6cd506f5
First-time contributor

Resolved 1 of 1 earlier findings: callsign_view.html.heex:352 now guards the heading on a non-blank @callsign and renders a plain "Weather" for the invalid path, matching page_title, so the dangling "Weather for " heading/title is gone. 0 still open.

Nothing new in 0f656dce..2e6cd506 (the new commit only touches that empty state).

Resolved 1 of 1 earlier findings: `callsign_view.html.heex:352` now guards the heading on a non-blank `@callsign` and renders a plain "Weather" for the invalid path, matching `page_title`, so the dangling "Weather for " heading/title is gone. 0 still open. Nothing new in `0f656dce..2e6cd506` (the new commit only touches that empty state). <!-- skippy-pr-review -->
graham merged commit eefd25b071 into main 2026-09-15 16:33:28 -05:00
graham deleted branch fix/w7-weather-callsign-validation 2026-09-15 16:33:28 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
graham/aprs.me!24
No description provided.