refactor(providers): centralize provider-specific parsing and invariants
- Introduce internal/providers/nws with shared timestamp parsing used by both NWS sources and normalizers - Migrate NWS observation source + normalizer to use the shared provider helper for consistent RFC3339/RFC3339Nano handling - Introduce internal/providers/openweather with a shared URL invariant helper enforcing units=metric - Remove duplicated OpenWeather URL validation logic from the observation source - Align provider layering: move provider “contract/quirk” logic out of normalizers and into internal/providers - Update normalizer and standards documentation to clearly distinguish: provider helpers (internal/providers) vs canonical mapping logic (internal/normalizers) This refactor reduces duplication between sources and normalizers, clarifies layering boundaries, and establishes a scalable pattern for future forecast and alert implementations.
This commit is contained in:
@@ -5,12 +5,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.maximumdirect.net/ejr/feedkit/config"
|
||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||
owcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openweather"
|
||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
||||
)
|
||||
@@ -27,7 +26,7 @@ func NewObservationSource(cfg config.SourceConfig) (*ObservationSource, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := requireMetricUnits(hs.URL); err != nil {
|
||||
if err := owcommon.RequireMetricUnits(hs.URL); err != nil {
|
||||
return nil, fmt.Errorf("%s %q: %w", hs.Driver, hs.Name, err)
|
||||
}
|
||||
|
||||
@@ -39,7 +38,7 @@ func (s *ObservationSource) Name() string { return s.http.Name }
|
||||
func (s *ObservationSource) Kind() event.Kind { return event.Kind("observation") }
|
||||
|
||||
func (s *ObservationSource) Poll(ctx context.Context) ([]event.Event, error) {
|
||||
if err := requireMetricUnits(s.http.URL); err != nil {
|
||||
if err := owcommon.RequireMetricUnits(s.http.URL); err != nil {
|
||||
return nil, fmt.Errorf("%s %q: %w", s.http.Driver, s.http.Name, err)
|
||||
}
|
||||
|
||||
@@ -93,20 +92,3 @@ func (s *ObservationSource) fetchRaw(ctx context.Context) (json.RawMessage, open
|
||||
|
||||
return raw, meta, nil
|
||||
}
|
||||
|
||||
func requireMetricUnits(rawURL string) error {
|
||||
u, err := url.Parse(strings.TrimSpace(rawURL))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid url %q: %w", rawURL, err)
|
||||
}
|
||||
|
||||
units := strings.ToLower(strings.TrimSpace(u.Query().Get("units")))
|
||||
if units != "metric" {
|
||||
if units == "" {
|
||||
units = "(missing; defaults to standard)"
|
||||
}
|
||||
return fmt.Errorf("url must include units=metric (got units=%s)", units)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user