Refresh Weather API integration guide
This commit is contained in:
@@ -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.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user