Usage
By adding these:
package main
import (
"fmt"
"net/http"
"github.com/avrebarra/ctl"
)
func main() {
cpx := ctl.GetGlobal()
// setting config values
cpx.Set("flags.hello_enabled", true) // no error handling
// setup handlers
http.DefaultServeMux.Handle("/ctl/", ctl.MakeHandler(ctl.ConfigHandler{PathPrefix: "/ctl/", Ctl: ctl.GetGlobal()}))
http.DefaultServeMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
// get config value
flaghello, _ := cpx.Get("flags.hello_enabled").Bool()
// use it
if flaghello {
fmt.Fprintf(w, "hello\n")
return
}
fmt.Fprintf(w, "sorry, no hello\n")
})
fmt.Println("listening http://localhost:3333...")
http.ListenAndServe(":3333", http.DefaultServeMux)
}
You will have a dynamic configurables plus HTTP control panel:
$ curl --location --request GET 'localhost:3333/ctl/config'
{"flags.hello_enabled":"true","settings.logging_defaults":"{\"Version\":\"1.0\",\"ClusterID\":\"88888\"}","settings.logging_min_amount":"100000","settings.logging_prefix":"trx_log"}
$ curl --location --request PATCH 'localhost:3333/ctl/config/flags.hello_enabled' \
--header 'Content-Type: application/json' \
--data-raw '{
"value": "false"
}'
{"key":"flags.transaction_logging_enabled","value":"false"}
Available endpoints:
Other Examples
Managing values via code
Note: It's recommended to specify a centralized storage. By doing so, multiple instances of same service could make use of shared/synchronized dynamic configs. You can also define your own store for db/redis/consul etc by implementing Store
interface
package main
import (
"fmt"
"github.com/avrebarra/ctl"
)
func main() {
// setup instance with file storage
store, _ := ctl.NewStoreFile(ctl.ConfigStoreFile{FilePath: "fixture/store.json"})
cpx, _ := ctl.New(ctl.Config{
Store: store,
RefreshRate: 10 * time.Second, // how often to refetch data from store
})
// setting configurations
cpx.Set("flags.logging_enabled", true) // no error handling
cpx.Set("settings.logging_prefix", "trx_log")
cpx.Set("settings.logging_defaults", DataField{Version: "1.0", ClusterID: "88888"})
// handling for errors
err := cpx.Set("settings.logging_min_amount", 100000).Err()
if err != nil {
fmt.Println(err.Error())
return
}
// getting config and assert as multiple types (boolean, float, string, object)
flagEnableBanner, _ := cpx.Get("flags.enable_banner").Bool()
confMinAmt, _ := cpx.Get("settings.logging_min_amount").Int()
// binding value to object
datafield := DataField{}
_ = cpx.Get("settings.logging_defaults").Bind(&datafield)
}
Referencing and Refreshing Values
package main
import (
"fmt"
"github.com/avrebarra/ctl"
)
func main() {
ref := ctl.GetGlobal().Get("flags.isok") // initial value: none
fmt.Println(ref.Refresh().Bool())
ctl.GetGlobal().Set("flags.isok", "true") // change value
fmt.Println(ref.Refresh().Bool())
// Output:
// false value not found
// true <nil>
}
Replacing the Global Singleton
By default the global instance will be generated with memstore, but you can override it using custom store and options.
package main
import (
"fmt"
"github.com/avrebarra/ctl"
)
func main() {
store, _ := ctl.NewStoreFile(ctl.ConfigStoreFile{FilePath: "fixture/store.json"})
cpx, _ := ctl.New(ctl.Config{
Store: store,
RefreshRate: 10 * time.Second,
})
ctl.RegisterGlobal(cpx)
flagEnableBanner, _ = ctl.GetGlobal().Get("flags.enable_banner").Bool()
fmt.Println("values:", flagEnableBanner)
}
Milestones
- Value.Float()
- Ctl.Reset()
- Ctl.Subscribe()
- Ctl.StopSubscribe()
- Persistence
- Value Encryption
- Reference values
- REST API Handler helper for management