forked from Praqma/helmsman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(Praqma#137): use nullable boolean type for the Release boolean fi…
…elds
- Loading branch information
Showing
13 changed files
with
400 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
## This is a minimal example. | ||
## It will use your current kube context and will deploy Tiller without RBAC service account. | ||
## For the full config spec and options, check https://github.com/Praqma/helmsman/blob/master/docs/desired_state_specification.md | ||
|
||
apps: | ||
jenkins: | ||
enabled: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package app | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/invopop/jsonschema" | ||
"reflect" | ||
"strconv" | ||
) | ||
|
||
// truthy and falsy NullBool values | ||
var ( | ||
True = NullBool{HasValue: true, Value: true} | ||
False = NullBool{HasValue: true, Value: false} | ||
) | ||
|
||
// NullBool represents a bool that may be null. | ||
type NullBool struct { | ||
Value bool | ||
HasValue bool // true if bool is not null | ||
} | ||
|
||
func (b NullBool) MarshalJSON() ([]byte, error) { | ||
value := b.HasValue && b.Value | ||
return json.Marshal(value) | ||
} | ||
|
||
func (b *NullBool) UnmarshalJSON(data []byte) error { | ||
var unmarshalledJson bool | ||
|
||
err := json.Unmarshal(data, &unmarshalledJson) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
b.Value = unmarshalledJson | ||
b.HasValue = true | ||
|
||
return nil | ||
} | ||
|
||
func (b *NullBool) UnmarshalText(text []byte) error { | ||
str := string(text) | ||
if len(str) < 1 { | ||
return nil | ||
} | ||
|
||
value, err := strconv.ParseBool(str) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
b.HasValue = true | ||
b.Value = value | ||
|
||
return nil | ||
} | ||
|
||
// JSONSchema instructs the jsonschema generator to represent NullBool type as boolean | ||
func (NullBool) JSONSchema() *jsonschema.Schema { | ||
return &jsonschema.Schema{ | ||
Type: "boolean", | ||
} | ||
} | ||
|
||
type MergoTransformer func(typ reflect.Type) func(dst, src reflect.Value) error | ||
|
||
func (m MergoTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { | ||
return m(typ) | ||
} | ||
|
||
// NullBoolTransformer is a custom imdario/mergo transformer for the NullBool type | ||
func NullBoolTransformer(typ reflect.Type) func(dst, src reflect.Value) error { | ||
if typ != reflect.TypeOf(NullBool{}) { | ||
return nil | ||
} | ||
|
||
return func(dst, src reflect.Value) error { | ||
if src.FieldByName("HasValue").Bool() { | ||
dst.Set(src) | ||
} | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
package app | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestNullBool_MarshalJSON(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
value NullBool | ||
want []byte | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "should be false", | ||
want: []byte(`false`), | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "should be true", | ||
want: []byte(`true`), | ||
value: NullBool{HasValue: true, Value: true}, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "should be false when HasValue is false", | ||
want: []byte(`false`), | ||
value: NullBool{HasValue: false, Value: true}, | ||
wantErr: false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := tt.value.MarshalJSON() | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("MarshalJSON() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestNullBool_UnmarshalJSON(t *testing.T) { | ||
type output struct { | ||
Value NullBool `json:"value"` | ||
} | ||
tests := []struct { | ||
name string | ||
data []byte | ||
want output | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "should have value set to false", | ||
data: []byte(`{"value": false}`), | ||
want: output{NullBool{HasValue: true, Value: false}}, | ||
}, | ||
{ | ||
name: "should have value set to true", | ||
data: []byte(`{"value": true}`), | ||
want: output{NullBool{HasValue: true, Value: true}}, | ||
}, | ||
{ | ||
name: "should have value unset", | ||
data: []byte("{}"), | ||
want: output{NullBool{HasValue: false, Value: false}}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
var got output | ||
if err := json.NewDecoder(bytes.NewReader(tt.data)).Decode(&got); (err != nil) != tt.wantErr { | ||
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("UnmarshalJSON() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestNullBool_UnmarshalText(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
text []byte | ||
want NullBool | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "should have the value set to false", | ||
text: []byte("false"), | ||
want: NullBool{HasValue: true, Value: false}, | ||
}, | ||
{ | ||
name: "should have the value set to true", | ||
text: []byte("false"), | ||
want: NullBool{HasValue: true, Value: false}, | ||
}, | ||
{ | ||
name: "should have the value unset", | ||
text: []byte(""), | ||
want: NullBool{HasValue: false, Value: false}, | ||
}, | ||
{ | ||
name: "should return an error on wrong input", | ||
text: []byte("wrong_input"), | ||
wantErr: true, | ||
want: NullBool{HasValue: false, Value: false}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
var got NullBool | ||
if err := got.UnmarshalText(tt.text); (err != nil) != tt.wantErr { | ||
t.Errorf("UnmarshalText() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("UnmarshalText() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestNullBoolTransformer(t *testing.T) { | ||
type args struct { | ||
dst NullBool | ||
src NullBool | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want NullBool | ||
}{ | ||
{ | ||
name: "should overwrite true to false when the dst has the value", | ||
args: args{ | ||
dst: NullBool{HasValue: true, Value: true}, | ||
src: NullBool{HasValue: true, Value: false}, | ||
}, | ||
want: NullBool{HasValue: true, Value: false}, | ||
}, | ||
{ | ||
name: "shouldn't overwrite when the value is unset", | ||
args: args{ | ||
dst: NullBool{HasValue: true, Value: true}, | ||
src: NullBool{HasValue: false, Value: false}, | ||
}, | ||
want: NullBool{HasValue: true, Value: true}, | ||
}, | ||
{ | ||
name: "shouldn overwrite when the value is set and equal true", | ||
args: args{ | ||
dst: NullBool{HasValue: true, Value: false}, | ||
src: NullBool{HasValue: true, Value: true}, | ||
}, | ||
want: NullBool{HasValue: true, Value: true}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
dst := tt.args.dst | ||
src := tt.args.src | ||
|
||
transformer := NullBoolTransformer(reflect.TypeOf(NullBool{})) | ||
|
||
transformer(reflect.ValueOf(&dst).Elem(), reflect.ValueOf(src)) | ||
|
||
if !reflect.DeepEqual(dst, tt.want) { | ||
t.Errorf("NullBoolTransformer() = %v, want %v", dst, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.