Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for YO! and SMS Central channels #31

Merged
merged 9 commits into from
Aug 15, 2017
2 changes: 1 addition & 1 deletion handlers/external/external.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type handler struct {
handlers.BaseHandler
}

// NewHandler returns a new Externla handler
// NewHandler returns a new External handler
func NewHandler() courier.ChannelHandler {
return &handler{handlers.NewBaseHandler(courier.ChannelType("EX"), "External")}
}
Expand Down
115 changes: 115 additions & 0 deletions handlers/smscentral/smscentral.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,121 @@
package smscentral
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests for smscentral.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the file


import (
"fmt"
"net/http"
"net/url"
"strings"

"github.com/nyaruka/courier"
"github.com/nyaruka/courier/handlers"
"github.com/nyaruka/courier/utils"
"github.com/pkg/errors"
)

/*
POST /handlers/smscentral/receive/uuid/
mobile=9779811781111&message=Msg
*/

var sendURL = "http://smail.smscentral.com.np/bp/ApiSms.php"

func init() {
courier.RegisterHandler(NewHandler())
}

type handler struct {
handlers.BaseHandler
}

// NewHandler returns a new Yo! handler
func NewHandler() courier.ChannelHandler {
return &handler{handlers.NewBaseHandler(courier.ChannelType("SC"), "SMS Central")}
}

// Initialize is called by the engine once everything is loaded
func (h *handler) Initialize(s courier.Server) error {
h.SetServer(s)
err := s.AddReceiveMsgRoute(h, "POST", "receive", h.ReceiveMessage)
if err != nil {
return err
}

return nil
}

type smsCentralMessage struct {
Message string `validate:"required" name:"message"`
Mobile string `validate:"required" name:"mobile"`
}

// ReceiveMessage is our HTTP handler function for incoming messages
func (h *handler) ReceiveMessage(channel courier.Channel, w http.ResponseWriter, r *http.Request) ([]courier.Msg, error) {
smsCentralMessage := &smsCentralMessage{}
handlers.DecodeAndValidateQueryParams(smsCentralMessage, r)

// if this is a post, also try to parse the form body
if r.Method == http.MethodPost {
handlers.DecodeAndValidateForm(smsCentralMessage, r)
}

// validate whether our required fields are present
err := handlers.Validate(smsCentralMessage)
if err != nil {
return nil, err
}

// create our URN
urn := courier.NewTelURNForChannel(smsCentralMessage.Mobile, channel)

// build our msg
msg := h.Backend().NewIncomingMsg(channel, urn, smsCentralMessage.Message)

// and finally queue our message
err = h.Backend().WriteMsg(msg)
if err != nil {
return nil, err
}

return []courier.Msg{msg}, courier.WriteReceiveSuccess(w, r, msg)

}

// SendMsg sends the passed in message, returning any error
func (h *handler) SendMsg(msg courier.Msg) (courier.MsgStatus, error) {
username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "")
if username == "" {
return nil, fmt.Errorf("no username set for SC channel")
}

password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "")
if password == "" {
return nil, fmt.Errorf("no password set for SC channel")
}

// build our request
form := url.Values{
"user": []string{username},
"pass": []string{password},
"mobile": []string{strings.TrimPrefix(msg.URN().Path(), "+")},
"content": []string{courier.GetTextAndAttachments(msg)},
}

req, err := http.NewRequest(http.MethodPost, sendURL, strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rr, err := utils.MakeHTTPRequest(req)

// record our status and log
status := h.Backend().NewMsgStatusForID(msg.Channel(), msg.ID(), courier.MsgErrored)
status.AddLog(courier.NewChannelLogFromRR(msg.Channel(), msg.ID(), rr))
if err != nil {
return status, err
}

if rr.StatusCode/100 != 2 {
return status, errors.Errorf("Got non-200 response [%d] from API")
}

status.SetStatus(courier.MsgWired)

return status, nil
}
79 changes: 79 additions & 0 deletions handlers/smscentral/smscentral_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package smscentral

