# 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,
alert active-time selection, 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`;
- pass the current UTC instant to active-alert application filtering;
- 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`, `alertNow`, 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.

## Alert Active Time

`/alerts/active` uses the shared `format` and `units` binder. The handler calls
the application service with `alertNow().UTC()` so active alert filtering uses
the request-time instant while remaining deterministic in endpoint tests.

## 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.
