Add tenant feature

This commit is contained in:
lucas 2016-08-31 23:25:51 -03:00
parent 16990631d4
commit f9a0422e88
3 changed files with 246 additions and 0 deletions

View file

@ -599,6 +599,56 @@ y.SetDefault("ContentDir", "foobar")
When working with multiple vipers, it is up to the user to keep track of the When working with multiple vipers, it is up to the user to keep track of the
different vipers. different vipers.
### Working with tenant
Sometime you want your config to be override by a more specific one, use tenant for it.
Example:
config file:
```json
[shared]
name = "name_shared"
[tenant_a]
name = "name_a"
[tenant_b]
```
code:
```go
viper.SetTenantDefault("shared")
viper.SetConfigName("teste")
viper.AddConfigPath(".")
viper.ReadInConfig()
fmt.Printf(viper.GetStringTenant("tenant_a", "name")) // name_a
fmt.Printf(viper.GetStringTenant("tenant_b", "name")) // name_shared
```
config file:
```json
name = "name_shared"
[tenant_a]
name = "name_a"
[tenant_b]
```
code:
```go
viper.SetConfigName("teste")
viper.AddConfigPath(".")
viper.ReadInConfig()
fmt.Printf(viper.GetStringTenant("tenant_a", "name")) // name_a
fmt.Printf(viper.GetStringTenant("tenant_b", "name")) // name_shared
```
## Q & A ## Q & A
Q: Why not INI files? Q: Why not INI files?

162
viper.go
View file

@ -157,6 +157,9 @@ type Viper struct {
typeByDefValue bool typeByDefValue bool
onConfigChange func(fsnotify.Event) onConfigChange func(fsnotify.Event)
//name of default tenant
tenantDefault string
} }
// Returns an initialized Viper instance. // Returns an initialized Viper instance.
@ -270,6 +273,12 @@ func (v *Viper) WatchConfig() {
}() }()
} }
// Explicitly define the tenant default that will be override by others tenants
func SetTenantDefault(tenDefault string) { v.SetTenantDefault(tenDefault) }
func (v *Viper) SetTenantDefault(tenDefault string) {
v.tenantDefault = tenDefault
}
// Explicitly define the path, name and extension of the config file // Explicitly define the path, name and extension of the config file
// Viper will use this and not check any of the config paths // Viper will use this and not check any of the config paths
func SetConfigFile(in string) { v.SetConfigFile(in) } func SetConfigFile(in string) { v.SetConfigFile(in) }
@ -542,66 +551,219 @@ func (v *Viper) Sub(key string) *Viper {
} }
} }
// Returns the value associated with the key as a string considering the tenant selected
func GetStringTenant(tenant string, key string) string { return v.GetStringTenant(tenant, key) }
func (v *Viper) GetStringTenant(tenant string, key string) string {
if v.IsSet(tenant + "." + key) {
return cast.ToString(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToString(v.Get(v.tenantDefault + "." + key))
}
return cast.ToString(v.Get(key))
}
// Returns the value associated with the key as a string // Returns the value associated with the key as a string
func GetString(key string) string { return v.GetString(key) } func GetString(key string) string { return v.GetString(key) }
func (v *Viper) GetString(key string) string { func (v *Viper) GetString(key string) string {
return cast.ToString(v.Get(key)) return cast.ToString(v.Get(key))
} }
// Returns the value associated with the key as a boolean considering the tenant selected
func GetBoolTenant(tenant string, key string) bool { return v.GetBoolTenant(tenant, key) }
func (v *Viper) GetBoolTenant(tenant string, key string) bool {
if v.IsSet(tenant + "." + key) {
return cast.ToBool(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToBool(v.Get(v.tenantDefault + "." + key))
}
return cast.ToBool(v.Get(key))
}
// Returns the value associated with the key as a boolean // Returns the value associated with the key as a boolean
func GetBool(key string) bool { return v.GetBool(key) } func GetBool(key string) bool { return v.GetBool(key) }
func (v *Viper) GetBool(key string) bool { func (v *Viper) GetBool(key string) bool {
return cast.ToBool(v.Get(key)) return cast.ToBool(v.Get(key))
} }
// Returns the value associated with the key as a int considering the tenant selected
func GetIntTenant(tenant string, key string) int { return v.GetIntTenant(tenant, key) }
func (v *Viper) GetIntTenant(tenant string, key string) int {
if v.IsSet(tenant + "." + key) {
return cast.ToInt(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToInt(v.Get(v.tenantDefault + "." + key))
}
return cast.ToInt(v.Get(key))
}
// Returns the value associated with the key as an integer // Returns the value associated with the key as an integer
func GetInt(key string) int { return v.GetInt(key) } func GetInt(key string) int { return v.GetInt(key) }
func (v *Viper) GetInt(key string) int { func (v *Viper) GetInt(key string) int {
return cast.ToInt(v.Get(key)) return cast.ToInt(v.Get(key))
} }
// Returns the value associated with the key as a int considering the tenant selected
func GetInt64Tenant(tenant string, key string) int64 { return v.GetInt64Tenant(tenant, key) }
func (v *Viper) GetInt64Tenant(tenant string, key string) int64 {
if v.IsSet(tenant + "." + key) {
return cast.ToInt64(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToInt64(v.Get(v.tenantDefault + "." + key))
}
return cast.ToInt64(v.Get(key))
}
// Returns the value associated with the key as an integer // Returns the value associated with the key as an integer
func GetInt64(key string) int64 { return v.GetInt64(key) } func GetInt64(key string) int64 { return v.GetInt64(key) }
func (v *Viper) GetInt64(key string) int64 { func (v *Viper) GetInt64(key string) int64 {
return cast.ToInt64(v.Get(key)) return cast.ToInt64(v.Get(key))
} }
// Returns the value associated with the key as a float considering the tenant selected
func GetFloat64Tenant(tenant string, key string) float64 { return v.GetFloat64Tenant(tenant, key) }
func (v *Viper) GetFloat64Tenant(tenant string, key string) float64 {
if v.IsSet(tenant + "." + key) {
return cast.ToFloat64(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToFloat64(v.Get(v.tenantDefault + "." + key))
}
return cast.ToFloat64(v.Get(key))
}
// Returns the value associated with the key as a float64 // Returns the value associated with the key as a float64
func GetFloat64(key string) float64 { return v.GetFloat64(key) } func GetFloat64(key string) float64 { return v.GetFloat64(key) }
func (v *Viper) GetFloat64(key string) float64 { func (v *Viper) GetFloat64(key string) float64 {
return cast.ToFloat64(v.Get(key)) return cast.ToFloat64(v.Get(key))
} }
// Returns the value associated with the key as a time considering the tenant selected
func GetTimeTenant(tenant string, key string) time.Time { return v.GetTimeTenant(tenant, key) }
func (v *Viper) GetTimeTenant(tenant string, key string) time.Time {
if v.IsSet(tenant + "." + key) {
return cast.ToTime(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToTime(v.Get(v.tenantDefault + "." + key))
}
return cast.ToTime(v.Get(key))
}
// Returns the value associated with the key as time // Returns the value associated with the key as time
func GetTime(key string) time.Time { return v.GetTime(key) } func GetTime(key string) time.Time { return v.GetTime(key) }
func (v *Viper) GetTime(key string) time.Time { func (v *Viper) GetTime(key string) time.Time {
return cast.ToTime(v.Get(key)) return cast.ToTime(v.Get(key))
} }
// Returns the value associated with the key as a duration considering the tenant selected
func GetDurationTenant(tenant string, key string) time.Duration {
return v.GetDurationTenant(tenant, key)
}
func (v *Viper) GetDurationTenant(tenant string, key string) time.Duration {
if v.IsSet(tenant + "." + key) {
return cast.ToDuration(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToDuration(v.Get(v.tenantDefault + "." + key))
}
return cast.ToDuration(v.Get(key))
}
// Returns the value associated with the key as a duration // Returns the value associated with the key as a duration
func GetDuration(key string) time.Duration { return v.GetDuration(key) } func GetDuration(key string) time.Duration { return v.GetDuration(key) }
func (v *Viper) GetDuration(key string) time.Duration { func (v *Viper) GetDuration(key string) time.Duration {
return cast.ToDuration(v.Get(key)) return cast.ToDuration(v.Get(key))
} }
// Returns the value associated with the key as a slice of strings considering the tenant selected
func GetStringSliceTenant(tenant string, key string) []string {
return v.GetStringSliceTenant(tenant, key)
}
func (v *Viper) GetStringSliceTenant(tenant string, key string) []string {
if v.IsSet(tenant + "." + key) {
return cast.ToStringSlice(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToStringSlice(v.Get(v.tenantDefault + "." + key))
}
return cast.ToStringSlice(v.Get(key))
}
// Returns the value associated with the key as a slice of strings // Returns the value associated with the key as a slice of strings
func GetStringSlice(key string) []string { return v.GetStringSlice(key) } func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
func (v *Viper) GetStringSlice(key string) []string { func (v *Viper) GetStringSlice(key string) []string {
return cast.ToStringSlice(v.Get(key)) return cast.ToStringSlice(v.Get(key))
} }
// Returns the value associated with the key as a map of interfaces considering the tenant selected
func GetStringMapTenant(tenant string, key string) map[string]interface{} {
return v.GetStringMapTenant(tenant, key)
}
func (v *Viper) GetStringMapTenant(tenant string, key string) map[string]interface{} {
if v.IsSet(tenant + "." + key) {
return cast.ToStringMap(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToStringMap(v.Get(v.tenantDefault + "." + key))
}
return cast.ToStringMap(v.Get(key))
}
// Returns the value associated with the key as a map of interfaces // Returns the value associated with the key as a map of interfaces
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) } func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
func (v *Viper) GetStringMap(key string) map[string]interface{} { func (v *Viper) GetStringMap(key string) map[string]interface{} {
return cast.ToStringMap(v.Get(key)) return cast.ToStringMap(v.Get(key))
} }
// Returns the value associated with the key as a map of strings considering the tenant selected
func GetStringMapStringTenant(tenant string, key string) map[string]string {
return v.GetStringMapStringTenant(tenant, key)
}
func (v *Viper) GetStringMapStringTenant(tenant string, key string) map[string]string {
if v.IsSet(tenant + "." + key) {
return cast.ToStringMapString(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToStringMapString(v.Get(v.tenantDefault + "." + key))
}
return cast.ToStringMapString(v.Get(key))
}
// Returns the value associated with the key as a map of strings // Returns the value associated with the key as a map of strings
func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) } func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
func (v *Viper) GetStringMapString(key string) map[string]string { func (v *Viper) GetStringMapString(key string) map[string]string {
return cast.ToStringMapString(v.Get(key)) return cast.ToStringMapString(v.Get(key))
} }
// Returns the value associated with the key as map to a slice of strings considering the tenant selected
func GetStringMapStringSliceTenant(tenant string, key string) map[string][]string {
return v.GetStringMapStringSliceTenant(tenant, key)
}
func (v *Viper) GetStringMapStringSliceTenant(tenant string, key string) map[string][]string {
if v.IsSet(tenant + "." + key) {
return cast.ToStringMapStringSlice(v.Get(tenant + "." + key))
}
if v.tenantDefault != "" {
return cast.ToStringMapStringSlice(v.Get(v.tenantDefault + "." + key))
}
return cast.ToStringMapStringSlice(v.Get(key))
}
// Returns the value associated with the key as a map to a slice of strings. // Returns the value associated with the key as a map to a slice of strings.
func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) } func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {

View file

@ -102,6 +102,21 @@ var remoteExample = []byte(`{
"newkey":"remote" "newkey":"remote"
}`) }`)
var tenantExample = []byte(`
title = "TOML Example"
attr = "Foo"
[owner]
title = "TOML!"`)
var tenantExampleWithDefault = []byte(`
[default]
title = "TOML Example"
attr = "Foo"
[owner]
title = "TOML!"`)
func initConfigs() { func initConfigs() {
Reset() Reset()
SetConfigType("yaml") SetConfigType("yaml")
@ -760,6 +775,25 @@ func TestSub(t *testing.T) {
assert.Equal(t, subv, (*Viper)(nil)) assert.Equal(t, subv, (*Viper)(nil))
} }
func TestTenant(t *testing.T) {
v := New()
v.SetConfigType("toml")
v.ReadConfig(bytes.NewBuffer(tenantExample))
assert.Equal(t, v.GetStringTenant("owner", "title"), "TOML!")
assert.Equal(t, v.GetStringTenant("owner", "attr"), "Foo")
}
func TestTenantWithDefault(t *testing.T) {
v := New()
v.SetConfigType("toml")
v.SetTenantDefault("default")
v.ReadConfig(bytes.NewBuffer(tenantExampleWithDefault))
assert.Equal(t, v.GetStringTenant("owner", "title"), "TOML!")
assert.Equal(t, v.GetStringTenant("owner", "attr"), "Foo")
}
var yamlMergeExampleTgt = []byte(` var yamlMergeExampleTgt = []byte(`
hello: hello:
pop: 37890 pop: 37890