Skip to content

Commit

Permalink
updated default env variable implementation to be generic to work for…
Browse files Browse the repository at this point in the history
… all types, fixes #15
  • Loading branch information
gurkankaymak committed Aug 6, 2022
1 parent 2254e3f commit 8573646
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 47 deletions.
14 changes: 7 additions & 7 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
NullType
SubstitutionType
ConcatenationType
stringWithAlternativeType
valueWithAlternativeType
)

// Config stores the root of the configuration tree
Expand Down Expand Up @@ -287,19 +287,19 @@ func (s String) Type() Type { return StringType }
func (s String) String() string { return strings.Trim(string(s), `"`) }
func (s String) isConcatenable() bool { return true }

// stringWithAlternative represents a string value with Substitution which might override the original string value
type stringWithAlternative struct {
value String
// valueWithAlternative represents a value with Substitution which might override the original value
type valueWithAlternative struct {
value Value
alternative *Substitution
}

func (s *stringWithAlternative) Type() Type { return stringWithAlternativeType }
func (s *valueWithAlternative) Type() Type { return valueWithAlternativeType }

func (s *stringWithAlternative) String() string {
func (s *valueWithAlternative) String() string {
return fmt.Sprintf("(%s | %s)", s.value, s.alternative.String())
}

func (s *stringWithAlternative) isConcatenable() bool { return false }
func (s *valueWithAlternative) isConcatenable() bool { return false }

// Object represents an object node in the configuration tree
type Object map[string]Value
Expand Down
6 changes: 3 additions & 3 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,10 +442,10 @@ func TestSubstitution_String(t *testing.T) {
})
}

