diff --git a/viperx/vars.go b/viperx/vars.go index 27549190..bb76bd51 100644 --- a/viperx/vars.go +++ b/viperx/vars.go @@ -20,8 +20,24 @@ func UnmarshalKey(key string, destination interface{}) error { } var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(mapx.ToJSONMap(viper.Get(key))); err != nil { + + // This may be a string in the case where a value was provided via an env var. + // If it's a string, try to decode it as json. + if v, ok := value.(string); ok { + if _, err := b.WriteString(v); err != nil { + return errors.WithStack(err) + } + + // Try decoding the json directly. If it decodes successfully, return immediately. + if err := json.NewDecoder(&b).Decode(destination); err == nil { + return nil + } + } + + // If it's not a string or not valid json, use the value as it was originally provided. + if err := json.NewEncoder(&b).Encode(mapx.ToJSONMap(value)); err != nil { return errors.WithStack(err) } + return errors.WithStack(json.NewDecoder(&b).Decode(destination)) } diff --git a/viperx/vars_test.go b/viperx/vars_test.go index 506d4474..e5c5feb1 100644 --- a/viperx/vars_test.go +++ b/viperx/vars_test.go @@ -11,15 +11,56 @@ import ( ) func TestUnmarshalKey(t *testing.T) { - viper.SetConfigFile("./stub/.unmarshal-key.yml") - require.NoError(t, viper.ReadInConfig()) + t.Run("case=Unmarshal a map from viper", func(t *testing.T) { + viper.SetConfigFile("./stub/.unmarshal-key.yml") + require.NoError(t, viper.ReadInConfig()) - var config struct { - Enabled bool `json:"enabled"` - Config json.RawMessage `json:"config"` - } - require.NoError(t, UnmarshalKey("selfservice.strategies.oidc", &config)) + var config struct { + Enabled bool `json:"enabled"` + Config json.RawMessage `json:"config"` + } + require.NoError(t, UnmarshalKey("selfservice.strategies.oidc", &config)) + + assert.EqualValues(t, true, config.Enabled) + assert.EqualValues(t, `{"providers":[{"client_id":"kratos-client","client_secret":"kratos-secret","id":"hydra","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/hydra.jsonnet","provider":"generic","scope":["offline"]},{"client_id":"google-client","client_secret":"kratos-secret","id":"google","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/hydra.jsonnet","provider":"generic","scope":["offline"]},{"client_id":"github-client","client_secret":"kratos-secret","id":"github","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/hydra.jsonnet","provider":"generic","scope":["offline"]}]}`, string(config.Config)) + }) + + t.Run("case=Unmarshal a string containing a JSON object", func(t *testing.T) { + viper.SetConfigFile("./stub/.unmarshal-key.yml") + viper.Set("selfservice.strategies.oidc", `{"enabled":true, "config":{"providers": [{"client_id":"a-different-client","client_secret":"a-different-secret","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/a-different-file.jsonnet","provider":"github","scope":["user:email"]}]}}`) + require.NoError(t, viper.ReadInConfig()) + + var config struct { + Enabled bool `json:"enabled"` + Config json.RawMessage `json:"config"` + } + require.NoError(t, UnmarshalKey("selfservice.strategies.oidc", &config)) + + assert.EqualValues(t, true, config.Enabled) + assert.EqualValues(t, `{"providers": [{"client_id":"a-different-client","client_secret":"a-different-secret","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/a-different-file.jsonnet","provider":"github","scope":["user:email"]}]}`, string(config.Config)) + }) + + t.Run("case=Unmarshal a string containing invalid JSON", func(t *testing.T) { + viper.SetConfigFile("./stub/.unmarshal-key.yml") + viper.Set("selfservice.strategies.oidc", `{"enabled":true, "config":{"providers": [{"client_id":"a-different-client",`) + require.NoError(t, viper.ReadInConfig()) + + var config struct { + Enabled bool `json:"enabled"` + Config json.RawMessage `json:"config"` + } + require.Error(t, UnmarshalKey("selfservice.strategies.oidc", &config)) + }) + + t.Run("case=Unmarshal a regular string", func(t *testing.T) { + viper.SetConfigFile("./stub/.unmarshal-key.yml") + viper.Set("identity.default_schema_url", `https://ory.sh`) + require.NoError(t, viper.ReadInConfig()) + + var defaultSchemaURL string + require.NoError(t, UnmarshalKey("identity.default_schema_url", &defaultSchemaURL)) + + assert.EqualValues(t, `https://ory.sh`, defaultSchemaURL) + }) - assert.EqualValues(t, true, config.Enabled) - assert.EqualValues(t, `{"providers":[{"client_id":"kratos-client","client_secret":"kratos-secret","id":"hydra","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/hydra.jsonnet","provider":"generic","scope":["offline"]},{"client_id":"google-client","client_secret":"kratos-secret","id":"google","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/hydra.jsonnet","provider":"generic","scope":["offline"]},{"client_id":"github-client","client_secret":"kratos-secret","id":"github","issuer_url":"http://127.0.0.1:4444/","mapper_url":"file://test/e2e/profiles/oidc/hydra.jsonnet","provider":"generic","scope":["offline"]}]}`, string(config.Config)) }