From 7a5b960276b5094453e56fb7fa9cd7107434ca83 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Mon, 20 Apr 2020 12:47:40 -0500 Subject: [PATCH] Fix anywhere we truncate strings to do it by rune --- excellent/types/text_test.go | 1 + flows/actions/modifiers/base_test.go | 2 +- flows/actions/modifiers/field.go | 15 ++++--- flows/actions/modifiers/name.go | 8 ++-- flows/actions/modifiers/testdata/field.json | 46 +++++++++++++++++++++ flows/actions/modifiers/testdata/name.json | 24 +++++++++++ flows/runs/run.go | 4 +- flows/runs/run_test.go | 40 ++++++++++++++++++ 8 files changed, 126 insertions(+), 14 deletions(-) diff --git a/excellent/types/text_test.go b/excellent/types/text_test.go index e56d2f09b..e35f48a8c 100644 --- a/excellent/types/text_test.go +++ b/excellent/types/text_test.go @@ -33,6 +33,7 @@ func TestXText(t *testing.T) { assert.Equal(t, types.NewXText("abc"), types.NewXText("abcdef").Slice(0, 3)) assert.Equal(t, types.NewXText("cd"), types.NewXText("abcdef").Slice(2, 4)) assert.Equal(t, types.NewXText("😁😁"), types.NewXText("😁😁😁😁").Slice(2, 4)) + assert.Equal(t, types.NewXText("界"), types.NewXText("世界").Slice(1, 2)) assert.Equal(t, types.NewXText("abc"), types.NewXText("abcd").Slice(-1, 3)) assert.Equal(t, types.NewXText("bcd"), types.NewXText("abcd").Slice(1, 4)) diff --git a/flows/actions/modifiers/base_test.go b/flows/actions/modifiers/base_test.go index 90f0eaa2c..a7f162ea5 100644 --- a/flows/actions/modifiers/base_test.go +++ b/flows/actions/modifiers/base_test.go @@ -71,7 +71,7 @@ func testModifierType(t *testing.T, sessionAssets flows.SessionAssets, typeName // apply the modifier eventLog := test.NewEventLog() - modifier.Apply(envs.NewBuilder().Build(), sessionAssets, contact, eventLog.Log) + modifier.Apply(envs.NewBuilder().WithMaxValueLength(256).Build(), sessionAssets, contact, eventLog.Log) // clone test case and populate with actual values actual := tc diff --git a/flows/actions/modifiers/field.go b/flows/actions/modifiers/field.go index 1a5d2e150..4994c55f7 100644 --- a/flows/actions/modifiers/field.go +++ b/flows/actions/modifiers/field.go @@ -5,6 +5,7 @@ import ( "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/envs" + "github.com/nyaruka/goflow/excellent/types" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/utils" @@ -40,13 +41,17 @@ func (m *FieldModifier) Apply(env envs.Environment, assets flows.SessionAssets, oldValue := contact.Fields().Get(m.field) if !m.value.Equals(oldValue) { - // truncate text value if necessary - if m.value != nil && m.value.Text.Length() > env.MaxValueLength() { - m.value.Text = m.value.Text.Slice(0, env.MaxValueLength()) + var value *flows.Value + + // copy and truncate text value if necessary + if m.value != nil { + v := *m.value + value = &v + value.Text = types.NewXText(utils.Truncate(value.Text.Native(), env.MaxValueLength())) } - contact.Fields().Set(m.field, m.value) - log(events.NewContactFieldChanged(m.field, m.value)) + contact.Fields().Set(m.field, value) + log(events.NewContactFieldChanged(m.field, value)) m.reevaluateGroups(env, assets, contact, false, log) } } diff --git a/flows/actions/modifiers/name.go b/flows/actions/modifiers/name.go index 9ee6863c8..3f19ef449 100644 --- a/flows/actions/modifiers/name.go +++ b/flows/actions/modifiers/name.go @@ -36,12 +36,10 @@ func NewName(name string) *NameModifier { func (m *NameModifier) Apply(env envs.Environment, assets flows.SessionAssets, contact *flows.Contact, log flows.EventCallback) { if contact.Name() != m.Name { // truncate value if necessary - if len(m.Name) > env.MaxValueLength() { - m.Name = m.Name[0:env.MaxValueLength()] - } + name := utils.Truncate(m.Name, env.MaxValueLength()) - contact.SetName(m.Name) - log(events.NewContactNameChanged(m.Name)) + contact.SetName(name) + log(events.NewContactNameChanged(name)) m.reevaluateGroups(env, assets, contact, false, log) } } diff --git a/flows/actions/modifiers/testdata/field.json b/flows/actions/modifiers/testdata/field.json index 69186fef1..1eaf0dcdb 100644 --- a/flows/actions/modifiers/testdata/field.json +++ b/flows/actions/modifiers/testdata/field.json @@ -118,5 +118,51 @@ "value": null } ] + }, + { + "description": "truncates text value if necessary", + "contact_before": { + "uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f", + "name": "Bob", + "fields": { + "gender": { + "text": "M" + } + }, + "created_on": "2018-06-20T11:40:30.123456789Z" + }, + "modifier": { + "type": "field", + "field": { + "key": "gender", + "name": "Gender" + }, + "value": { + "text": "創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息" + } + }, + "contact_after": { + "uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f", + "name": "Bob", + "created_on": "2018-06-20T11:40:30.123456789Z", + "fields": { + "gender": { + "text": "創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程" + } + } + }, + "events": [ + { + "type": "contact_field_changed", + "created_on": "2018-10-18T14:20:30.000123456Z", + "field": { + "key": "gender", + "name": "Gender" + }, + "value": { + "text": "創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程" + } + } + ] } ] \ No newline at end of file diff --git a/flows/actions/modifiers/testdata/name.json b/flows/actions/modifiers/testdata/name.json index f3aae83f0..d8b5f7e9a 100644 --- a/flows/actions/modifiers/testdata/name.json +++ b/flows/actions/modifiers/testdata/name.json @@ -79,5 +79,29 @@ ] } ] + }, + { + "description": "truncates name if necessary", + "contact_before": { + "uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f", + "name": "Bob", + "created_on": "2018-06-20T11:40:30.123456789Z" + }, + "modifier": { + "type": "name", + "name": "創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息" + }, + "contact_after": { + "uuid": "5d76d86b-3bb9-4d5a-b822-c9d86f5d8e4f", + "name": "創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程", + "created_on": "2018-06-20T11:40:30.123456789Z" + }, + "events": [ + { + "type": "contact_name_changed", + "created_on": "2018-10-18T14:20:30.000123456Z", + "name": "創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程以發送消息創建流程" + } + ] } ] \ No newline at end of file diff --git a/flows/runs/run.go b/flows/runs/run.go index 7e2419a76..f9c74e86f 100644 --- a/flows/runs/run.go +++ b/flows/runs/run.go @@ -78,9 +78,7 @@ func (r *flowRun) Events() []flows.Event { return r.events } func (r *flowRun) Results() flows.Results { return r.results } func (r *flowRun) SaveResult(result *flows.Result) { // truncate value if necessary - if len(result.Value) > r.Environment().MaxValueLength() { - result.Value = result.Value[0:r.Environment().MaxValueLength()] - } + result.Value = utils.Truncate(result.Value, r.Environment().MaxValueLength()) r.results.Save(result) r.modifiedOn = dates.Now() diff --git a/flows/runs/run_test.go b/flows/runs/run_test.go index 38e2223a1..ec8555ac8 100644 --- a/flows/runs/run_test.go +++ b/flows/runs/run_test.go @@ -1,6 +1,7 @@ package runs_test import ( + "strings" "testing" "time" @@ -262,3 +263,42 @@ func TestMissingRelatedRunContext(t *testing.T) { assert.NoError(t, err) assert.Equal(t, types.NewXErrorf("null doesn't support lookups"), val) } + +func TestSaveResult(t *testing.T) { + sa, err := test.CreateSessionAssets([]byte(sessionAssets), "") + require.NoError(t, err) + + trigger, err := triggers.ReadTrigger(sa, []byte(sessionTrigger), assets.IgnoreMissing) + require.NoError(t, err) + + eng := test.NewEngine() + session, _, err := eng.NewSession(sa, trigger) + require.NoError(t, err) + + run := session.Runs()[0] + + dates.SetNowSource(dates.NewFixedNowSource(time.Date(2020, 4, 20, 12, 39, 30, 123456789, time.UTC))) + defer dates.SetNowSource(dates.DefaultNowSource) + + // no results means empty object with default of empty string + test.AssertXEqual(t, types.NewXObject(map[string]types.XValue{"__default__": types.XTextEmpty}), flows.Context(session.Environment(), run.Results())) + + run.SaveResult(flows.NewResult("Response 1", "red", "Red", "Rojo", "6d35528e-cae3-4e30-b842-8fe6ed7d5c02", "I like red", nil, dates.Now())) + + // name is snaked + assert.Equal(t, "red", run.Results().Get("response_1").Value) + assert.Equal(t, "Red", run.Results().Get("response_1").Category) + assert.Equal(t, time.Date(2020, 4, 20, 12, 39, 30, 123456789, time.UTC), run.ModifiedOn()) + + run.SaveResult(flows.NewResult("Response 1", "blue", "Blue", "Azul", "6d35528e-cae3-4e30-b842-8fe6ed7d5c02", "I like blue", nil, dates.Now())) + + // result is overwritten + assert.Equal(t, "blue", run.Results().Get("response_1").Value) + assert.Equal(t, "Blue", run.Results().Get("response_1").Category) + assert.Equal(t, time.Date(2020, 4, 20, 12, 39, 30, 123456789, time.UTC), run.ModifiedOn()) + + // long values should truncated + run.SaveResult(flows.NewResult("Response 1", strings.Repeat("創", 700), "Blue", "Azul", "6d35528e-cae3-4e30-b842-8fe6ed7d5c02", "I like blue", nil, dates.Now())) + + assert.Equal(t, strings.Repeat("創", 640), run.Results().Get("response_1").Value) +}