Skip to content

Commit

Permalink
Merge branch 'main' into results_by_name
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Dec 10, 2024
2 parents 9a19efe + 8a46e3a commit cf32f41
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 8 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
v0.225.3 (2024-12-09)
-------------------------
* Add patch flow migration to fix result references in expressions that need to be truncated

v0.225.2 (2024-12-09)
-------------------------
* Allow reading of results in sessions with invalid names

v0.225.1 (2024-12-05)
-------------------------
* Add custom validators for result name and category and make them match current floweditor validation

v0.225.0 (2024-12-03)
-------------------------
* Add new 13.6 flow migration to truncate result names and categories
Expand Down
2 changes: 1 addition & 1 deletion flows/definition/flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

// CurrentSpecVersion is the flow spec version supported by this library
var CurrentSpecVersion = semver.MustParse("13.6.0")
var CurrentSpecVersion = semver.MustParse("13.6.1")

// IsVersionSupported checks the given version is supported
func IsVersionSupported(v *semver.Version) bool {
Expand Down
36 changes: 36 additions & 0 deletions flows/definition/migrations/13_x.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
"github.com/nyaruka/gocommon/i18n"
"github.com/nyaruka/gocommon/stringsx"
"github.com/nyaruka/gocommon/uuids"
"github.com/nyaruka/goflow/excellent"
"github.com/nyaruka/goflow/excellent/refactor"
)

