From 9540c0d9d62b7f57a255ab8c75f18902363aa5ef Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Tue, 13 Mar 2018 13:50:10 -0500 Subject: [PATCH 1/3] Comment all variable resolver methods and remove those we don't actually use --- excellent/legacy.go | 4 ++++ excellent/tests.go | 9 ++++----- flows/attachments.go | 4 ++-- flows/channel.go | 4 ++-- flows/contact.go | 3 ++- flows/definition/flow.go | 2 ++ flows/definition/node.go | 14 -------------- flows/fields.go | 6 ++++-- flows/group.go | 8 ++++---- flows/inputs/base.go | 2 +- flows/inputs/msg.go | 4 ++-- flows/interfaces.go | 2 -- flows/results.go | 4 ++-- flows/runs/context.go | 4 +++- flows/runs/run.go | 2 ++ flows/runs/step.go | 28 ---------------------------- flows/triggers/base.go | 11 ++++++----- flows/urns.go | 5 ++++- utils/http.go | 4 +++- utils/json.go | 13 ++++++------- utils/resolver.go | 6 +++++- 21 files changed, 58 insertions(+), 81 deletions(-) diff --git a/excellent/legacy.go b/excellent/legacy.go index 6af607dd4..4cdab26c5 100644 --- a/excellent/legacy.go +++ b/excellent/legacy.go @@ -57,6 +57,7 @@ func (v *varMapper) rebase(prefix string) *varMapper { } } +// Resolve resolves the given key to a mapped expression func (v *varMapper) Resolve(key string) interface{} { // is this a complete substitution? @@ -109,6 +110,7 @@ func (v *varMapper) Resolve(key string) interface{} { return strings.Join(newPath, ".") } +// Default returns the value of this mapper when it is the result of an expression func (v *varMapper) Default() interface{} { return v.base } @@ -129,6 +131,7 @@ type extraMapper struct { extraAs ExtraVarsMapping } +// Resolve resolves the given key to a new expression func (m *extraMapper) Resolve(key string) interface{} { newPath := []string{} if m.path != "" { @@ -138,6 +141,7 @@ func (m *extraMapper) Resolve(key string) interface{} { return &extraMapper{extraAs: m.extraAs, path: strings.Join(newPath, ".")} } +// Default returns the value of this extra mapper when it is the result of an expression func (m *extraMapper) Default() interface{} { return m } diff --git a/excellent/tests.go b/excellent/tests.go index 0547b4987..ee8a2ae64 100644 --- a/excellent/tests.go +++ b/excellent/tests.go @@ -75,19 +75,18 @@ func (t XTestResult) Matched() bool { return t.matched } // Match returns the item which was matched func (t XTestResult) Match() interface{} { return t.match } -// Resolve satisfies the utils.VariableResolver interface, users can look up the match or whether we matched +// Resolve resolves the given key when this result is referenced in an expression func (t XTestResult) Resolve(key string) interface{} { switch key { case "matched": return t.matched - case "match": return t.match } return fmt.Errorf("no such key '%s' on test result", key) } -// Default satisfies the utils.VariableResolver interface, we always default to whether we matched +// Default returns the value of this result when it is the result of an expression func (t XTestResult) Default() interface{} { return t.matched } @@ -391,7 +390,7 @@ func HasBeginning(env utils.Environment, args ...interface{}) interface{} { // Returned by the has_pattern test as its match value type patternMatch []string -// Resolve satisfies the utils.VariableResolver interface, users can look up a numbered matching group +// Resolve resolves the given key when this match is referenced in an expression func (m patternMatch) Resolve(key string) interface{} { switch key { case "groups": @@ -401,7 +400,7 @@ func (m patternMatch) Resolve(key string) interface{} { return fmt.Errorf("no such key '%s' on pattern match", key) } -// Default satisfies the utils.VariableResolver interface, and returns the group 0 match as the default value +// Default returns the value of this match when it is the result of an expression func (m patternMatch) Default() interface{} { return m[0] } diff --git a/flows/attachments.go b/flows/attachments.go index ee3425fbb..41cd9e4ce 100644 --- a/flows/attachments.go +++ b/flows/attachments.go @@ -28,12 +28,11 @@ func (a Attachment) URL() string { return string(a) } +// Resolve resolves the given key when this attachment is referenced in an expression func (a Attachment) Resolve(key string) interface{} { switch key { - case "content_type": return a.ContentType() - case "url": return a.URL() } @@ -41,6 +40,7 @@ func (a Attachment) Resolve(key string) interface{} { return fmt.Errorf("No field '%s' on attachment", key) } +// Default returns the value of this attachment when it is the result of an expression func (a Attachment) Default() interface{} { return a } func (a Attachment) String() string { return a.URL() } diff --git a/flows/channel.go b/flows/channel.go index c774d924b..f478e88a6 100644 --- a/flows/channel.go +++ b/flows/channel.go @@ -71,7 +71,7 @@ func (c *channel) HasRole(role ChannelRole) bool { return false } -// Resolve satisfies our resolver interface +// Resolve resolves the given key when this channel is referenced in an expression func (c *channel) Resolve(key string) interface{} { switch key { case "uuid": @@ -85,7 +85,7 @@ func (c *channel) Resolve(key string) interface{} { return fmt.Errorf("No field '%s' on channel", key) } -// Default returns the default value for a channel, which is itself +// Default returns the value of this channel when it is the result of an expression func (c *channel) Default() interface{} { return c } diff --git a/flows/contact.go b/flows/contact.go index 1371bb7e2..1eca4f028 100644 --- a/flows/contact.go +++ b/flows/contact.go @@ -105,6 +105,7 @@ func (c *Contact) Fields() FieldValues { return c.fields } // Reference returns a reference to this contact func (c *Contact) Reference() *ContactReference { return NewContactReference(c.uuid, c.name) } +// Resolve resolves the given key when this contact is referenced in an expression func (c *Contact) Resolve(key string) interface{} { switch key { case "uuid": @@ -137,7 +138,7 @@ func (c *Contact) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on contact", key) } -// Default returns our default value in the context +// Default returns the value of this contact when it is the result of an expression func (c *Contact) Default() interface{} { return c } diff --git a/flows/definition/flow.go b/flows/definition/flow.go index a2f493d58..62926d9e3 100644 --- a/flows/definition/flow.go +++ b/flows/definition/flow.go @@ -44,6 +44,7 @@ func (f *flow) Validate(assets flows.SessionAssets) error { return err } +// Resolve resolves the given key when this flow is referenced in an expression func (f *flow) Resolve(key string) interface{} { switch key { case "uuid": @@ -55,6 +56,7 @@ func (f *flow) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on flow", key) } +// Default returns the value of this flow when it is the result of an expression func (f *flow) Default() interface{} { return f } diff --git a/flows/definition/node.go b/flows/definition/node.go index 711e47ee7..8f73fc69c 100644 --- a/flows/definition/node.go +++ b/flows/definition/node.go @@ -2,7 +2,6 @@ package definition import ( "encoding/json" - "fmt" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/actions" @@ -41,19 +40,6 @@ func (n *node) Actions() []flows.Action { return n.actions } func (n *node) Exits() []flows.Exit { return n.exits } func (n *node) Wait() flows.Wait { return n.wait } -func (n *node) Resolve(key string) interface{} { - switch key { - case "uuid": - return n.uuid - } - return fmt.Errorf("No field '%s' on node", key) -} - -func (n *node) Default() interface{} { return n.uuid } -func (n *node) String() string { return n.uuid.String() } - -var _ utils.VariableResolver = (*node)(nil) - //------------------------------------------------------------------------------------------ // JSON Encoding / Decoding //------------------------------------------------------------------------------------------ diff --git a/flows/fields.go b/flows/fields.go index 9bf7cf9f0..f60db9757 100644 --- a/flows/fields.go +++ b/flows/fields.go @@ -82,6 +82,7 @@ func NewFieldValue(field *Field, value interface{}, createdOn time.Time) *FieldV return &FieldValue{field: field, value: value, createdOn: createdOn} } +// Resolve resolves the given key when this field value is referenced in an expression func (v *FieldValue) Resolve(key string) interface{} { switch key { case "value": @@ -92,7 +93,7 @@ func (v *FieldValue) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on field value", key) } -// Default returns the default value for FieldValue, which is the value +// Default returns the value of this field value when it is the result of an expression func (v *FieldValue) Default() interface{} { return v.value } @@ -138,11 +139,12 @@ func (f FieldValues) Save(env utils.Environment, field *Field, rawValue string) return nil } +// Resolve resolves the given key when this set of field values is referenced in an expression func (f FieldValues) Resolve(key string) interface{} { return f[FieldKey(key)] } -// Default returns the default value for FieldValues, which is ourselves +// Default returns the value of this set of field values when it is the result of an expression func (f FieldValues) Default() interface{} { return f } diff --git a/flows/group.go b/flows/group.go index 28749b9ab..46448276c 100644 --- a/flows/group.go +++ b/flows/group.go @@ -60,7 +60,7 @@ func (g *Group) CheckDynamicMembership(session Session, contact *Contact) (bool, func (g *Group) Reference() *GroupReference { return NewGroupReference(g.uuid, g.name) } -// Resolve resolves the passed in key to a value +// Resolve resolves the given key when this group is referenced in an expression func (g *Group) Resolve(key string) interface{} { switch key { case "uuid": @@ -72,7 +72,7 @@ func (g *Group) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on group", key) } -// Default returns the default value for this group +// Default returns the value of this group when it is the result of an expression func (g *Group) Default() interface{} { return g } // String satisfies the stringer interface returning the name of the group @@ -131,7 +131,7 @@ func (l *GroupList) Count() int { return len(l.groups) } -// Resolve looks up the passed in key for the group list, which must be either "count" or a numerical index +// Resolve resolves the given key when this group list is referenced in an expression func (l *GroupList) Resolve(key string) interface{} { if key == "count" { return l.Count() @@ -148,7 +148,7 @@ func (l *GroupList) Resolve(key string) interface{} { return nil } -// Default returns the default value for this group, which is our entire list +// Default returns the value of this group list when it is the result of an expression func (l GroupList) Default() interface{} { return l } diff --git a/flows/inputs/base.go b/flows/inputs/base.go index 7c5fac391..7d75e06de 100644 --- a/flows/inputs/base.go +++ b/flows/inputs/base.go @@ -17,7 +17,7 @@ func (i *baseInput) UUID() flows.InputUUID { return i.uuid } func (i *baseInput) Channel() flows.Channel { return i.channel } func (i *baseInput) CreatedOn() time.Time { return i.createdOn } -// Resolve resolves the passed in key to a value, returning an error if the key is unknown +// Resolve resolves the given key when this input is referenced in an expression func (i *baseInput) Resolve(key string) interface{} { switch key { case "uuid": diff --git a/flows/inputs/msg.go b/flows/inputs/msg.go index 119f878e5..bb6216830 100644 --- a/flows/inputs/msg.go +++ b/flows/inputs/msg.go @@ -34,7 +34,7 @@ func NewMsgInput(uuid flows.InputUUID, channel flows.Channel, createdOn time.Tim // Type returns the type of this event func (i *MsgInput) Type() string { return TypeMsg } -// Resolve resolves the passed in key to a value, returning an error if the key is unknown +// Resolve resolves the given key when this input is referenced in an expression func (i *MsgInput) Resolve(key string) interface{} { switch key { case "urn": @@ -47,7 +47,7 @@ func (i *MsgInput) Resolve(key string) interface{} { return i.baseInput.Resolve(key) } -// Default returns our default value if evaluated in a context, which in this case is the text and attachments combined +// Default returns the value of this input when it is the result of an expression func (i *MsgInput) Default() interface{} { return i } diff --git a/flows/interfaces.go b/flows/interfaces.go index 79be53d4d..0f95c5496 100644 --- a/flows/interfaces.go +++ b/flows/interfaces.go @@ -249,8 +249,6 @@ type Input interface { } type Step interface { - utils.VariableResolver - UUID() StepUUID NodeUUID() NodeUUID ExitUUID() ExitUUID diff --git a/flows/results.go b/flows/results.go index d4b4e2c0d..3d81581d3 100644 --- a/flows/results.go +++ b/flows/results.go @@ -41,7 +41,7 @@ func (r *Result) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on result", key) } -// Default returns the default value for a result, which is our value +// Default returns the value of this result when it is the result of an expression func (r *Result) Default() interface{} { return r.Value } @@ -92,7 +92,7 @@ func (r Results) Resolve(key string) interface{} { return result } -// Default returns the default value for our Results, which is the entire map +// Default returns the value of this result set when it is the result of an expression func (r Results) Default() interface{} { return r } diff --git a/flows/runs/context.go b/flows/runs/context.go index 1ecc27379..5d35cc81a 100644 --- a/flows/runs/context.go +++ b/flows/runs/context.go @@ -16,6 +16,7 @@ func newRunContext(run flows.FlowRun) utils.VariableResolver { return &runContext{run: run} } +// Resolve resolves the given top-level key in an expression func (c *runContext) Resolve(key string) interface{} { switch key { case "contact": @@ -33,6 +34,7 @@ func (c *runContext) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on context", key) } +// Default returns the value of this context when it is the result of an expression func (c *runContext) Default() interface{} { return c } @@ -54,7 +56,7 @@ func newRelatedRunContext(run flows.RunSummary) *relatedRunContext { return nil } -// Resolve provides a more limited set of results for parent and child runs +// Resolve resolves the given key when this related run is referenced in an expression func (c *relatedRunContext) Resolve(key string) interface{} { switch key { case "uuid": diff --git a/flows/runs/run.go b/flows/runs/run.go index 7cc8b949b..c58c99aef 100644 --- a/flows/runs/run.go +++ b/flows/runs/run.go @@ -298,6 +298,7 @@ func (r *flowRun) GetTranslatedTextArray(uuid utils.UUID, key string, native []s return native } +// Resolve resolves the given key when this run is referenced in an expression func (r *flowRun) Resolve(key string) interface{} { switch key { case "uuid": @@ -323,6 +324,7 @@ func (r *flowRun) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on run", key) } +// Default returns the value of this run when it is the result of an expression func (r *flowRun) Default() interface{} { return r } diff --git a/flows/runs/step.go b/flows/runs/step.go index 87d17316b..bf9143f53 100644 --- a/flows/runs/step.go +++ b/flows/runs/step.go @@ -2,7 +2,6 @@ package runs import ( "encoding/json" - "fmt" "time" "github.com/nyaruka/goflow/flows" @@ -26,33 +25,6 @@ func (s *step) ArrivedOn() time.Time { return s.arrivedOn } func (s *step) LeftOn() *time.Time { return s.leftOn } func (s *step) Events() []flows.Event { return s.events } -func (s *step) Resolve(key string) interface{} { - switch key { - case "uuid": - return s.UUID - case "node_uuid": - return s.NodeUUID - case "exit_uuid": - return s.ExitUUID - case "arrived_on": - return s.ArrivedOn - case "left_on": - return s.LeftOn - } - - return fmt.Errorf("No field '%s' on step", key) -} - -func (s *step) Default() interface{} { - return s -} - -var _ utils.VariableResolver = (*step)(nil) - -func (s *step) String() string { - return string(s.nodeUUID) -} - func (s *step) Leave(exit flows.ExitUUID) { now := time.Now().UTC() s.exitUUID = exit diff --git a/flows/triggers/base.go b/flows/triggers/base.go index 1ad50c384..4e15c57e3 100644 --- a/flows/triggers/base.go +++ b/flows/triggers/base.go @@ -22,11 +22,7 @@ func (t *baseTrigger) Contact() *flows.Contact { return t.contact } func (t *baseTrigger) Params() utils.JSONFragment { return t.params } func (t *baseTrigger) TriggeredOn() time.Time { return t.triggeredOn } -func (t *baseTrigger) Default() interface{} { - return t -} - -// Resolve resolves the passed in key to a value, returning an error if the key is unknown +// Resolve resolves the given key when this trigger is referenced in an expression func (t *baseTrigger) Resolve(key string) interface{} { switch key { case "params": @@ -36,6 +32,11 @@ func (t *baseTrigger) Resolve(key string) interface{} { return fmt.Errorf("No such field '%s' on trigger", key) } +// Default returns the value of this trigger when it is the result of an expression +func (t *baseTrigger) Default() interface{} { + return t +} + func (t *baseTrigger) String() string { return string(t.flow.UUID()) } diff --git a/flows/urns.go b/flows/urns.go index b9e09c5a9..54890c2bd 100644 --- a/flows/urns.go +++ b/flows/urns.go @@ -45,7 +45,7 @@ func (u *ContactURN) Channel() Channel { return u.channel } // SetChannel sets the channel associated with this URN func (u *ContactURN) SetChannel(channel Channel) { u.channel = channel } -// Resolve is called when a URN is part of an excellent expression +// Resolve resolves the given key when this URN is referenced in an expression func (u *ContactURN) Resolve(key string) interface{} { switch key { case "scheme": @@ -60,6 +60,7 @@ func (u *ContactURN) Resolve(key string) interface{} { return fmt.Errorf("no field '%s' on URN", key) } +// Default returns the value of this URN when it is the result of an expression func (u *ContactURN) Default() interface{} { return u } func (u *ContactURN) String() string { return string(u.URN) } @@ -127,6 +128,7 @@ func (l URNList) WithScheme(scheme string) URNList { return matching } +// Resolve resolves the given key when this URN list is referenced in an expression func (l URNList) Resolve(key string) interface{} { // first try as numeric index to a single URN index, err := strconv.Atoi(key) @@ -148,6 +150,7 @@ func (l URNList) Resolve(key string) interface{} { return l.WithScheme(scheme) } +// Default returns the value of this URN list when it is the result of an expression func (l URNList) Default() interface{} { return l } diff --git a/utils/http.go b/utils/http.go index f2cd20bb0..8e833f2d3 100644 --- a/utils/http.go +++ b/utils/http.go @@ -86,6 +86,7 @@ func (r *RequestResponse) Body() string { return r.body } // JSON returns the response as a JSON fragment func (r *RequestResponse) JSON() JSONFragment { return JSONFragment([]byte(r.body)) } +// Resolve resolves the given key when this webhook is referenced in an expression func (r *RequestResponse) Resolve(key string) interface{} { switch key { case "body": @@ -104,9 +105,10 @@ func (r *RequestResponse) Resolve(key string) interface{} { return r.StatusCode() } - return fmt.Errorf("No field '%s' on webhook", key) + return fmt.Errorf("no field '%s' on webhook", key) } +// Default returns the value of this webhook when it is the result of an expression func (r *RequestResponse) Default() interface{} { return r } diff --git a/utils/json.go b/utils/json.go index 28c984770..109e3a710 100644 --- a/utils/json.go +++ b/utils/json.go @@ -36,13 +36,7 @@ var EmptyJSONFragment = JSONFragment{} // into the json in that byte array type JSONFragment []byte -// Default returns the default value for this JSON, which is the JSON itself -func (j JSONFragment) Default() interface{} { - return j -} - -// Resolve resolves the passed in key, which is expected to be either an integer in the case -// that our JSON is an array or a key name if it is a map +// Resolve resolves the given key when this JSON fragment is referenced in an expression func (j JSONFragment) Resolve(key string) interface{} { _, err := strconv.Atoi(key) @@ -76,6 +70,11 @@ func (j JSONFragment) Resolve(key string) interface{} { return JSONFragment(val) } +// Default returns the value of this JSON fragment when it is the result of an expression +func (j JSONFragment) Default() interface{} { + return j +} + var _ VariableResolver = EmptyJSONFragment // String returns the string representation of this JSON, which is just the JSON itself diff --git a/utils/resolver.go b/utils/resolver.go index a37ee2444..caae4373a 100644 --- a/utils/resolver.go +++ b/utils/resolver.go @@ -144,6 +144,7 @@ func NewMapResolver(values map[string]interface{}) VariableResolver { } } +// Resolve resolves the given key when this map is referenced in an expression func (r *mapResolver) Resolve(key string) interface{} { val, found := r.values[key] if !found { @@ -151,5 +152,8 @@ func (r *mapResolver) Resolve(key string) interface{} { } return val } + +// Default returns the value of this map when it is the result of an expression func (r *mapResolver) Default() interface{} { return r } -func (r *mapResolver) String() string { return fmt.Sprintf("%s", r.values) } + +func (r *mapResolver) String() string { return fmt.Sprintf("%s", r.values) } From 4ae82f831b4557cd41585382402a429b32d38a1a Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Tue, 13 Mar 2018 15:39:59 -0500 Subject: [PATCH 2/3] Docstring comments... --- cmd/flowserver/server.go | 1 + excellent/functions_test.go | 4 +-- excellent/legacy.go | 1 + excellent/tests_test.go | 2 +- flows/contact.go | 1 + flows/definition/flow.go | 1 + flows/definition/legacy.go | 5 ++- flows/definition/node.go | 4 +++ flows/engine/assets.go | 1 + flows/engine/log.go | 1 + flows/engine/session.go | 1 + flows/inputs/msg.go | 1 + flows/runs/run.go | 1 + flows/runs/step.go | 2 ++ flows/runsummary.go | 1 + flows/triggers/flow_action.go | 1 + flows/triggers/manual.go | 1 + utils/conversions.go | 67 ++++++++++++++++++----------------- utils/dates.go | 43 ++++++++++++---------- utils/dates_test.go | 50 +++++++++++++------------- utils/envelope.go | 4 +++ utils/environment.go | 5 +-- utils/http.go | 2 ++ utils/json.go | 3 +- utils/language.go | 2 ++ utils/locations.go | 22 +++++++++--- utils/validator.go | 2 ++ 27 files changed, 141 insertions(+), 88 deletions(-) diff --git a/cmd/flowserver/server.go b/cmd/flowserver/server.go index b384042ba..6a652af42 100644 --- a/cmd/flowserver/server.go +++ b/cmd/flowserver/server.go @@ -114,6 +114,7 @@ type sessionResponse struct { Log []flows.LogEntry `json:"log"` } +// MarshalJSON marshals this session reponse into JSON func (r *sessionResponse) MarshalJSON() ([]byte, error) { envelope := sessionResponse{ Session: r.Session, diff --git a/excellent/functions_test.go b/excellent/functions_test.go index 2ba2e0535..e7e19347c 100644 --- a/excellent/functions_test.go +++ b/excellent/functions_test.go @@ -322,7 +322,7 @@ var funcTests = []struct { } func TestFunctions(t *testing.T) { - env := utils.NewEnvironment(utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm_ss, time.UTC, utils.LanguageList{}) + env := utils.NewEnvironment(utils.DateFormatDayMonthYear, utils.TimeFormatHourMinuteSecond, time.UTC, utils.LanguageList{}) for _, test := range funcTests { xFunc := XFUNCTIONS[test.name] @@ -381,7 +381,7 @@ var rangeTests = []struct { } func TestRangeFunctions(t *testing.T) { - env := utils.NewEnvironment(utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm_ss, time.UTC, utils.LanguageList{}) + env := utils.NewEnvironment(utils.DateFormatDayMonthYear, utils.TimeFormatHourMinuteSecond, time.UTC, utils.LanguageList{}) for _, test := range rangeTests { xFunc := XFUNCTIONS[test.name] diff --git a/excellent/legacy.go b/excellent/legacy.go index 4cdab26c5..9ec4cff30 100644 --- a/excellent/legacy.go +++ b/excellent/legacy.go @@ -17,6 +17,7 @@ var topLevelScopes = []string{"contact", "child", "parent", "run", "trigger"} // ExtraVarsMapping defines how @extra.* variables should be migrated type ExtraVarsMapping string +// different ways of mapping @extra in legacy flows const ( ExtraAsWebhookJSON ExtraVarsMapping = "run.webhook.json" ExtraAsTriggerParams ExtraVarsMapping = "trigger.params" diff --git a/excellent/tests_test.go b/excellent/tests_test.go index 3fa295530..59173f400 100644 --- a/excellent/tests_test.go +++ b/excellent/tests_test.go @@ -169,7 +169,7 @@ var testTests = []struct { } func TestTests(t *testing.T) { - env := utils.NewEnvironment(utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm_ss, time.UTC, utils.LanguageList{}) + env := utils.NewEnvironment(utils.DateFormatDayMonthYear, utils.TimeFormatHourMinuteSecond, time.UTC, utils.LanguageList{}) for _, test := range testTests { testFunc := XTESTS[test.name] diff --git a/flows/contact.go b/flows/contact.go index 1eca4f028..8668210e1 100644 --- a/flows/contact.go +++ b/flows/contact.go @@ -300,6 +300,7 @@ func ReadContact(session Session, data json.RawMessage) (*Contact, error) { return c, nil } +// MarshalJSON marshals this contact into JSON func (c *Contact) MarshalJSON() ([]byte, error) { var ce contactEnvelope diff --git a/flows/definition/flow.go b/flows/definition/flow.go index 62926d9e3..e61b0aeed 100644 --- a/flows/definition/flow.go +++ b/flows/definition/flow.go @@ -137,6 +137,7 @@ func ReadFlow(data json.RawMessage) (flows.Flow, error) { return f, nil } +// MarshalJSON marshals this flow into JSON func (f *flow) MarshalJSON() ([]byte, error) { var fe = flowEnvelope{} diff --git a/flows/definition/legacy.go b/flows/definition/legacy.go index b663c9a07..0475ad88d 100644 --- a/flows/definition/legacy.go +++ b/flows/definition/legacy.go @@ -18,6 +18,7 @@ import ( // represents a decimal value which may be provided as a string or floating point value type decimalString string +// UnmarshalJSON unmarshals a decimal string from the given JSON func (s *decimalString) UnmarshalJSON(data []byte) error { if data[0] == '"' { // data is a quoted string @@ -101,6 +102,7 @@ func (l *legacyLabelReference) Migrate() *flows.LabelReference { return flows.NewVariableLabelReference(l.Name) } +// UnmarshalJSON unmarshals a legacy label reference from the given JSON func (l *legacyLabelReference) UnmarshalJSON(data []byte) error { // label reference may be a string if data[0] == '"' { @@ -149,6 +151,7 @@ func (g *legacyGroupReference) Migrate() *flows.GroupReference { return flows.NewVariableGroupReference(g.Name) } +// UnmarshalJSON unmarshals a legacy group reference from the given JSON func (g *legacyGroupReference) UnmarshalJSON(data []byte) error { // group reference may be a string if data[0] == '"' { @@ -998,7 +1001,7 @@ func ReadLegacyFlow(data json.RawMessage) (*LegacyFlow, error) { return f, err } -// MarshalJSON sends turns our legacy flow into bytes +// MarshalJSON marshals this legacy flow into JSON func (f *LegacyFlow) MarshalJSON() ([]byte, error) { var fe = flowEnvelope{} diff --git a/flows/definition/node.go b/flows/definition/node.go index 8f73fc69c..7869fea28 100644 --- a/flows/definition/node.go +++ b/flows/definition/node.go @@ -52,6 +52,7 @@ type nodeEnvelope struct { Wait *utils.TypedEnvelope `json:"wait,omitempty"` } +// UnmarshalJSON unmarshals a flow node from the given JSON func (n *node) UnmarshalJSON(data []byte) error { var envelope nodeEnvelope err := utils.UnmarshalAndValidate(data, &envelope, "node") @@ -95,6 +96,7 @@ func (n *node) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON marshals this flow node into JSON func (n *node) MarshalJSON() ([]byte, error) { envelope := nodeEnvelope{} var err error @@ -134,6 +136,7 @@ type exitEnvelope struct { Name string `json:"name,omitempty"` } +// UnmarshalJSON unmarshals a node exit from the given JSON func (e *exit) UnmarshalJSON(data []byte) error { var envelope exitEnvelope err := utils.UnmarshalAndValidate(data, &envelope, "exit") @@ -148,6 +151,7 @@ func (e *exit) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON marshals this node exit into JSON func (e *exit) MarshalJSON() ([]byte, error) { envelope := exitEnvelope{e.uuid, e.destination, e.name} return json.Marshal(envelope) diff --git a/flows/engine/assets.go b/flows/engine/assets.go index f45ee951a..0878fbcae 100644 --- a/flows/engine/assets.go +++ b/flows/engine/assets.go @@ -243,6 +243,7 @@ func (s *mockAssetServer) fetchAsset(url string, itemType assetType, isSet bool, return readAsset(assetBuf, itemType, isSet) } +// MarshalJSON marshals this mock asset server into JSON func (s *mockAssetServer) MarshalJSON() ([]byte, error) { envelope := &assetServerEnvelope{} envelope.TypeURLs = s.typeURLs diff --git a/flows/engine/log.go b/flows/engine/log.go index 50c21364d..ae9920092 100644 --- a/flows/engine/log.go +++ b/flows/engine/log.go @@ -32,6 +32,7 @@ type logEntryEnvelope struct { Event *utils.TypedEnvelope `json:"event" validate:"required"` } +// MarshalJSON marshals this log entry into JSON func (s *logEntry) MarshalJSON() ([]byte, error) { var se logEntryEnvelope diff --git a/flows/engine/session.go b/flows/engine/session.go index a334ce3d2..60bd386cb 100644 --- a/flows/engine/session.go +++ b/flows/engine/session.go @@ -531,6 +531,7 @@ func ReadSession(assetCache *AssetCache, assetServer AssetServer, data json.RawM return s, nil } +// MarshalJSON marshals this session into JSON func (s *session) MarshalJSON() ([]byte, error) { var envelope sessionEnvelope var err error diff --git a/flows/inputs/msg.go b/flows/inputs/msg.go index bb6216830..83c98e427 100644 --- a/flows/inputs/msg.go +++ b/flows/inputs/msg.go @@ -108,6 +108,7 @@ func ReadMsgInput(session flows.Session, data json.RawMessage) (*MsgInput, error return &input, nil } +// MarshalJSON marshals this msg input into JSON func (i *MsgInput) MarshalJSON() ([]byte, error) { var envelope msgInputEnvelope diff --git a/flows/runs/run.go b/flows/runs/run.go index c58c99aef..bf563dfe8 100644 --- a/flows/runs/run.go +++ b/flows/runs/run.go @@ -420,6 +420,7 @@ func ReadRun(session flows.Session, data json.RawMessage) (flows.FlowRun, error) return r, nil } +// MarshalJSON marshals this flow run into JSON func (r *flowRun) MarshalJSON() ([]byte, error) { var re runEnvelope var err error diff --git a/flows/runs/step.go b/flows/runs/step.go index bf9143f53..b8dffa780 100644 --- a/flows/runs/step.go +++ b/flows/runs/step.go @@ -53,6 +53,7 @@ type stepEnvelope struct { Events []*utils.TypedEnvelope `json:"events,omitempty" validate:"omitempty,dive"` } +// UnmarshalJSON unmarshals a run step from the given JSON func (s *step) UnmarshalJSON(data []byte) error { var se stepEnvelope var err error @@ -79,6 +80,7 @@ func (s *step) UnmarshalJSON(data []byte) error { return err } +// MarshalJSON marshals this run step into JSON func (s *step) MarshalJSON() ([]byte, error) { var se stepEnvelope diff --git a/flows/runsummary.go b/flows/runsummary.go index 66e84bbe1..421b6aa33 100644 --- a/flows/runsummary.go +++ b/flows/runsummary.go @@ -72,6 +72,7 @@ func ReadRunSummary(session Session, data json.RawMessage) (RunSummary, error) { return run, nil } +// MarshalJSON marshals this run summary into JSON func (r *runSummary) MarshalJSON() ([]byte, error) { envelope := runSummaryEnvelope{} var err error diff --git a/flows/triggers/flow_action.go b/flows/triggers/flow_action.go index e297b92b0..1b2f5bd03 100644 --- a/flows/triggers/flow_action.go +++ b/flows/triggers/flow_action.go @@ -77,6 +77,7 @@ func ReadFlowActionTrigger(session flows.Session, envelope *utils.TypedEnvelope) return trigger, nil } +// MarshalJSON marshals this trigger into JSON func (t *FlowActionTrigger) MarshalJSON() ([]byte, error) { var envelope flowActionTriggerEnvelope var err error diff --git a/flows/triggers/manual.go b/flows/triggers/manual.go index a0c42844c..f1416b355 100644 --- a/flows/triggers/manual.go +++ b/flows/triggers/manual.go @@ -56,6 +56,7 @@ func ReadManualTrigger(session flows.Session, envelope *utils.TypedEnvelope) (fl return &trigger, nil } +// MarshalJSON marshals this trigger into JSON func (t *ManualTrigger) MarshalJSON() ([]byte, error) { var envelope baseTriggerEnvelope diff --git a/utils/conversions.go b/utils/conversions.go index 844aa43e7..ab8646d51 100644 --- a/utils/conversions.go +++ b/utils/conversions.go @@ -60,11 +60,13 @@ func MapLength(v interface{}) (int, error) { return val.Len(), nil } +// IsMap returns whether the given object is a map func IsMap(v interface{}) bool { val := reflect.ValueOf(v) return val.Kind() == reflect.Map } +// IsNil returns whether the given object is nil or an interface to a nil func IsNil(v interface{}) bool { // if v doesn't have a type or value then v == nil if v == nil { @@ -549,67 +551,68 @@ func ToBool(env Environment, test interface{}) (bool, error) { // XType is an an enumeration of the possible types we can deal with type XType int -const ( // primitive types we convert to - NIL = iota - STRING - DECIMAL - TIME - LOCATION - BOOLEAN - ERROR - STRING_SLICE - DECIMAL_SLICE - TIME_SLICE - BOOL_SLICE - MAP +// primitive types we convert to +const ( + XTypeNil = iota + XTypeString + XTypeDecimal + XTypeTime + XTypeLocation + XTypeBool + XTypeError + XTypeStringSlice + XTypeDecimalSlice + XTypeTimeSlice + XTypeBoolSlice + XTypeMap ) // ToXAtom figures out the raw type of the passed in interface, returning that type func ToXAtom(env Environment, val interface{}) (interface{}, XType, error) { if val == nil { - return val, NIL, nil + return val, XTypeNil, nil } switch val := val.(type) { case error: - return val, ERROR, nil + return val, XTypeError, nil case decimal.Decimal: - return val, DECIMAL, nil + return val, XTypeDecimal, nil case int, int32, int64, float32, float64: decVal, err := ToDecimal(env, val) if err != nil { - return val, NIL, err + return val, XTypeNil, err } - return decVal, DECIMAL, nil + return decVal, XTypeDecimal, nil case time.Time: - return val, TIME, nil + return val, XTypeTime, nil case Location: - return val, LOCATION, nil + return val, XTypeLocation, nil case string: - return val, STRING, nil + return val, XTypeString, nil case bool: - return val, BOOLEAN, nil + return val, XTypeBool, nil case []string: - return val, STRING_SLICE, nil + return val, XTypeStringSlice, nil case []time.Time: - return val, TIME_SLICE, nil + return val, XTypeTimeSlice, nil case []decimal.Decimal: - return val, DECIMAL_SLICE, nil + return val, XTypeDecimalSlice, nil case []bool: - return val, BOOL_SLICE, nil + return val, XTypeBoolSlice, nil } - return val, NIL, fmt.Errorf("Unknown type '%s' with value '%+v'", reflect.TypeOf(val), val) + return val, XTypeNil, fmt.Errorf("Unknown type '%s' with value '%+v'", reflect.TypeOf(val), val) } // Compare returns the difference between the two passed interfaces which must be of the same type @@ -623,13 +626,13 @@ func Compare(env Environment, arg1 interface{}, arg2 interface{}) (int, error) { // common types, do real comparisons switch { - case arg1Type == arg2Type && arg1Type == ERROR: + case arg1Type == arg2Type && arg1Type == XTypeError: return strings.Compare(arg1.(error).Error(), arg2.(error).Error()), nil - case arg1Type == arg2Type && arg1Type == DECIMAL: + case arg1Type == arg2Type && arg1Type == XTypeDecimal: return arg1.(decimal.Decimal).Cmp(arg2.(decimal.Decimal)), nil - case arg1Type == arg2Type && arg1Type == BOOLEAN: + case arg1Type == arg2Type && arg1Type == XTypeBool: bool1 := arg1.(bool) bool2 := arg2.(bool) @@ -642,7 +645,7 @@ func Compare(env Environment, arg1 interface{}, arg2 interface{}) (int, error) { return 1, nil } - case arg1Type == arg2Type && arg1Type == TIME: + case arg1Type == arg2Type && arg1Type == XTypeTime: time1 := arg1.(time.Time) time2 := arg2.(time.Time) @@ -655,7 +658,7 @@ func Compare(env Environment, arg1 interface{}, arg2 interface{}) (int, error) { return 1, nil } - case arg1Type == arg2Type && arg1Type == STRING: + case arg1Type == arg2Type && arg1Type == XTypeString: return strings.Compare(arg1.(string), arg2.(string)), nil } diff --git a/utils/dates.go b/utils/dates.go index 28f5c47bd..4ffe2f5e3 100644 --- a/utils/dates.go +++ b/utils/dates.go @@ -10,23 +10,27 @@ import ( ) // patterns for date and time formats supported for human-entered data -var dd_mm_yyyy = regexp.MustCompile(`([0-9]{1,2})[-.\\/_ ]([0-9]{1,2})[-.\\/_ ]([0-9]{4}|[0-9]{2})`) -var mm_dd_yyyy = regexp.MustCompile(`([0-9]{1,2})[-.\\/_ ]([0-9]{1,2})[-.\\/_ ]([0-9]{4}|[0-9]{2})`) -var yyyy_mm_dd = regexp.MustCompile(`([0-9]{4}|[0-9]{2})[-.\\/_ ]([0-9]{1,2})[-.\\/_ ]([0-9]{1,2})`) -var hh_mm_ss = regexp.MustCompile(`([0-9]{1,2}):([0-9]{2})(:([0-9]{2})(\.(\d+))?)?\W*([aApP][mM])?`) +var patternDayMonthYear = regexp.MustCompile(`([0-9]{1,2})[-.\\/_ ]([0-9]{1,2})[-.\\/_ ]([0-9]{4}|[0-9]{2})`) +var patternMonthDayYear = regexp.MustCompile(`([0-9]{1,2})[-.\\/_ ]([0-9]{1,2})[-.\\/_ ]([0-9]{4}|[0-9]{2})`) +var patternYearMonthDay = regexp.MustCompile(`([0-9]{4}|[0-9]{2})[-.\\/_ ]([0-9]{1,2})[-.\\/_ ]([0-9]{1,2})`) +var patternTime = regexp.MustCompile(`([0-9]{1,2}):([0-9]{2})(:([0-9]{2})(\.(\d+))?)?\W*([aApP][mM])?`) +// DateFormat a date format string type DateFormat string + +// TimeFormat a time format string type TimeFormat string +// standard date and time formats const ( - DateFormat_yyyy_MM_dd DateFormat = "yyyy-MM-dd" - DateFormat_MM_dd_yyyy DateFormat = "MM-dd-yyyy" - DateFormat_dd_MM_yyyy DateFormat = "dd-MM-yyyy" - - TimeFormat_HH_mm TimeFormat = "hh:mm" - TimeFormat_hh_mm_tt TimeFormat = "hh:mm tt" - TimeFormat_HH_mm_ss TimeFormat = "HH:mm:ss" - TimeFormat_hh_mm_ss_tt TimeFormat = "hh:mm:ss tt" + DateFormatYearMonthDay DateFormat = "yyyy-MM-dd" + DateFormatMonthDayYear DateFormat = "MM-dd-yyyy" + DateFormatDayMonthYear DateFormat = "dd-MM-yyyy" + + TimeFormatHourMinute TimeFormat = "hh:mm" + TimeFormatHourMinuteAmPm TimeFormat = "hh:mm tt" + TimeFormatHourMinuteSecond TimeFormat = "HH:mm:ss" + TimeFormatHourMinuteSecondAmPm TimeFormat = "hh:mm:ss tt" ) func (df DateFormat) String() string { return string(df) } @@ -130,14 +134,14 @@ func DateFromString(env Environment, str string) (time.Time, error) { var err error switch env.DateFormat() { - case DateFormat_yyyy_MM_dd: - parsed, err = dateFromFormats(env, currentYear, yyyy_mm_dd, 3, 2, 1, str) + case DateFormatYearMonthDay: + parsed, err = dateFromFormats(env, currentYear, patternYearMonthDay, 3, 2, 1, str) - case DateFormat_dd_MM_yyyy: - parsed, err = dateFromFormats(env, currentYear, dd_mm_yyyy, 1, 2, 3, str) + case DateFormatDayMonthYear: + parsed, err = dateFromFormats(env, currentYear, patternDayMonthYear, 1, 2, 3, str) - case DateFormat_MM_dd_yyyy: - parsed, err = dateFromFormats(env, currentYear, mm_dd_yyyy, 2, 1, 3, str) + case DateFormatMonthDayYear: + parsed, err = dateFromFormats(env, currentYear, patternMonthDayYear, 2, 1, 3, str) default: err = fmt.Errorf("unknown date format: %s", env.DateFormat()) @@ -149,7 +153,7 @@ func DateFromString(env Environment, str string) (time.Time, error) { } // can we pull out a time? - matches := hh_mm_ss.FindAllStringSubmatch(str, -1) + matches := patternTime.FindAllStringSubmatch(str, -1) for _, match := range matches { hour, _ := strconv.Atoi(match[1]) @@ -359,6 +363,7 @@ func ToGoDateFormat(format string) (string, error) { return goFormat.String(), nil } +// DateToUTCRange returns the UTC time range of the given day func DateToUTCRange(d time.Time, tz *time.Location) (time.Time, time.Time) { localMidnight := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location()) utcMidnight := localMidnight.In(tz) diff --git a/utils/dates_test.go b/utils/dates_test.go index 4c2158d12..74c40a666 100644 --- a/utils/dates_test.go +++ b/utils/dates_test.go @@ -16,50 +16,50 @@ var timeTests = []struct { Error bool }{ // valid cases, varying formats - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "01-02-2001", "01-02-2001 00:00:00 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "date is 01.02.2001 yes", "01-02-2001 00:00:00 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "date is 1-2-99 yes", "01-02-1999 00:00:00 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "01/02/2001", "01-02-2001 00:00:00 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "01-02-2001", "01-02-2001 00:00:00 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "date is 01.02.2001 yes", "01-02-2001 00:00:00 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "date is 1-2-99 yes", "01-02-1999 00:00:00 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "01/02/2001", "01-02-2001 00:00:00 +0000 UTC", false}, // month first - {utils.DateFormat_MM_dd_yyyy, utils.TimeFormat_HH_mm, "UTC", "01-02-2001", "02-01-2001 00:00:00 +0000 UTC", false}, + {utils.DateFormatMonthDayYear, utils.TimeFormatHourMinute, "UTC", "01-02-2001", "02-01-2001 00:00:00 +0000 UTC", false}, // year first - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01", "01-02-2001 00:00:00 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01", "01-02-2001 00:00:00 +0000 UTC", false}, // specific timezone - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "America/Los_Angeles", "01\\02\\2001", "01-02-2001 00:00:00 -0800 PST", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "America/Los_Angeles", "01\\02\\2001", "01-02-2001 00:00:00 -0800 PST", false}, // illegal day - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "33-01-2001", "01-01-0001 00:00:00 +0000 UTC", true}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "33-01-2001", "01-01-0001 00:00:00 +0000 UTC", true}, // illegal month - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "01-13-2001", "01-01-0001 00:00:00 +0000 UTC", true}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "01-13-2001", "01-01-0001 00:00:00 +0000 UTC", true}, // valid two digit cases - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "01-01-99", "01-01-1999 00:00:00 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "01-01-16", "01-01-2016 00:00:00 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "01-01-99", "01-01-1999 00:00:00 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "01-01-16", "01-01-2016 00:00:00 +0000 UTC", false}, // iso dates - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "2016-05-01T18:30:15-08:00", "01-05-2016 18:30:15 -0800 PST", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "2016-05-01T18:30:15Z", "01-05-2016 18:30:15 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "2016-05-01T18:30:15.250Z", "01-05-2016 18:30:15.250 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "1977-06-23T08:34:00.000-07:00", "23-06-1977 15:34:00.000 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "1977-06-23T08:34:00.000250-07:00", "23-06-1977 15:34:00.000250 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "1977-06-23T08:34:00.000250500-07:00", "23-06-1977 15:34:00.000250500 +0000 UTC", false}, - {utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm, "UTC", "2017-06-10T17:34-06:00", "10-06-2017 23:34:00.000000 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "2016-05-01T18:30:15-08:00", "01-05-2016 18:30:15 -0800 PST", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "2016-05-01T18:30:15Z", "01-05-2016 18:30:15 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "2016-05-01T18:30:15.250Z", "01-05-2016 18:30:15.250 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "1977-06-23T08:34:00.000-07:00", "23-06-1977 15:34:00.000 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "1977-06-23T08:34:00.000250-07:00", "23-06-1977 15:34:00.000250 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "1977-06-23T08:34:00.000250500-07:00", "23-06-1977 15:34:00.000250500 +0000 UTC", false}, + {utils.DateFormatDayMonthYear, utils.TimeFormatHourMinute, "UTC", "2017-06-10T17:34-06:00", "10-06-2017 23:34:00.000000 +0000 UTC", false}, // with time - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01 03:15", "01-02-2001 03:15:00 +0000 UTC", false}, - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01 03:15pm", "01-02-2001 15:15:00 +0000 UTC", false}, - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01 03:15 AM", "01-02-2001 03:15:00 +0000 UTC", false}, - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01 03:15:34", "01-02-2001 03:15:34 +0000 UTC", false}, - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01 03:15:34.123", "01-02-2001 03:15:34.123 +0000 UTC", false}, - {utils.DateFormat_yyyy_MM_dd, utils.TimeFormat_HH_mm, "UTC", "2001-02-01 03:15:34.123456", "01-02-2001 03:15:34.123456 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01 03:15", "01-02-2001 03:15:00 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01 03:15pm", "01-02-2001 15:15:00 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01 03:15 AM", "01-02-2001 03:15:00 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01 03:15:34", "01-02-2001 03:15:34 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01 03:15:34.123", "01-02-2001 03:15:34.123 +0000 UTC", false}, + {utils.DateFormatYearMonthDay, utils.TimeFormatHourMinute, "UTC", "2001-02-01 03:15:34.123456", "01-02-2001 03:15:34.123456 +0000 UTC", false}, } func TestDateFromString(t *testing.T) { - env := utils.NewEnvironment(utils.DateFormat_dd_MM_yyyy, utils.TimeFormat_HH_mm_ss, time.UTC, utils.LanguageList{}) + env := utils.NewEnvironment(utils.DateFormatDayMonthYear, utils.TimeFormatHourMinuteSecond, time.UTC, utils.LanguageList{}) for _, test := range timeTests { env.SetDateFormat(test.DateFormat) diff --git a/utils/envelope.go b/utils/envelope.go index eb343bcd6..90e4be961 100644 --- a/utils/envelope.go +++ b/utils/envelope.go @@ -5,6 +5,7 @@ import ( "encoding/json" ) +// Typed is an interface of objects that are marshalled as typed envelopes type Typed interface { Type() string } @@ -15,6 +16,7 @@ type TypedEnvelope struct { Data []byte `json:"-"` } +// UnmarshalJSON unmarshals a typed envelope from the given JSON func (e *TypedEnvelope) UnmarshalJSON(b []byte) (err error) { typeE := &struct { Type string `json:"type"` @@ -30,6 +32,7 @@ func (e *TypedEnvelope) UnmarshalJSON(b []byte) (err error) { return err } +// MarshalJSON marshals this envelope into JSON func (e *TypedEnvelope) MarshalJSON() ([]byte, error) { // we want the insert the type into our parent data and return that typeE := &struct { @@ -59,6 +62,7 @@ func (e *TypedEnvelope) MarshalJSON() ([]byte, error) { return data.Bytes(), nil } +// EnvelopeFromTyped marshals the give object into a typed envelope func EnvelopeFromTyped(typed Typed) (*TypedEnvelope, error) { if typed == nil { return nil, nil diff --git a/utils/environment.go b/utils/environment.go index 1765e09fb..0ea911537 100644 --- a/utils/environment.go +++ b/utils/environment.go @@ -24,8 +24,8 @@ type Environment interface { // NewDefaultEnvironment creates a new Environment with our usual defaults in the UTC timezone func NewDefaultEnvironment() Environment { return &environment{ - dateFormat: DateFormat_yyyy_MM_dd, - timeFormat: TimeFormat_HH_mm, + dateFormat: DateFormatYearMonthDay, + timeFormat: TimeFormatHourMinute, timezone: time.UTC, languages: LanguageList{}, } @@ -103,6 +103,7 @@ func ReadEnvironment(data json.RawMessage) (*environment, error) { return env, nil } +// MarshalJSON marshals this environment into JSON func (e *environment) MarshalJSON() ([]byte, error) { ee := envEnvelope{ DateFormat: e.dateFormat, diff --git a/utils/http.go b/utils/http.go index 8e833f2d3..40a9b6f2e 100644 --- a/utils/http.go +++ b/utils/http.go @@ -226,6 +226,7 @@ type rrEnvelope struct { Response string `json:"response"` } +// UnmarshalJSON unmarshals a request response from the given JSON func (r *RequestResponse) UnmarshalJSON(data []byte) error { var envelope rrEnvelope var err error @@ -245,6 +246,7 @@ func (r *RequestResponse) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON marshals this request reponse into JSON func (r *RequestResponse) MarshalJSON() ([]byte, error) { var re rrEnvelope diff --git a/utils/json.go b/utils/json.go index 109e3a710..6d7004af3 100644 --- a/utils/json.go +++ b/utils/json.go @@ -8,7 +8,7 @@ import ( "github.com/buger/jsonparser" ) -// Convenience function to unmarshal an object and validate it +// UnmarshalAndValidate is a convenience function to unmarshal an object and validate it func UnmarshalAndValidate(data []byte, obj interface{}, objName string) error { err := json.Unmarshal(data, obj) if err != nil { @@ -23,6 +23,7 @@ func UnmarshalAndValidate(data []byte, obj interface{}, objName string) error { return nil } +// UnmarshalArray unmarshals an array of objects from the given JSON func UnmarshalArray(data json.RawMessage) ([]json.RawMessage, error) { var items []json.RawMessage err := json.Unmarshal(data, &items) diff --git a/utils/language.go b/utils/language.go index 823d1e09c..68b89be33 100644 --- a/utils/language.go +++ b/utils/language.go @@ -26,8 +26,10 @@ func ParseLanguage(lang string) (Language, error) { return Language(base.ISO3()), nil } +// LanguageList is a list of languages type LanguageList []Language +// RemoveDuplicates returns a new language list with duplicates removed func (ll LanguageList) RemoveDuplicates() LanguageList { result := LanguageList{} seen := map[Language]bool{} diff --git a/utils/locations.go b/utils/locations.go index 4fc0b2b4e..40c176822 100644 --- a/utils/locations.go +++ b/utils/locations.go @@ -28,11 +28,22 @@ func NewLocation(id LocationID, level LocationLevel, name string) *Location { return &Location{id: id, level: level, name: name} } -func (b *Location) ID() LocationID { return b.id } -func (b *Location) Level() LocationLevel { return b.level } -func (b *Location) Name() string { return b.name } -func (b *Location) Aliases() []string { return b.aliases } -func (b *Location) Parent() *Location { return b.parent } +// ID gets the id of this location +func (b *Location) ID() LocationID { return b.id } + +// Level gets the level of this location +func (b *Location) Level() LocationLevel { return b.level } + +// Name gets the name of this location +func (b *Location) Name() string { return b.name } + +// Aliases gets the aliases of this location +func (b *Location) Aliases() []string { return b.aliases } + +// Parent gets the parent of this location +func (b *Location) Parent() *Location { return b.parent } + +// Children gets the children of this location func (b *Location) Children() []*Location { return b.children } type locationVisitor func(Location *Location) @@ -94,6 +105,7 @@ func (s *LocationHierarchy) addLookups(location *Location) { } } +// Root gets the root location of this hierarchy func (s *LocationHierarchy) Root() *Location { return s.root } diff --git a/utils/validator.go b/utils/validator.go index 3ca735a92..067685d1e 100644 --- a/utils/validator.go +++ b/utils/validator.go @@ -32,8 +32,10 @@ func ValidateAs(obj interface{}, objName string) error { return validate(obj, objName) } +// ValidationErrors combines multiple validation errors as a single error type ValidationErrors []error +// NewValidationErrors creates new ValidationErrors fromn the given error messages func NewValidationErrors(messages ...string) ValidationErrors { errs := make([]error, len(messages)) for m, msg := range messages { From 01c812d5ec11a51008746d413454cb943cfb7129 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Tue, 13 Mar 2018 16:03:32 -0500 Subject: [PATCH 3/3] Fix warning about exported function returning unexported type --- utils/environment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/environment.go b/utils/environment.go index 0ea911537..8fa254b32 100644 --- a/utils/environment.go +++ b/utils/environment.go @@ -81,7 +81,7 @@ type envEnvelope struct { } // ReadEnvironment reads an environment from the given JSON -func ReadEnvironment(data json.RawMessage) (*environment, error) { +func ReadEnvironment(data json.RawMessage) (Environment, error) { env := NewDefaultEnvironment().(*environment) var envelope envEnvelope