179 lines
6.1 KiB
Markdown
179 lines
6.1 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, outlook active/location filter construction,
|
|
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`;
|
|
- construct outlook active/location filters.
|
|
|
|
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` and `outlookNow` are
|
|
package-level state only to make time-dependent endpoint tests deterministic.
|
|
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;
|
|
- convective outlooks;
|
|
- 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
|
|
|
|
Binder shapes include:
|
|
|
|
- `bindQuery`: `format` and `units`;
|
|
- `bindPrecisionQuery`: `format`, `units`, and `precision`;
|
|
- `bindForecastPrecisionQuery`: `format`, `units`, `precision`, and timezone;
|
|
- `bindTimezoneQuery`: `format`, `units`, and timezone.
|
|
- outlook binders: `format`, `units`, timezone, and outlook filters.
|
|
|
|
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, weather story, and
|
|
outlook routes.
|
|
|
|
Outlook routes accept `day`, `outlookType`, and, except for
|
|
`/outlooks/convective/location`, `containsLocation`. The location route always
|
|
adds `containsLocation=true` after binding and rejects an explicit
|
|
`containsLocation` query value.
|
|
|
|
## 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.
|
|
|
|
## Outlook Filters
|
|
|
|
Outlook route filters are built at the HTTP boundary and passed to the
|
|
application service:
|
|
|
|
- `/outlooks/convective` uses only user-supplied filters;
|
|
- `/outlooks/convective/active` adds `ActiveAt=outlookNow().UTC()`;
|
|
- `/outlooks/convective/location` adds the same active timestamp and
|
|
`ContainsLocation=true`.
|
|
|
|
The package variable `outlookNow` exists so endpoint tests can make active and
|
|
location filtering deterministic.
|
|
|
|
The application service returns filtered outlook copies and trims run-level
|
|
discussions to days represented by retained outlooks.
|
|
`/outlooks/convective/location` is retained for compatibility and active
|
|
local-outlook behavior under the weatherfeeder outlook v2 contract.
|
|
|
|
## 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.
|