// Copyright © 2023 Roberto Hidalgo // SPDX-License-Identifier: Apache-2.0 package config import ( "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "strings" "git.rob.mx/nidito/event-gateway/internal/sink" "git.rob.mx/nidito/event-gateway/internal/sink/types" source "git.rob.mx/nidito/event-gateway/internal/source/types" ) // RawSource is an intermediate source representation. type RawSource struct { Kind source.Kind `json:"kind"` Config json.RawMessage } func (s *RawSource) UnmarshalJSON(raw []byte) error { s.Config = raw inner := &struct { Kind source.Kind `json:"kind"` }{} if err := json.Unmarshal(raw, &inner); err != nil { return fmt.Errorf("unable to decode source: %s: %s", raw, err) } s.Kind = inner.Kind return nil } // Source is an interface concrete. type Source interface { Initialize() Kind() source.Kind Register(listener *Listener) error Deregister(ID string) } // Listener is the configuration for a source. type Listener struct { ID string Source *RawSource Event types.Event `json:"sink"` Hash string } func (l *Listener) UnmarshalJSON(raw []byte) error { cfg := &struct { Event json.RawMessage `json:"sink"` Source *RawSource `json:"source"` }{} if err := json.Unmarshal(raw, &cfg); err != nil { return fmt.Errorf("unable to decode %s: %s", raw, err) } l.Source = cfg.Source if cfg.Event == nil { return fmt.Errorf("sink configuration not provided: %s", raw) } sink, err := sink.Parse(cfg.Event) if err != nil { return fmt.Errorf("sink configuration for %s is invalid: %+v", raw, err) } l.Event = sink hasher := sha256.New() hasher.Write(raw) l.Hash = base64.URLEncoding.EncodeToString(hasher.Sum(nil)) return nil } // Loader represents a configurator interface. type Loader interface { Load() ([]*Listener, error) String() string Watch(chan []*Listener) } // FromURN returns a Loader given a :// path. func FromURN(urn string) (Loader, error) { URLparts := strings.SplitN(urn, "://", 2) if len(URLparts) == 1 { URLparts = []string{"file", URLparts[0]} } scheme := URLparts[0] configAddr := URLparts[1] switch scheme { case "file": return &File{Path: configAddr}, nil case "consul": return NewConsul(configAddr) } return nil, fmt.Errorf("unknown config address: %s", urn) }