All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
106 lines
2.9 KiB
Go
106 lines
2.9 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/ejr/feedapi/bind"
|
|
"gitea.maximumdirect.net/ejr/feedapi/endpoint"
|
|
"gitea.maximumdirect.net/ejr/feedapi/render"
|
|
"gitea.maximumdirect.net/ejr/feedapi/response"
|
|
"gitea.maximumdirect.net/ejr/weatherapi/internal/core"
|
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
|
)
|
|
|
|
// Service describes the weather use-cases needed by the HTTP adapter.
|
|
type Service interface {
|
|
LatestObservation(ctx context.Context) (*model.WeatherObservation, error)
|
|
LatestHourlyForecast(ctx context.Context) (*model.WeatherForecastRun, error)
|
|
LatestActiveAlerts(ctx context.Context) (*model.WeatherAlertRun, error)
|
|
}
|
|
|
|
type queryRequest struct {
|
|
Units core.Units
|
|
}
|
|
|
|
func Definitions(svc Service) []endpoint.Definition {
|
|
return []endpoint.Definition{
|
|
endpoint.GET(
|
|
"/observations",
|
|
bindQuery,
|
|
func(ctx context.Context, req queryRequest) (any, error) {
|
|
obs, err := svc.LatestObservation(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response.Envelope{Data: core.ObservationPayload(obs, req.Units)}, nil
|
|
},
|
|
endpoint.WithProduces(render.FormatJSON, render.FormatXML, render.FormatText),
|
|
endpoint.WithTemplate("observations.txt.tmpl"),
|
|
),
|
|
endpoint.GET(
|
|
"/forecast/hourly",
|
|
bindQuery,
|
|
func(ctx context.Context, req queryRequest) (any, error) {
|
|
run, err := svc.LatestHourlyForecast(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response.Envelope{Data: core.ForecastPayload(run, req.Units)}, nil
|
|
},
|
|
endpoint.WithProduces(render.FormatJSON, render.FormatXML, render.FormatText),
|
|
endpoint.WithTemplate("forecast_hourly.txt.tmpl"),
|
|
),
|
|
endpoint.GET(
|
|
"/alerts/active",
|
|
bindQuery,
|
|
func(ctx context.Context, req queryRequest) (any, error) {
|
|
run, err := svc.LatestActiveAlerts(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response.Envelope{Data: core.AlertsPayload(run, req.Units)}, nil
|
|
},
|
|
endpoint.WithProduces(render.FormatJSON, render.FormatXML, render.FormatText),
|
|
endpoint.WithTemplate("alerts_active.txt.tmpl"),
|
|
),
|
|
}
|
|
}
|
|
|
|
func bindQuery(r *http.Request) (queryRequest, error) {
|
|
normalizeCommonQueryValue(r, "units")
|
|
normalizeCommonQueryValue(r, "format")
|
|
|
|
common, err := bind.CommonQueryParams(r, bind.QueryPolicy{
|
|
AllowUnits: true,
|
|
AllowFormat: true,
|
|
DefaultUnits: string(core.UnitsMetric),
|
|
RejectUnknown: true,
|
|
})
|
|
if err != nil {
|
|
return queryRequest{}, err
|
|
}
|
|
|
|
units := core.Units(strings.ToLower(strings.TrimSpace(common.Units)))
|
|
if units == "" {
|
|
units = core.UnitsMetric
|
|
}
|
|
return queryRequest{Units: units}, nil
|
|
}
|
|
|
|
func normalizeCommonQueryValue(r *http.Request, key string) {
|
|
q := r.URL.Query()
|
|
values, ok := q[key]
|
|
if !ok || len(values) == 0 {
|
|
return
|
|
}
|
|
|
|
normalized := strings.ToLower(strings.TrimSpace(values[0]))
|
|
if normalized == values[0] {
|
|
return
|
|
}
|
|
q.Set(key, normalized)
|
|
r.URL.RawQuery = q.Encode()
|
|
}
|