func init() {
registerMigration(semver.MustParse("13.6.1"), Migrate13_6_1)
registerMigration(semver.MustParse("13.6.0"), Migrate13_6)
registerMigration(semver.MustParse("13.5.0"), Migrate13_5)
registerMigration(semver.MustParse("13.4.0"), Migrate13_4)
Expand All @@ -19,6 +21,40 @@ func init() {
registerMigration(semver.MustParse("13.1.0"), Migrate13_1)
}

// Migrate13_6_1 fixes result lookups that need to be truncated.
//
// @version 13_6_1 "13.6.1"
func Migrate13_6_1(f Flow, cfg *Config) (Flow, error) {
const maxResultRef = 64

RewriteTemplates(f, GetTemplateCatalog(semver.MustParse("13.6.0")), func(s string) string {
// refactor any @result.* or @(...) template to find result lookups that need to be truncated
refactored, _ := refactor.Template(s, []string{"results"}, func(exp excellent.Expression) bool {
changed := false

exp.Visit(func(e excellent.Expression) {
switch typed := e.(type) {
case *excellent.DotLookup:
if asRef, isRef := typed.Container.(*excellent.ContextReference); isRef {
if asRef.Name == "results" {
old := typed.Lookup
typed.Lookup = stringsx.Truncate(old, maxResultRef)
if typed.Lookup != old {
changed = true
}
}
}
}
})

return changed
})

return refactored
})
return f, nil
}

// Migrate13_6 ensures that names of results and categories respect definition limits.
//
// @version 13_6 "13.6"
Expand Down
89 changes: 89 additions & 0 deletions flows/definition/migrations/specdata/templates.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,93 @@
{
"13.6.1": {
"actions": {
"add_contact_groups": [
".groups[*].name_match"
],
"add_contact_urn": [
".path"
],
"add_input_labels": [
".labels[*].name_match"
],
"call_classifier": [
".input"
],
"call_resthook": [],
"call_webhook": [
".body",
".headers.*",
".url"
],
"enter_flow": [],
"open_ticket": [
".assignee.email_match",
".body"
],
"play_audio": [
".audio_url"
],
"remove_contact_groups": [
".groups[*].name_match"
],
"request_optin": [],
"say_msg": [
".text"
],
"send_broadcast": [
".attachments[*]",
".contact_query",
".groups[*].name_match",
".legacy_vars[*]",
".quick_replies[*]",
".text"
],
"send_email": [
".addresses[*]",
".body",
".subject"
],
"send_msg": [
".attachments[*]",
".quick_replies[*]",
".template_variables[*]",
".text"
],
"set_contact_channel": [],
"set_contact_field": [
".value"
],
"set_contact_language": [
".language"
],
"set_contact_name": [
".name"
],
"set_contact_status": [],
"set_contact_timezone": [
".timezone"
],
"set_run_result": [
".value"
],
"start_session": [
".contact_query",
".groups[*].name_match",
".legacy_vars[*]"
],
"transfer_airtime": []
},
"routers": {
"random": [
".operand",
".cases[*].arguments[*]"
],
"switch": [
".operand",
".cases[*].arguments[*]"
]
}
},
"13.6.0": {
"actions": {
"add_contact_groups": [
Expand Down
71 changes: 71 additions & 0 deletions flows/definition/migrations/testdata/migrations/13.6.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
[
{
"description": "flow with localization",
"original": {
"uuid": "25a2d8b2-ae7c-4fed-964a-506fb8c3f0c0",
"name": "Test Flow",
"spec_version": "13.6.0",
"language": "eng",
"type": "messaging",
"localization": {
"spa": {
"9d9290a7-3713-4c22-8821-4af0a64c0821": {
"text": [
"Hola @results.this_single_variable_is_too_long_and_needs_to_be_truncated_to_match_the_flow @(if(results.this_in_a_complex_expression_is_also_too_long_and_needs_to_be_truncated.value))"
]
}
}
},
"nodes": [
{
"uuid": "32bc60ad-5c86-465e-a6b8-049c44ecce49",
"actions": [
{
"uuid": "9d9290a7-3713-4c22-8821-4af0a64c0821",
"type": "send_msg",
"text": "Hi @results.this_single_variable_is_too_long_and_needs_to_be_truncated_to_match_the_flow @(if(results.this_in_a_complex_expression_is_also_too_long_and_needs_to_be_truncated.value))"
}
],
"exits": [
{
"uuid": "2d481ce6-efcf-4898-a825-f76208e32f2a"
}
]
}
]
},
"migrated": {
"uuid": "25a2d8b2-ae7c-4fed-964a-506fb8c3f0c0",
"name": "Test Flow",
"spec_version": "13.6.1",
"language": "eng",
"type": "messaging",
"localization": {
"spa": {
"9d9290a7-3713-4c22-8821-4af0a64c0821": {
"text": [
"Hola @results.this_single_variable_is_too_long_and_needs_to_be_truncated_to_ma @(if(results.this_in_a_complex_expression_is_also_too_long_and_needs_to_be_tr.value))"
]
}
}
},
"nodes": [
{
"uuid": "32bc60ad-5c86-465e-a6b8-049c44ecce49",
"actions": [
{
"uuid": "9d9290a7-3713-4c22-8821-4af0a64c0821",
"type": "send_msg",
"text": "Hi @results.this_single_variable_is_too_long_and_needs_to_be_truncated_to_ma @(if(results.this_in_a_complex_expression_is_also_too_long_and_needs_to_be_tr.value))"
}
],
"exits": [
{
"uuid": "2d481ce6-efcf-4898-a825-f76208e32f2a"
}
]
}
]
}
}
]
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"uuid": "19cad1f2-9110-4271-98d4-1b968bf19410",
"name": "Change Language",
"spec_version": "13.6.0",
"spec_version": "13.6.1",
"language": "ara",
"type": "messaging",
"revision": 16,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"uuid": "19cad1f2-9110-4271-98d4-1b968bf19410",
"name": "Change Language",
"spec_version": "13.6.0",
"spec_version": "13.6.1",
"language": "kin",
"type": "messaging",
"revision": 16,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"uuid": "19cad1f2-9110-4271-98d4-1b968bf19410",
"name": "Change Language",
"spec_version": "13.6.0",
"spec_version": "13.6.1",
"language": "spa",
"type": "messaging",
"revision": 16,
Expand Down
2 changes: 1 addition & 1 deletion flows/definition/testdata/change_language.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"uuid": "19cad1f2-9110-4271-98d4-1b968bf19410",
"name": "Change Language",
"spec_version": "13.6.0",
"spec_version": "13.6.1",
"language": "eng",
"type": "messaging",
"revision": 16,
Expand Down
9 changes: 6 additions & 3 deletions flows/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/go-playground/validator/v10"
"github.com/nyaruka/gocommon/stringsx"
"github.com/nyaruka/goflow/envs"
"github.com/nyaruka/goflow/excellent/types"
"github.com/nyaruka/goflow/utils"
Expand Down Expand Up @@ -39,7 +40,7 @@ func init() {
// Result describes a value captured during a run's execution. It might have been implicitly created by a router, or explicitly
// created by a [set_run_result](#action:set_run_result) action.
type Result struct {
Name string `json:"name" validate:"required,result_name"`
Name string `json:"name" validate:"required"` // TODO add result_name validation when we're sure sessions no longer have invalid result names
Value string `json:"value"`
Category string `json:"category,omitempty"`
CategoryLocalized string `json:"category_localized,omitempty"`
Expand Down Expand Up @@ -160,17 +161,19 @@ func (r Results) format() string {
sort.Strings(lines)
return strings.Join(lines, "\n")
}

func (r *Results) UnmarshalJSON(data []byte) error {
// load map which may be by snakified name or name
// load map which may be keyed by snakified name or name
var m map[string]*Result
if err := json.Unmarshal(data, &m); err != nil {
return err
}

*r = make(Results, len(m))

// use actual name as key
// we enforce result names being at most 64 chars but old sessions may have longer names
for _, v := range m {
v.Name = strings.TrimSpace(stringsx.Truncate(v.Name, 64))
(*r)[v.Name] = v
}

Expand Down
9 changes: 9 additions & 0 deletions flows/results_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ func TestResults(t *testing.T) {
err = json.Unmarshal(marshaled, &unmarshaled)
assert.NoError(t, err)
assert.Equal(t, results, unmarshaled)

// test unmarshalling with result names/keys that are too long
err = json.Unmarshal([]byte(`{
"Beer 123456789012345678901234567890123456789012345678901234567890": {"category": "Skol", "created_on":"2019-04-05T14:16:30.000123456Z", "name": "Beer 123456789012345678901234567890123456789012345678901234567890", "node_uuid": "26493ebb-a254-4461-a28d-c7761784e276", "value": "skol!"},
"Empty 123456789012345678901234567890123456789012345678901234567890": {"created_on":"2019-04-05T14:16:30.000123456Z", "name": "Empty 123456789012345678901234567890123456789012345678901234567890", "node_uuid": "26493ebb-a254-4461-a28d-c7761784e276", "value": ""}
}`), &unmarshaled)
assert.NoError(t, err)
assert.Equal(t, "Skol", unmarshaled.Get("Beer 12345678901234567890123456789012345678901234567890123456789").Category)
assert.Equal(t, "", unmarshaled.Get("Empty 1234567890123456789012345678901234567890123456789012345678").Category)
}

func TestResultNameAndCategoryValidation(t *testing.T) {
Expand Down

0 comments on commit cf32f41

Please sign in to comment.