event-gateway/internal/config/config.go

105 lines
2.3 KiB
Go
Raw Normal View History

2024-04-20 20:31:06 +00:00
// Copyright © 2023 Roberto Hidalgo <event-gateway@un.rob.mx>
// 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)
}