diff --git a/api.go b/api.go index e99b88a..f3bce95 100644 --- a/api.go +++ b/api.go @@ -5,6 +5,7 @@ import ( _ "embed" "encoding/json" "fmt" + "io" "net/http" "time" @@ -92,8 +93,41 @@ func (r *Router) handleConfigHTTP(w http.ResponseWriter, req *http.Request) { http.Error(w, "Config update in progress.", http.StatusConflict) return } + //TODO(jwetzell): again way too much marshaling + cfgBytes, err := io.ReadAll(req.Body) + if err != nil { + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + cfgMap := make(map[string]any) + err = json.Unmarshal(cfgBytes, &cfgMap) + if err != nil { + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + err = schema.ApplyDefaults(&cfgMap) + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + err = schema.ValidateConfig(cfgMap) + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + validCfgBytes, err := json.Marshal(cfgMap) + if err != nil { + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + var newConfig config.Config - err := json.NewDecoder(req.Body).Decode(&newConfig) + err = json.Unmarshal(validCfgBytes, &newConfig) if err != nil { http.Error(w, "Bad request", http.StatusBadRequest) return diff --git a/cmd/showbridge/main.go b/cmd/showbridge/main.go index 703c0ab..33def2c 100644 --- a/cmd/showbridge/main.go +++ b/cmd/showbridge/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "errors" "fmt" "log/slog" @@ -15,6 +16,7 @@ import ( "github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/module" "github.com/jwetzell/showbridge-go/internal/route" + "github.com/jwetzell/showbridge-go/internal/schema" "github.com/urfave/cli/v3" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" @@ -103,7 +105,28 @@ func readConfig(configPath string) (config.Config, error) { return config.Config{}, err } - err = yaml.Unmarshal(configBytes, &cfg) + //TODO(jwetzell): this is an annoying amount of marshaling + + yamlMap := make(map[string]any) + + err = yaml.Unmarshal(configBytes, &yamlMap) + if err != nil { + return config.Config{}, err + } + + err = schema.ApplyDefaults(&yamlMap) + if err != nil { + return config.Config{}, err + } + + err = schema.ValidateConfig(yamlMap) + if err != nil { + return config.Config{}, err + } + + validatedConfigBytes, err := json.Marshal(yamlMap) + + err = json.Unmarshal(validatedConfigBytes, &cfg) if err != nil { return config.Config{}, err } diff --git a/internal/schema/config.go b/internal/schema/config.go index bcf5d28..90a8640 100644 --- a/internal/schema/config.go +++ b/internal/schema/config.go @@ -1,10 +1,7 @@ package schema import ( - "encoding/json" - "github.com/google/jsonschema-go/jsonschema" - "github.com/jwetzell/showbridge-go/internal/config" ) var ConfigSchema = jsonschema.Schema{ @@ -24,22 +21,19 @@ var ConfigSchema = jsonschema.Schema{ }, } -func ValidateConfig(config config.Config) error { +func ApplyDefaults(cfg *map[string]any) error { resolvedSchema, err := GetResolvedConfigSchema() if err != nil { return err } - jsonBytes, err := json.Marshal(config) - if err != nil { - return err - } - - jsonMap := make(map[string]any) - err = json.Unmarshal(jsonBytes, &jsonMap) - if err != nil { - return err - } - - return resolvedSchema.Validate(jsonMap) + return resolvedSchema.ApplyDefaults(cfg) +} + +func ValidateConfig(cfg map[string]any) error { + resolvedSchema, err := GetResolvedConfigSchema() + if err != nil { + return err + } + return resolvedSchema.Validate(cfg) } diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 1946b84..911f46a 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -8,9 +8,7 @@ import ( ) func GetResolvedConfigSchema() (*jsonschema.Resolved, error) { - configSchema := ConfigSchema - - return configSchema.Resolve(&jsonschema.ResolveOptions{ + return ConfigSchema.Resolve(&jsonschema.ResolveOptions{ Loader: func(uri *url.URL) (*jsonschema.Schema, error) { switch uri.String() { case "https://showbridge.io/modules.schema.json": @@ -23,5 +21,6 @@ func GetResolvedConfigSchema() (*jsonschema.Resolved, error) { return nil, fmt.Errorf("unknown schema reference: %s", uri.String()) } }, + ValidateDefaults: true, }) }