diff --git a/handlers/m3tech/handler.go b/handlers/m3tech/handler.go
index 5262f63e8..f2fb715b4 100644
--- a/handlers/m3tech/handler.go
+++ b/handlers/m3tech/handler.go
@@ -69,19 +69,10 @@ func (h *handler) WriteMsgSuccessResponse(ctx context.Context, w http.ResponseWr
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "")
- if username == "" {
- return nil, fmt.Errorf("no username set for M3 channel")
- }
-
password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "")
- if password == "" {
- return nil, fmt.Errorf("no password set for M3 channel")
+ if username == "" || password == "" {
+ return courier.ErrChannelConfig
}
// figure out if we need to send as unicode (encoding 7)
@@ -91,8 +82,6 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
encoding = "7"
}
- // send our message
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
for _, part := range handlers.SplitMsgByChannel(msg.Channel(), text, maxMsgLength) {
// build our request
params := url.Values{
@@ -114,17 +103,16 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
msgURL.RawQuery = params.Encode()
req, err := http.NewRequest(http.MethodGet, msgURL.String(), nil)
if err != nil {
- return nil, err
+ return err
}
resp, _, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- break
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
-
- // all went well, set ourselves to wired
- status.SetStatus(courier.MsgStatusWired)
}
- return status, nil
+ return nil
}
diff --git a/handlers/m3tech/handler_test.go b/handlers/m3tech/handler_test.go
index 12d84196f..2048b8fdb 100644
--- a/handlers/m3tech/handler_test.go
+++ b/handlers/m3tech/handler_test.go
@@ -1,12 +1,13 @@
package m3tech
import (
- "net/http/httptest"
+ "net/url"
"testing"
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var testChannels = []courier.Channel{
@@ -47,52 +48,114 @@ func BenchmarkHandler(b *testing.B) {
RunChannelBenchmarks(b, testChannels, newHandler(), handleTestCases)
}
-// setSendURL takes care of setting the send_url to our test server host
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- sendURL = s.URL
-}
-
var defaultSendTestCases = []OutgoingTestCase{
{Label: "Plain Send",
- MsgText: "Simple Message", MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- MockResponseBody: `[{"Response": "0"}]`, MockResponseStatus: 200,
- ExpectedURLParams: map[string]string{
- "MobileNo": "250788383383",
- "SMS": "Simple Message",
- "SMSChannel": "0",
- "AuthKey": "m3-Tech",
- "HandsetPort": "0",
- "MsgHeader": "2020",
- "Telco": "0",
- "SMSType": "0",
- "UserId": "Username",
- "Password": "Password",
+ MsgText: "Simple Message",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://secure.m3techservice.com/GenericServiceRestAPI/api/SendSMS*": {
+ httpx.NewMockResponse(200, nil, []byte(`[{"Response": "0"}]`)),
+ },
},
- SendPrep: setSendURL},
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{
+ "MobileNo": {"250788383383"},
+ "SMS": {"Simple Message"},
+ "SMSChannel": {"0"},
+ "AuthKey": {"m3-Tech"},
+ "HandsetPort": {"0"},
+ "MsgHeader": {"2020"},
+ "MsgId": {"10"},
+ "Telco": {"0"},
+ "SMSType": {"0"},
+ "UserId": {"Username"},
+ "Password": {"Password"},
+ },
+ }},
+ },
{Label: "Unicode Send",
- MsgText: "☺", MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- MockResponseBody: `[{"Response": "0"}]`, MockResponseStatus: 200,
- ExpectedURLParams: map[string]string{"SMS": "☺", "SMSType": "7"},
- SendPrep: setSendURL},
+ MsgText: "☺",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://secure.m3techservice.com/GenericServiceRestAPI/api/SendSMS*": {
+ httpx.NewMockResponse(200, nil, []byte(`[{"Response": "0"}]`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{
+ "SMS": {"☺"},
+ "MobileNo": {"250788383383"},
+ "SMSChannel": {"0"},
+ "AuthKey": {"m3-Tech"},
+ "HandsetPort": {"0"},
+ "MsgHeader": {"2020"},
+ "MsgId": {"10"},
+ "Telco": {"0"},
+ "SMSType": {"7"},
+ "UserId": {"Username"},
+ "Password": {"Password"},
+ },
+ }},
+ },
{Label: "Smart Encoding",
- MsgText: "Fancy “Smart” Quotes", MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- MockResponseBody: `[{"Response": "0"}]`, MockResponseStatus: 200,
- ExpectedURLParams: map[string]string{"SMS": `Fancy "Smart" Quotes`, "SMSType": "0"},
- SendPrep: setSendURL},
+ MsgText: "Fancy “Smart” Quotes",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://secure.m3techservice.com/GenericServiceRestAPI/api/SendSMS*": {
+ httpx.NewMockResponse(200, nil, []byte(`[{"Response": "0"}]`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{
+ "SMS": {`Fancy "Smart" Quotes`},
+ "MobileNo": {"250788383383"},
+ "SMSChannel": {"0"},
+ "AuthKey": {"m3-Tech"},
+ "HandsetPort": {"0"},
+ "MsgHeader": {"2020"},
+ "MsgId": {"10"},
+ "Telco": {"0"},
+ "SMSType": {"0"},
+ "UserId": {"Username"},
+ "Password": {"Password"},
+ },
+ }},
+ },
{Label: "Send Attachment",
- MsgText: "My pic!", MsgURN: "tel:+250788383383", MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- ExpectedMsgStatus: "W",
- MockResponseBody: `[{"Response": "0"}]`, MockResponseStatus: 200,
- ExpectedURLParams: map[string]string{"SMS": "My pic!\nhttps://foo.bar/image.jpg", "SMSType": "0"},
- SendPrep: setSendURL},
+ MsgText: "My pic!",
+ MsgURN: "tel:+250788383383",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://secure.m3techservice.com/GenericServiceRestAPI/api/SendSMS*": {
+ httpx.NewMockResponse(200, nil, []byte(`[{"Response": "0"}]`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{
+ "SMS": {"My pic!\nhttps://foo.bar/image.jpg"},
+ "MobileNo": {"250788383383"},
+ "SMSChannel": {"0"},
+ "AuthKey": {"m3-Tech"},
+ "HandsetPort": {"0"},
+ "MsgHeader": {"2020"},
+ "MsgId": {"10"},
+ "Telco": {"0"},
+ "SMSType": {"0"},
+ "UserId": {"Username"},
+ "Password": {"Password"},
+ },
+ }},
+ },
{Label: "Error Sending",
- MsgText: "Error Sending", MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `[{"Response": "101"}]`, MockResponseStatus: 403,
- SendPrep: setSendURL},
+ MsgText: "Error Sending",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://secure.m3techservice.com/GenericServiceRestAPI/api/SendSMS*": {
+ httpx.NewMockResponse(403, nil, []byte(`[{"Response": "101"}]`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseStatus,
+ },
}
func TestOutgoing(t *testing.T) {
diff --git a/handlers/macrokiosk/handler.go b/handlers/macrokiosk/handler.go
index a60ad982c..4a51dec45 100644
--- a/handlers/macrokiosk/handler.go
+++ b/handlers/macrokiosk/handler.go
@@ -150,17 +150,12 @@ type mtPayload struct {
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "")
password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "")
servID := msg.Channel().StringConfigForKey(configMacrokioskServiceID, "")
senderID := msg.Channel().StringConfigForKey(configMacrokioskSenderID, "")
if username == "" || password == "" || servID == "" || senderID == "" {
- return nil, fmt.Errorf("missing username, password, serviceID or senderID for MK channel")
+ return courier.ErrChannelConfig
}
// figure out if we need to send as unicode (encoding 5)
@@ -170,9 +165,8 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
encoding = "5"
}
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
parts := handlers.SplitMsgByChannel(msg.Channel(), text, maxMsgLength)
- for i, part := range parts {
+ for _, part := range parts {
payload := &mtPayload{
From: senderID,
ServID: servID,
@@ -188,26 +182,24 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
// build our request
req, err := http.NewRequest(http.MethodPost, sendURL, requestBody)
if err != nil {
- return nil, err
+ return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, respBody, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- return status, nil
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
externalID, err := jsonparser.GetString(respBody, "MsgID")
if err != nil {
- return status, fmt.Errorf("unable to parse response body from Macrokiosk")
- }
-
- // set the external id if this is our first part
- if i == 0 {
- status.SetExternalID(externalID)
+ clog.Error(courier.ErrorResponseValueMissing("MsgID"))
+ } else {
+ res.AddExternalID(externalID)
}
}
- status.SetStatus(courier.MsgStatusWired)
- return status, nil
+ return nil
}
diff --git a/handlers/macrokiosk/handler_test.go b/handlers/macrokiosk/handler_test.go
index 27a3814b7..a18db5708 100644
--- a/handlers/macrokiosk/handler_test.go
+++ b/handlers/macrokiosk/handler_test.go
@@ -1,13 +1,13 @@
package macrokiosk
import (
- "net/http/httptest"
"testing"
"time"
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var testChannels = []courier.Channel{
@@ -72,82 +72,103 @@ func TestIncoming(t *testing.T) {
RunIncomingTestCases(t, testChannels, newHandler(), incomingTestCases)
}
-// setSendURL takes care of setting the send_url to our test server host
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- sendURL = s.URL
-}
-
var outgoingTestCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"abc123"},
- MockResponseBody: `{ "MsgID":"abc123" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
+ Label: "Plain Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://www.etracker.cc/bulksms/send": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "MsgID":"abc123" }`)),
+ },
},
- ExpectedRequestBody: `{"user":"Username","pass":"Password","to":"250788383383","text":"Simple Message ☺","from":"macro","servid":"service-id","type":"5"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ },
+ Body: `{"user":"Username","pass":"Password","to":"250788383383","text":"Simple Message ☺","from":"macro","servid":"service-id","type":"5"}`,
+ }},
+ ExpectedExtIDs: []string{"abc123"},
},
{
- Label: "Long Send",
- MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"abc123"},
- MockResponseBody: `{ "MsgID":"abc123" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
+ Label: "Long Send",
+ MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://www.etracker.cc/bulksms/send": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "MsgID":"abc123" }`)),
+ httpx.NewMockResponse(200, nil, []byte(`{ "MsgID":"abc123" }`)),
+ },
},
- ExpectedRequestBody: `{"user":"Username","pass":"Password","to":"250788383383","text":"I need to keep adding more things to make it work","from":"macro","servid":"service-id","type":"0"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ },
+ Body: `{"user":"Username","pass":"Password","to":"250788383383","text":"This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say,","from":"macro","servid":"service-id","type":"0"}`,
+ },
+ {
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ },
+ Body: `{"user":"Username","pass":"Password","to":"250788383383","text":"I need to keep adding more things to make it work","from":"macro","servid":"service-id","type":"0"}`,
+ },
+ },
+ ExpectedExtIDs: []string{"abc123", "abc123"},
},
{
- Label: "Send Attachment",
- MsgText: "My pic!",
- MsgURN: "tel:+250788383383",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"abc123"},
- MockResponseBody: `{ "MsgID":"abc123" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
+ Label: "Send Attachment",
+ MsgText: "My pic!",
+ MsgURN: "tel:+250788383383",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://www.etracker.cc/bulksms/send": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "MsgID":"abc123" }`)),
+ },
},
- ExpectedRequestBody: `{"user":"Username","pass":"Password","to":"250788383383","text":"My pic!\nhttps://foo.bar/image.jpg","from":"macro","servid":"service-id","type":"0"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ },
+ Body: `{"user":"Username","pass":"Password","to":"250788383383","text":"My pic!\nhttps://foo.bar/image.jpg","from":"macro","servid":"service-id","type":"0"}`,
+ }},
+ ExpectedExtIDs: []string{"abc123"},
},
{
- Label: "No External Id",
- MsgText: "No External ID",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `{ "missing":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 200,
- ExpectedLogErrors: []*courier.ChannelError{courier.NewChannelError("", "", "unable to parse response body from Macrokiosk")},
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
+ Label: "No External Id",
+ MsgText: "No External ID",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://www.etracker.cc/bulksms/send": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "missing":"missing" }`)),
+ },
},
- ExpectedRequestBody: `{"user":"Username","pass":"Password","to":"250788383383","text":"No External ID","from":"macro","servid":"service-id","type":"0"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ },
+ Body: `{"user":"Username","pass":"Password","to":"250788383383","text":"No External ID","from":"macro","servid":"service-id","type":"0"}`,
+ }},
+ ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("MsgID")},
},
{
- Label: "Error Sending",
- MsgText: "Error Message",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `{ "error": "failed" }`,
- MockResponseStatus: 401,
- ExpectedRequestBody: `{"user":"Username","pass":"Password","to":"250788383383","text":"Error Message","from":"macro","servid":"service-id","type":"0"}`,
- SendPrep: setSendURL,
+ Label: "Error Sending",
+ MsgText: "Error Message",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://www.etracker.cc/bulksms/send": {
+ httpx.NewMockResponse(401, nil, []byte(`{ "error": "failed" }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"user":"Username","pass":"Password","to":"250788383383","text":"Error Message","from":"macro","servid":"service-id","type":"0"}`,
+ }},
+ ExpectedError: courier.ErrResponseStatus,
},
}
diff --git a/handlers/mblox/handler.go b/handlers/mblox/handler.go
index 1543f35c9..089109a5f 100644
--- a/handlers/mblox/handler.go
+++ b/handlers/mblox/handler.go
@@ -113,18 +113,12 @@ type mtPayload struct {
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "")
password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "")
if username == "" || password == "" {
- return nil, fmt.Errorf("Missing username or password for MB channel")
+ return courier.ErrChannelConfig
}
-
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
parts := handlers.SplitMsgByChannel(msg.Channel(), handlers.GetTextAndAttachments(msg), maxMsgLength)
for _, part := range parts {
payload := &mtPayload{}
@@ -139,25 +133,26 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
// build our request
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/%s/batches", sendURL, username), requestBody)
if err != nil {
- return nil, err
+ return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", password))
resp, respBody, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- return status, nil
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
externalID, err := jsonparser.GetString(respBody, "id")
if err != nil {
- return status, fmt.Errorf("unable to parse response body from MBlox")
+ clog.Error(courier.ErrorResponseValueMissing("id"))
+ } else {
+ res.AddExternalID(externalID)
}
-
- status.SetStatus(courier.MsgStatusWired)
- status.SetExternalID(externalID)
}
- return status, nil
+ return nil
}
diff --git a/handlers/mblox/handler_test.go b/handlers/mblox/handler_test.go
index b72ee7637..123205b78 100644
--- a/handlers/mblox/handler_test.go
+++ b/handlers/mblox/handler_test.go
@@ -1,13 +1,13 @@
package mblox
import (
- "net/http/httptest"
"testing"
"time"
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var testChannels = []courier.Channel{
@@ -89,86 +89,108 @@ func BenchmarkHandler(b *testing.B) {
RunChannelBenchmarks(b, testChannels, newHandler(), testCases)
}
-// setSendURL takes care of setting the send_url to our test server host
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- sendURL = s.URL
-}
-
var defaultSendTestCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- MockResponseBody: `{ "id":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer Password",
+ Label: "Plain Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mblox.com/xms/v1/Username/batches": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "id":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"from":"2020","to":["250788383383"],"body":"Simple Message ☺","delivery_report":"per_recipient"}`,
- ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer Password",
+ },
+ Body: `{"from":"2020","to":["250788383383"],"body":"Simple Message ☺","delivery_report":"per_recipient"}`,
+ }},
+ ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
},
{
- Label: "Long Send",
- MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- MockResponseBody: `{ "id":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer Password",
+ Label: "Long Send",
+ MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mblox.com/xms/v1/Username/batches": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "id":"OzYDlvf3SQVc" }`)),
+ httpx.NewMockResponse(200, nil, []byte(`{ "id":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"from":"2020","to":["250788383383"],"body":"I need to keep adding more things to make it work","delivery_report":"per_recipient"}`,
- ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer Password",
+ },
+ Body: `{"from":"2020","to":["250788383383"],"body":"This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say,","delivery_report":"per_recipient"}`,
+ },
+ {
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer Password",
+ },
+ Body: `{"from":"2020","to":["250788383383"],"body":"I need to keep adding more things to make it work","delivery_report":"per_recipient"}`,
+ },
+ },
+ ExpectedExtIDs: []string{"OzYDlvf3SQVc", "OzYDlvf3SQVc"},
},
{
- Label: "Send Attachment",
- MsgText: "My pic!",
- MsgURN: "tel:+250788383383",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- ExpectedMsgStatus: "W",
- MockResponseBody: `{ "id":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer Password",
+ Label: "Send Attachment",
+ MsgText: "My pic!",
+ MsgURN: "tel:+250788383383",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mblox.com/xms/v1/Username/batches": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "id":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"from":"2020","to":["250788383383"],"body":"My pic!\nhttps://foo.bar/image.jpg","delivery_report":"per_recipient"}`,
- ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer Password",
+ },
+ Body: `{"from":"2020","to":["250788383383"],"body":"My pic!\nhttps://foo.bar/image.jpg","delivery_report":"per_recipient"}`,
+ }},
+ ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
},
{
- Label: "No External Id",
- MsgText: "No External ID",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `{ "missing":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 200,
- ExpectedLogErrors: []*courier.ChannelError{courier.NewChannelError("", "", "unable to parse response body from MBlox")},
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer Password",
+ Label: "No External Id",
+ MsgText: "No External ID",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mblox.com/xms/v1/Username/batches": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "missing":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"from":"2020","to":["250788383383"],"body":"No External ID","delivery_report":"per_recipient"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer Password",
+ },
+ Body: `{"from":"2020","to":["250788383383"],"body":"No External ID","delivery_report":"per_recipient"}`,
+ }},
+ ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("id")},
},
{
- Label: "Error Sending",
- MsgText: "Error Message",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `{ "error": "failed" }`,
- MockResponseStatus: 401,
- ExpectedRequestBody: `{"from":"2020","to":["250788383383"],"body":"Error Message","delivery_report":"per_recipient"}`,
- SendPrep: setSendURL,
+ Label: "Error Sending",
+ MsgText: "Error Message",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mblox.com/xms/v1/Username/batches": {
+ httpx.NewMockResponse(401, nil, []byte(`{ "error": "failed" }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"from":"2020","to":["250788383383"],"body":"Error Message","delivery_report":"per_recipient"}`,
+ }},
+ ExpectedError: courier.ErrResponseStatus,
},
}
diff --git a/handlers/messagebird/handler.go b/handlers/messagebird/handler.go
index 6e2bfcdfc..01127ed21 100644
--- a/handlers/messagebird/handler.go
+++ b/handlers/messagebird/handler.go
@@ -185,20 +185,12 @@ func (h *handler) receiveMessage(ctx context.Context, channel courier.Channel, w
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
-
authToken := msg.Channel().StringConfigForKey(courier.ConfigAuthToken, "")
if authToken == "" {
- return nil, fmt.Errorf("missing config 'auth_token' for Messagebird channel")
+ return courier.ErrChannelConfig
}
user := msg.URN().Path()
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
-
// create base payload
payload := &Message{
Recipients: []string{user},
@@ -224,9 +216,8 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
jsonBody := jsonx.MustMarshal(payload)
req, err := http.NewRequest(http.MethodPost, sendUrl, bytes.NewReader(jsonBody))
-
if err != nil {
- return nil, err
+ return err
}
req.Header.Set("Content-Type", "application/json")
@@ -234,18 +225,20 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
req.Header.Set("Authorization", bearer)
resp, respBody, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- return status, nil
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
- status.SetStatus(courier.MsgStatusWired)
externalID, err := jsonparser.GetString(respBody, "id")
if err != nil {
- clog.Error(courier.ErrorResponseUnparseable("JSON"))
- return status, nil
+ clog.Error(courier.ErrorResponseValueMissing("id"))
+ } else {
+ res.AddExternalID(externalID)
}
- status.SetExternalID(externalID)
- return status, nil
+
+ return nil
}
func verifyToken(tokenString string, secret string) (jwt.MapClaims, error) {
diff --git a/handlers/messagebird/handler_test.go b/handlers/messagebird/handler_test.go
index ace002b9a..b157c271f 100644
--- a/handlers/messagebird/handler_test.go
+++ b/handlers/messagebird/handler_test.go
@@ -2,7 +2,6 @@ package messagebird
import (
"net/http"
- "net/http/httptest"
"testing"
"time"
@@ -10,6 +9,7 @@ import (
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var testChannels = []courier.Channel{
@@ -187,109 +187,127 @@ func BenchmarkHandler(b *testing.B) {
RunChannelBenchmarks(b, testChannels, newHandler("MBD", "Messagebird", true), defaultReceiveTestCases)
}
-func setSmsSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- smsURL = s.URL
-}
-
-func setMmsSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- mmsURL = s.URL
-}
-
var defaultSendTestCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:188885551515",
- MockResponseBody: validResponse,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺"}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
- SendPrep: setSmsSendURL,
+ Label: "Plain Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:188885551515",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/messages": {
+ httpx.NewMockResponse(200, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺"}`,
+ }},
+ ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
},
{
- Label: "Send with text and image",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:188885551515",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: validResponse,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺","mediaUrls":["https://foo.bar/image.jpg"]}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
- SendPrep: setMmsSendURL,
+ Label: "Send with text and image",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:188885551515",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/mms": {
+ httpx.NewMockResponse(200, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺","mediaUrls":["https://foo.bar/image.jpg"]}`,
+ }},
+ ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
},
{
- Label: "Send with image only",
- MsgURN: "tel:188885551515",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: validResponse,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/image.jpg"]}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
- SendPrep: setMmsSendURL,
+ Label: "Send with image only",
+ MsgURN: "tel:188885551515",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/mms": {
+ httpx.NewMockResponse(200, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/image.jpg"]}`,
+ }},
+ ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
},
{
- Label: "Send with two images",
- MsgURN: "tel:188885551515",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg", "image/jpeg:https://foo.bar/image2.jpg"},
- MockResponseBody: validResponse,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/image.jpg","https://foo.bar/image2.jpg"]}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
- SendPrep: setMmsSendURL,
+ Label: "Send with two images",
+ MsgURN: "tel:188885551515",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg", "image/jpeg:https://foo.bar/image2.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/mms": {
+ httpx.NewMockResponse(200, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/image.jpg","https://foo.bar/image2.jpg"]}`,
+ }},
+ ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
},
{
- Label: "Send with video only",
- MsgURN: "tel:188885551515",
- MsgAttachments: []string{"video/mp4:https://foo.bar/movie.mp4"},
- MockResponseBody: validResponse,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/movie.mp4"]}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
- SendPrep: setMmsSendURL,
+ Label: "Send with video only",
+ MsgURN: "tel:188885551515",
+ MsgAttachments: []string{"video/mp4:https://foo.bar/movie.mp4"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/mms": {
+ httpx.NewMockResponse(200, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/movie.mp4"]}`,
+ }},
+ ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
},
{
- Label: "Send with pdf",
- MsgURN: "tel:188885551515",
- MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
- MockResponseBody: validResponse,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/document.pdf"]}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
- SendPrep: setMmsSendURL,
+ Label: "Send with pdf",
+ MsgURN: "tel:188885551515",
+ MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/mms": {
+ httpx.NewMockResponse(200, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","mediaUrls":["https://foo.bar/document.pdf"]}`,
+ }},
+ ExpectedExtIDs: []string{"efa6405d518d4c0c88cce11f7db775fb"},
},
{
- Label: "500 on Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:188885551515",
- MockResponseBody: validResponse,
- MockResponseStatus: 500,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺"}`,
- ExpectedMsgStatus: "E",
- SendPrep: setSmsSendURL,
+ Label: "500 on Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:188885551515",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/messages": {
+ httpx.NewMockResponse(500, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺"}`,
+ }},
+ ExpectedError: courier.ErrConnectionFailed,
},
{
- Label: "404 on Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:188885551515",
- MockResponseBody: validResponse,
- MockResponseStatus: 404,
- ExpectedHeaders: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
- ExpectedRequestBody: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺"}`,
- ExpectedMsgStatus: "E",
- SendPrep: setSmsSendURL,
+ Label: "404 on Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:188885551515",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://rest.messagebird.com/messages": {
+ httpx.NewMockResponse(404, nil, []byte(validResponse)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{"Content-Type": "application/json", "Authorization": "AccessKey authtoken"},
+ Body: `{"recipients":["188885551515"],"reference":"10","originator":"18005551212","body":"Simple Message ☺"}`,
+ }},
+ ExpectedError: courier.ErrResponseStatus,
},
}
diff --git a/handlers/messangi/handler.go b/handlers/messangi/handler.go
index 8e298f11c..a88f43d1f 100644
--- a/handlers/messangi/handler.go
+++ b/handlers/messangi/handler.go
@@ -60,32 +60,14 @@ type mtResponse struct {
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
publicKey := msg.Channel().StringConfigForKey(configPublicKey, "")
- if publicKey == "" {
- return nil, fmt.Errorf("no public_key set for MG channel")
- }
-
privateKey := msg.Channel().StringConfigForKey(configPrivateKey, "")
- if privateKey == "" {
- return nil, fmt.Errorf("no private_key set for MG channel")
- }
-
instanceId := msg.Channel().IntConfigForKey(configInstanceId, -1)
- if instanceId == -1 {
- return nil, fmt.Errorf("no instance_id set for MG channel")
- }
-
carrierId := msg.Channel().IntConfigForKey(configCarrierId, -1)
- if carrierId == -1 {
- return nil, fmt.Errorf("no carrier_id set for MG channel")
+ if publicKey == "" || privateKey == "" || instanceId == -1 || carrierId == -1 {
+ return courier.ErrChannelConfig
}
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
parts := handlers.SplitMsgByChannel(msg.Channel(), handlers.GetTextAndAttachments(msg), maxMsgLength)
for _, part := range parts {
shortcode := strings.TrimPrefix(msg.Channel().Address(), "+")
@@ -97,31 +79,28 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
req, err := http.NewRequest(http.MethodGet, fullURL, nil)
if err != nil {
- return nil, err
+ return err
}
resp, respBody, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- return status, nil
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
// parse our response as XML
response := &mtResponse{}
err = xml.Unmarshal(respBody, response)
if err != nil {
- clog.Error(courier.ErrorResponseUnparseable("XML"))
- break
+ return courier.ErrResponseUnparseable
}
// we always get 204 on success
- if response.Status == "OK" {
- status.SetStatus(courier.MsgStatusWired)
- } else {
- status.SetStatus(courier.MsgStatusFailed)
- clog.Error(courier.ErrorResponseValueUnexpected("status", "OK"))
- break
+ if response.Status != "OK" {
+ return courier.ErrResponseStatus
}
}
- return status, nil
+ return nil
}
diff --git a/handlers/messangi/handler_test.go b/handlers/messangi/handler_test.go
index a490cf5f8..6492bb0d4 100644
--- a/handlers/messangi/handler_test.go
+++ b/handlers/messangi/handler_test.go
@@ -1,12 +1,12 @@
package messangi
import (
- "net/http/httptest"
"testing"
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var testChannels = []courier.Channel{
@@ -42,57 +42,80 @@ func BenchmarkHandler(b *testing.B) {
RunChannelBenchmarks(b, testChannels, newHandler(), testCases)
}
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- sendURL = s.URL
-}
-
var defaultSendTestCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:+18765422035",
- MockResponseBody: `sendMTOKCompleted`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "W",
- SendPrep: setSendURL,
+ Label: "Plain Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:+18765422035",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://flow.messangi.me/mmc/rest/api/sendMT/*": {
+ httpx.NewMockResponse(200, nil, []byte(`sendMTOKCompleted`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Path: "/mmc/rest/api/sendMT/7/2020/2/18765422035/U2ltcGxlIE1lc3NhZ2Ug4pi6/my-public-key/f69bc6a924480d3ed82970d9679c4be90589bd3064add51c47e8bf50a211d55f",
+ }},
},
{
- Label: "Long Send",
- MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work",
- MsgURN: "tel:+18765422035",
- MockResponseBody: `sendMTOKCompleted`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "W",
- SendPrep: setSendURL,
+ Label: "Long Send",
+ MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work",
+ MsgURN: "tel:+18765422035",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://flow.messangi.me/mmc/rest/api/sendMT/*": {
+ httpx.NewMockResponse(200, nil, []byte(`sendMTOKCompleted`)),
+ httpx.NewMockResponse(200, nil, []byte(`sendMTOKCompleted`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Path: "/mmc/rest/api/sendMT/7/2020/2/18765422035/VGhpcyBpcyBhIGxvbmdlciBtZXNzYWdlIHRoYW4gMTYwIGNoYXJhY3RlcnMgYW5kIHdpbGwgY2F1c2UgdXMgdG8gc3BsaXQgaXQgaW50byB0d28gc2VwYXJhdGUgcGFydHMsIGlzbid0IHRoYXQgcmlnaHQgYnV0IGl0IGlzIGV2ZW4gbG9uZ2VyIHRoYW4gYmVmb3JlIEkgc2F5LA/my-public-key/48c658e8db8635843ac3d3e497a81cf79cc0d75b8630dae03c6e7d93a749ab90",
+ },
+ {
+ Path: "/mmc/rest/api/sendMT/7/2020/2/18765422035/SSBuZWVkIHRvIGtlZXAgYWRkaW5nIG1vcmUgdGhpbmdzIHRvIG1ha2UgaXQgd29yaw/my-public-key/ba305915a6cf56c1255071655de42b4408071460317bb5bf3419bb9f865c5078",
+ },
+ },
},
{
- Label: "Send Attachment",
- MsgText: "My pic!",
- MsgURN: "tel:+18765422035",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: `sendMTOKCompleted`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "W",
- SendPrep: setSendURL,
+ Label: "Send Attachment",
+ MsgText: "My pic!",
+ MsgURN: "tel:+18765422035",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://flow.messangi.me/mmc/rest/api/sendMT/*": {
+ httpx.NewMockResponse(200, nil, []byte(`sendMTOKCompleted`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Path: "/mmc/rest/api/sendMT/7/2020/2/18765422035/TXkgcGljIQpodHRwczovL2Zvby5iYXIvaW1hZ2UuanBn/my-public-key/4babdf316c0b5c7b6b40855329b421b1da1b8e63690d59eb5c231049dc4067fd",
+ }},
},
{
- Label: "Invalid Parameters",
- MsgText: "Invalid Parameters",
- MsgURN: "tel:+18765422035",
- MockResponseBody: "",
- MockResponseStatus: 404,
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
+ Label: "Invalid Parameters",
+ MsgText: "Invalid Parameters",
+ MsgURN: "tel:+18765422035",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://flow.messangi.me/mmc/rest/api/sendMT/*": {
+ httpx.NewMockResponse(404, nil, []byte(``)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Path: "/mmc/rest/api/sendMT/7/2020/2/18765422035/SW52YWxpZCBQYXJhbWV0ZXJz/my-public-key/f3d2ea825cf61226925dee2db3c14b7fc00f3183f11809d2183d1e2dbd230df6",
+ }},
+ ExpectedError: courier.ErrResponseStatus,
},
{
- Label: "Error Response",
- MsgText: "Error Response",
- MsgURN: "tel:+18765422035",
- MockResponseBody: `sendMTERRORCompleted`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "F",
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueUnexpected("status", "OK")},
- SendPrep: setSendURL,
+ Label: "Error Response",
+ MsgText: "Error Response",
+ MsgURN: "tel:+18765422035",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://flow.messangi.me/mmc/rest/api/sendMT/*": {
+ httpx.NewMockResponse(200, nil, []byte(`sendMTERRORCompleted`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Path: "/mmc/rest/api/sendMT/7/2020/2/18765422035/RXJyb3IgUmVzcG9uc2U/my-public-key/27f4c67fa00848ea6029cc0b1797aae6d05e2970ecb6e44ca486b463b933e61a",
+ }},
+ ExpectedError: courier.ErrResponseStatus,
},
}
diff --git a/handlers/meta/facebook_test.go b/handlers/meta/facebook_test.go
index e2ee4c5ed..46e5ecdec 100644
--- a/handlers/meta/facebook_test.go
+++ b/handlers/meta/facebook_test.go
@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
+ "net/url"
"strings"
"testing"
"time"
@@ -12,6 +13,7 @@ import (
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
"github.com/nyaruka/gocommon/urns"
"github.com/stretchr/testify/assert"
)
@@ -354,49 +356,55 @@ func TestFacebookVerify(t *testing.T) {
})
}
-// setSendURL takes care of setting the send_url to our test server host
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- sendURL = s.URL
- graphURL = s.URL
-}
-
var facebookOutgoingTests = []OutgoingTestCase{
{
- Label: "Text only chat message",
- MsgText: "Simple Message",
- MsgURN: "facebook:12345",
- MsgOrigin: courier.MsgOriginChat,
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"HUMAN_AGENT","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
- },
- {
- Label: "Text only broadcast message",
- MsgText: "Simple Message",
- MsgURN: "facebook:12345",
- MsgOrigin: courier.MsgOriginBroadcast,
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
- },
- {
- Label: "Text only broadcast with opt-in auth token",
- MsgText: "Simple Message",
- MsgURN: "facebook:12345",
- MsgURNAuth: "345678",
- MsgOrigin: courier.MsgOriginBroadcast,
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"notification_messages_token":"345678"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Text only chat message",
+ MsgText: "Simple Message",
+ MsgURN: "facebook:12345",
+ MsgOrigin: courier.MsgOriginChat,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"HUMAN_AGENT","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Text only broadcast message",
+ MsgText: "Simple Message",
+ MsgURN: "facebook:12345",
+ MsgOrigin: courier.MsgOriginBroadcast,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Text only broadcast with opt-in auth token",
+ MsgText: "Simple Message",
+ MsgURN: "facebook:12345",
+ MsgURNAuth: "345678",
+ MsgOrigin: courier.MsgOriginBroadcast,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"notification_messages_token":"345678"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
Label: "Text only flow response",
@@ -404,12 +412,16 @@ var facebookOutgoingTests = []OutgoingTestCase{
MsgURN: "facebook:12345",
MsgOrigin: courier.MsgOriginFlow,
MsgResponseToExternalID: "23526",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"RESPONSE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"RESPONSE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
Label: "Text only flow response using referal URN",
@@ -417,126 +429,189 @@ var facebookOutgoingTests = []OutgoingTestCase{
MsgURN: "facebook:ref:67890",
MsgOrigin: courier.MsgOriginFlow,
MsgResponseToExternalID: "23526",
- MockResponseBody: `{"message_id": "mid.133", "recipient_id": "12345"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"RESPONSE","recipient":{"user_ref":"67890"},"message":{"text":"Simple Message"}}`,
- ExpectedContactURNs: map[string]bool{"facebook:12345": true, "ext:67890": true, "facebook:ref:67890": false},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
- },
- {
- Label: "Quick replies on a broadcast message",
- MsgText: "Are you happy?",
- MsgURN: "facebook:12345",
- MsgOrigin: courier.MsgOriginBroadcast,
- MsgQuickReplies: []string{"Yes", "No"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Are you happy?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
- },
- {
- Label: "Message that exceeds max text length",
- MsgText: "This is a long message which spans more than one part, what will actually be sent in the end if we exceed the max length?",
- MsgURN: "facebook:12345",
- MsgQuickReplies: []string{"Yes", "No"},
- MsgTopic: "account",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"ACCOUNT_UPDATE","recipient":{"id":"12345"},"message":{"text":"we exceed the max length?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
- ExpectedMsgStatus: "W",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133", "recipient_id": "12345"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"RESPONSE","recipient":{"user_ref":"67890"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedContactURNs: map[string]bool{"facebook:12345": true, "ext:67890": true, "facebook:ref:67890": false},
ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
},
{
- Label: "Image attachment",
- MsgURN: "facebook:12345",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"image","payload":{"url":"https://foo.bar/image.jpg","is_reusable":true}}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
- },
- {
- Label: "Text, image attachment, quick replies and explicit message topic",
- MsgText: "This is some text.",
- MsgURN: "facebook:12345",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MsgQuickReplies: []string{"Yes", "No"},
- MsgTopic: "event",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"CONFIRMED_EVENT_UPDATE","recipient":{"id":"12345"},"message":{"text":"This is some text.","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Quick replies on a broadcast message",
+ MsgText: "Are you happy?",
+ MsgURN: "facebook:12345",
+ MsgOrigin: courier.MsgOriginBroadcast,
+ MsgQuickReplies: []string{"Yes", "No"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Are you happy?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Quick replies on a broadcast message",
+ MsgText: "Are you happy?",
+ MsgURN: "facebook:12345",
+ MsgOrigin: courier.MsgOriginBroadcast,
+ MsgQuickReplies: []string{"Yes", "No"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Are you happy?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Message that exceeds max text length",
+ MsgText: "This is a long message which spans more than one part, what will actually be sent in the end if we exceed the max length?",
+ MsgURN: "facebook:12345",
+ MsgQuickReplies: []string{"Yes", "No"},
+ MsgTopic: "account",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"ACCOUNT_UPDATE","recipient":{"id":"12345"},"message":{"text":"This is a long message which spans more than one part, what will actually be sent in the end if"}}`,
+ },
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"ACCOUNT_UPDATE","recipient":{"id":"12345"},"message":{"text":"we exceed the max length?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ },
+ },
+ ExpectedExtIDs: []string{"mid.133", "mid.133"},
},
{
- Label: "Document attachment",
- MsgURN: "facebook:12345",
- MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"file","payload":{"url":"https://foo.bar/document.pdf","is_reusable":true}}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Image attachment",
+ MsgURN: "facebook:12345",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"image","payload":{"url":"https://foo.bar/image.jpg","is_reusable":true}}}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Text, image attachment, quick replies and explicit message topic",
+ MsgText: "This is some text.",
+ MsgURN: "facebook:12345",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MsgQuickReplies: []string{"Yes", "No"},
+ MsgTopic: "event",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"CONFIRMED_EVENT_UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"image","payload":{"url":"https://foo.bar/image.jpg","is_reusable":true}}}}`,
+ },
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"CONFIRMED_EVENT_UPDATE","recipient":{"id":"12345"},"message":{"text":"This is some text.","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ },
+ },
+ ExpectedExtIDs: []string{"mid.133", "mid.133"},
},
{
- Label: "Opt-in request",
- MsgURN: "facebook:12345",
- MsgOptIn: &courier.OptInReference{ID: 3456, Name: "Joke Of The Day"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"template","payload":{"template_type":"notification_messages","title":"Joke Of The Day","payload":"3456"}}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
- },
- {
- Label: "Response doesn't contain message id",
- MsgText: "ID Error",
- MsgURN: "facebook:12345",
- MockResponseBody: `{ "is_error": true }`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "E",
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("message_id")},
- SendPrep: setSendURL,
- },
- {
- Label: "Response status code is non-200",
- MsgText: "Error",
- MsgURN: "facebook:12345",
- MockResponseBody: `{ "is_error": true }`,
- MockResponseStatus: 403,
- ExpectedMsgStatus: "E",
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("message_id")},
- SendPrep: setSendURL,
- },
- {
- Label: "Response is invalid JSON",
- MsgText: "Error",
- MsgURN: "facebook:12345",
- MockResponseBody: `bad json`,
- MockResponseStatus: 200,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")},
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
- },
- {
- Label: "Response is channel specific error",
- MsgText: "Error",
- MsgURN: "facebook:12345",
- MockResponseBody: `{ "error": {"message": "The image size is too large.","code": 36000 }}`,
- MockResponseStatus: 400,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("36000", "The image size is too large.")},
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
+ Label: "Document attachment",
+ MsgURN: "facebook:12345",
+ MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"file","payload":{"url":"https://foo.bar/document.pdf","is_reusable":true}}}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Opt-in request",
+ MsgURN: "facebook:12345",
+ MsgOptIn: &courier.OptInReference{ID: 3456, Name: "Joke Of The Day"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"template","payload":{"template_type":"notification_messages","title":"Joke Of The Day","payload":"3456"}}}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
+ },
+ {
+ Label: "Response doesn't contain message id",
+ MsgText: "ID Error",
+ MsgURN: "facebook:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "is_error": true }`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseUnexpected,
+ },
+ {
+ Label: "Response status code is non-200",
+ MsgText: "Error",
+ MsgURN: "facebook:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(403, nil, []byte(`{ "is_error": true }`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseStatus,
+ },
+ {
+ Label: "Response is invalid JSON",
+ MsgText: "Error",
+ MsgURN: "facebook:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`bad json`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseUnparseable,
+ },
+ {
+ Label: "Response is channel specific error",
+ MsgText: "Error",
+ MsgURN: "facebook:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "error": {"message": "The image size is too large.","code": 36000 }}`)),
+ },
+ },
+ ExpectedError: courier.ErrFailedWithReason("36000", "The image size is too large."),
},
}
diff --git a/handlers/meta/handlers.go b/handlers/meta/handlers.go
index 8a556833e..903e4cc03 100644
--- a/handlers/meta/handlers.go
+++ b/handlers/meta/handlers.go
@@ -622,25 +622,20 @@ func (h *handler) processFacebookInstagramPayload(ctx context.Context, channel c
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
if msg.Channel().ChannelType() == "FBA" || msg.Channel().ChannelType() == "IG" {
- return h.sendFacebookInstagramMsg(ctx, msg, clog)
+ return h.sendFacebookInstagramMsg(ctx, msg, res, clog)
} else if msg.Channel().ChannelType() == "WAC" {
- return h.sendWhatsAppMsg(ctx, msg, clog)
+ return h.sendWhatsAppMsg(ctx, msg, res, clog)
}
- return nil, fmt.Errorf("unssuported channel type")
+ return fmt.Errorf("unssuported channel type")
}
-func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
+func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
// can't do anything without an access token
accessToken := msg.Channel().StringConfigForKey(courier.ConfigAuthToken, "")
if accessToken == "" {
- return nil, fmt.Errorf("missing access token")
+ return courier.ErrChannelConfig
}
isHuman := msg.Origin() == courier.MsgOriginChat || msg.Origin() == courier.MsgOriginTicket
@@ -677,8 +672,6 @@ func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgO
query.Set("access_token", accessToken)
msgURL.RawQuery = query.Encode()
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
-
// Send each text segment and attachment separately. We send attachments first as otherwise quick replies get
// attached to attachment segments and are hidden when images load.
for _, part := range handlers.SplitMsg(msg, handlers.SplitOptions{MaxTextLen: maxMsgLength}) {
@@ -720,80 +713,75 @@ func (h *handler) sendFacebookInstagramMsg(ctx context.Context, msg courier.MsgO
req, err := http.NewRequest(http.MethodPost, msgURL.String(), bytes.NewReader(jsonBody))
if err != nil {
- return nil, err
+ return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
- _, respBody, _ := h.RequestHTTP(req, clog)
+ resp, respBody, err := h.RequestHTTP(req, clog)
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
+ }
+
respPayload := &messenger.SendResponse{}
err = json.Unmarshal(respBody, respPayload)
if err != nil {
- clog.Error(courier.ErrorResponseUnparseable("JSON"))
- return status, nil
+ return courier.ErrResponseUnparseable
}
if respPayload.Error.Code != 0 {
- clog.Error(courier.ErrorExternal(strconv.Itoa(respPayload.Error.Code), respPayload.Error.Message))
- return status, nil
+ return courier.ErrFailedWithReason(strconv.Itoa(respPayload.Error.Code), respPayload.Error.Message)
}
if respPayload.ExternalID == "" {
- clog.Error(courier.ErrorResponseValueMissing("message_id"))
- return status, nil
+ return courier.ErrResponseUnexpected
}
- // if this is our first message, record the external id
- if part.IsFirst {
- status.SetExternalID(respPayload.ExternalID)
- if msg.URN().IsFacebookRef() {
- recipientID := respPayload.RecipientID
- if recipientID == "" {
- clog.Error(courier.ErrorResponseValueMissing("recipient_id"))
- return status, nil
- }
-
- referralID := msg.URN().FacebookRef()
+ res.AddExternalID(respPayload.ExternalID)
+ if msg.URN().IsFacebookRef() {
+ recipientID := respPayload.RecipientID
+ if recipientID == "" {
+ return courier.ErrResponseUnexpected
+ }
- realIDURN, err := urns.NewFacebookURN(recipientID)
- if err != nil {
- clog.RawError(errors.Errorf("unable to make facebook urn from %s", recipientID))
- }
+ referralID := msg.URN().FacebookRef()
- contact, err := h.Backend().GetContact(ctx, msg.Channel(), msg.URN(), nil, "", clog)
- if err != nil {
- clog.RawError(errors.Errorf("unable to get contact for %s", msg.URN().String()))
- }
- realURN, err := h.Backend().AddURNtoContact(ctx, msg.Channel(), contact, realIDURN, nil)
- if err != nil {
- clog.RawError(errors.Errorf("unable to add real facebook URN %s to contact with uuid %s", realURN.String(), contact.UUID()))
- }
- referralIDExtURN, err := urns.NewURNFromParts(urns.ExternalScheme, referralID, "", "")
- if err != nil {
- clog.RawError(errors.Errorf("unable to make ext urn from %s", referralID))
- }
- extURN, err := h.Backend().AddURNtoContact(ctx, msg.Channel(), contact, referralIDExtURN, nil)
- if err != nil {
- clog.RawError(errors.Errorf("unable to add URN %s to contact with uuid %s", extURN.String(), contact.UUID()))
- }
+ realIDURN, err := urns.NewFacebookURN(recipientID)
+ if err != nil {
+ clog.RawError(errors.Errorf("unable to make facebook urn from %s", recipientID))
+ }
- referralFacebookURN, err := h.Backend().RemoveURNfromContact(ctx, msg.Channel(), contact, msg.URN())
- if err != nil {
- clog.RawError(errors.Errorf("unable to remove referral facebook URN %s from contact with uuid %s", referralFacebookURN.String(), contact.UUID()))
- }
+ contact, err := h.Backend().GetContact(ctx, msg.Channel(), msg.URN(), nil, "", clog)
+ if err != nil {
+ clog.RawError(errors.Errorf("unable to get contact for %s", msg.URN().String()))
+ }
+ realURN, err := h.Backend().AddURNtoContact(ctx, msg.Channel(), contact, realIDURN, nil)
+ if err != nil {
+ clog.RawError(errors.Errorf("unable to add real facebook URN %s to contact with uuid %s", realURN.String(), contact.UUID()))
+ }
+ referralIDExtURN, err := urns.NewURNFromParts(urns.ExternalScheme, referralID, "", "")
+ if err != nil {
+ clog.RawError(errors.Errorf("unable to make ext urn from %s", referralID))
+ }
+ extURN, err := h.Backend().AddURNtoContact(ctx, msg.Channel(), contact, referralIDExtURN, nil)
+ if err != nil {
+ clog.RawError(errors.Errorf("unable to add URN %s to contact with uuid %s", extURN.String(), contact.UUID()))
+ }
+ referralFacebookURN, err := h.Backend().RemoveURNfromContact(ctx, msg.Channel(), contact, msg.URN())
+ if err != nil {
+ clog.RawError(errors.Errorf("unable to remove referral facebook URN %s from contact with uuid %s", referralFacebookURN.String(), contact.UUID()))
}
}
-
- // this was wired successfully
- status.SetStatus(courier.MsgStatusWired)
}
- return status, nil
+ return nil
}
-func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
+func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
// can't do anything without an access token
accessToken := h.Server().Config().WhatsappAdminSystemUserToken
@@ -801,8 +789,6 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
path, _ := url.Parse(fmt.Sprintf("/%s/messages", msg.Channel().Address()))
wacPhoneURL := base.ResolveReference(path)
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
-
hasCaption := false
msgParts := make([]string, 0)
@@ -821,7 +807,7 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
// do we have a template?
templating, err := whatsapp.GetTemplating(msg)
if err != nil {
- return nil, errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID())
+ return errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID())
}
if templating != nil {
payload.Type = "template"
@@ -840,6 +826,12 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
} else {
if len(qrs) > 0 {
payload.Type = "interactive"
+ if len(qrs) > 10 {
+ clog.Error(courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies"))
+ // limit to the first 10
+ qrs = qrs[:10]
+ }
+
// We can use buttons
if len(qrs) <= 3 {
interactive := whatsapp.Interactive{Type: "button", Body: struct {
@@ -884,8 +876,6 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
}}
payload.Interactive = &interactive
- } else {
- return nil, fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies")
}
} else {
// this is still a msg part
@@ -933,6 +923,12 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
} else {
if len(qrs) > 0 {
payload.Type = "interactive"
+ if len(qrs) > 10 {
+ clog.Error(courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies"))
+ // limit to the first 10
+ qrs = qrs[:10]
+ }
+
// We can use buttons
if len(qrs) <= 3 {
interactive := whatsapp.Interactive{Type: "button", Body: struct {
@@ -971,7 +967,7 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
} else if attType == "document" {
filename, err := utils.BasePathForURL(attURL)
if err != nil {
- return nil, err
+ return err
}
document := whatsapp.Media{
Link: attURL,
@@ -985,14 +981,11 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
Document *whatsapp.Media "json:\"document,omitempty\""
}{Type: "document", Document: &document}
} else if attType == "audio" {
- var zeroIndex bool
- if i == 0 {
- zeroIndex = true
- }
+
payloadAudio = whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path(), Type: "audio", Audio: &whatsapp.Media{Link: attURL}}
- err := h.requestWAC(payloadAudio, accessToken, status, wacPhoneURL, zeroIndex, clog)
+ err := h.requestWAC(payloadAudio, accessToken, res, wacPhoneURL, clog)
if err != nil {
- return status, nil
+ return err
}
} else {
interactive.Type = "button"
@@ -1039,8 +1032,6 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
}}
payload.Interactive = &interactive
- } else {
- return nil, fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies")
}
} else {
// this is still a msg part
@@ -1054,24 +1045,19 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, clog
}
}
- var zeroIndex bool
- if i == 0 {
- zeroIndex = true
- }
-
- err := h.requestWAC(payload, accessToken, status, wacPhoneURL, zeroIndex, clog)
+ err := h.requestWAC(payload, accessToken, res, wacPhoneURL, clog)
if err != nil {
- return status, err
+ return err
}
if hasCaption {
break
}
}
- return status, nil
+ return nil
}
-func (h *handler) requestWAC(payload whatsapp.SendRequest, accessToken string, status courier.StatusUpdate, wacPhoneURL *url.URL, zeroIndex bool, clog *courier.ChannelLog) error {
+func (h *handler) requestWAC(payload whatsapp.SendRequest, accessToken string, res *courier.SendResult, wacPhoneURL *url.URL, clog *courier.ChannelLog) error {
jsonBody := jsonx.MustMarshal(payload)
req, err := http.NewRequest(http.MethodPost, wacPhoneURL.String(), bytes.NewReader(jsonBody))
@@ -1087,21 +1073,17 @@ func (h *handler) requestWAC(payload whatsapp.SendRequest, accessToken string, s
respPayload := &whatsapp.SendResponse{}
err = json.Unmarshal(respBody, respPayload)
if err != nil {
- clog.Error(courier.ErrorResponseUnparseable("JSON"))
- return nil
+ return courier.ErrResponseUnparseable
}
if respPayload.Error.Code != 0 {
- clog.Error(courier.ErrorExternal(strconv.Itoa(respPayload.Error.Code), respPayload.Error.Message))
- return nil
+ return courier.ErrFailedWithReason(strconv.Itoa(respPayload.Error.Code), respPayload.Error.Message)
}
externalID := respPayload.Messages[0].ID
- if zeroIndex && externalID != "" {
- status.SetExternalID(externalID)
+ if externalID != "" {
+ res.AddExternalID(externalID)
}
- // this was wired successfully
- status.SetStatus(courier.MsgStatusWired)
return nil
}
diff --git a/handlers/meta/instagram_test.go b/handlers/meta/instagram_test.go
index 77a5afcd4..2d791158a 100644
--- a/handlers/meta/instagram_test.go
+++ b/handlers/meta/instagram_test.go
@@ -4,6 +4,7 @@ import (
"context"
"net/http"
"net/http/httptest"
+ "net/url"
"strings"
"testing"
"time"
@@ -11,6 +12,7 @@ import (
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
"github.com/nyaruka/gocommon/urns"
"github.com/stretchr/testify/assert"
)
@@ -183,28 +185,36 @@ func TestInstagramIncoming(t *testing.T) {
var instagramOutgoingTests = []OutgoingTestCase{
{
- Label: "Text only chat message",
- MsgText: "Simple Message",
- MsgURN: "instagram:12345",
- MsgOrigin: courier.MsgOriginChat,
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"HUMAN_AGENT","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Text only chat message",
+ MsgText: "Simple Message",
+ MsgURN: "instagram:12345",
+ MsgOrigin: courier.MsgOriginChat,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"HUMAN_AGENT","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
- Label: "Text only broadcast message",
- MsgText: "Simple Message",
- MsgURN: "instagram:12345",
- MsgOrigin: courier.MsgOriginBroadcast,
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Text only broadcast message",
+ MsgText: "Simple Message",
+ MsgURN: "instagram:12345",
+ MsgOrigin: courier.MsgOriginBroadcast,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
Label: "Text only flow response",
@@ -212,126 +222,172 @@ var instagramOutgoingTests = []OutgoingTestCase{
MsgURN: "instagram:12345",
MsgOrigin: courier.MsgOriginFlow,
MsgResponseToExternalID: "23526",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"RESPONSE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"RESPONSE","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
- Label: "Quick replies on a broadcast message",
- MsgText: "Are you happy?",
- MsgURN: "instagram:12345",
- MsgOrigin: courier.MsgOriginBroadcast,
- MsgQuickReplies: []string{"Yes", "No"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Are you happy?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Quick replies on a broadcast message",
+ MsgText: "Are you happy?",
+ MsgURN: "instagram:12345",
+ MsgOrigin: courier.MsgOriginBroadcast,
+ MsgQuickReplies: []string{"Yes", "No"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"text":"Are you happy?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
- Label: "Message that exceeds max text length",
- MsgText: "This is a long message which spans more than one part, what will actually be sent in the end if we exceed the max length?",
- MsgURN: "instagram:12345",
- MsgQuickReplies: []string{"Yes", "No"},
- MsgTopic: "account",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"ACCOUNT_UPDATE","recipient":{"id":"12345"},"message":{"text":"we exceed the max length?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Message that exceeds max text length",
+ MsgText: "This is a long message which spans more than one part, what will actually be sent in the end if we exceed the max length?",
+ MsgURN: "instagram:12345",
+ MsgQuickReplies: []string{"Yes", "No"},
+ MsgTopic: "account",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"ACCOUNT_UPDATE","recipient":{"id":"12345"},"message":{"text":"This is a long message which spans more than one part, what will actually be sent in the end if"}}`,
+ },
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"ACCOUNT_UPDATE","recipient":{"id":"12345"},"message":{"text":"we exceed the max length?","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ },
+ },
+ ExpectedExtIDs: []string{"mid.133", "mid.133"},
},
{
- Label: "Image attachment",
- MsgURN: "instagram:12345",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"image","payload":{"url":"https://foo.bar/image.jpg","is_reusable":true}}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Image attachment",
+ MsgURN: "instagram:12345",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"image","payload":{"url":"https://foo.bar/image.jpg","is_reusable":true}}}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
- Label: "Text, image attachment, quick replies and explicit message topic",
- MsgText: "This is some text.",
- MsgURN: "instagram:12345",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MsgQuickReplies: []string{"Yes", "No"},
- MsgTopic: "event",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"CONFIRMED_EVENT_UPDATE","recipient":{"id":"12345"},"message":{"text":"This is some text.","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Text, image attachment, quick replies and explicit message topic",
+ MsgText: "This is some text.",
+ MsgURN: "instagram:12345",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MsgQuickReplies: []string{"Yes", "No"},
+ MsgTopic: "event",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"CONFIRMED_EVENT_UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"image","payload":{"url":"https://foo.bar/image.jpg","is_reusable":true}}}}`,
+ },
+ {
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"CONFIRMED_EVENT_UPDATE","recipient":{"id":"12345"},"message":{"text":"This is some text.","quick_replies":[{"title":"Yes","payload":"Yes","content_type":"text"},{"title":"No","payload":"No","content_type":"text"}]}}`,
+ },
+ },
+ ExpectedExtIDs: []string{"mid.133", "mid.133"},
},
{
- Label: "Explicit human agent tag",
- MsgText: "Simple Message",
- MsgURN: "instagram:12345",
- MsgTopic: "agent",
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"MESSAGE_TAG","tag":"HUMAN_AGENT","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Explicit human agent tag",
+ MsgText: "Simple Message",
+ MsgURN: "instagram:12345",
+ MsgTopic: "agent",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"MESSAGE_TAG","tag":"HUMAN_AGENT","recipient":{"id":"12345"},"message":{"text":"Simple Message"}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
- Label: "Document attachment",
- MsgURN: "instagram:12345",
- MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
- MockResponseBody: `{"message_id": "mid.133"}`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"file","payload":{"url":"https://foo.bar/document.pdf","is_reusable":true}}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"mid.133"},
- SendPrep: setSendURL,
+ Label: "Document attachment",
+ MsgURN: "instagram:12345",
+ MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"message_id": "mid.133"}`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Params: url.Values{"access_token": {"a123"}},
+ Body: `{"messaging_type":"UPDATE","recipient":{"id":"12345"},"message":{"attachment":{"type":"file","payload":{"url":"https://foo.bar/document.pdf","is_reusable":true}}}}`,
+ }},
+ ExpectedExtIDs: []string{"mid.133"},
},
{
- Label: "Response doesn't contain message id",
- MsgText: "ID Error",
- MsgURN: "instagram:12345",
- MockResponseBody: `{ "is_error": true }`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "E",
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("message_id")},
- SendPrep: setSendURL,
+ Label: "Response doesn't contain message id",
+ MsgText: "ID Error",
+ MsgURN: "instagram:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "is_error": true }`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseUnexpected,
},
{
- Label: "Response status code is non-200",
- MsgText: "Error",
- MsgURN: "instagram:12345",
- MockResponseBody: `{ "is_error": true }`,
- MockResponseStatus: 403,
- ExpectedMsgStatus: "E",
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("message_id")},
- SendPrep: setSendURL,
+ Label: "Response status code is non-200",
+ MsgText: "Error",
+ MsgURN: "instagram:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(403, nil, []byte(`{ "is_error": true }`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseStatus,
},
{
- Label: "Response is invalid JSON",
- MsgText: "Error",
- MsgURN: "instagram:12345",
- MockResponseBody: `bad json`,
- MockResponseStatus: 200,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")},
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
+ Label: "Response is invalid JSON",
+ MsgText: "Error",
+ MsgURN: "instagram:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`bad json`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseUnparseable,
},
{
- Label: "Response is channel specific error",
- MsgText: "Error",
- MsgURN: "instagram:12345",
- MockResponseBody: `{ "error": {"message": "The image size is too large.","code": 36000 }}`,
- MockResponseStatus: 400,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("36000", "The image size is too large.")},
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
+ Label: "Response is channel specific error",
+ MsgText: "Error",
+ MsgURN: "instagram:12345",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://graph.facebook.com/v17.0/me/messages*": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "error": {"message": "The image size is too large.","code": 36000 }}`)),
+ },
+ },
+ ExpectedError: courier.ErrFailedWithReason("36000", "The image size is too large."),
},
}
diff --git a/handlers/meta/whataspp_test.go b/handlers/meta/whataspp_test.go
index 0d2a7cb17..ddc85566e 100644
--- a/handlers/meta/whataspp_test.go
+++ b/handlers/meta/whataspp_test.go
@@ -285,36 +285,38 @@ func TestWhatsAppIncoming(t *testing.T) {
var whatsappOutgoingTests = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message",
- MsgURN: "whatsapp:250788123123",
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Plain Send",
+ MsgText: "Simple Message",
+ MsgURN: "whatsapp:250788123123",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Simple Message","preview_url":false}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
- Label: "Unicode Send",
- MsgText: "☺",
- MsgURN: "whatsapp:250788123123",
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Unicode Send",
+ MsgText: "☺",
+ MsgURN: "whatsapp:250788123123",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"☺","preview_url":false}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
Label: "Audio Send",
@@ -331,177 +333,212 @@ var whatsappOutgoingTests = []OutgoingTestCase{
{Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"audio","audio":{"link":"https://foo.bar/audio.mp3"}}`},
{Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"audio caption","preview_url":false}}`},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8", "157b5e14568e8"},
},
{
- Label: "Document Send",
- MsgText: "document caption",
- MsgURN: "whatsapp:250788123123",
- MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Document Send",
+ MsgText: "document caption",
+ MsgURN: "whatsapp:250788123123",
+ MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"document","document":{"link":"https://foo.bar/document.pdf","caption":"document caption","filename":"document.pdf"}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
- Label: "Image Send",
- MsgText: "image caption",
- MsgURN: "whatsapp:250788123123",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Image Send",
+ MsgText: "image caption",
+ MsgURN: "whatsapp:250788123123",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"image","image":{"link":"https://foo.bar/image.jpg","caption":"image caption"}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
- Label: "Video Send",
- MsgText: "video caption",
- MsgURN: "whatsapp:250788123123",
- MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Video Send",
+ MsgText: "video caption",
+ MsgURN: "whatsapp:250788123123",
+ MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"video","video":{"link":"https://foo.bar/video.mp4","caption":"video caption"}}`,
},
},
- ExpectedMsgStatus: "W",
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Template Send",
+ MsgText: "templated message",
+ MsgURN: "whatsapp:250788123123",
+ MsgLocale: "eng",
+ MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "params": {"body": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}, "language": "en_US"}}`),
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"body","sub_type":"","index":"","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`,
+ }},
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Template Send, no variables",
+ MsgText: "templated message",
+ MsgURN: "whatsapp:250788123123",
+ MsgLocale: "eng",
+ MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "params": {}, "variables": [], "language": "en_US"}}`),
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"}}}`,
+ }},
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Interactive Button Message Send",
+ MsgText: "Interactive Button Msg",
+ MsgURN: "whatsapp:250788123123",
+ MsgQuickReplies: []string{"BUTTON1"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`,
+ }},
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Interactive List Message Send",
+ MsgText: "Interactive List Msg",
+ MsgURN: "whatsapp:250788123123",
+ MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`,
+ }},
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Interactive List Message Send, more than 10 QRs",
+ MsgText: "Interactive List Msg",
+ MsgURN: "whatsapp:250788123123",
+ MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4", "ROW5", "ROW6", "ROW7", "ROW8", "ROW9", "ROW10", "ROW11", "ROW12"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"},{"id":"4","title":"ROW5"},{"id":"5","title":"ROW6"},{"id":"6","title":"ROW7"},{"id":"7","title":"ROW8"},{"id":"8","title":"ROW9"},{"id":"9","title":"ROW10"}]}]}}}`,
+ }},
ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
- },
- {
- Label: "Template Send",
- MsgText: "templated message",
- MsgURN: "whatsapp:250788123123",
- MsgLocale: "eng",
- MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "params": {"body": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}, "language": "en_US"}}`),
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"body","sub_type":"","index":"","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`,
- SendPrep: setSendURL,
- },
- {
- Label: "Template Send, no variables",
- MsgText: "templated message",
- MsgURN: "whatsapp:250788123123",
- MsgLocale: "eng",
- MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "params": {}, "variables": [], "language": "en_US"}}`),
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 200,
- ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"}}}`,
- SendPrep: setSendURL,
- },
- {
- Label: "Interactive Button Message Send",
- MsgText: "Interactive Button Msg",
- MsgURN: "whatsapp:250788123123",
- MsgQuickReplies: []string{"BUTTON1"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
- ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
- },
- {
- Label: "Interactive List Message Send",
- MsgText: "Interactive List Msg",
- MsgURN: "whatsapp:250788123123",
- MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
- ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
- },
- {
- Label: "Interactive List Message Send In Spanish",
- MsgText: "Hola",
- MsgURN: "whatsapp:250788123123",
- MsgLocale: "spa",
- MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
- ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Hola"},"action":{"button":"Menú","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`,
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
- },
- {
- Label: "Interactive Button Message Send with image attachment",
- MsgText: "Interactive Button Msg",
- MsgURN: "whatsapp:250788123123",
- MsgQuickReplies: []string{"BUTTON1"},
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ ExpectedLogErrors: []*courier.ChannelError{courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies")},
+ },
+ {
+ Label: "Interactive List Message Send In Spanish",
+ MsgText: "Hola",
+ MsgURN: "whatsapp:250788123123",
+ MsgLocale: "spa",
+ MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Hola"},"action":{"button":"Menú","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`,
+ }},
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Interactive Button Message Send with image attachment",
+ MsgText: "Interactive Button Msg",
+ MsgURN: "whatsapp:250788123123",
+ MsgQuickReplies: []string{"BUTTON1"},
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","header":{"type":"image","image":{"link":"https://foo.bar/image.jpg"}},"body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
- Label: "Interactive Button Message Send with video attachment",
- MsgText: "Interactive Button Msg",
- MsgURN: "whatsapp:250788123123",
- MsgQuickReplies: []string{"BUTTON1"},
- MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Interactive Button Message Send with video attachment",
+ MsgText: "Interactive Button Msg",
+ MsgURN: "whatsapp:250788123123",
+ MsgQuickReplies: []string{"BUTTON1"},
+ MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","header":{"type":"video","video":{"link":"https://foo.bar/video.mp4"}},"body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
- Label: "Interactive Button Message Send with document attachment",
- MsgText: "Interactive Button Msg",
- MsgURN: "whatsapp:250788123123",
- MsgQuickReplies: []string{"BUTTON1"},
- MsgAttachments: []string{"document/pdf:https://foo.bar/document.pdf"},
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Interactive Button Message Send with document attachment",
+ MsgText: "Interactive Button Msg",
+ MsgURN: "whatsapp:250788123123",
+ MsgQuickReplies: []string{"BUTTON1"},
+ MsgAttachments: []string{"document/pdf:https://foo.bar/document.pdf"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","header":{"type":"document","document":{"link":"https://foo.bar/document.pdf","filename":"document.pdf"}},"body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
},
{
Label: "Interactive Button Message Send with audio attachment",
@@ -519,9 +556,7 @@ var whatsappOutgoingTests = []OutgoingTestCase{
{Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"audio","audio":{"link":"https://foo.bar/audio.mp3"}}`},
{Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"ROW1"}},{"type":"reply","reply":{"id":"1","title":"ROW2"}},{"type":"reply","reply":{"id":"2","title":"ROW3"}}]}}}`},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8", "157b5e14568e8"},
},
{
Label: "Interactive List Message Send with attachment",
@@ -539,46 +574,46 @@ var whatsappOutgoingTests = []OutgoingTestCase{
{Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"image","image":{"link":"https://foo.bar/image.jpg"}}`},
{Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`},
},
-
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8", "157b5e14568e8"},
},
{
- Label: "Link Sending",
- MsgText: "Link Sending https://link.com",
- MsgURN: "whatsapp:250788123123",
- MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`,
- MockResponseStatus: 201,
+ Label: "Link Sending",
+ MsgText: "Link Sending https://link.com",
+ MsgURN: "whatsapp:250788123123",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{
Path: "/12345_ID/messages",
Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Link Sending https://link.com","preview_url":true}}`,
},
},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"157b5e14568e8"},
- SendPrep: setSendURL,
- },
- {
- Label: "Error Bad JSON",
- MsgText: "Error",
- MsgURN: "whatsapp:250788123123",
- MockResponseBody: `bad json`,
- MockResponseStatus: 403,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")},
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
- },
- {
- Label: "Error",
- MsgText: "Error",
- MsgURN: "whatsapp:250788123123",
- MockResponseBody: `{ "error": {"message": "(#130429) Rate limit hit","code": 130429 }}`,
- MockResponseStatus: 403,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("130429", "(#130429) Rate limit hit")},
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"157b5e14568e8"},
+ },
+ {
+ Label: "Error Bad JSON",
+ MsgText: "Error",
+ MsgURN: "whatsapp:250788123123",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(403, nil, []byte(`bad json`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseUnparseable,
+ },
+ {
+ Label: "Error",
+ MsgText: "Error",
+ MsgURN: "whatsapp:250788123123",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "*/12345_ID/messages": {
+ httpx.NewMockResponse(403, nil, []byte(`{ "error": {"message": "(#130429) Rate limit hit","code": 130429 }}`)),
+ },
+ },
+ ExpectedError: courier.ErrFailedWithReason("130429", "(#130429) Rate limit hit"),
},
}
diff --git a/handlers/mtarget/handler.go b/handlers/mtarget/handler.go
index a03a90c1d..ed0b6a173 100644
--- a/handlers/mtarget/handler.go
+++ b/handlers/mtarget/handler.go
@@ -148,23 +148,12 @@ func (h *handler) receiveMsg(ctx context.Context, c courier.Channel, w http.Resp
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "")
- if username == "" {
- return nil, fmt.Errorf("no username set for MT channel")
- }
-
password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "")
- if password == "" {
- return nil, fmt.Errorf("no password set for MT channel")
+ if username == "" || password == "" {
+ return courier.ErrChannelConfig
}
- // send our message
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
for _, part := range handlers.SplitMsgByChannel(msg.Channel(), handlers.GetTextAndAttachments(msg), maxMsgLength) {
// build our request
params := url.Values{
@@ -180,12 +169,14 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
msgURL.RawQuery = params.Encode()
req, err := http.NewRequest(http.MethodPost, msgURL.String(), nil)
if err != nil {
- return nil, err
+ return err
}
resp, respBody, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- return status, nil
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
// parse our response for our status code and ticket (external id)
@@ -202,14 +193,12 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
externalID, _ := jsonparser.GetString(respBody, "results", "[0]", "ticket")
if code == "0" && externalID != "" {
// all went well, set ourselves to wired
- status.SetStatus(courier.MsgStatusWired)
- status.SetExternalID(externalID)
+ res.AddExternalID(externalID)
} else {
- status.SetStatus(courier.MsgStatusFailed)
- clog.RawError(fmt.Errorf("Error status code, failing permanently"))
- break
+ reason, _ := jsonparser.GetString(respBody, "results", "[0]", "reason")
+ return courier.ErrFailedWithReason(code, reason)
}
}
- return status, nil
+ return nil
}
diff --git a/handlers/mtarget/handler_test.go b/handlers/mtarget/handler_test.go
index de5cc8cb0..6951afc73 100644
--- a/handlers/mtarget/handler_test.go
+++ b/handlers/mtarget/handler_test.go
@@ -1,13 +1,13 @@
package mtarget
import (
- "net/http/httptest"
"net/url"
"testing"
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var (
@@ -118,70 +118,72 @@ func TestIncoming(t *testing.T) {
var outgoingCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message",
- MsgURN: "tel:+250788383383",
- MockResponseBody: `{"results":[{"code": "0", "ticket": "externalID"}]}`,
- MockResponseStatus: 200,
+ Label: "Plain Send",
+ MsgText: "Simple Message",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api-public.mtarget.fr/api-sms.json*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"results":[{"code": "0", "ticket": "externalID"}]}`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{Params: url.Values{"msisdn": {"+250788383383"}, "msg": {"Simple Message"}, "username": {"Username"}, "password": {"Password"}, "serviceid": {"2020"}, "allowunicode": {"true"}}},
},
- ExpectedExtIDs: []string{"externalID"},
- ExpectedMsgStatus: "W",
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"externalID"},
},
{
- Label: "Unicode Send",
- MsgText: "☺",
- MsgURN: "tel:+250788383383",
- MockResponseBody: `{"results":[{"code": "0", "ticket": "externalID"}]}`,
- MockResponseStatus: 200,
+ Label: "Unicode Send",
+ MsgText: "☺",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api-public.mtarget.fr/api-sms.json*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"results":[{"code": "0", "ticket": "externalID"}]}`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{Params: url.Values{"msisdn": {"+250788383383"}, "msg": {"☺"}, "username": {"Username"}, "password": {"Password"}, "serviceid": {"2020"}, "allowunicode": {"true"}}},
},
- ExpectedExtIDs: []string{"externalID"},
- ExpectedMsgStatus: "W",
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"externalID"},
},
{
- Label: "Send Attachment",
- MsgText: "My pic!",
- MsgURN: "tel:+250788383383",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- MockResponseBody: `{"results":[{"code": "0", "ticket": "externalID"}]}`,
- MockResponseStatus: 200,
+ Label: "Send Attachment",
+ MsgText: "My pic!",
+ MsgURN: "tel:+250788383383",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api-public.mtarget.fr/api-sms.json*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"results":[{"code": "0", "ticket": "externalID"}]}`)),
+ },
+ },
ExpectedRequests: []ExpectedRequest{
{Params: url.Values{"msisdn": {"+250788383383"}, "msg": {"My pic!\nhttps://foo.bar/image.jpg"}, "username": {"Username"}, "password": {"Password"}, "serviceid": {"2020"}, "allowunicode": {"true"}}},
},
- ExpectedExtIDs: []string{"externalID"},
- ExpectedMsgStatus: "W",
- SendPrep: setSendURL,
+ ExpectedExtIDs: []string{"externalID"},
},
{
- Label: "Error Sending",
- MsgText: "Error Sending",
- MsgURN: "tel:+250788383383",
- MockResponseBody: `{"results":[{"code": "3", "ticket": "null"}]}`,
- MockResponseStatus: 403,
- ExpectedMsgStatus: "E",
- SendPrep: setSendURL,
+ Label: "Error Sending",
+ MsgText: "Error Sending",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api-public.mtarget.fr/api-sms.json*": {
+ httpx.NewMockResponse(403, nil, []byte(`{"results":[{"code": "3", "reason": "FAILED", "ticket": "null"}]}`)),
+ },
+ },
+ ExpectedError: courier.ErrResponseStatus,
},
{
- Label: "Error Response",
- MsgText: "Error Sending",
- MsgURN: "tel:+250788383383",
- MockResponseBody: `{"results":[{"code": "3", "ticket": "null"}]}`,
- MockResponseStatus: 200,
- ExpectedMsgStatus: "F",
- ExpectedLogErrors: []*courier.ChannelError{courier.NewChannelError("", "", "Error status code, failing permanently")},
- SendPrep: setSendURL,
+ Label: "Error Response",
+ MsgText: "Error Sending",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api-public.mtarget.fr/api-sms.json*": {
+ httpx.NewMockResponse(200, nil, []byte(`{"results":[{"code": "3", "reason": "FAILED", "ticket": "null"}]}`)),
+ },
+ },
+ ExpectedError: courier.ErrFailedWithReason("3", "FAILED"),
},
}
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- sendURL = s.URL
-}
-
func TestOutgoing(t *testing.T) {
var defaultChannel = test.NewMockChannel("8eb23e93-5ecb-45ba-b726-3b064e0c56ab", "MT", "2020", "FR",
map[string]any{
diff --git a/handlers/mtn/handler.go b/handlers/mtn/handler.go
index 978e53868..db473e16e 100644
--- a/handlers/mtn/handler.go
+++ b/handlers/mtn/handler.go
@@ -121,22 +121,15 @@ type mtPayload struct {
}
func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error {
- // TODO convert functionality from legacy method below
- return nil
-}
-
-func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) {
accessToken, err := h.getAccessToken(ctx, msg.Channel(), clog)
if err != nil {
- return nil, err
+ return courier.ErrChannelConfig
}
baseURL := msg.Channel().StringConfigForKey(configAPIHost, apiHostURL)
cpAddress := msg.Channel().StringConfigForKey(configCPAddress, "")
partSendURL, _ := url.Parse(fmt.Sprintf("%s/%s", baseURL, "v2/messages/sms/outbound"))
- status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog)
-
mtMsg := &mtPayload{}
mtMsg.From = strings.TrimPrefix(msg.Channel().Address(), "+")
mtMsg.To = []string{strings.TrimPrefix(msg.URN().Path(), "+")}
@@ -152,29 +145,27 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour
// build our request
req, err := http.NewRequest(http.MethodPost, partSendURL.String(), requestBody)
if err != nil {
- return nil, err
+ return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
resp, respBody, err := h.RequestHTTP(req, clog)
- if err != nil || resp.StatusCode/100 != 2 {
- return status, nil
+ if err != nil || resp.StatusCode/100 == 5 {
+ return courier.ErrConnectionFailed
+ } else if resp.StatusCode/100 != 2 {
+ return courier.ErrResponseStatus
}
externalID, err := jsonparser.GetString(respBody, "transactionId")
if err != nil {
clog.Error(courier.ErrorResponseValueMissing("transactionId"))
- return status, nil
+ } else {
+ res.AddExternalID(externalID)
}
- // if this is our first message, record the external id
-
- status.SetExternalID(externalID)
- status.SetStatus(courier.MsgStatusWired)
-
- return status, nil
+ return nil
}
func (h *handler) RedactValues(ch courier.Channel) []string {
diff --git a/handlers/mtn/handler_test.go b/handlers/mtn/handler_test.go
index ca56b2cf4..50e4958f4 100644
--- a/handlers/mtn/handler_test.go
+++ b/handlers/mtn/handler_test.go
@@ -1,13 +1,13 @@
package mtn
import (
- "net/http/httptest"
"testing"
"time"
"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/test"
+ "github.com/nyaruka/gocommon/httpx"
)
var (
@@ -155,89 +155,102 @@ func TestIncoming(t *testing.T) {
var outgoingCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
- MockResponseBody: `{ "transactionId":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 201,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer ACCESS_TOKEN",
+ Label: "Plain Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mtn.com/v2/messages/sms/outbound": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "transactionId":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"Simple Message ☺","clientCorrelator":"10"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer ACCESS_TOKEN",
+ },
+ Body: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"Simple Message ☺","clientCorrelator":"10"}`,
+ }},
+ ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
},
{
- Label: "Send Attachment",
- MsgText: "My pic!",
- MsgURN: "tel:+250788383383",
- MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
- MockResponseBody: `{ "transactionId":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 200,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer ACCESS_TOKEN",
+ Label: "Send Attachment",
+ MsgText: "My pic!",
+ MsgURN: "tel:+250788383383",
+ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mtn.com/v2/messages/sms/outbound": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "transactionId":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"My pic!\nhttps://foo.bar/image.jpg","clientCorrelator":"10"}`,
- SendPrep: setSendURL,
+
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer ACCESS_TOKEN",
+ },
+ Body: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"My pic!\nhttps://foo.bar/image.jpg","clientCorrelator":"10"}`,
+ }},
+ ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
},
{
- Label: "No External Id",
- MsgText: "No External ID",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `{"statusCode":"0000"}`,
- MockResponseStatus: 200,
- ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("transactionId")},
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer ACCESS_TOKEN",
+ Label: "No External Id",
+ MsgText: "No External ID",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mtn.com/v2/messages/sms/outbound": {
+ httpx.NewMockResponse(200, nil, []byte(`{"statusCode":"0000"}`)),
+ },
},
- ExpectedRequestBody: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"No External ID","clientCorrelator":"10"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer ACCESS_TOKEN",
+ },
+ Body: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"No External ID","clientCorrelator":"10"}`,
+ }},
+ ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("transactionId")},
},
{
- Label: "Error Sending",
- MsgText: "Error Message",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "E",
- MockResponseBody: `{ "error": "failed" }`,
- MockResponseStatus: 401,
- ExpectedRequestBody: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"Error Message","clientCorrelator":"10"}`,
- SendPrep: setSendURL,
+ Label: "Error Sending",
+ MsgText: "Error Message",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mtn.com/v2/messages/sms/outbound": {
+ httpx.NewMockResponse(401, nil, []byte(`{ "error": "failed" }`)),
+ },
+ },
+ ExpectedRequests: []ExpectedRequest{{
+ Body: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"Error Message","clientCorrelator":"10"}`,
+ }},
+ ExpectedError: courier.ErrResponseStatus,
},
}
var cpAddressOutgoingCases = []OutgoingTestCase{
{
- Label: "Plain Send",
- MsgText: "Simple Message ☺",
- MsgURN: "tel:+250788383383",
- ExpectedMsgStatus: "W",
- ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
- MockResponseBody: `{ "transactionId":"OzYDlvf3SQVc" }`,
- MockResponseStatus: 201,
- ExpectedHeaders: map[string]string{
- "Content-Type": "application/json",
- "Accept": "application/json",
- "Authorization": "Bearer ACCESS_TOKEN",
+ Label: "Plain Send",
+ MsgText: "Simple Message ☺",
+ MsgURN: "tel:+250788383383",
+ MockResponses: map[string][]*httpx.MockResponse{
+ "https://api.mtn.com/v2/messages/sms/outbound": {
+ httpx.NewMockResponse(201, nil, []byte(`{ "transactionId":"OzYDlvf3SQVc" }`)),
+ },
},
- ExpectedRequestBody: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"Simple Message ☺","clientCorrelator":"10","cpAddress":"FOO"}`,
- SendPrep: setSendURL,
+ ExpectedRequests: []ExpectedRequest{{
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "Authorization": "Bearer ACCESS_TOKEN",
+ },
+ Body: `{"senderAddress":"2020","receiverAddress":["250788383383"],"message":"Simple Message ☺","clientCorrelator":"10","cpAddress":"FOO"}`,
+ }},
+ ExpectedExtIDs: []string{"OzYDlvf3SQVc"},
},
}
-func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) {
- apiHostURL = s.URL
-}
-
func setupBackend(mb *test.MockBackend) {
// ensure there's a cached access token
rc := mb.RedisPool().Get()