func TestStringWithAlternative_String(t *testing.T) {
t.Run("return the string of string with alternative", func(t *testing.T) {
func TestValueWithAlternative_String(t *testing.T) {
t.Run("return the string of valueWithAlternative", func(t *testing.T) {
substitution := Substitution{path: "a", optional: false}
withAlt := stringWithAlternative{value: "value", alternative: &substitution}
withAlt := valueWithAlternative{value: String("value"), alternative: &substitution}
got := withAlt.String()
assertEquals(t, got, "(value | ${a})")
})
Expand Down
18 changes: 6 additions & 12 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ func processSubstitution(root Object, value Value, resolveFunc func(value Value)
}
resolveFunc(processed)
return nil
} else if valueType == stringWithAlternativeType {
withAlternative := value.(*stringWithAlternative)
} else if valueType == valueWithAlternativeType {
withAlternative := value.(*valueWithAlternative)
if withAlternative.alternative != nil {
processed, err := processSubstitutionType(root, withAlternative.alternative)
if err != nil {
Expand Down Expand Up @@ -306,16 +306,10 @@ func (p *parser) extractObject(isSubObject ...bool) (Object, error) {
(existingValue.Type() == ObjectType && value.Type() == SubstitutionType) ||
(existingValue.Type() == SubstitutionType && value.Type() == ObjectType) {
value = concatenation{existingValue, value}
} else if existingValue.Type() == StringType && value.Type() == SubstitutionType {
value = &stringWithAlternative{
value: existingValue.(String),
alternative: value.(*Substitution),
}
} else if existingValue.Type() == stringWithAlternativeType && value.Type() == SubstitutionType {
value = &stringWithAlternative{
value: existingValue.(*stringWithAlternative).value,
alternative: value.(*Substitution),
}
} else if existingValue.Type() == valueWithAlternativeType && value.Type() == SubstitutionType {
value = &valueWithAlternative{value: existingValue, alternative: value.(*Substitution)}
} else if value.Type() == SubstitutionType {
value = &valueWithAlternative{value: existingValue, alternative: value.(*Substitution)}
}
}

Expand Down
85 changes: 60 additions & 25 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"os"
"reflect"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -365,6 +364,20 @@ func TestExtractObject(t *testing.T) {
assertDeepEqual(t, got, expected)
})

t.Run("return valueWithAlternative object if the current value (after colon separator) is substitution and the existing value is neither a substitution nor object", func(t *testing.T) {
parser := newParser(strings.NewReader("{a:1,a:${?b}}"))
parser.advance()
expected := Object{
"a": &valueWithAlternative{
value: Int(1),
alternative: &Substitution{path: "b", optional: true},
},
}
got, err := parser.extractObject()
assertNoError(t, err)
assertDeepEqual(t, got, expected)
})

t.Run("override the existing value if the current value (after colon separator) is object and there is an existing non-object with the same key", func(t *testing.T) {
parser := newParser(strings.NewReader("{a:1,a:{c:2}}"))
parser.advance()
Expand Down Expand Up @@ -564,7 +577,7 @@ func TestResolveSubstitutions(t *testing.T) {
testEnv := "TEST_ENV"
testEnvValue := "test"
envSubstitution := &Substitution{path: testEnv, optional: false}
staticWithEnv := &stringWithAlternative{value: "static", alternative: envSubstitution}
staticWithEnv := &valueWithAlternative{value: String("static"), alternative: envSubstitution}
object := Object{"a": staticWithEnv}
err := os.Setenv(testEnv, testEnvValue)
assertNoError(t, err)
Expand All @@ -582,7 +595,7 @@ func TestResolveSubstitutions(t *testing.T) {
t.Run("resolve to the static value if substitution path does not exist and environment variable is not set and default value was not provided", func(t *testing.T) {
defaultValue := String("default")
envSubstitution := &Substitution{path: "TEST_ENV", optional: true}
staticWithEnv := &stringWithAlternative{value: defaultValue, alternative: envSubstitution}
staticWithEnv := &valueWithAlternative{value: defaultValue, alternative: envSubstitution}
object := Object{"a": staticWithEnv}
err := resolveSubstitutions(object)
assertNoError(t, err)
Expand All @@ -595,7 +608,7 @@ func TestResolveSubstitutions(t *testing.T) {
t.Run("return an error if cannot find required substitution and default value was provided", func(t *testing.T) {
defaultValue := String("default")
envSubstitution := &Substitution{path: "TEST_ENV", optional: false}
staticWithEnv := &stringWithAlternative{value: defaultValue, alternative: envSubstitution}
staticWithEnv := &valueWithAlternative{value: defaultValue, alternative: envSubstitution}
object := Object{"a": staticWithEnv}
err := resolveSubstitutions(object)

Expand Down Expand Up @@ -696,36 +709,58 @@ func TestResolveSubstitutions(t *testing.T) {
assertError(t, err, expectedError)
})

t.Run("extract string with alternative value", func(t *testing.T) {
parser := newParser(strings.NewReader("a: static, a:${?b}"))
expected := Object{"a": &stringWithAlternative{
value: "static",
alternative: &Substitution{
path: "b",
optional: true,
},
t.Run("extract valueWithAlternative value with string type", func(t *testing.T) {
parser := newParser(strings.NewReader("a: stringValue, a:${?b}"))
expected := Object{"a": &valueWithAlternative{
value: String("stringValue"),
alternative: &Substitution{path: "b", optional: true},
}}
got, err := parser.extractObject()
assertNoError(t, err)
if !reflect.DeepEqual(expected, got) {
t.Errorf("expected: %v, got: %v", expected, got)
}
assertDeepEqual(t, got, expected)
})

t.Run("extract string with alternative value and overwrite alternatives", func(t *testing.T) {
parser := newParser(strings.NewReader("a: static, a:${?c}, a:${?b}"))
expected := Object{"a": &stringWithAlternative{
value: "static",
alternative: &Substitution{
path: "b",
optional: true,
},
t.Run("extract valueWithAlternative value with number type", func(t *testing.T) {
parser := newParser(strings.NewReader("a: 1, a:${?b}"))
expected := Object{"a": &valueWithAlternative{
value: Int(1),
alternative: &Substitution{path: "b", optional: true},
}}
got, err := parser.extractObject()
assertNoError(t, err)
assertDeepEqual(t, got, expected)
})

t.Run("extract valueWithAlternative value with duration type", func(t *testing.T) {
parser := newParser(strings.NewReader("a: 1s, a:${?b}"))
expected := Object{"a": &valueWithAlternative{
value: Duration(time.Second),
alternative: &Substitution{path: "b", optional: true},
}}
got, err := parser.extractObject()
assertNoError(t, err)
assertDeepEqual(t, got, expected)
})

t.Run("extract valueWithAlternative value with boolean type", func(t *testing.T) {
parser := newParser(strings.NewReader("a: true, a:${?b}"))
expected := Object{"a": &valueWithAlternative{
value: Boolean(true),
alternative: &Substitution{path: "b", optional: true},
}}
got, err := parser.extractObject()
assertNoError(t, err)
if !reflect.DeepEqual(expected, got) {
t.Errorf("expected: %v, got: %v", expected, got)
assertDeepEqual(t, got, expected)
})

t.Run("extract valueWithAlternative value and overwrite alternatives", func(t *testing.T) {
parser := newParser(strings.NewReader("a: static, a:${?b}"))
expected := Object{
"a": &valueWithAlternative{value: String("static"), alternative: &Substitution{path: "b", optional: true}},
}
got, err := parser.extractObject()
assertNoError(t, err)
assertDeepEqual(t, got, expected)
})
}

Expand Down

0 comments on commit 8573646

Please sign in to comment.