Skip to content

Commit c00020c

Browse files
authored
Merge pull request #696 from rapidpro/upmerge
Upmerge from Nyaruka
2 parents 4855d47 + e03c989 commit c00020c

File tree

16 files changed

+67
-41
lines changed

16 files changed

+67
-41
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
v8.0.0 (2023-01-09)
2+
-------------------------
3+
* Update test database to latest schema
4+
5+
v7.5.36 (2023-01-02)
6+
-------------------------
7+
* Update to latest goflow which adds locale field to MsgOut
8+
* Improve error reporting when courier call fails
9+
110
v7.5.35 (2022-12-05)
211
-------------------------
312
* Retry messages which fail to queue to courier

core/models/msgs.go

+22-4
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,23 @@ func buildMsgMetadata(m *flows.MsgOut) map[string]interface{} {
418418
metadata["quick_replies"] = m.QuickReplies()
419419
}
420420
if m.Templating() != nil {
421-
metadata["templating"] = m.Templating()
421+
mLanguage, mCountry := m.Locale().ToParts()
422+
423+
// TODO once we're queuing messages with locale and courier is reading that, can just add templating directly
424+
// without language and country
425+
metadata["templating"] = struct {
426+
Template *assets.TemplateReference `json:"template"`
427+
Language envs.Language `json:"language"`
428+
Country envs.Country `json:"country"`
429+
Variables []string `json:"variables,omitempty"`
430+
Namespace string `json:"namespace"`
431+
}{
432+
Template: m.Templating_.Template(),
433+
Language: mLanguage,
434+
Country: mCountry,
435+
Variables: m.Templating().Variables(),
436+
Namespace: m.Templating().Namespace(),
437+
}
422438
}
423439
if m.Topic() != flows.NilMsgTopic {
424440
metadata["topic"] = string(m.Topic())
@@ -1017,12 +1033,14 @@ func (b *BroadcastBatch) CreateMessages(ctx context.Context, rt *runtime.Runtime
10171033

10181034
// not found? try org default language
10191035
if t == nil {
1020-
t = trans[oa.Env().DefaultLanguage()]
1036+
lang = oa.Env().DefaultLanguage()
1037+
t = trans[lang]
10211038
}
10221039

10231040
// not found? use broadcast base language
10241041
if t == nil {
1025-
t = trans[b.BaseLanguage]
1042+
lang = b.BaseLanguage
1043+
t = trans[lang]
10261044
}
10271045

10281046
if t == nil {
@@ -1066,7 +1084,7 @@ func (b *BroadcastBatch) CreateMessages(ctx context.Context, rt *runtime.Runtime
10661084
}
10671085

10681086
// create our outgoing message
1069-
out := flows.NewMsgOut(urn, channel.ChannelReference(), text, t.Attachments, t.QuickReplies, nil, flows.NilMsgTopic, unsendableReason)
1087+
out := flows.NewMsgOut(urn, channel.ChannelReference(), text, t.Attachments, t.QuickReplies, nil, flows.NilMsgTopic, envs.NewLocale(lang, envs.NilCountry), unsendableReason)
10701088
msg, err := NewOutgoingBroadcastMsg(rt, oa.Org(), channel, contact, out, time.Now(), b.BroadcastID)
10711089
if err != nil {
10721090
return nil, errors.Wrapf(err, "error creating outgoing message")

core/models/msgs_test.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"github.com/nyaruka/mailroom/testsuite/testdata"
2323
"github.com/nyaruka/null"
2424
"github.com/nyaruka/redisx/assertredis"
25-
2625
"github.com/stretchr/testify/assert"
2726
"github.com/stretchr/testify/require"
2827
)
@@ -176,7 +175,7 @@ func TestNewOutgoingFlowMsg(t *testing.T) {
176175
session.SetIncomingMsg(tc.ResponseTo, null.NullString)
177176
}
178177

179-
flowMsg := flows.NewMsgOut(tc.URN, assets.NewChannelReference(tc.ChannelUUID, "Test Channel"), tc.Text, tc.Attachments, tc.QuickReplies, nil, tc.Topic, tc.Unsendable)
178+
flowMsg := flows.NewMsgOut(tc.URN, assets.NewChannelReference(tc.ChannelUUID, "Test Channel"), tc.Text, tc.Attachments, tc.QuickReplies, nil, tc.Topic, envs.NilLocale, tc.Unsendable)
180179
msg, err := models.NewOutgoingFlowMsg(rt, oa.Org(), channel, session, flow, flowMsg, now)
181180

182181
assert.NoError(t, err)
@@ -221,7 +220,7 @@ func TestNewOutgoingFlowMsg(t *testing.T) {
221220

222221
// check that msg loop detection triggers after 20 repeats of the same text
223222
newOutgoing := func(text string) *models.Msg {
224-
flowMsg := flows.NewMsgOut(urns.URN(fmt.Sprintf("tel:+250700000001?id=%d", testdata.Cathy.URNID)), assets.NewChannelReference(testdata.TwilioChannel.UUID, "Twilio"), text, nil, nil, nil, flows.NilMsgTopic, flows.NilUnsendableReason)
223+
flowMsg := flows.NewMsgOut(urns.URN(fmt.Sprintf("tel:+250700000001?id=%d", testdata.Cathy.URNID)), assets.NewChannelReference(testdata.TwilioChannel.UUID, "Twilio"), text, nil, nil, nil, flows.NilMsgTopic, envs.NilLocale, flows.NilUnsendableReason)
225224
msg, err := models.NewOutgoingFlowMsg(rt, oa.Org(), channel, session, flow, flowMsg, now)
226225
require.NoError(t, err)
227226
return msg
@@ -266,6 +265,7 @@ func TestMarshalMsg(t *testing.T) {
266265
[]string{"yes", "no"},
267266
nil,
268267
flows.MsgTopicPurchase,
268+
envs.NilLocale,
269269
flows.NilUnsendableReason,
270270
)
271271

@@ -324,6 +324,7 @@ func TestMarshalMsg(t *testing.T) {
324324
"Hi there",
325325
nil, nil, nil,
326326
flows.NilMsgTopic,
327+
envs.NilLocale,
327328
flows.NilUnsendableReason,
328329
)
329330
in1 := testdata.InsertIncomingMsg(db, testdata.Org1, testdata.TwilioChannel, testdata.Cathy, "test", models.MsgStatusHandled)
@@ -366,7 +367,7 @@ func TestMarshalMsg(t *testing.T) {
366367

367368
// try a broadcast message which won't have session and flow fields set
368369
bcastID := testdata.InsertBroadcast(db, testdata.Org1, `eng`, map[envs.Language]string{`eng`: "Blast"}, models.NilScheduleID, []*testdata.Contact{testdata.Cathy}, nil)
369-
bcastMsg1 := flows.NewMsgOut(urn, assets.NewChannelReference(testdata.TwilioChannel.UUID, "Test Channel"), "Blast", nil, nil, nil, flows.NilMsgTopic, flows.NilUnsendableReason)
370+
bcastMsg1 := flows.NewMsgOut(urn, assets.NewChannelReference(testdata.TwilioChannel.UUID, "Test Channel"), "Blast", nil, nil, nil, flows.NilMsgTopic, envs.NilLocale, flows.NilUnsendableReason)
370371
msg3, err := models.NewOutgoingBroadcastMsg(rt, oa.Org(), channel, cathy, bcastMsg1, time.Date(2021, 11, 9, 14, 3, 30, 0, time.UTC), bcastID)
371372
require.NoError(t, err)
372373

@@ -526,8 +527,8 @@ func TestGetMsgRepetitions(t *testing.T) {
526527
oa := testdata.Org1.Load(rt)
527528
_, cathy := testdata.Cathy.Load(db, oa)
528529

529-
msg1 := flows.NewMsgOut(testdata.Cathy.URN, nil, "foo", nil, nil, nil, flows.NilMsgTopic, flows.NilUnsendableReason)
530-
msg2 := flows.NewMsgOut(testdata.Cathy.URN, nil, "bar", nil, nil, nil, flows.NilMsgTopic, flows.NilUnsendableReason)
530+
msg1 := flows.NewMsgOut(testdata.Cathy.URN, nil, "foo", nil, nil, nil, flows.NilMsgTopic, envs.NilLocale, flows.NilUnsendableReason)
531+
msg2 := flows.NewMsgOut(testdata.Cathy.URN, nil, "bar", nil, nil, nil, flows.NilMsgTopic, envs.NilLocale, flows.NilUnsendableReason)
531532

532533
assertRepetitions := func(m *flows.MsgOut, expected int) {
533534
count, err := models.GetMsgRepetitions(rp, cathy, m)
@@ -694,7 +695,7 @@ func TestNewOutgoingIVR(t *testing.T) {
694695

695696
createdOn := time.Date(2021, 7, 26, 12, 6, 30, 0, time.UTC)
696697

697-
flowMsg := flows.NewIVRMsgOut(testdata.Cathy.URN, vonage.ChannelReference(), "Hello", "eng", "http://example.com/hi.mp3")
698+
flowMsg := flows.NewIVRMsgOut(testdata.Cathy.URN, vonage.ChannelReference(), "Hello", "http://example.com/hi.mp3", "eng")
698699
dbMsg := models.NewOutgoingIVR(rt.Config, testdata.Org1.ID, conn, flowMsg, createdOn)
699700

700701
assert.Equal(t, flowMsg.UUID(), dbMsg.UUID())

core/models/templates.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ func (t *TemplateTranslation) UnmarshalJSON(data []byte) error { return json.Unm
5656
func (t *TemplateTranslation) MarshalJSON() ([]byte, error) { return json.Marshal(t.t) }
5757

5858
func (t *TemplateTranslation) Channel() assets.ChannelReference { return t.t.Channel }
59-
func (t *TemplateTranslation) Language() envs.Language { return t.t.Language }
60-
func (t *TemplateTranslation) Country() envs.Country { return envs.Country(t.t.Country) }
61-
func (t *TemplateTranslation) Content() string { return t.t.Content }
62-
func (t *TemplateTranslation) Namespace() string { return t.t.Namespace }
63-
func (t *TemplateTranslation) VariableCount() int { return t.t.VariableCount }
59+
func (t *TemplateTranslation) Locale() envs.Locale {
60+
return envs.NewLocale(t.t.Language, envs.Country(t.t.Country))
61+
}
62+
func (t *TemplateTranslation) Content() string { return t.t.Content }
63+
func (t *TemplateTranslation) Namespace() string { return t.t.Namespace }
64+
func (t *TemplateTranslation) VariableCount() int { return t.t.VariableCount }
6465

6566
// loads the templates for the passed in org
6667
func loadTemplates(ctx context.Context, db sqlx.Queryer, orgID OrgID) ([]assets.Template, error) {

core/models/templates_test.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,14 @@ func TestTemplates(t *testing.T) {
2727

2828
assert.Equal(t, 1, len(templates[0].Translations()))
2929
tt := templates[0].Translations()[0]
30-
assert.Equal(t, envs.Language("fra"), tt.Language())
31-
assert.Equal(t, envs.NilCountry, tt.Country())
30+
assert.Equal(t, envs.Locale("fra"), tt.Locale())
3231
assert.Equal(t, "", tt.Namespace())
3332
assert.Equal(t, testdata.TwitterChannel.UUID, tt.Channel().UUID)
3433
assert.Equal(t, "Salut!", tt.Content())
3534

3635
assert.Equal(t, 1, len(templates[1].Translations()))
3736
tt = templates[1].Translations()[0]
38-
assert.Equal(t, envs.Language("eng"), tt.Language())
39-
assert.Equal(t, envs.Country("US"), tt.Country())
37+
assert.Equal(t, envs.Locale("eng-US"), tt.Locale())
4038
assert.Equal(t, "2d40b45c_25cd_4965_9019_f05d0124c5fa", tt.Namespace())
4139
assert.Equal(t, testdata.TwitterChannel.UUID, tt.Channel().UUID)
4240
assert.Equal(t, "Hi {{1}}, are you still experiencing problems with {{2}}?", tt.Content())

core/msgio/courier.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,11 @@ func FetchAttachment(ctx context.Context, rt *runtime.Runtime, ch *models.Channe
180180
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rt.Config.CourierAuthToken))
181181

182182
resp, err := httpx.DoTrace(courierHttpClient, req, nil, nil, -1)
183-
if err != nil || resp.Response.StatusCode != 200 {
184-
return "", "", errors.New("error calling courier endpoint")
183+
if err != nil {
184+
return "", "", errors.Wrap(err, "error calling courier endpoint")
185+
}
186+
if resp.Response.StatusCode != 200 {
187+
return "", "", errors.Errorf("error calling courier endpoint, got non-200 status: %s", string(resp.ResponseTrace))
185188
}
186189
fa := &fetchAttachmentResponse{}
187190
if err := json.Unmarshal(resp.ResponseBody, fa); err != nil {

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/lib/pq v1.10.7
1717
github.com/nyaruka/ezconf v0.2.1
1818
github.com/nyaruka/gocommon v1.33.1
19-
github.com/nyaruka/goflow v0.175.0
19+
github.com/nyaruka/goflow v0.178.1
2020
github.com/nyaruka/logrus_sentry v0.8.2-0.20190129182604-c2962b80ba7d
2121
github.com/nyaruka/null v1.2.0
2222
github.com/nyaruka/redisx v0.2.2

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ github.com/nyaruka/ezconf v0.2.1 h1:TDXWoqjqYya1uhou1mAJZg7rgFYL98EB0Tb3+BWtUh0=
221221
github.com/nyaruka/ezconf v0.2.1/go.mod h1:ey182kYkw2MIi4XiWe1FR/mzI33WCmTWuceDYYxgnQw=
222222
github.com/nyaruka/gocommon v1.33.1 h1:RUy1O5Ly4tAaQDDpahds8z+4uewwsXg6SNCH0hYm7pE=
223223
github.com/nyaruka/gocommon v1.33.1/go.mod h1:gusIA2aNC8EPB3ozlP4O0PaBiHUNq5+f1peRNvcn0DI=
224-
github.com/nyaruka/goflow v0.175.0 h1:ofrTm5qlf19oR1mjg8wFCmvNS9faFyDIQiFNs039kss=
225-
github.com/nyaruka/goflow v0.175.0/go.mod h1:C3Hj+jvJ2RY6w/ANx4zjcbVjYzd8gzOcryyPW2OEa8E=
224+
github.com/nyaruka/goflow v0.178.1 h1:ubVQXcrlFIebDnfJOvDRMaGc3CyGpngrtJLiVDgsHDc=
225+
github.com/nyaruka/goflow v0.178.1/go.mod h1:C3Hj+jvJ2RY6w/ANx4zjcbVjYzd8gzOcryyPW2OEa8E=
226226
github.com/nyaruka/librato v1.0.0 h1:Vznj9WCeC1yZXbBYyYp40KnbmXLbEkjKmHesV/v2SR0=
227227
github.com/nyaruka/librato v1.0.0/go.mod h1:pkRNLFhFurOz0QqBz6/DuTFhHHxAubWxs4Jx+J7yUgg=
228228
github.com/nyaruka/logrus_sentry v0.8.2-0.20190129182604-c2962b80ba7d h1:hyp9u36KIwbTCo2JAJ+TuJcJBc+UZzEig7RI/S5Dvkc=

mailroom_test.dump

-12.2 KB
Binary file not shown.

services/ivr/twiml/service.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616

1717
"github.com/nyaruka/gocommon/httpx"
1818
"github.com/nyaruka/gocommon/urns"
19-
"github.com/nyaruka/goflow/envs"
2019
"github.com/nyaruka/goflow/flows"
2120
"github.com/nyaruka/goflow/flows/events"
2221
"github.com/nyaruka/goflow/flows/routers/waits/hints"
@@ -461,11 +460,8 @@ func ResponseForSprint(cfg *runtime.Config, urn urns.URN, resumeURL string, es [
461460
switch event := e.(type) {
462461
case *events.IVRCreatedEvent:
463462
if len(event.Msg.Attachments()) == 0 {
464-
urnCountry := envs.DeriveCountryFromTel(urn.Path())
465-
msgLocale := envs.NewLocale(event.Msg.TextLanguage, urnCountry)
466-
467463
// only send locale if it's a supported say language for Twilio
468-
msgLocaleCode := msgLocale.ToBCP47()
464+
msgLocaleCode := event.Msg.Locale().ToBCP47()
469465
if _, valid := supportedSayLanguages[msgLocaleCode]; !valid {
470466
msgLocaleCode = ""
471467
}

services/ivr/twiml/service_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,21 @@ func TestResponseForSprint(t *testing.T) {
4848
{
4949
// ivr msg, supported text language specified
5050
events: []flows.Event{
51-
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "Hi there", "eng", "")),
51+
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "Hi there", "", "eng-US")),
5252
},
5353
expected: `<Response><Say language="en-US">Hi there</Say><Hangup></Hangup></Response>`,
5454
},
5555
{
5656
// ivr msg, unsupported text language specified
5757
events: []flows.Event{
58-
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "Amakuru", "kin", "")),
58+
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "Amakuru", "", "kin")),
5959
},
6060
expected: `<Response><Say>Amakuru</Say><Hangup></Hangup></Response>`,
6161
},
6262
{
6363
// ivr msg with audio attachment, text language ignored
6464
events: []flows.Event{
65-
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "Hi there", "eng", "/recordings/foo.wav")),
65+
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "Hi there", "/recordings/foo.wav", "eng-US")),
6666
},
6767
expected: `<Response><Play>https://mailroom.io/recordings/foo.wav</Play><Hangup></Hangup></Response>`,
6868
},

services/ivr/vonage/service_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ func TestResponseForSprint(t *testing.T) {
8181
},
8282
{
8383
[]flows.Event{
84-
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "hello world", "", "/recordings/foo.wav")),
84+
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "hello world", "/recordings/foo.wav", "")),
8585
},
8686
`[{"action":"stream","streamUrl":["/recordings/foo.wav"]}]`,
8787
},
8888
{
8989
[]flows.Event{
90-
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "hello world", "", "https://temba.io/recordings/foo.wav")),
90+
events.NewIVRCreated(flows.NewIVRMsgOut(urn, channelRef, "hello world", "https://temba.io/recordings/foo.wav", "")),
9191
},
9292
`[{"action":"stream","streamUrl":["https://temba.io/recordings/foo.wav"]}]`,
9393
},

