feat(sources)!: split source contracts into PollSource/StreamSource and add mode-aware source config
- Introduce explicit source interfaces: sources.PollSource and sources.StreamSource, with shared sources.Input (Name() only). - Remove mandatory Kind() from the base source contract to support sources that emit multiple kinds. - Add config.SourceMode (poll, stream, or omitted/auto) and SourceConfig.Kinds (plural expected kinds), while keeping legacy SourceConfig.Kind for compatibility. - Enforce mode semantics in config validation (poll requires every, stream forbids every) and detect mode/driver mismatches in sources.Registry. - Update docs and tests for the new source model and config behavior.
This commit is contained in:
@@ -21,20 +21,56 @@ type Config struct {
|
||||
Routes []RouteConfig `yaml:"routes"`
|
||||
}
|
||||
|
||||
// SourceConfig describes one polling job.
|
||||
// SourceMode selects how a source receives upstream input.
|
||||
//
|
||||
// Empty mode means "auto": feedkit infers mode from the registered driver type.
|
||||
type SourceMode string
|
||||
|
||||
const (
|
||||
SourceModeAuto SourceMode = ""
|
||||
SourceModePoll SourceMode = "poll"
|
||||
SourceModeStream SourceMode = "stream"
|
||||
)
|
||||
|
||||
// Normalize lowercases and trims the mode.
|
||||
func (m SourceMode) Normalize() SourceMode {
|
||||
switch strings.ToLower(strings.TrimSpace(string(m))) {
|
||||
case "":
|
||||
return SourceModeAuto
|
||||
case string(SourceModePoll):
|
||||
return SourceModePoll
|
||||
case string(SourceModeStream):
|
||||
return SourceModeStream
|
||||
default:
|
||||
return SourceMode(strings.ToLower(strings.TrimSpace(string(m))))
|
||||
}
|
||||
}
|
||||
|
||||
// SourceConfig describes one input source.
|
||||
//
|
||||
// This is intentionally generic:
|
||||
// - driver-specific knobs belong in Params.
|
||||
// - "kind" is allowed (useful for safety checks / routing), but feedkit does not
|
||||
// restrict the allowed values.
|
||||
// - mode controls polling vs streaming behavior.
|
||||
// - expected emitted kinds are optional and domain-defined.
|
||||
type SourceConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Driver string `yaml:"driver"` // e.g. "openmeteo_observation", "rss_feed", etc.
|
||||
|
||||
Every Duration `yaml:"every"` // "15m", "1m", etc.
|
||||
// Mode is optional:
|
||||
// - "poll": Every must be set (>0)
|
||||
// - "stream": Every must be omitted/zero
|
||||
// - empty: infer from driver registration type (poll vs stream)
|
||||
Mode SourceMode `yaml:"mode"`
|
||||
|
||||
// Kind is optional and domain-defined. If set, it should be a non-empty string.
|
||||
// Domains commonly use it to enforce "this source should only emit kind X".
|
||||
// Every is the poll cadence for poll-mode sources ("15m", "1m", etc.).
|
||||
Every Duration `yaml:"every"`
|
||||
|
||||
// Kinds is optional and domain-defined.
|
||||
// If set, it describes the expected emitted event kinds for this source.
|
||||
Kinds []string `yaml:"kinds"`
|
||||
|
||||
// Kind is the legacy singular form. Prefer "kinds".
|
||||
// If both kind and kinds are set, validation fails.
|
||||
Kind string `yaml:"kind"`
|
||||
|
||||
// Params are driver-specific settings (URL, headers, station IDs, API keys, etc.).
|
||||
@@ -42,6 +78,26 @@ type SourceConfig struct {
|
||||
Params map[string]any `yaml:"params"`
|
||||
}
|
||||
|
||||
// ExpectedKinds returns normalized expected kinds from config.
|
||||
// "kinds" takes precedence; "kind" is used as a legacy fallback.
|
||||
func (cfg SourceConfig) ExpectedKinds() []string {
|
||||
if len(cfg.Kinds) > 0 {
|
||||
out := make([]string, 0, len(cfg.Kinds))
|
||||
for _, k := range cfg.Kinds {
|
||||
k = strings.TrimSpace(k)
|
||||
if k == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, k)
|
||||
}
|
||||
return out
|
||||
}
|
||||
if k := strings.TrimSpace(cfg.Kind); k != "" {
|
||||
return []string{k}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SinkConfig describes one output sink adapter.
|
||||
type SinkConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
|
||||
Reference in New Issue
Block a user