-
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.
Merge pull request #10 from ccremer/context
Add Pipeline Context
- Loading branch information
Showing
17 changed files
with
551 additions
and
75 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
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 |
---|---|---|
@@ -1,6 +1,9 @@ | ||
comment: false | ||
coverage: | ||
status: | ||
patch: | ||
default: | ||
threshold: 50% | ||
project: | ||
default: | ||
threshold: 5% | ||
threshold: 10% |
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,100 @@ | ||
package pipeline | ||
|
||
// Context contains data relevant for the pipeline execution. | ||
// It's primary purpose is to store and retrieve data within an ActionFunc. | ||
type Context interface { | ||
// Value returns the raw value identified by key. | ||
// Returns nil if the key doesn't exist. | ||
Value(key interface{}) interface{} | ||
// ValueOrDefault returns the value identified by key if it exists. | ||
// If not, then the given default value is returned. | ||
ValueOrDefault(key interface{}, defaultValue interface{}) interface{} | ||
// StringValue is a sugared accessor like ValueOrDefault, but converts the value to string. | ||
// If the key cannot be found or if the value is not of type string, then the defaultValue is returned. | ||
StringValue(key interface{}, defaultValue string) string | ||
// BoolValue is a sugared accessor like ValueOrDefault, but converts the value to bool. | ||
// If the key cannot be found or if the value is not of type bool, then the defaultValue is returned. | ||
BoolValue(key interface{}, defaultValue bool) bool | ||
// IntValue is a sugared accessor like ValueOrDefault, but converts the value to int. | ||
// If the key cannot be found or if the value is not of type int, then the defaultValue is returned. | ||
IntValue(key interface{}, defaultValue int) int | ||
// SetValue sets the value at the given key. | ||
SetValue(key interface{}, value interface{}) | ||
} | ||
|
||
// DefaultContext implements Context using a Map internally. | ||
type DefaultContext struct { | ||
values map[interface{}]interface{} | ||
} | ||
|
||
// Value implements Context.Value. | ||
func (ctx *DefaultContext) Value(key interface{}) interface{} { | ||
if ctx.values == nil { | ||
return nil | ||
} | ||
return ctx.values[key] | ||
} | ||
|
||
// ValueOrDefault implements Context.ValueOrDefault. | ||
func (ctx *DefaultContext) ValueOrDefault(key interface{}, defaultValue interface{}) interface{} { | ||
if ctx.values == nil { | ||
return defaultValue | ||
} | ||
if raw, exists := ctx.values[key]; exists { | ||
return raw | ||
} | ||
return defaultValue | ||
} | ||
|
||
// StringValue implements Context.StringValue. | ||
func (ctx *DefaultContext) StringValue(key interface{}, defaultValue string) string { | ||
if ctx.values == nil { | ||
return defaultValue | ||
} | ||
raw, exists := ctx.values[key] | ||
if !exists { | ||
return defaultValue | ||
} | ||
if strValue, isString := raw.(string); isString { | ||
return strValue | ||
} | ||
return defaultValue | ||
} | ||
|
||
// BoolValue implements Context.BoolValue. | ||
func (ctx *DefaultContext) BoolValue(key interface{}, defaultValue bool) bool { | ||
if ctx.values == nil { | ||
return defaultValue | ||
} | ||
raw, exists := ctx.values[key] | ||
if !exists { | ||
return defaultValue | ||
} | ||
if boolValue, isBool := raw.(bool); isBool { | ||
return boolValue | ||
} | ||
return defaultValue | ||
} | ||
|
||
// IntValue implements Context.IntValue. | ||
func (ctx *DefaultContext) IntValue(key interface{}, defaultValue int) int { | ||
if ctx.values == nil { | ||
return defaultValue | ||
} | ||
raw, exists := ctx.values[key] | ||
if !exists { | ||
return defaultValue | ||
} | ||
if intValue, isInt := raw.(int); isInt { | ||
return intValue | ||
} | ||
return defaultValue | ||
} | ||
|
||
// SetValue implements Context.SetValue. | ||
func (ctx *DefaultContext) SetValue(key interface{}, value interface{}) { | ||
if ctx.values == nil { | ||
ctx.values = map[interface{}]interface{}{} | ||
} | ||
ctx.values[key] = value | ||
} |
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,164 @@ | ||
package pipeline | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const stringKey = "stringKey" | ||
const boolKey = "boolKey" | ||
const intKey = "intKey" | ||
const valueKey = "value" | ||
|
||
func TestDefaultContext_Implements_Context(t *testing.T) { | ||
assert.Implements(t, (*Context)(nil), new(DefaultContext)) | ||
} | ||
|
||
type valueTestCase struct { | ||
givenValues map[interface{}]interface{} | ||
|
||
defaultBool bool | ||
defaultString string | ||
defaultInt int | ||
|
||
expectedBool bool | ||
expectedString string | ||
expectedInt int | ||
} | ||
|
||
var valueTests = map[string]valueTestCase{ | ||
"GivenNilValues_ThenExpectDefaults": { | ||
givenValues: nil, | ||
}, | ||
"GivenNonExistingKey_ThenExpectDefaults": { | ||
givenValues: map[interface{}]interface{}{}, | ||
defaultBool: true, | ||
expectedBool: true, | ||
defaultString: "default", | ||
expectedString: "default", | ||
defaultInt: 10, | ||
expectedInt: 10, | ||
}, | ||
"GivenExistingKey_WhenInvalidType_ThenExpectDefaults": { | ||
givenValues: map[interface{}]interface{}{ | ||
boolKey: "invalid", | ||
stringKey: 0, | ||
intKey: "invalid", | ||
}, | ||
defaultBool: true, | ||
expectedBool: true, | ||
defaultString: "default", | ||
expectedString: "default", | ||
defaultInt: 10, | ||
expectedInt: 10, | ||
}, | ||
"GivenExistingKey_WhenValidType_ThenExpectValues": { | ||
givenValues: map[interface{}]interface{}{ | ||
boolKey: true, | ||
stringKey: "string", | ||
intKey: 10, | ||
}, | ||
expectedBool: true, | ||
expectedString: "string", | ||
expectedInt: 10, | ||
}, | ||
} | ||
|
||
func TestDefaultContext_BoolValue(t *testing.T) { | ||
for name, tt := range valueTests { | ||
t.Run(name, func(t *testing.T) { | ||
ctx := DefaultContext{values: tt.givenValues} | ||
result := ctx.BoolValue(boolKey, tt.defaultBool) | ||
assert.Equal(t, tt.expectedBool, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestDefaultContext_StringValue(t *testing.T) { | ||
for name, tt := range valueTests { | ||
t.Run(name, func(t *testing.T) { | ||
ctx := DefaultContext{values: tt.givenValues} | ||
result := ctx.StringValue(stringKey, tt.defaultString) | ||
assert.Equal(t, tt.expectedString, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestDefaultContext_IntValue(t *testing.T) { | ||
for name, tt := range valueTests { | ||
t.Run(name, func(t *testing.T) { | ||
ctx := DefaultContext{values: tt.givenValues} | ||
result := ctx.IntValue(intKey, tt.defaultInt) | ||
assert.Equal(t, tt.expectedInt, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestDefaultContext_SetValue(t *testing.T) { | ||
ctx := DefaultContext{values: map[interface{}]interface{}{}} | ||
ctx.SetValue(stringKey, "string") | ||
assert.Equal(t, "string", ctx.values[stringKey]) | ||
} | ||
|
||
func TestDefaultContext_Value(t *testing.T) { | ||
t.Run("GivenNilValues_ThenExpectNil", func(t *testing.T) { | ||
ctx := DefaultContext{values: nil} | ||
result := ctx.Value(valueKey) | ||
assert.Nil(t, result) | ||
}) | ||
t.Run("GivenNonExistingKey_ThenExpectNil", func(t *testing.T) { | ||
ctx := DefaultContext{values: map[interface{}]interface{}{}} | ||
result := ctx.Value(valueKey) | ||
assert.Nil(t, result) | ||
}) | ||
t.Run("GivenExistingKey_WhenKeyContainsNil_ThenExpectNil", func(t *testing.T) { | ||
ctx := DefaultContext{values: map[interface{}]interface{}{ | ||
valueKey: nil, | ||
}} | ||
result := ctx.Value(valueKey) | ||
assert.Nil(t, result) | ||
}) | ||
} | ||
|
||
func TestDefaultContext_ValueOrDefault(t *testing.T) { | ||
t.Run("GivenNilValues_ThenExpectDefault", func(t *testing.T) { | ||
ctx := DefaultContext{values: nil} | ||
result := ctx.ValueOrDefault(valueKey, valueKey) | ||
assert.Equal(t, result, valueKey) | ||
}) | ||
t.Run("GivenNonExistingKey_ThenExpectDefault", func(t *testing.T) { | ||
ctx := DefaultContext{values: map[interface{}]interface{}{}} | ||
result := ctx.ValueOrDefault(valueKey, valueKey) | ||
assert.Equal(t, result, valueKey) | ||
}) | ||
t.Run("GivenExistingKey_ThenExpectValue", func(t *testing.T) { | ||
ctx := DefaultContext{values: map[interface{}]interface{}{ | ||
valueKey: valueKey, | ||
}} | ||
result := ctx.ValueOrDefault(valueKey, "default") | ||
assert.Equal(t, result, valueKey) | ||
}) | ||
} | ||
|
||
func ExampleDefaultContext_BoolValue() { | ||
ctx := DefaultContext{} | ||
ctx.SetValue("key", true) | ||
fmt.Println(ctx.BoolValue("key", false)) | ||
// Output: true | ||
} | ||
|
||
func ExampleDefaultContext_StringValue() { | ||
ctx := DefaultContext{} | ||
ctx.SetValue("key", "string") | ||
fmt.Println(ctx.StringValue("key", "default")) | ||
// Output: string | ||
} | ||
|
||
func ExampleDefaultContext_IntValue() { | ||
ctx := DefaultContext{} | ||
ctx.SetValue("key", 1) | ||
fmt.Println(ctx.IntValue("key", 0)) | ||
// Output: 1 | ||
} |
Oops, something went wrong.