Refresh Weather API integration guide

This commit is contained in:
2026-07-31 01:11:22 +00:00
parent 154d31c3e8
commit 2f3558cf33

View File

@@ -1,37 +1,51 @@
# Weather API Integration # Weather API Integration
This document describes the external Weather API contract used by Weatherreporter fetches normalized weather inputs from a configured Weather API
`weatherreporter`. base URL. This guide defines the HTTP contract the service must satisfy; it is
not a general Weather API reference. Configuration values are defined in the
[configuration reference](../config.md). Normalization and collection behavior
are documented in [Weather data internals](../internal/weather-data.md) and
[Collection internals](../internal/collect.md).
## Purpose ## Base URL And Requests
`weatherreporter` uses a configured Weather API base URL to fetch normalized `weather_api.base_url` must be an absolute URL. Weatherreporter joins each
weather source data and assemble a `weatherdata.Bundle`. This is an integration endpoint path to the configured base URL path, so a service hosted under a path
contract for the project adapter, not a complete public API reference for the prefix must keep that prefix available. Requests use `GET` and carry the
upstream service. configured timeout on every HTTP attempt.
## Base URL Every request sends `format` and, except where noted below, `units`. The
configured format must be `json`.
`weather_api.base_url` must be an absolute URL. Adapter requests join this base Before retrieving sources, Weatherreporter warms up
URL with the endpoint paths listed below. Generation and explicit bundle fetches `/conditions/current` with the same `format`, `units`, and `precision` query
fail before any HTTP request when the base URL is empty or not absolute. parameters used for current conditions. The warmup only requires a readable
2xx response; its body is not decoded. Failure after its internal retry budget
stops the fetch before source requests begin.
The HTTP client uses `weather_api.timeout`. ## Endpoints And Query Parameters
Before fetching bundle sources, the adapter performs a warmup `GET` to The adapter makes one source request for each endpoint after a successful
`/conditions/current` with the same query parameters as the current-conditions warmup, subject to retry on transient failures.
source request. This is a temporary connectivity check for VPN wake-up behavior
until the upstream service provides a dedicated health endpoint. A successful
warmup requires a 2xx response whose body can be read; the adapter does not
decode or validate the response envelope during warmup.
Warmup attempts, warmup delay, source-fetch retry attempts, and source-fetch | Source | Endpoint | Query parameters | Availability |
retry delay are internal adapter defaults. They are not configuration-file | --- | --- | --- | --- |
fields or CLI flags yet. `weather_api.timeout` applies to each HTTP attempt. | Observations | `/observations` | `format`, `units`, `precision` | Optional |
| Current conditions | `/conditions/current` | `format`, `units`, `precision` | Optional |
| Hourly forecast | `/forecast/hourly` | `format`, `units`, `precision`, `tz` | Required |
| Narrative forecast | `/forecast/narrative` | `format`, `units`, `precision`, `tz` | Optional |
| Active alerts | `/alerts/active` | `format`, `units` | Optional; `data: null` means checked with no active alerts |
| Forecast discussion | `/discussion` | `format`, `units`, `tz` | Optional |
| Weather story | `/weatherstories/latest` | `format` | Optional |
| SPC convective outlooks | `/outlooks/convective` | `format`, `tz` | Optional; non-null empty lists are checked empty data |
`precision` comes from `weather_api.precision`; `tz` comes from
`weather_api.timezone`. Weatherreporter does not call day-slice forecast or
discussion-subsection endpoints.
## Response Envelope ## Response Envelope
Every response used by the adapter must be JSON with a top-level `data` field: Each endpoint response must be JSON with a top-level `data` member:
```json ```json
{ {
@@ -39,181 +53,95 @@ Every response used by the adapter must be JSON with a top-level `data` field:
} }
``` ```
For most sources, `data: null` is treated as a missing source. Missing optional An absent `data` member is treated as a missing source. For ordinary sources,
sources follow the configured missing-source policy. Missing hourly forecast `data: null` is also missing. The active-alert exception is listed above: its
data fails bundle fetching because hourly periods are required for report explicit `null` payload represents an empty alert result.
generation.
`/alerts/active` is the exception: a successful response with `data: null` Hourly forecast data must be present and contain at least one `period`; a
means the endpoint was checked and there are no current active alerts. The missing, malformed, or empty hourly product fails collection. The remaining
adapter records a non-missing alerts source and an empty alert run. sources follow the configured missing-source policy. Under `error`, collection
fails; under `warn`, the source is omitted and an inspectable warning is
recorded; under `none`, the source is omitted without a warning. A per-source
policy overrides the default. See [Configuration](../config.md) for policy
settings and [Weather data internals](../internal/weather-data.md) for recorded
source metadata.
For `/outlooks/convective`, `data: null` means no latest run is available and Malformed top-level JSON envelopes and HTTP failures are direct request errors.
follows missing-source policy. A non-null run with empty `outlooks` and Malformed `data` for an optional source follows its missing-source policy.
`discussions` arrays is checked empty data, not a missing source.
Malformed JSON envelopes, non-2xx statuses, and response read failures include ## Payload Fields Used
endpoint context in returned errors. Decode errors include source context when
they fail the fetch; optional malformed sources follow the missing-source policy.
Source-fetch transport failures and retryable HTTP statuses are retried before Weatherreporter decodes only the fields below; additional upstream fields are
the adapter returns an error. Retryable statuses are `408`, `429`, `500`, ignored. Timestamps must be JSON values accepted by Go's `time.Time` decoder.
`502`, `503`, and `504`. Non-retryable statuses, malformed JSON envelopes,
missing `data`, `data: null` missing-source outcomes, and source decode errors
are not retried.
## Query Parameters ### Observations And Current Conditions
The adapter sends these query parameters: `/observations` uses `stationId`, `stationName`, `timestamp`, `conditionCode`,
`isDay`, `textDescription`, `temperatureC`, `temperatureF`, `dewpointC`,
`dewpointF`, `windSpeedKmh`, `windSpeedMph`, `windGustKmh`, `windGustMph`,
`windDirectionDegrees`, `barometricPressurePa`, `barometricPressureInHg`,
`visibilityMeters`, `visibilityMiles`, `relativeHumidityPercent`,
`apparentTemperatureC`, `apparentTemperatureF`, and `presentWeather`.
- `format`: from `weather_api.format`; configuration validation requires `json` `/conditions/current` uses `conditionText`, `isDay`,
- `units`: from `weather_api.units` `relativeHumidityPercent`, `windDirectionDegrees`, `temperatureC`,
- `precision`: from `weather_api.precision` on observations, current `temperatureF`, `apparentTemperatureC`, `apparentTemperatureF`, `dewpointC`,
conditions, hourly forecast, and narrative forecast requests; the built-in `dewpointF`, `windSpeedKmh`, and `windSpeedMph`.
default is `0`
- `tz`: from `weather_api.timezone` on hourly forecast, narrative forecast,
discussion, and SPC convective outlook requests
Alerts do not receive `precision` or `tz`. Weather story requests receive only ### Hourly And Narrative Forecasts
`format=json`. SPC convective outlook requests receive only `format=json` and
`tz`; they do not receive `units` or `precision`.
## SPC Convective Outlooks Both forecast endpoints use run-level `locationId`, `locationName`, `issuedAt`,
`updatedAt`, `product`, `latitude`, `longitude`, `elevationMeters`,
`elevationFeet`, and `periods`.
The adapter fetches SPC convective outlook data from: Each `periods` item uses `startTime`, `endTime`, `name`, `isDay`,
`conditionCode`, `textDescription`, `temperatureC`, `temperatureF`,
`temperatureCMin`, `temperatureFMin`, `temperatureCMax`, `temperatureFMax`,
`dewpointC`, `dewpointF`, `windSpeedKmh`, `windSpeedMph`, `windGustKmh`,
`windGustMph`, `windDirectionDegrees`, `barometricPressurePa`,
`barometricPressureInHg`, `visibilityMeters`, `visibilityMiles`,
`apparentTemperatureC`, `apparentTemperatureF`, `cloudCoverPercent`,
`probabilityOfPrecipitationPercent`, `precipitationAmountMm`,
`precipitationAmountIn`, `snowfallDepthMM`, `snowfallDepthIn`, `uvIndex`, and
`relativeHumidityPercent`.
```text ### Alerts, Discussion, And Weather Story
GET /outlooks/convective?format=json&tz=<weather_api.timezone>
```
The response uses the standard `data` envelope. `data: null` means no latest `/alerts/active` uses the `asOf` timestamp and keeps each item in `alerts` as
run is available and follows missing-source policy. A non-null object with an alert payload. Weatherreporter does not require a separate alert-item schema
empty `outlooks` and `discussions` arrays is accepted as checked empty data. at this integration boundary.
Run fields consumed by weatherreporter: `/discussion` uses `officeId`, `officeName`, `product`, `issuedAt`,
`updatedAt`, `keyMessages`, and the `shortTerm` and `longTerm` sections. Each
section uses `qualifier`, `text`, and `issuedAt`.
- `locationId` `/weatherstories/latest` uses `officeId`, `startTime`, `endTime`, `updatedAt`,
- `locationName` `title`, `description`, `altText`, `priority`, `order`, and `downloadUrl`.
- `asOf`
- `issuedAt`
- `updatedAt`
- `product`
- `outlooks`
- `discussions`
Outlook fields consumed: ### SPC Convective Outlooks
- `id` `/outlooks/convective` uses run-level `locationId`, `locationName`, `asOf`,
- `provider` `issuedAt`, `updatedAt`, `product`, `outlooks`, and `discussions`.
- `product`
- `day`
- `outlookType`
- `label`
- `labelText`
- `forecaster`
- `severityRank`
- `validFrom`
- `validTo`
- `issuedAt`
- `expiresAt`
- `sourceUrl`
- `imageUrl`
- `containsLocation`
- `geometry`
Discussion fields consumed: Each outlook uses `id`, `provider`, `product`, `day`, `outlookType`, `label`,
`labelText`, `forecaster`, `severityRank`, `validFrom`, `validTo`, `issuedAt`,
`expiresAt`, `sourceUrl`, `imageUrl`, `containsLocation`, and GeoJSON
`geometry`. Each discussion uses `day`, `headline`, `summary`, `discussion`,
and `updatedAt`.
- `day` ## Timeouts, Retries, And Failures
- `headline`
- `summary`
- `discussion`
- `updatedAt`
GeoJSON `geometry` is decoded into collected weather facts and persisted in The configured Weather API timeout applies to each warmup and source HTTP
bundle/debug artifacts, but prompt-facing SPC module output omits geometry. attempt. Weatherreporter retries transient transport and response-read failures
and these response statuses: `408`, `429`, `500`, `502`, `503`, and `504`.
It does not retry other HTTP statuses, malformed envelopes, missing data, or
payload decoding failures. A canceled context also stops an in-progress retry
delay.
## Endpoints Used The adapter reads at most 10 MiB from one response body. A non-2xx response,
request construction failure, read failure, or decode failure includes endpoint
context in its error.
The adapter warms up `/conditions/current` once before bundle fetching begins, Retry counts and delays are adapter behavior rather than Weather API request
with retries if needed. It then fetches these source endpoints once per bundle, parameters. Do not depend on a particular attempt count when implementing the
except when a source request is retried after a transient transport or server service.
failure:
- `/observations`
- `/conditions/current`
- `/forecast/hourly`
- `/forecast/narrative`
- `/alerts/active`
- `/discussion`
- `/weatherstories/latest`
- `/outlooks/convective`
`weatherreporter` does not call day-slice forecast endpoints or discussion
subsection endpoints. Report-period selection and daypart summarization happen
inside Go after the full hourly and narrative products are fetched.
## Required And Optional Sources
Hourly forecast is required:
- `data: null` for `/forecast/hourly` fails the fetch.
- an hourly forecast with no `periods` fails the fetch.
- malformed hourly data fails the fetch.
Other fetched sources are optional and follow `missing_source.default` or a
source-specific `missing_source.sources` policy:
- `observations` for `/observations`
- `current` for `/conditions/current`
- `narrative` for `/forecast/narrative`
- `alerts` for `/alerts/active`
- `discussion` for `/discussion`
- `weather_story` for `/weatherstories/latest`
- `spc_convective_outlooks` for `/outlooks/convective`
Policy behavior:
- `error`: fail the fetch for that source
- `warn`: omit the source data, add a warning, and continue
- `none`: omit the source data and continue without a warning
For `/alerts/active`, an HTTP error or missing `data` field still fails or
follows the relevant error path, but explicit `data: null` is not a
missing-source condition.
For `/outlooks/convective`, a non-null data object with empty outlook and
discussion arrays is accepted as checked empty data.
## Source Identity
For source payloads accepted into the bundle, including the explicit `null`
alerts payload, the adapter records:
- source name
- endpoint path
- query parameters sent
- fetch time
- source issue and update timestamps when present in the payload
- SHA-256 hash of the compact raw `data` JSON
Warnings are recorded both on the affected source and on the bundle-level
warnings list.
## Compatibility Assumptions
The adapter expects payload fields compatible with the internal weather data
bundle types in `internal/weatherdata/bundle.go`, including:
- observation timestamps and observation values
- current condition values
- forecast run metadata and `periods`
- active alert run data
- discussion metadata, key messages, and short/long-term section text
- latest weather story title, description, timing, priority, order, alt text,
and download URL
- SPC convective outlook run metadata, outlooks, discussions, and GeoJSON
geometry
The adapter intentionally keeps upstream transport and envelope details inside
`internal/adapters/weatherapi`; downstream packages consume the normalized
bundle.