-
-
Notifications
You must be signed in to change notification settings - Fork 272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for specifying an array of metadata objects to use for the outgoing requests #234
base: master
Are you sure you want to change the base?
Changes from 6 commits
ff8c81c
439f571
51f3353
d294b1b
a59228f
f46a37e
d586c35
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"bytes" | ||
"encoding/json" | ||
"math/rand" | ||
"strings" | ||
"text/template" | ||
"time" | ||
|
||
|
@@ -102,7 +103,7 @@ func (td *callTemplateData) executeData(data string) ([]byte, error) { | |
} | ||
|
||
func (td *callTemplateData) executeMetadata(metadata string) (map[string]string, error) { | ||
var mdMap map[string]string | ||
var md map[string]string | ||
|
||
if len(metadata) > 0 { | ||
input := []byte(metadata) | ||
|
@@ -111,13 +112,40 @@ func (td *callTemplateData) executeMetadata(metadata string) (map[string]string, | |
input = tpl.Bytes() | ||
} | ||
|
||
err = json.Unmarshal(input, &mdMap) | ||
err = json.Unmarshal(input, &md) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return mdMap, nil | ||
return md, nil | ||
} | ||
|
||
// Same as executeMetadata, but this method ensures that the input metadata JSON string is always | ||
// an array. If the input is an object, but not an array, it's converted to an array. | ||
func (td *callTemplateData) executeMetadataArray(metadata string) ([]map[string]string, error) { | ||
var mdArray []map[string]string | ||
var metadataSanitized = strings.TrimSpace(metadata) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I am missing something, but can we ensure that the metadata string is sanitized / trimmed earlier, wherever we set it as |
||
|
||
// If the input is an object and not an array, we ensure we always work with an array | ||
if !strings.HasPrefix(metadataSanitized, "[") && !strings.HasSuffix(metadataSanitized, "]") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably not a big deal, but should this go in the check |
||
metadata = "[" + metadataSanitized + "]" | ||
} | ||
|
||
if len(metadata) > 0 { | ||
input := []byte(metadata) | ||
tpl, err := td.execute(metadata) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bojand I started looking and profiling this code again and I think it would be good to add another optimization here so we don't re-render / evaluate the template and unmarshall the string for every single request. That's especially important when using large metadata JSON objects like in my case. Basically, I'm looking at adding some new flag (e.g. This should substantially speed things up and reduce memory usage (I'm just testing this change to confirm that). WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we go, here is a quick WIP version - d586c35. I confirmed that using Which is kinda expected, because trying to render the template + parsing JSON for every single request will never be efficient when working with large metadata objects. |
||
if err == nil { | ||
input = tpl.Bytes() | ||
} | ||
|
||
err = json.Unmarshal(input, &mdArray) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return mdArray, nil | ||
} | ||
|
||
func newUUID() string { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package runner | |
import ( | ||
"errors" | ||
"path" | ||
"reflect" | ||
"strings" | ||
"time" | ||
|
||
|
@@ -79,7 +80,7 @@ type Config struct { | |
DataPath string `json:"data-file" toml:"data-file" yaml:"data-file"` | ||
BinData []byte `json:"-" toml:"-" yaml:"-"` | ||
BinDataPath string `json:"binary-file" toml:"binary-file" yaml:"binary-file"` | ||
Metadata map[string]string `json:"metadata,omitempty" toml:"metadata,omitempty" yaml:"metadata,omitempty"` | ||
Metadata interface{} `json:"metadata,omitempty" toml:"metadata,omitempty" yaml:"metadata,omitempty"` | ||
MetadataPath string `json:"metadata-file" toml:"metadata-file" yaml:"metadata-file"` | ||
SI Duration `json:"stream-interval" toml:"stream-interval" yaml:"stream-interval"` | ||
Output string `json:"output" toml:"output" yaml:"output"` | ||
|
@@ -96,6 +97,7 @@ type Config struct { | |
EnableCompression bool `json:"enable-compression,omitempty" toml:"enable-compression,omitempty" yaml:"enable-compression,omitempty"` | ||
} | ||
|
||
// Ensure that the data field value is either a map or an array of map items | ||
func checkData(data interface{}) error { | ||
_, isObjData := data.(map[string]interface{}) | ||
if !isObjData { | ||
|
@@ -118,13 +120,37 @@ func checkData(data interface{}) error { | |
return nil | ||
} | ||
|
||
// Ensure that the metadata field value is either a map or an array of map items | ||
func checkMetadata(metadata interface{}) error { | ||
_, isObjData := metadata.(map[string]interface{}) | ||
if !isObjData { | ||
arrData, isArrData := metadata.([]map[string]interface{}) | ||
if !isArrData { | ||
return errors.New("Unsupported type for Metadata") | ||
} | ||
if len(arrData) == 0 { | ||
return errors.New("Metadata array must not be empty") | ||
} | ||
for _, elem := range arrData { | ||
elemType := reflect.ValueOf(elem).Kind() | ||
if elemType != reflect.Map { | ||
return errors.New("Metadata array contains unsupported type") | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// LoadConfig loads the config from a file | ||
func LoadConfig(p string, c *Config) error { | ||
err := configor.Load(c, p) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Process data field - we support two notations for this field - either an object or | ||
// an array of objects so we do the conversion here | ||
if c.Data != nil { | ||
ext := path.Ext(p) | ||
if strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml") { | ||
|
@@ -151,6 +177,62 @@ func LoadConfig(p string, c *Config) error { | |
} | ||
} | ||
|
||
// Process metadata field - we support two notations for this field - either an object or | ||
// an array of objects so we do the conversion here | ||
if c.Metadata != nil { | ||
ext := path.Ext(p) | ||
if strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml") { | ||
// Ensure that keys are of a string type and cast them | ||
objData, isObjData2 := c.Metadata.(map[interface{}]interface{}) | ||
if isObjData2 { | ||
nd := make(map[string]interface{}) | ||
for k, v := range objData { | ||
sk, isString := k.(string) | ||
if !isString { | ||
return errors.New("Data key must string") | ||
} | ||
if len(sk) > 0 { | ||
nd[sk] = v | ||
} | ||
} | ||
|
||
c.Metadata = nd | ||
} else { | ||
// TODO: Refactor this into utility function | ||
arrData, isArray := c.Metadata.([]interface{}) | ||
|
||
if isArray { | ||
var array []map[string]interface{} | ||
for _, item := range arrData { | ||
objData3, isObjData3 := item.(map[interface{}]interface{}) | ||
newItem := make(map[string]interface{}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe valid metadata object has to be |
||
|
||
if isObjData3 { | ||
for k, v := range objData3 { | ||
sk, isString := k.(string) | ||
if !isString { | ||
return errors.New("Data key must string") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the errors here should reference |
||
} | ||
if len(sk) > 0 { | ||
newItem[sk] = v | ||
} | ||
} | ||
|
||
array = append(array, newItem) | ||
} | ||
} | ||
|
||
c.Metadata = array | ||
} | ||
} | ||
} | ||
|
||
err := checkMetadata(c.Metadata) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
c.ZStop = strings.ToLower(c.ZStop) | ||
if c.ZStop != "close" && c.ZStop != "ignore" && c.ZStop != "wait" { | ||
c.ZStop = "close" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With respect to determining the underlying type we are trying to Unmarshal, instead of relying on
strings.Contains()
of the error message, I think we can take advantage of the actual UnmarshalTypeError. The approach may be a little bit more reliable. Example of what I mean: