add browse command and implement -b option for most operations

This commit is contained in:
Cory Bennett
2017-08-20 23:19:43 -05:00
parent f32cc7079c
commit a91b9d56b0
95 changed files with 599 additions and 9853 deletions
+9
View File
@@ -0,0 +1,9 @@
str1:
foo: bar
arr1: strval1
map1:
- abc
- def
int1: true
float1: "strval2"
bool1: 123.45
+2
View File
@@ -2,9 +2,11 @@ str1: d3str1val1
arr1:
- d3arr1val1
- d3arr1val2
- dupval
map1:
key2: d3map1val2
key3: d3map1val3
dup: d3dupval
int1: 333
float1: 3.33
bool1: true
+2
View File
@@ -2,9 +2,11 @@ str1: d2str1val1
arr1:
- d2arr1val1
- d2arr1val2
- dupval
map1:
key1: d2map1val1
key2: d2map1val2
dup: d2dupval
int1: 222
float1: 2.22
bool1: false
+2
View File
@@ -2,9 +2,11 @@ str1: d1str1val1
arr1:
- d1arr1val1
- d1arr1val2
- dupval
map1:
key0: d1map1val0
key1: d1map1val1
dup: d1dupval
int1: 111
float1: 1.11
bool1: true
+6 -6
View File
@@ -33,11 +33,11 @@ func TestOptionsEnv(t *testing.T) {
sort.StringSlice(got).Sort()
expected := []string{
"FIGTREE_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\"]",
"FIGTREE_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\",\"dupval\"]",
"FIGTREE_BOOL_1=true",
"FIGTREE_FLOAT_1=1.11",
"FIGTREE_INT_1=111",
"FIGTREE_MAP_1={\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"FIGTREE_MAP_1={\"dup\":\"d1dupval\",\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"FIGTREE_STRING_1=d1str1val1",
}
@@ -70,11 +70,11 @@ func TestOptionsNamedEnv(t *testing.T) {
sort.StringSlice(got).Sort()
expected := []string{
"TEST_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\"]",
"TEST_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\",\"dupval\"]",
"TEST_BOOL_1=true",
"TEST_FLOAT_1=1.11",
"TEST_INT_1=111",
"TEST_MAP_1={\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"TEST_MAP_1={\"dup\":\"d1dupval\",\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"TEST_STRING_1=d1str1val1",
}
@@ -100,12 +100,12 @@ func TestBuiltinEnv(t *testing.T) {
sort.StringSlice(got).Sort()
expected := []string{
"FIGTREE_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\"]",
"FIGTREE_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\",\"dupval\"]",
"FIGTREE_BOOL_1=true",
"FIGTREE_FLOAT_1=1.11",
"FIGTREE_INT_1=111",
"FIGTREE_LEAVE_EMPTY=",
"FIGTREE_MAP_1={\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"FIGTREE_MAP_1={\"dup\":\"d1dupval\",\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"FIGTREE_STRING_1=d1str1val1",
}
+38 -1
View File
@@ -172,6 +172,17 @@ func (m *merger) mustOverwrite(name string) bool {
return false
}
func isDefault(v reflect.Value) bool {
if v.CanAddr() {
if option, ok := v.Addr().Interface().(Option); ok {
if option.GetSource() == "default" {
return true
}
}
}
return false
}
func isEmpty(v reflect.Value) bool {
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
@@ -249,7 +260,7 @@ func (m *merger) mergeStructs(ov, nv reflect.Value) {
}
fieldName := yamlFieldName(ovStructField)
if (isEmpty(ov.Field(i)) || m.mustOverwrite(fieldName)) && !isSame(ov.Field(i), nv.Field(i)) {
if (isEmpty(ov.Field(i)) || isDefault(ov.Field(i)) || m.mustOverwrite(fieldName)) && !isEmpty(nv.Field(i)) && !isSame(ov.Field(i), nv.Field(i)) {
log.Debugf("Setting %s to %#v", nv.Type().Field(i).Name, nv.Field(i).Interface())
ov.Field(i).Set(nv.Field(i))
} else {
@@ -317,6 +328,15 @@ Outer:
niv := nv.Index(ni)
for oi := 0; oi < ov.Len(); oi++ {
oiv := ov.Index(oi)
if oiv.CanAddr() && niv.CanAddr() {
if oOption, ok := oiv.Addr().Interface().(Option); ok {
if nOption, ok := niv.Addr().Interface().(Option); ok {
if reflect.DeepEqual(oOption.GetValue(), nOption.GetValue()) {
continue Outer
}
}
}
}
if reflect.DeepEqual(niv.Interface(), oiv.Interface()) {
continue Outer
}
@@ -340,7 +360,24 @@ func (f *FigTree) populateEnv(data interface{}) {
// unexported field, skipping
continue
}
name := strings.Join(camelcase.Split(structField.Name), "_")
if tag := structField.Tag.Get("figtree"); tag != "" {
if strings.HasSuffix(tag, ",inline") {
// if we have a tag like: `figtree:",inline"` then we
// want to the field as a top level member and not serialize
// the raw struct to json, so just recurse here
f.populateEnv(options.Field(i).Interface())
continue
}
// next look for `figtree:"env,..."` to set the env name to that
parts := strings.Split(tag, ",")
if len(parts) > 0 {
name = parts[0]
}
}
envName := fmt.Sprintf("%s_%s", f.EnvPrefix, strings.ToUpper(name))
envName = strings.Map(func(r rune) rune {
+68
View File
@@ -4,11 +4,14 @@ import (
"os"
"testing"
logging "gopkg.in/op/go-logging.v1"
"github.com/stretchr/testify/assert"
)
func init() {
StringifyValue = false
logging.SetLevel(logging.NOTICE, "")
}
type TestOptions struct {
@@ -39,6 +42,7 @@ func TestOptionsLoadConfigD3(t *testing.T) {
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../../figtree.yml", true, "d1arr1val1"})
@@ -53,6 +57,7 @@ func TestOptionsLoadConfigD3(t *testing.T) {
"key1": StringOption{"../figtree.yml", true, "d2map1val1"},
"key2": StringOption{"figtree.yml", true, "d3map1val2"},
"key3": StringOption{"figtree.yml", true, "d3map1val3"},
"dup": StringOption{"figtree.yml", true, "d3dupval"},
},
Int1: IntOption{"figtree.yml", true, 333},
Float1: Float32Option{"figtree.yml", true, 3.33},
@@ -72,6 +77,7 @@ func TestOptionsLoadConfigD2(t *testing.T) {
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d1arr1val2"})
@@ -83,6 +89,7 @@ func TestOptionsLoadConfigD2(t *testing.T) {
"key0": StringOption{"../figtree.yml", true, "d1map1val0"},
"key1": StringOption{"figtree.yml", true, "d2map1val1"},
"key2": StringOption{"figtree.yml", true, "d2map1val2"},
"dup": StringOption{"figtree.yml", true, "d2dupval"},
},
Int1: IntOption{"figtree.yml", true, 222},
Float1: Float32Option{"figtree.yml", true, 2.22},
@@ -102,6 +109,7 @@ func TestOptionsLoadConfigD1(t *testing.T) {
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
expected := TestOptions{
String1: StringOption{"figtree.yml", true, "d1str1val1"},
@@ -110,6 +118,7 @@ func TestOptionsLoadConfigD1(t *testing.T) {
Map1: map[string]StringOption{
"key0": StringOption{"figtree.yml", true, "d1map1val0"},
"key1": StringOption{"figtree.yml", true, "d1map1val1"},
"dup": StringOption{"figtree.yml", true, "d1dupval"},
},
Int1: IntOption{"figtree.yml", true, 111},
Float1: Float32Option{"figtree.yml", true, 1.11},
@@ -121,6 +130,15 @@ func TestOptionsLoadConfigD1(t *testing.T) {
assert.Exactly(t, expected, opts)
}
func TestOptionsCorrupt(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1")
defer os.Chdir("..")
err := LoadAllConfigs("corrupt.yml", &opts)
assert.NotNil(t, err)
}
func TestBuiltinLoadConfigD3(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2/d3")
@@ -129,6 +147,7 @@ func TestBuiltinLoadConfigD3(t *testing.T) {
arr1 := []string{}
arr1 = append(arr1, "d3arr1val1")
arr1 = append(arr1, "d3arr1val2")
arr1 = append(arr1, "dupval")
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "d1arr1val1")
@@ -143,6 +162,7 @@ func TestBuiltinLoadConfigD3(t *testing.T) {
"key1": "d2map1val1",
"key2": "d3map1val2",
"key3": "d3map1val3",
"dup": "d3dupval",
},
Int1: 333,
Float1: 3.33,
@@ -162,6 +182,7 @@ func TestBuiltinLoadConfigD2(t *testing.T) {
arr1 := []string{}
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "dupval")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
@@ -173,6 +194,7 @@ func TestBuiltinLoadConfigD2(t *testing.T) {
"key0": "d1map1val0",
"key1": "d2map1val1",
"key2": "d2map1val2",
"dup": "d2dupval",
},
Int1: 222,
Float1: 2.22,
@@ -194,6 +216,7 @@ func TestBuiltinLoadConfigD1(t *testing.T) {
arr1 := []string{}
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
arr1 = append(arr1, "dupval")
expected := TestBuiltin{
String1: "d1str1val1",
@@ -202,6 +225,7 @@ func TestBuiltinLoadConfigD1(t *testing.T) {
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d1map1val1",
"dup": "d1dupval",
},
Int1: 111,
Float1: 1.11,
@@ -212,3 +236,47 @@ func TestBuiltinLoadConfigD1(t *testing.T) {
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinCorrupt(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1")
defer os.Chdir("..")
err := LoadAllConfigs("corrupt.yml", &opts)
assert.NotNil(t, err)
}
func TestOptionsLoadConfigDefaults(t *testing.T) {
opts := TestOptions{
String1: NewStringOption("defaultVal1"),
LeaveEmpty: NewStringOption("emptyVal1"),
Int1: NewIntOption(999),
Float1: NewFloat32Option(9.99),
Bool1: NewBoolOption(false),
}
os.Chdir("d1")
defer os.Chdir("..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
expected := TestOptions{
String1: StringOption{"figtree.yml", true, "d1str1val1"},
LeaveEmpty: StringOption{"default", true, "emptyVal1"},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"figtree.yml", true, "d1map1val0"},
"key1": StringOption{"figtree.yml", true, "d1map1val1"},
"dup": StringOption{"figtree.yml", true, "d1dupval"},
},
Int1: IntOption{"figtree.yml", true, 111},
Float1: Float32Option{"figtree.yml", true, 1.11},
Bool1: BoolOption{"figtree.yml", true, true},
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
+80
View File
@@ -31,6 +31,10 @@ func (o *BoolOption) SetSource(source string) {
o.Source = source
}
func (o *BoolOption) GetSource() string {
return o.Source
}
func (o BoolOption) GetValue() interface{} {
return o.Value
}
@@ -250,6 +254,10 @@ func (o *ByteOption) SetSource(source string) {
o.Source = source
}
func (o *ByteOption) GetSource() string {
return o.Source
}
func (o ByteOption) GetValue() interface{} {
return o.Value
}
@@ -469,6 +477,10 @@ func (o *Complex128Option) SetSource(source string) {
o.Source = source
}
func (o *Complex128Option) GetSource() string {
return o.Source
}
func (o Complex128Option) GetValue() interface{} {
return o.Value
}
@@ -688,6 +700,10 @@ func (o *Complex64Option) SetSource(source string) {
o.Source = source
}
func (o *Complex64Option) GetSource() string {
return o.Source
}
func (o Complex64Option) GetValue() interface{} {
return o.Value
}
@@ -907,6 +923,10 @@ func (o *ErrorOption) SetSource(source string) {
o.Source = source
}
func (o *ErrorOption) GetSource() string {
return o.Source
}
func (o ErrorOption) GetValue() interface{} {
return o.Value
}
@@ -1126,6 +1146,10 @@ func (o *Float32Option) SetSource(source string) {
o.Source = source
}
func (o *Float32Option) GetSource() string {
return o.Source
}
func (o Float32Option) GetValue() interface{} {
return o.Value
}
@@ -1345,6 +1369,10 @@ func (o *Float64Option) SetSource(source string) {
o.Source = source
}
func (o *Float64Option) GetSource() string {
return o.Source
}
func (o Float64Option) GetValue() interface{} {
return o.Value
}
@@ -1564,6 +1592,10 @@ func (o *IntOption) SetSource(source string) {
o.Source = source
}
func (o *IntOption) GetSource() string {
return o.Source
}
func (o IntOption) GetValue() interface{} {
return o.Value
}
@@ -1783,6 +1815,10 @@ func (o *Int16Option) SetSource(source string) {
o.Source = source
}
func (o *Int16Option) GetSource() string {
return o.Source
}
func (o Int16Option) GetValue() interface{} {
return o.Value
}
@@ -2002,6 +2038,10 @@ func (o *Int32Option) SetSource(source string) {
o.Source = source
}
func (o *Int32Option) GetSource() string {
return o.Source
}
func (o Int32Option) GetValue() interface{} {
return o.Value
}
@@ -2221,6 +2261,10 @@ func (o *Int64Option) SetSource(source string) {
o.Source = source
}
func (o *Int64Option) GetSource() string {
return o.Source
}
func (o Int64Option) GetValue() interface{} {
return o.Value
}
@@ -2440,6 +2484,10 @@ func (o *Int8Option) SetSource(source string) {
o.Source = source
}
func (o *Int8Option) GetSource() string {
return o.Source
}
func (o Int8Option) GetValue() interface{} {
return o.Value
}
@@ -2659,6 +2707,10 @@ func (o *RuneOption) SetSource(source string) {
o.Source = source
}
func (o *RuneOption) GetSource() string {
return o.Source
}
func (o RuneOption) GetValue() interface{} {
return o.Value
}
@@ -2878,6 +2930,10 @@ func (o *StringOption) SetSource(source string) {
o.Source = source
}
func (o *StringOption) GetSource() string {
return o.Source
}
func (o StringOption) GetValue() interface{} {
return o.Value
}
@@ -3097,6 +3153,10 @@ func (o *UintOption) SetSource(source string) {
o.Source = source
}
func (o *UintOption) GetSource() string {
return o.Source
}
func (o UintOption) GetValue() interface{} {
return o.Value
}
@@ -3316,6 +3376,10 @@ func (o *Uint16Option) SetSource(source string) {
o.Source = source
}
func (o *Uint16Option) GetSource() string {
return o.Source
}
func (o Uint16Option) GetValue() interface{} {
return o.Value
}
@@ -3535,6 +3599,10 @@ func (o *Uint32Option) SetSource(source string) {
o.Source = source
}
func (o *Uint32Option) GetSource() string {
return o.Source
}
func (o Uint32Option) GetValue() interface{} {
return o.Value
}
@@ -3754,6 +3822,10 @@ func (o *Uint64Option) SetSource(source string) {
o.Source = source
}
func (o *Uint64Option) GetSource() string {
return o.Source
}
func (o Uint64Option) GetValue() interface{} {
return o.Value
}
@@ -3973,6 +4045,10 @@ func (o *Uint8Option) SetSource(source string) {
o.Source = source
}
func (o *Uint8Option) GetSource() string {
return o.Source
}
func (o Uint8Option) GetValue() interface{} {
return o.Value
}
@@ -4192,6 +4268,10 @@ func (o *UintptrOption) SetSource(source string) {
o.Source = source
}
func (o *UintptrOption) GetSource() string {
return o.Source
}
func (o UintptrOption) GetValue() interface{} {
return o.Value
}
+2
View File
@@ -34,6 +34,7 @@ func TestCommandLine(t *testing.T) {
arr1 := ListStringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../../figtree.yml", true, "d1arr1val1"})
@@ -49,6 +50,7 @@ func TestCommandLine(t *testing.T) {
"key1": StringOption{"../figtree.yml", true, "d2map1val1"},
"key2": StringOption{"figtree.yml", true, "d3map1val2"},
"key3": StringOption{"figtree.yml", true, "d3map1val3"},
"dup": StringOption{"figtree.yml", true, "d3dupval"},
"k1": StringOption{"override", true, "v1"},
"k2": StringOption{"override", true, "v2"},
},
+3 -1
View File
@@ -28,11 +28,13 @@ func TestOptionsMarshalYAML(t *testing.T) {
arr1:
- d3arr1val1
- d3arr1val2
- dupval
- d2arr1val1
- d2arr1val2
- d1arr1val1
- d1arr1val2
map1:
dup: d3dupval
key0: d1map1val0
key1: d2map1val1
key2: d3map1val2
@@ -60,6 +62,6 @@ func TestOptionsMarshalJSON(t *testing.T) {
// note that "leave-empty" is serialized even though "omitempty" tag is set
// this is because json always assumes structs are not empty and there
// is no interface to override this behavior
expected := `{"str1":"d3str1val1","leave-empty":"","arr1":["d3arr1val1","d3arr1val2","d2arr1val1","d2arr1val2","d1arr1val1","d1arr1val2"],"map1":{"key0":"d1map1val0","key1":"d2map1val1","key2":"d3map1val2","key3":"d3map1val3"},"int1":333,"float1":3.33,"bool1":true}`
expected := `{"str1":"d3str1val1","leave-empty":"","arr1":["d3arr1val1","d3arr1val2","dupval","d2arr1val1","d2arr1val2","d1arr1val1","d1arr1val2"],"map1":{"dup":"d3dupval","key0":"d1map1val0","key1":"d2map1val1","key2":"d3map1val2","key3":"d3map1val3"},"int1":333,"float1":3.33,"bool1":true}`
assert.Equal(t, expected, string(got))
}
+1
View File
@@ -7,6 +7,7 @@ type Option interface {
GetValue() interface{}
SetValue(interface{}) error
SetSource(string)
GetSource() string
}
var StringifyValue = true
+4
View File
@@ -33,6 +33,10 @@ func (o *RawTypeOption) SetSource(source string) {
o.Source = source
}
func (o *RawTypeOption) GetSource() string {
return o.Source
}
func (o RawTypeOption) GetValue() interface{} {
return o.Value
}
+5 -6
View File
@@ -186,6 +186,9 @@ func (e *SaveCookieError) Error() string {
}
func (c *Client) saveCookies(resp *http.Response) error {
if c.cookieFile == "" {
return nil
}
if _, ok := resp.Header["Set-Cookie"]; !ok {
return nil
}
@@ -311,9 +314,10 @@ func (c *Client) Do(req *http.Request) (resp *http.Response, err error) {
log.Debugf("%s %s", req.Method, req.URL.String())
if log.IsEnabledFor(logging.DEBUG) && TraceRequestBody {
out, _ := httputil.DumpRequestOut(req, true)
out, _ := httputil.DumpRequest(req, true)
log.Debugf("Request: %s", out)
}
resp, err = c.Client.Do(req)
if err != nil {
return nil, err
@@ -331,11 +335,6 @@ func (c *Client) Do(req *http.Request) (resp *http.Response, err error) {
}
}
if log.IsEnabledFor(logging.DEBUG) && TraceResponseBody {
out, _ := httputil.DumpResponse(resp, true)
log.Debugf("Response: %s", out)
}
err = c.saveCookies(resp)
if err != nil {
return resp, err