testsuite/testdata/flows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ func InsertFlow(db *sqlx.DB, org *Org, definition []byte) *Flow {
3434

3535
var id models.FlowID
3636
must(db.Get(&id,
37-
`INSERT INTO flows_flow(org_id, uuid, name, flow_type, version_number, expires_after_minutes, ignore_triggers, has_issues, is_active, is_archived, is_system, created_by_id, created_on, modified_by_id, modified_on, saved_on, saved_by_id)
38-
VALUES($1, $2, $3, 'M', 1, 10, FALSE, FALSE, TRUE, FALSE, FALSE, $4, NOW(), $4, NOW(), NOW(), $4) RETURNING id`, org.ID, uuid, name, Admin.ID,
37+
`INSERT INTO flows_flow(org_id, uuid, name, flow_type, version_number, base_language, expires_after_minutes, ignore_triggers, has_issues, is_active, is_archived, is_system, created_by_id, created_on, modified_by_id, modified_on, saved_on, saved_by_id)
38+
VALUES($1, $2, $3, 'M', '13.1.0', 'eng', 10, FALSE, FALSE, TRUE, FALSE, FALSE, $4, NOW(), $4, NOW(), NOW(), $4) RETURNING id`, org.ID, uuid, name, Admin.ID,
3939
))
4040

4141
db.MustExec(`INSERT INTO flows_flowrevision(flow_id, definition, spec_version, revision, is_active, created_by_id, created_on, modified_by_id, modified_on)

testsuite/testdata/msgs.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func insertOutgoingMsg(db *sqlx.DB, org *Org, channel *Channel, contact *Contact
5454
channelID = channel.ID
5555
}
5656

57-
msg := flows.NewMsgOut(contact.URN, channelRef, text, attachments, nil, nil, flows.NilMsgTopic, flows.NilUnsendableReason)
57+
msg := flows.NewMsgOut(contact.URN, channelRef, text, attachments, nil, nil, flows.NilMsgTopic, envs.NilLocale, flows.NilUnsendableReason)
5858

5959
var sentOn *time.Time
6060
if status == models.MsgStatusWired || status == models.MsgStatusSent || status == models.MsgStatusDelivered {

testsuite/testsuite.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ DELETE FROM msgs_msg;
197197
DELETE FROM flows_flowrun;
198198
DELETE FROM flows_flowpathcount;
199199
DELETE FROM flows_flownodecount;
200-
DELETE FROM flows_flowruncount;
200+
DELETE FROM flows_flowrunstatuscount;
201201
DELETE FROM flows_flowcategorycount;
202202
DELETE FROM flows_flowsession;
203203
DELETE FROM flows_flowrevision WHERE flow_id >= 30000;

web/ivr/ivr_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ func TestTwilioIVR(t *testing.T) {
199199
`<Say>You said</Say>`,
200200
`<Say>I hope hearing that makes you feel better. Good day and good bye.</Say>`,
201201
`<Dial action=`,
202-
`>2065551212</Dial>`,
202+
`>+12065551212</Dial>`,
203203
},
204204
expectedConnStatus: map[string]string{"Call1": "I", "Call2": "W", "Call3": "W"},
205205
},
@@ -364,7 +364,7 @@ func mockVonageHandler(w http.ResponseWriter, r *http.Request) {
364364
} else if form.To[0].Number == "16055743333" {
365365
w.WriteHeader(http.StatusCreated)
366366
w.Write([]byte(`{ "uuid": "Call2","status": "started","direction": "outbound","conversation_uuid": "Conversation2"}`))
367-
} else if form.To[0].Number == "2065551212" {
367+
} else if form.To[0].Number == "12065551212" {
368368
// start of a transfer leg
369369
w.WriteHeader(http.StatusCreated)
370370
w.Write([]byte(`{ "uuid": "Call3","status": "started","direction": "outbound","conversation_uuid": "Conversation3"}`))

0 commit comments

Comments
 (0)