-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathconfig.go
99 lines (83 loc) · 2.95 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package main
import (
"17thshard.com/sanderson-notifications/common"
. "17thshard.com/sanderson-notifications/plugins"
"fmt"
"github.com/mitchellh/mapstructure"
"gopkg.in/yaml.v3"
"os"
)
type ConfigLoader struct {
AvailablePlugins map[string]func() Plugin
}
type Config struct {
DiscordWebhook string `yaml:"discordWebhook"`
DiscordMentions common.DiscordMentions `yaml:"discordMentions"`
Connectors []Connector `yaml:"-"`
SharedPluginConfigs map[string]map[string]interface{} `yaml:"shared"`
RawConnectors map[string]RawConnector `yaml:"connectors"`
}
type Connector struct {
Name string
Plugin *Plugin
}
type RawConnector struct {
Plugin string
Config map[string]interface{}
}
func (loader ConfigLoader) Load(path string) (*Config, error) {
configContent, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("could not read config file: %w", err)
}
var config Config
if err = yaml.Unmarshal(configContent, &config); err != nil {
return nil, fmt.Errorf("could not parse config file: %w", err)
}
if len(config.DiscordWebhook) == 0 {
return nil, fmt.Errorf("config is missing Discord webhook ID")
}
for name, rawConnector := range config.RawConnectors {
pluginBuilder, ok := loader.AvailablePlugins[rawConnector.Plugin]
if !ok {
return nil, fmt.Errorf("failed to load connector '%s': unknown plugin '%s'", name, rawConnector.Plugin)
}
plugin := pluginBuilder()
sharedConfig := config.SharedPluginConfigs[rawConnector.Plugin]
rawConnector.Config = mergeKeys(rawConnector.Config, sharedConfig)
if len(rawConnector.Config) > 0 {
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &plugin,
DecodeHook: mapstructure.ComposeDecodeHookFunc(mapstructure.StringToTimeDurationHookFunc()),
})
if err != nil {
return nil, fmt.Errorf("could not parse config for connector '%s' with plugin '%s': %w", name, rawConnector.Plugin, err)
}
if err = decoder.Decode(rawConnector.Config); err != nil {
return nil, fmt.Errorf("could not parse config for connector '%s' with plugin '%s': %w", name, rawConnector.Plugin, err)
}
}
if err = plugin.Validate(); err != nil {
return nil, fmt.Errorf("invalid configuration for connector '%s' with plugin '%s': %w", name, rawConnector.Plugin, err)
}
config.Connectors = append(config.Connectors, Connector{Name: name, Plugin: &plugin})
}
return &config, nil
}
type m = map[string]interface{}
// Given two maps, recursively merge right into left, NEVER replacing any key that already exists in left
func mergeKeys(left, right m) m {
if left == nil {
return right
}
for key, rightVal := range right {
if leftVal, present := left[key]; present {
//then we don't want to replace it - recurse
left[key] = mergeKeys(leftVal.(m), rightVal.(m))
} else {
// key not in left so we can just shove it in
left[key] = rightVal
}
}
return left
}