-
Notifications
You must be signed in to change notification settings - Fork 20
/
results.go
182 lines (149 loc) · 4.81 KB
/
results.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package flows
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/nyaruka/goflow/utils"
)
// NewResults returns a new empty Results object
func NewResults() *Results {
return &Results{make(map[string]*Result)}
}
// Results is our wrapper around a map of snakified result names to result objects
type Results struct {
results map[string]*Result
}
func (r *Results) Clone() *Results {
clone := make(map[string]*Result, len(r.results))
for k, v := range r.results {
clone[k] = v
}
return &Results{results: clone}
}
// Save saves a new result in our map. The key is saved in a snakified format
func (r *Results) Save(node NodeUUID, name string, value string, category string, categoryLocalized string, createdOn time.Time) {
result := Result{node, name, value, category, categoryLocalized, createdOn}
r.results[utils.Snakify(name)] = &result
}
// Resolve resolves the passed in key, which is snakified before lookup
func (r *Results) Resolve(key string) interface{} {
key = utils.Snakify(key)
result, ok := r.results[key]
if !ok {
return nil
}
return result
}
// Default returns the default value for our Results, which is the entire map
func (r *Results) Default() interface{} {
return r
}
// String returns the string representation of our Results, which is a key/value pairing of our fields
func (r *Results) String() string {
results := make([]string, 0, len(r.results))
for _, v := range r.results {
results = append(results, fmt.Sprintf("%s: %s", v.name, v.value))
}
return strings.Join(results, ", ")
}
var _ utils.VariableResolver = (*Results)(nil)
// Result represents a result value in our flow run. Results have a name for which they are the result for,
// the value itself of the result, optional category and the date and node the result was collected on
type Result struct {
node NodeUUID
name string
value string
category string
categoryLocalized string
createdOn time.Time
}
// Resolve resolves the passed in key to a value. Result values have a name, value, category, node and created_on
func (r *Result) Resolve(key string) interface{} {
switch key {
case "category":
return r.category
case "category_localized":
if r.categoryLocalized == "" {
return r.category
}
return r.categoryLocalized
case "created_on":
return r.createdOn
case "node_uuid":
return r.node
case "result_name":
return r.name
case "value":
return r.value
}
return fmt.Errorf("No field '%s' on result", key)
}
// Default returns the default value for a result, which is our value
func (r *Result) Default() interface{} {
return r.value
}
// String returns the string representation of a result, which is our value
func (r *Result) String() string {
return r.value
}
var _ utils.VariableResolver = (*Result)(nil)
//------------------------------------------------------------------------------------------
// JSON Encoding / Decoding
//------------------------------------------------------------------------------------------
// UnmarshalJSON is our custom unmarshalling of a Results object, we build our map only with
// with snakified keys
func (r *Results) UnmarshalJSON(data []byte) error {
r.results = make(map[string]*Result)
incoming := make(map[string]*Result)
err := json.Unmarshal(data, &incoming)
if err != nil {
return err
}
// populate ourselves with the values, but keyed with snakified values
for k, v := range incoming {
snaked := utils.Snakify(v.name)
if k != snaked {
return fmt.Errorf("invalid results map, key: '%s' does not match snaked result name: '%s'", k, v.name)
}
r.results[k] = v
}
return nil
}
// MarshalJSON is our custom marshalling of a Results object, we build a map with
// the full names and then marshal that with snakified keys
func (r *Results) MarshalJSON() ([]byte, error) {
return json.Marshal(r.results)
}
type resultEnvelope struct {
Node NodeUUID `json:"node_uuid"`
Name string `json:"result_name"`
Value string `json:"value"`
Category string `json:"category,omitempty"`
CategoryLocalized string `json:"category_localized,omitempty"`
CreatedOn time.Time `json:"created_on"`
}
// UnmarshalJSON is our custom unmarshalling of a Result object
func (r *Result) UnmarshalJSON(data []byte) error {
var re resultEnvelope
var err error
err = json.Unmarshal(data, &re)
r.node = re.Node
r.name = re.Name
r.value = re.Value
r.category = re.Category
r.categoryLocalized = re.CategoryLocalized
r.createdOn = re.CreatedOn
return err
}
// MarshalJSON is our custom marshalling of a Result object
func (r *Result) MarshalJSON() ([]byte, error) {
var re resultEnvelope
re.Node = r.node
re.Name = r.name
re.Value = r.value
re.Category = r.category
re.CategoryLocalized = r.categoryLocalized
re.CreatedOn = r.createdOn
return json.Marshal(re)
}