Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unmarshal keys containing dots #673

Merged
merged 1 commit into from Sep 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion viper.go
Expand Up @@ -1789,6 +1789,24 @@ outer:
return shadow
}

// Converts a fully qualified map key into a list of relative
// map keys, allowing for keys to contain the delimiter themselves
func keyComponents(v *Viper, key string) []string {
var result []string
components := strings.Split(key, v.keyDelim)
for index := 0; index < len(components); index++ {
potentialKey := strings.Join(components[0:index], v.keyDelim)
if v.Get(potentialKey) != nil {
result = append(result, potentialKey)
}
}
result = append(result, key)
for i := len(result) - 1; i > 0; i-- {
result[i] = strings.Replace(result[i], result[i-1]+v.keyDelim, "", 1)
}
return result
}

// AllSettings merges all settings and returns them as a map[string]interface{}.
func AllSettings() map[string]interface{} { return v.AllSettings() }
func (v *Viper) AllSettings() map[string]interface{} {
Expand All @@ -1801,7 +1819,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
// check just in case anything changes
continue
}
path := strings.Split(k, v.keyDelim)
path := keyComponents(v, k)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(m, path[0:len(path)-1])
// set innermost value
Expand Down
123 changes: 115 additions & 8 deletions viper_test.go
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
"runtime"
"sort"
Expand Down Expand Up @@ -45,6 +46,10 @@ clothing:
age: 35
eyes : brown
beard: true
emails:
steve@hacker.com:
created: 01/02/03
active: true
`)

var yamlExampleWithExtras = []byte(`Existing: true
Expand Down Expand Up @@ -207,11 +212,16 @@ func initHcl() {
func initDirs(t *testing.T) (string, string, func()) {

var (
testDirs = []string{`a a`, `b`, `c\c`, `D_`}
testDirs = []string{`a a`, `b`, `C_`}
config = `improbable`
)

if runtime.GOOS != "windows" {
testDirs = append(testDirs, `d\d`)
}

root, err := ioutil.TempDir("", "")
require.NoError(t, err, "Failed to create temporary directory")

cleanup := true
defer func() {
Expand All @@ -224,7 +234,7 @@ func initDirs(t *testing.T) (string, string, func()) {
assert.Nil(t, err)

err = os.Chdir(root)
assert.Nil(t, err)
require.Nil(t, err)

for _, dir := range testDirs {
err = os.Mkdir(dir, 0750)
Expand Down Expand Up @@ -415,7 +425,10 @@ func TestEmptyEnv(t *testing.T) {
BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set environment variable

os.Clearenv()
os.Unsetenv("type")
os.Unsetenv("TYPE")
os.Unsetenv("name")
os.Unsetenv("NAME")

os.Setenv("TYPE", "")

Expand All @@ -431,7 +444,10 @@ func TestEmptyEnv_Allowed(t *testing.T) {
BindEnv("type") // Empty environment variable
BindEnv("name") // Bound, but not set environment variable

os.Clearenv()
os.Unsetenv("type")
os.Unsetenv("TYPE")
os.Unsetenv("name")
os.Unsetenv("NAME")

os.Setenv("TYPE", "")

Expand Down Expand Up @@ -491,11 +507,98 @@ func TestSetEnvKeyReplacer(t *testing.T) {
func TestAllKeys(t *testing.T) {
initConfigs()

ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos",
"title_dotenv", "type_dotenv", "name_dotenv",
ks := sort.StringSlice{
"title",
"newkey",
"owner.organization",
"owner.dob",
"owner.bio",
"name",
"beard",
"ppu",
"batters.batter",
"hobbies",
"clothing.jacket",
"clothing.trousers",
"clothing.pants.size",
"age",
"hacker",
"id",
"type",
"eyes",
"p_id",
"p_ppu",
"p_batters.batter.type",
"p_type",
"p_name",
"foos",
"title_dotenv",
"type_dotenv",
"name_dotenv",
"emails.steve@hacker.com.active",
"emails.steve@hacker.com.created",
}
dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}, "title_dotenv": "DotEnv Example", "type_dotenv": "donut", "name_dotenv": "Cake"}
all := map[string]interface{}{
"owner": map[string]interface{}{
"organization": "MongoDB",
"bio": "MongoDB Chief Developer Advocate & Hacker at Large",
"dob": dob,
},
"title": "TOML Example",
"ppu": 0.55,
"emails": map[string]interface{}{
"steve@hacker.com": map[string]interface{}{
"active": true,
"created": "01/02/03",
},
},
"eyes": "brown",
"clothing": map[string]interface{}{
"trousers": "denim",
"jacket": "leather",
"pants": map[string]interface{}{"size": "large"},
},
"id": "0001",
"batters": map[string]interface{}{
"batter": []interface{}{
map[string]interface{}{"type": "Regular"},
map[string]interface{}{"type": "Chocolate"},
map[string]interface{}{"type": "Blueberry"},
map[string]interface{}{"type": "Devil's Food"},
},
},
"hacker": true,
"beard": true,
"hobbies": []interface{}{
"skateboarding",
"snowboarding",
"go",
},
"age": 35,
"type": "donut",
"newkey": "remote",
"name": "Cake",
"p_id": "0001",
"p_ppu": "0.55",
"p_name": "Cake",
"p_batters": map[string]interface{}{
"batter": map[string]interface{}{"type": "Regular"},
},
"p_type": "donut",
"foos": []map[string]interface{}{
{
"foo": []map[string]interface{}{
{"key": 1},
{"key": 2},
{"key": 3},
{"key": 4}},
},
},
"title_dotenv": "DotEnv Example",
"type_dotenv": "donut",
"name_dotenv": "Cake",
}

allkeys := sort.StringSlice(AllKeys())
allkeys.Sort()
Expand Down Expand Up @@ -960,7 +1063,7 @@ func TestDirsSearch(t *testing.T) {
err = v.ReadInConfig()
assert.Nil(t, err)

assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
}

func TestWrongDirsSearchNotFound(t *testing.T) {
Expand Down Expand Up @@ -1213,6 +1316,10 @@ clothing:
pants:
size: large
trousers: denim
emails:
steve@hacker.com:
active: true
created: 01/02/03
eyes: brown
hacker: true
hobbies:
Expand Down