import (
"net/http/httptest"
"testing"

"github.com/nyaruka/courier"
. "github.com/nyaruka/courier/handlers"
)

var (
receiveValidMessage = "/c/sc/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive/?mobile=%2B2349067554729&message=Join"
receiveNoParams = "/c/sc/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive/"
receiveNoSender = "/c/sc/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive/?message=Join"
receiveNoMessage = "/c/sc/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/receive/?mobile=%2B2349067554729"
)

var testChannels = []courier.Channel{
courier.NewMockChannel("8eb23e93-5ecb-45ba-b726-3b064e0c56ab", "SC", "2020", "US", map[string]interface{}{"username": "Username", "password": "Password"}),
}

var handleTestCases = []ChannelHandleTestCase{
{Label: "Receive Valid Message", URL: receiveValidMessage, Data: "empty", Status: 200, Response: "Accepted",
Text: Sp("Join"), URN: Sp("tel:+2349067554729")},
{Label: "Receive No Params", URL: receiveNoParams, Data: "empty", Status: 400, Response: "field 'message' required"},
{Label: "Receive No Sender", URL: receiveNoSender, Data: "empty", Status: 400, Response: "field 'mobile' required"},
{Label: "Receive No Message", URL: receiveNoMessage, Data: "empty", Status: 400, Response: "field 'message' required"},
}

func TestHandler(t *testing.T) {
RunChannelTestCases(t, testChannels, NewHandler(), handleTestCases)
}

func BenchmarkHandler(b *testing.B) {
RunChannelBenchmarks(b, testChannels, NewHandler(), handleTestCases)
}

// setSend takes care of setting the sendURL to call
func setSendURL(server *httptest.Server, channel courier.Channel, msg courier.Msg) {
sendURL = server.URL
}

var defaultSendTestCases = []ChannelSendTestCase{
{Label: "Plain Send",
Text: "Simple Message", URN: "tel:+250788383383",
Status: "W",
ResponseBody: `[{"id": "1002"}]`, ResponseStatus: 200,
PostParams: map[string]string{"content": "Simple Message", "mobile": "250788383383", "pass": "Password", "user": "Username"},
SendPrep: setSendURL},
{Label: "Unicode Send",
Text: "☺", URN: "tel:+250788383383",
Status: "W",
ResponseBody: `[{"id": "1002"}]`, ResponseStatus: 200,
PostParams: map[string]string{"content": "☺", "mobile": "250788383383", "pass": "Password", "user": "Username"},
SendPrep: setSendURL},
{Label: "Send Attachment",
Text: "My pic!", URN: "tel:+250788383383", Attachments: []string{"image/jpeg:https://foo.bar/image.jpg"},
Status: "W",
ResponseBody: `[{ "id": "1002" }]`, ResponseStatus: 200,
PostParams: map[string]string{"content": "My pic!\nhttps://foo.bar/image.jpg", "mobile": "250788383383", "pass": "Password", "user": "Username"},
SendPrep: setSendURL},
{Label: "Error Sending",
Text: "Error Message", URN: "tel:+250788383383",
Status: "E",
ResponseBody: `{ "error": "failed" }`, ResponseStatus: 401,
Error: "received non 200 status: 401",
PostParams: map[string]string{"content": `Error Message`, "mobile": "250788383383", "pass": "Password", "user": "Username"},
SendPrep: setSendURL},
}

func TestSending(t *testing.T) {
var defaultChannel = courier.NewMockChannel("8eb23e93-5ecb-45ba-b726-3b064e0c56ab", "SC", "2020", "US",
map[string]interface{}{
courier.ConfigPassword: "Password",
courier.ConfigUsername: "Username",
})

RunChannelSendTestCases(t, defaultChannel, NewHandler(), defaultSendTestCases)
}
Loading