Skip to content

feat(alerts): add fetch_alerts_detailed with area_desc and region_filtered - #16

Merged
crenshawdev merged 2 commits into
crenshawdev:mainfrom
nwxnw:feat/fetch-alerts-detailed
Sep 2, 2026
Merged

crenshawdev merged 2 commits into
crenshawdev:mainfrom
nwxnw:feat/fetch-alerts-detailed

Conversation

@nwxnw

@nwxnw nwxnw commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Closes #10. The shape agreed there, plus the untagged-entry fix.

fetch_alerts returns a list that may be local or national with nothing to tell a consumer which, and no field distinguishes two regional alerts for the same event. This adds a sibling that reports both, and leaves fetch_alerts and Alert exactly as they were.

What changed

  • fetch_alerts_detailed returns AlertReport { alerts: Vec<AlertEntry>, region_filtered: bool }, with AlertEntry { alert: Alert, area_desc: String }. fetch_alerts is now a thin wrapper over it.
  • area_desc comes from MeteoAlarm cap:areaDesc, NWS areaDesc, and the containing ECCC polygon's areaDesc (already parsed for the dedup key). BOM sends none, so it is "", per the missing-strings rule.
  • region_filtered is false only when a MeteoAlarm national feed was returned without an EMMA_ID for the location. NWS, ECCC and BOM filter by point, polygon and geohash, and an empty result is trivially filtered, so those are true.
  • Untagged MeteoAlarm entries, the fail-open you found: when an EMMA_ID resolved, an entry with no geocode is now dropped and counted at debug rather than leaking past the filter. Your two-entry fixture is the test.
  • region_filtered is also false when the feed carries no EMMA_ID geocodes at all (France tags entries with NUTS3, the review finding), in which case the feed renders unfiltered with a warning naming the scheme found, rather than being emptied by a filter that cannot apply. A geocode counts as an EMMA_ID only when its valueName says so.
  • Both new types are in tests/wire_contract.rs with their own snapshots. Alert's snapshot and the other 28 are byte-identical. CONTRACT.md and API.md document the new shapes.

Verified against live feeds

Site region_filtered area_desc
Warsaw true no active alerts
Lisbon false Faro, Leiria, Coimbra, Beja, Évora, Portalegre, ... one per entry
Paris false, FR101 resolved but the feed is NUTS3-tagged Drôme, Hérault, Gard, Aude, ... one per entry

Tests and gates

fmt, clippy --workspace --all-targets -- -D warnings, test --workspace green; coverage 86.20% total lines (85.91% before). New tests: area_desc decode for each provider, the untagged-entry drop with and without a filter, the NUTS3 cases (entry is not an EMMA_ID, all-NUTS3 feed renders unfiltered, mixed feed stays filtered, empty feed), the Unknown-region report, and the two wire-contract snapshots. No change to any existing wire shape, no CHANGELOG entry.

@crenshawdev

Copy link
Copy Markdown
Owner

This is good work. The sibling-function shape is right, and the 28 existing snapshots coming back byte-identical is the part I checked first.

One thing to sort out before this goes in. France doesn't tag its entries with EMMA_IDs. I pulled the live feed a few minutes ago and all 9 entries carry <valueName>NUTS3</valueName> with a value like FR713, while the actual EMMA_ID (FR031) sits in a link href we never parse. So a resolved French EMMA_ID matches nothing, every entry gets dropped, and region_filtered comes back true, which tells the consumer we narrowed the list to their area when we actually threw the whole thing away.

The dropping predates your PR, that mismatch was already there and it's probably one of the four reasons in #13. The flag is what makes it load-bearing, and I'd rather have no flag at all than one that reports true when it isn't.

Gating on valueName == "EMMA_ID" before you filter on the value should cover it, and a NUTS3-only entry then falls to the unfiltered path the same way an untagged one does instead of vanishing.

On the untagged drop itself, I checked Germany, Poland and Portugal alongside France. All EMMA_ID, 242 entries across the four feeds, not one of them untagged. That change is safe.

@nwxnw

nwxnw commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Confirmed on the live feed: all nine French entries carry <valueName>NUTS3</valueName>, and Paris resolves FR101 today, so a Paris user currently gets every French alert dropped as "wrong region". You are right that the flag turns that from a quiet bug into a false statement.

Gating on valueName == "EMMA_ID" is the fix, with one addition it needs to work: under the untagged-entry arm we agreed, an entry with no EMMA_ID is dropped when a filter is active, so on its own the gate would still throw away all of France and still report true. So the rule I will implement is feed-level: if an EMMA_ID resolved but no entry in the feed carries an EMMA_ID geocode, the filter cannot apply, the whole feed renders unfiltered, region_filtered is false, and a warning names the valueName that was found. The per-entry drop then only applies inside a feed that is EMMA_ID-tagged, which your 242-entry sample says is every feed except France. CONTRACT.md and API.md get the extra clause.

Two things I looked at and left out. The EMMA_ID in the first <link> href is parseable and does match the codename list (FR031 is Drôme there too), but the three <link> elements per entry are non-adjacent, and quick-xml's serde only collects those with the overlapped-lists feature. That is a manifest change and a different deserializer mode for every feed, too much for this PR. And it becomes unnecessary anyway once the matcher works from each entry's areaDesc (#13): filtering by area name needs no geocode at all, so France comes right in that PR without touching the href.

Revision coming with the fixture from your comment as a test.

@crenshawdev
crenshawdev merged commit 1a560b0 into crenshawdev:main Sep 2, 2026
3 checks passed
@nwxnw
nwxnw deleted the feat/fetch-alerts-detailed branch September 2, 2026 16:56
@crenshawdev crenshawdev mentioned this pull request Sep 2, 2026
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.

MeteoAlarm alerts routed by wrong country; region filter silently fails open

2 participants