model: add explicit JSON tags and document canonical payload contract

Add lowerCamelCase JSON tags to canonical model types (observation, forecast,
alert) to stabilize the emitted wire format and make payload structure explicit
for downstream sinks.

Introduce internal/model/doc.go to document these structs as versioned,
schema-governed payloads and clarify compatibility expectations (additive
changes preferred; breaking changes require schema bumps).

No functional behavior changes; this formalizes the canonical output contract
ahead of additional sinks and consumers.
This commit is contained in:
2026-01-15 22:39:37 -06:00
parent f13f43cf56
commit e10ba804ca
4 changed files with 56 additions and 63 deletions

View File

@@ -1,72 +1,56 @@
// FILE: internal/model/observation.go
package model
import "time"
type WeatherObservation struct {
// Identity / metadata
StationID string
StationName string
Timestamp time.Time
StationID string `json:"stationId,omitempty"`
StationName string `json:"stationName,omitempty"`
Timestamp time.Time `json:"timestamp"`
// Canonical internal representation (provider-independent).
//
// ConditionCode should be populated by all sources. ConditionText should be the
// canonical human-readable string derived from the WMO code (not the provider's
// original wording).
//
// IsDay is optional; some providers supply a day/night flag (e.g., Open-Meteo),
// while others may not (e.g., NWS observations). When unknown, it can be nil.
ConditionCode WMOCode
ConditionText string
IsDay *bool
ConditionCode WMOCode `json:"conditionCode"`
ConditionText string `json:"conditionText,omitempty"`
IsDay *bool `json:"isDay,omitempty"`
// Provider-specific “evidence” for troubleshooting mapping and drift.
//
// This is intentionally limited: it is not intended to be used downstream for
// business logic. Downstream logic should rely on ConditionCode / ConditionText.
ProviderRawDescription string
ProviderRawDescription string `json:"providerRawDescription,omitempty"`
// Human-facing (legacy / transitional)
//
// TextDescription currently carries provider text in existing drivers.
// As we transition to WMO-based normalization, downstream presentation should
// switch to using ConditionText. After migration, this may be removed or repurposed.
TextDescription string
TextDescription string `json:"textDescription,omitempty"`
// Provider-specific (legacy / transitional)
//
// IconURL is not part of the canonical internal vocabulary. It's retained only
// because current sources populate it; it is not required for downstream systems.
IconURL string
IconURL string `json:"iconUrl,omitempty"`
// Core measurements (nullable)
TemperatureC *float64
DewpointC *float64
TemperatureC *float64 `json:"temperatureC,omitempty"`
DewpointC *float64 `json:"dewpointC,omitempty"`
WindDirectionDegrees *float64
WindSpeedKmh *float64
WindGustKmh *float64
WindDirectionDegrees *float64 `json:"windDirectionDegrees,omitempty"`
WindSpeedKmh *float64 `json:"windSpeedKmh,omitempty"`
WindGustKmh *float64 `json:"windGustKmh,omitempty"`
BarometricPressurePa *float64
SeaLevelPressurePa *float64
VisibilityMeters *float64
BarometricPressurePa *float64 `json:"barometricPressurePa,omitempty"`
SeaLevelPressurePa *float64 `json:"seaLevelPressurePa,omitempty"`
VisibilityMeters *float64 `json:"visibilityMeters,omitempty"`
RelativeHumidityPercent *float64
WindChillC *float64
HeatIndexC *float64
RelativeHumidityPercent *float64 `json:"relativeHumidityPercent,omitempty"`
WindChillC *float64 `json:"windChillC,omitempty"`
HeatIndexC *float64 `json:"heatIndexC,omitempty"`
ElevationMeters *float64
RawMessage string
ElevationMeters *float64 `json:"elevationMeters,omitempty"`
RawMessage string `json:"rawMessage,omitempty"`
PresentWeather []PresentWeather
CloudLayers []CloudLayer
PresentWeather []PresentWeather `json:"presentWeather,omitempty"`
CloudLayers []CloudLayer `json:"cloudLayers,omitempty"`
}
type CloudLayer struct {
BaseMeters *float64
Amount string
BaseMeters *float64 `json:"baseMeters,omitempty"`
Amount string `json:"amount,omitempty"`
}
type PresentWeather struct {
Raw map[string]any
Raw map[string]any `json:"raw,omitempty"`
}