spf13 / viper

Go configuration with fangs
MIT License
27.19k stars 2.02k forks source link

Surgical update using a deep string path to the value #1140

Open ghstahl opened 3 years ago

ghstahl commented 3 years ago

Given a fully merged config, I would like to update an item based upon a path to the item.

Assume the following JSON config.

{
    "id": "0001",
    "type": "donut",
    "name": "Cake",
    "ppu": 0.55,
    "batters": {
        "batter": [{
                "type": "Regular"
            },
            {
                "type": "Chocolate"
            },
            {
                "type": "Blueberry"
            },
            {
                "type": "Devil's Food"
            }
        ],
        "topping": [
            "Icing", "Chocolate"
        ]
    }
}
v := New()
v.SetConfigType("json")
v.ReadConfig(bytes.NewBuffer(jsonExample))

os.Setenv("batters.doesnotexist", "ted")
os.Setenv("batters.batter.0.type", "bob")
os.Setenv("batters.topping.1", "frosting")
v.SurgicalPathUpdateFromEnv()

os.Remove("batters.doesnotexist")
os.Remove("batters.batter.0.type")
os.Remove("batters.topping.1")

should result in the following updates;

{
    "id": "0001",
    "type": "donut",
    "name": "Cake",
    "ppu": 0.55,
    "batters": {
        "batter": [{
                "type": "bob"
            },
            {
                "type": "Chocolate"
            },
            {
                "type": "Blueberry"
            },
            {
                "type": "Devil's Food"
            }
        ],
        "topping": [
            "Icing", "frosting"
        ]
    }
}

Describe alternatives you've considered reference project - viperEx
I was able to do this after the fact with the following technique

    allSettings := myViper.AllSettings()

        myViperEx := New("__")
    myViperEx.SurgicalUpdate("nest__Eggs__0__Weight", 1234, allSettings)
    myViperEx.SurgicalUpdate("nest__Eggs__0__SomeValues__1__Value", "abcd", allSettings)
    myViperEx.SurgicalUpdate("nest__Eggs__0__SomeStrings__1__", "abcd", allSettings)
    myViperEx.SurgicalUpdate("junk__A", "abcd", allSettings)
    myViperEx.SurgicalUpdate("nest__junk", "abcd", allSettings)

        settings := Settings{}
    err = myViper.Unmarshal(&settings)

where I wrote a ViperEx helper that updated the AllSettings that viper owns. Arguably it is really more of a mapstructure thing that traverses maps, etc. The difference is really should this be a separate project vs being part of Viper.

This type of processing is part of the asp.net core configuration builder which is very similar in design to the viper design. So, I vote for being part of Viper.

On the viper side, the biggest issue I think is setting an item in an array. i.e.

viper.Set("a.b.1.name", "bob")

This doesn't exist, but it should, especially when there is a getter that allows it.

github-actions[bot] commented 3 years ago

👋 Thanks for reporting!

A maintainer will take a look at your issue shortly. 👀

In the meantime: We are working on Viper v2 and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.

⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9

📣 If you've already given us your feedback, you can still help by spreading the news, either by sharing the above link or telling people about this on Twitter:

https://twitter.com/sagikazarmark/status/1306904078967074816

Thank you! ❤️