diff options
Diffstat (limited to 'config')
-rw-r--r-- | config/config.go | 84 | ||||
-rw-r--r-- | config/config_test.go | 12 |
2 files changed, 91 insertions, 5 deletions
diff --git a/config/config.go b/config/config.go index 2ec6706..cd2883b 100644 --- a/config/config.go +++ b/config/config.go @@ -1,9 +1,11 @@ package config import ( + "fmt" "github.com/pkg/errors" "io/ioutil" + "github.com/santhosh-tekuri/jsonschema" "gopkg.in/yaml.v2" ) @@ -47,7 +49,7 @@ type TelegramTdlibClientConfig struct { UseChatInfoDatabase bool `yaml:":use_chat_info_database"` } -func ReadConfig(path string) (Config, error) { +func ReadConfig(path string, schema_path string) (Config, error) { var config Config file, err := ioutil.ReadFile(path) @@ -60,5 +62,85 @@ func ReadConfig(path string) (Config, error) { return config, errors.Wrap(err, "Error parsing config") } + err = validateConfig(file, schema_path) + if err != nil { + return config, errors.Wrap(err, "Validation error") + } + return config, nil } + +func validateConfig(file []byte, schema_path string) error { + schema, err := jsonschema.Compile(schema_path) + if err != nil { + return errors.Wrap(err, "Corrupted JSON schema") + } + + var config_generic interface{} + + err = yaml.Unmarshal(file, &config_generic) + if err != nil { + return errors.Wrap(err, "Error re-parsing config") + } + + config_generic, err = convertToStringKeysRecursive(config_generic, "") + if err != nil { + return errors.Wrap(err, "Config conversion error") + } + + err = schema.ValidateInterface(config_generic) + if err != nil { + return errors.Wrap(err, "Config validation error") + } + + return nil +} + +// copied and adapted from https://github.com/docker/docker-ce/blob/de14285fad39e215ea9763b8b404a37686811b3f/components/cli/cli/compose/loader/loader.go#L330 +func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) { + if mapping, ok := value.(map[interface{}]interface{}); ok { + dict := make(map[string]interface{}) + for key, entry := range mapping { + str, ok := key.(string) + if !ok { + return nil, formatInvalidKeyError(keyPrefix, key) + } + var newKeyPrefix string + if keyPrefix == "" { + newKeyPrefix = str + } else { + newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str) + } + convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) + if err != nil { + return nil, err + } + dict[str] = convertedEntry + } + return dict, nil + } + if list, ok := value.([]interface{}); ok { + var convertedList []interface{} + for index, entry := range list { + newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index) + convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) + if err != nil { + return nil, err + } + convertedList = append(convertedList, convertedEntry) + } + return convertedList, nil + } + + return value, nil +} + +func formatInvalidKeyError(keyPrefix string, key interface{}) error { + var location string + if keyPrefix == "" { + location = "at top level" + } else { + location = fmt.Sprintf("in %s", keyPrefix) + } + return errors.Errorf("Non-string key %s: %#v", location, key) +} diff --git a/config/config_test.go b/config/config_test.go index 34009d7..6fce23b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -4,23 +4,27 @@ import ( "testing" ) +const SCHEMA_PATH string = "../config_schema.json" + func TestNoConfig(t *testing.T) { - _, err := ReadConfig("../test/sfklase.yml") + _, err := ReadConfig("../test/sfklase.yml", SCHEMA_PATH) if err == nil { t.Errorf("Non-existent config was successfully read") } } func TestGoodConfig(t *testing.T) { - _, err := ReadConfig("../test/good_config.yml") + _, err := ReadConfig("../test/good_config.yml", SCHEMA_PATH) if err != nil { t.Errorf("Good config is not accepted: %v", err) } } func TestBadConfig(t *testing.T) { - _, err := ReadConfig("../test/bad_config.yml") + _, err := ReadConfig("../test/bad_config.yml", SCHEMA_PATH) if err == nil { - t.Errorf("Bad config is accepted but it shoudn't!") + t.Errorf("Bad config is accepted but it shouldn't!") + } else { + t.Log(err) } } |