Files
weatherapi/docs/internal/http-adapter.md

152 lines
5.0 KiB
Markdown

# HTTP Adapter
This document describes the internal HTTP adapter under
`internal/adapters/inbound/httpapi`. The public endpoint contract belongs in
[`docs/api.md`](../api.md).
## Purpose
The HTTP adapter turns feedapi route definitions into calls on the application
service boundary. It owns route registration, query binding, request validation,
forecast day-slice filtering, response envelopes, and template names.
## Inputs and Outputs
Inputs:
- feedapi HTTP requests;
- query parameters;
- an implementation of the adapter-local `Service` interface.
Outputs:
- `endpoint.Definition` values registered by runtime composition;
- `response.Envelope{Data: ...}` values for successful requests;
- typed feedapi errors for invalid query parameters;
- endpoint-specific template names for text rendering.
## Boundaries
The adapter may:
- define routes and supported response formats;
- bind and validate query parameters;
- call the `Service` interface;
- choose the presenter function for an endpoint;
- filter forecast copies for `/today` and `/tomorrow`.
The adapter must not:
- execute SQL;
- open databases;
- mutate repository-returned models in place;
- duplicate public API reference text;
- move unit conversion or timezone presentation out of presenters.
## Config Fields Used
The HTTP adapter does not read config directly. Feedapi applies server and
renderer configuration before requests reach these handlers. Template names are
declared in endpoint definitions, but `templates.base_dir` is loaded by feedapi.
## External Adapters Used
- `feedapi/endpoint` for route definitions.
- `feedapi/render` for declared output formats.
- `feedapi/response` for success envelopes.
- `feedapi/bind` and `feedapi/errors` for query validation failures.
- `presenter` package for payload shaping.
## State
The adapter has no durable state. `forecastNow` is package-level state only to
make forecast day filtering testable. Do not add request caches or cross-request
mutable state here.
## Route Registry
`Definitions(svc)` returns all implemented route definitions. It includes:
- observations;
- active alerts;
- current conditions;
- weather stories;
- forecast discussions;
- hourly and narrative forecasts.
Each route uses `endpoint.GET`, declares JSON/XML/text production, and names a
text template. Feedapi owns routing, format negotiation, response rendering,
middleware, and error normalization after definitions are registered.
## Query Binding
There are three binder shapes:
- `bindQuery`: `format` and `units`;
- `bindPrecisionQuery`: `format`, `units`, and `precision`;
- `bindForecastPrecisionQuery`: `format`, `units`, `precision`, and timezone;
- `bindTimezoneQuery`: `format`, `units`, and timezone.
All binders use feedapi binding helpers with `RejectUnknown: true`. Supported
common query values are lowercased and trimmed before binding where applicable.
`precision` defaults to `0` and must be between `0` and `2`. Timezone parsing is
available only through binders used by forecast, discussion, and weather story
routes.
## Timezone Parsing
Timezone parsing accepts:
- IANA names through `time.LoadLocation`;
- configured aliases such as `Chicago` and `Stl`;
- common US abbreviations handled as fixed zones;
- signed UTC offsets.
If both `tz` and `TZ` are provided, they must match case-insensitively.
Invalid timezone input is converted to a feedapi invalid-parameter error.
## Forecast Day Slices
Forecast base routes return the latest run unchanged except for presentation.
`/today` and `/tomorrow` routes call `filterForecastRunByDaySlice`.
Filtering behavior:
- nil runs remain nil;
- missing timezone means UTC;
- day selection is based on `forecastNow()` in the resolved timezone;
- periods are included when `period.StartTime` falls on the target local date;
- the run is shallow-copied and `Periods` is replaced with the filtered slice.
The package variable `forecastNow` exists so endpoint tests can make day-slice
behavior deterministic.
## Failure Behavior
Binder failures become feedapi invalid-parameter responses. Handler service
errors are returned unchanged to feedapi for runtime normalization. Nil service
payloads are presented as nil data, so response rendering can produce
`data: null`.
## Templates
Endpoint definitions bind template names only. Template loading and rendering
belong to feedapi. Template files live under `templates/` and are operator
configuration through `templates.base_dir`.
## Tests to Inspect Before Changing
- `internal/adapters/inbound/httpapi/endpoints_test.go`
- `internal/adapters/inbound/httpapi/presenter/payload_test.go` when changing
presenter interaction
- `docs/api.md` when query behavior, route behavior, or payload behavior changes
## Invariants
- Preserve strict unknown-query rejection.
- Keep route-specific query policy in binder functions.
- Keep endpoint handlers thin: bind, call service, present, envelope.
- Keep day-slice filtering in the HTTP adapter, not in the repository.
- Preserve top-level `data` envelopes and nil-data behavior.