Skip to content

Commit

Permalink
Merge pull request rapidpro#419 from nyaruka/vonage
Browse files Browse the repository at this point in the history
πŸ“ž Nexmo βž” Vonage
  • Loading branch information
rowanseymour authored Apr 9, 2021
2 parents f6111b8 + b1249cd commit 7f7dcb9
Show file tree
Hide file tree
Showing 12 changed files with 53 additions and 53 deletions.
2 changes: 1 addition & 1 deletion cmd/mailroom/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (

_ "github.com/nyaruka/mailroom/core/handlers"
_ "github.com/nyaruka/mailroom/core/hooks"
_ "github.com/nyaruka/mailroom/core/ivr/nexmo"
_ "github.com/nyaruka/mailroom/core/ivr/twiml"
_ "github.com/nyaruka/mailroom/core/ivr/vonage"
_ "github.com/nyaruka/mailroom/core/tasks/broadcasts"
_ "github.com/nyaruka/mailroom/core/tasks/campaigns"
_ "github.com/nyaruka/mailroom/core/tasks/contacts"
Expand Down
22 changes: 11 additions & 11 deletions core/ivr/nexmo/nexmo.go β†’ core/ivr/vonage/vonage.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nexmo
package vonage

import (
"bytes"
Expand Down Expand Up @@ -37,7 +37,7 @@ import (
"github.com/sirupsen/logrus"
)

// CallURL is the API endpoint for Nexmo calls, public so our main IVR test can change it
// CallURL is the API endpoint for Vonage/Nexmo calls, public so our main IVR test can change it
var CallURL = `https://api.nexmo.com/v1/calls`

// IgnoreSignatures sets whether we ignore signatures (for unit tests)
Expand All @@ -54,7 +54,7 @@ var callStatusMap = map[string]flows.DialStatus{
}

const (
nexmoChannelType = models.ChannelType("NX")
vonageChannelType = models.ChannelType("NX")

gatherTimeout = 30
recordTimeout = 600
Expand Down Expand Up @@ -83,7 +83,7 @@ type client struct {
}

func init() {
ivr.RegisterClientType(nexmoChannelType, NewClientFromChannel)
ivr.RegisterClientType(vonageChannelType, NewClientFromChannel)
}

// NewClientFromChannel creates a new Twilio IVR client for the passed in account and and auth token
Expand Down Expand Up @@ -260,9 +260,9 @@ func (c *client) PreprocessStatus(ctx context.Context, db *sqlx.DB, rp *redis.Po
return nil, errors.Wrapf(err, "error reconnecting flow for call: %s", callUUID)
}

// nexmo return 204 on successful updates
// vonage return 204 on successful updates
if trace.Response.StatusCode != http.StatusNoContent {
return nil, fmt.Errorf("error reconnecting flow for call: %s, received %d from nexmo", callUUID, trace.Response.StatusCode)
return nil, fmt.Errorf("error reconnecting flow for call: %s, received %d from vonage", callUUID, trace.Response.StatusCode)
}

return c.MakeEmptyResponseBody(fmt.Sprintf("reconnected call: %s to flow with dial status: %s", callUUID, status)), nil
Expand Down Expand Up @@ -394,7 +394,7 @@ type CallRequest struct {
RingingTimer int `json:"ringing_timer,omitempty"`
}

// CallResponse is our struct for a Nexmo call response
// CallResponse is our struct for a Vonage call response
// {
// "uuid": "63f61863-4a51-4f6b-86e1-46edebcf9356",
// "status": "started",
Expand Down Expand Up @@ -446,7 +446,7 @@ func (c *client) RequestCall(number urns.URN, resumeURL string, statusURL string
return ivr.CallID(call.UUID), trace, nil
}

// HangupCall asks Nexmo to hang up the call that is passed in
// HangupCall asks Vonage to hang up the call that is passed in
func (c *client) HangupCall(callID string) (*httpx.Trace, error) {
hangupBody := map[string]string{"action": "hangup"}
url := c.callURL + "/" + callID
Expand Down Expand Up @@ -839,7 +839,7 @@ func (c *client) responseForSprint(ctx context.Context, rp *redis.Pool, channel
waitActions = append(waitActions, input)

case *hints.AudioHint:
// Nexmo is goofy in that they do not synchronously send us recordings. Rather the move on in
// Vonage is goofy in that they do not synchronously send us recordings. Rather the move on in
// the NCCO script immediately and then asynchronously call the event URL on the record URL
// when the recording is ready.
//
Expand All @@ -861,7 +861,7 @@ func (c *client) responseForSprint(ctx context.Context, rp *redis.Pool, channel
}
waitActions = append(waitActions, record)

// nexmo is goofy in that they do not call our event URL upon gathering the recording but
// Vonage is goofy in that they do not call our event URL upon gathering the recording but
// instead move on. So we need to put in an input here as well
eventURL = resumeURL + "&wait_type=record&recording_uuid=" + recordingUUID
eventURL = eventURL + "&sig=" + url.QueryEscape(c.calculateSignature(eventURL))
Expand All @@ -879,7 +879,7 @@ func (c *client) responseForSprint(ctx context.Context, rp *redis.Pool, channel
}

case *waits.ActivatedDialWait:
// Nexmo handles forwards a bit differently. We have to create a new call to the forwarded number, then
// Vonage handles forwards a bit differently. We have to create a new call to the forwarded number, then
// join the current call with the call we are starting.
//
// See: https://developer.nexmo.com/use-cases/contact-center
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nexmo
package vonage

import (
"net/http"
Expand Down Expand Up @@ -26,15 +26,15 @@ func TestResponseForSprint(t *testing.T) {
models.FlushCache()

urn := urns.URN("tel:+12067799294")
channelRef := assets.NewChannelReference(models.NexmoChannelUUID, "Nexmo Channel")
channelRef := assets.NewChannelReference(models.VonageChannelUUID, "Vonage Channel")

resumeURL := "http://temba.io/resume?session=1"

// deactivate our twilio channel
db.MustExec(`UPDATE channels_channel SET is_active = FALSE WHERE id = $1`, models.TwilioChannelID)

// add auth tokens
db.MustExec(`UPDATE channels_channel SET config = '{"nexmo_app_id": "app_id", "nexmo_app_private_key": "-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKNwapOQ6rQJHetP\nHRlJBIh1OsOsUBiXb3rXXE3xpWAxAha0MH+UPRblOko+5T2JqIb+xKf9Vi3oTM3t\nKvffaOPtzKXZauscjq6NGzA3LgeiMy6q19pvkUUOlGYK6+Xfl+B7Xw6+hBMkQuGE\nnUS8nkpR5mK4ne7djIyfHFfMu4ptAgMBAAECgYA+s0PPtMq1osG9oi4xoxeAGikf\nJB3eMUptP+2DYW7mRibc+ueYKhB9lhcUoKhlQUhL8bUUFVZYakP8xD21thmQqnC4\nf63asad0ycteJMLb3r+z26LHuCyOdPg1pyLk3oQ32lVQHBCYathRMcVznxOG16VK\nI8BFfstJTaJu0lK/wQJBANYFGusBiZsJQ3utrQMVPpKmloO2++4q1v6ZR4puDQHx\nTjLjAIgrkYfwTJBLBRZxec0E7TmuVQ9uJ+wMu/+7zaUCQQDDf2xMnQqYknJoKGq+\noAnyC66UqWC5xAnQS32mlnJ632JXA0pf9pb1SXAYExB1p9Dfqd3VAwQDwBsDDgP6\nHD8pAkEA0lscNQZC2TaGtKZk2hXkdcH1SKru/g3vWTkRHxfCAznJUaza1fx0wzdG\nGcES1Bdez0tbW4llI5By/skZc2eE3QJAFl6fOskBbGHde3Oce0F+wdZ6XIJhEgCP\niukIcKZoZQzoiMJUoVRrA5gqnmaYDI5uRRl/y57zt6YksR3KcLUIuQJAd242M/WF\n6YAZat3q/wEeETeQq1wrooew+8lHl05/Nt0cCpV48RGEhJ83pzBm3mnwHf8lTBJH\nx6XroMXsmbnsEw==\n-----END PRIVATE KEY-----", "callback_domain": "localhost:8090"}', role='SRCA' WHERE id = $1`, models.NexmoChannelID)
db.MustExec(`UPDATE channels_channel SET config = '{"nexmo_app_id": "app_id", "nexmo_app_private_key": "-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKNwapOQ6rQJHetP\nHRlJBIh1OsOsUBiXb3rXXE3xpWAxAha0MH+UPRblOko+5T2JqIb+xKf9Vi3oTM3t\nKvffaOPtzKXZauscjq6NGzA3LgeiMy6q19pvkUUOlGYK6+Xfl+B7Xw6+hBMkQuGE\nnUS8nkpR5mK4ne7djIyfHFfMu4ptAgMBAAECgYA+s0PPtMq1osG9oi4xoxeAGikf\nJB3eMUptP+2DYW7mRibc+ueYKhB9lhcUoKhlQUhL8bUUFVZYakP8xD21thmQqnC4\nf63asad0ycteJMLb3r+z26LHuCyOdPg1pyLk3oQ32lVQHBCYathRMcVznxOG16VK\nI8BFfstJTaJu0lK/wQJBANYFGusBiZsJQ3utrQMVPpKmloO2++4q1v6ZR4puDQHx\nTjLjAIgrkYfwTJBLBRZxec0E7TmuVQ9uJ+wMu/+7zaUCQQDDf2xMnQqYknJoKGq+\noAnyC66UqWC5xAnQS32mlnJ632JXA0pf9pb1SXAYExB1p9Dfqd3VAwQDwBsDDgP6\nHD8pAkEA0lscNQZC2TaGtKZk2hXkdcH1SKru/g3vWTkRHxfCAznJUaza1fx0wzdG\nGcES1Bdez0tbW4llI5By/skZc2eE3QJAFl6fOskBbGHde3Oce0F+wdZ6XIJhEgCP\niukIcKZoZQzoiMJUoVRrA5gqnmaYDI5uRRl/y57zt6YksR3KcLUIuQJAd242M/WF\n6YAZat3q/wEeETeQq1wrooew+8lHl05/Nt0cCpV48RGEhJ83pzBm3mnwHf8lTBJH\nx6XroMXsmbnsEw==\n-----END PRIVATE KEY-----", "callback_domain": "localhost:8090"}', role='SRCA' WHERE id = $1`, models.VonageChannelID)

// set our UUID generator
uuids.SetGenerator(uuids.NewSeededGenerator(0))
Expand All @@ -46,7 +46,7 @@ func TestResponseForSprint(t *testing.T) {
oa, err := models.GetOrgAssets(ctx, db, models.Org1)
assert.NoError(t, err)

channel := oa.ChannelByUUID(models.NexmoChannelUUID)
channel := oa.ChannelByUUID(models.VonageChannelUUID)
assert.NotNil(t, channel)

c, err := NewClientFromChannel(http.DefaultClient, channel)
Expand Down
4 changes: 2 additions & 2 deletions core/models/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func TestCloneForSimulation(t *testing.T) {
assert.Equal(t, "Test Channel 2", testChannel2.Name())

// as well as the regular channels
nexmo := clone.SessionAssets().Channels().Get(models.NexmoChannelUUID)
assert.Equal(t, "Nexmo", nexmo.Name())
vonage := clone.SessionAssets().Channels().Get(models.VonageChannelUUID)
assert.Equal(t, "Vonage", vonage.Name())

// original assets still has original flow definition
flow, err = oa.Flow(models.FavoritesFlowUUID)
Expand Down
8 changes: 4 additions & 4 deletions core/models/channels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestChannels(t *testing.T) {
db := testsuite.DB()

// add some tel specific config to channel 2
db.MustExec(`UPDATE channels_channel SET config = '{"matching_prefixes": ["250", "251"], "allow_international": true}' WHERE id = $1`, NexmoChannelID)
db.MustExec(`UPDATE channels_channel SET config = '{"matching_prefixes": ["250", "251"], "allow_international": true}' WHERE id = $1`, VonageChannelID)

// make twitter channel have a parent of twilio channel
db.MustExec(`UPDATE channels_channel SET parent_id = $1 WHERE id = $2`, TwilioChannelID, TwitterChannelID)
Expand Down Expand Up @@ -44,9 +44,9 @@ func TestChannels(t *testing.T) {
nil,
},
{
NexmoChannelID,
NexmoChannelUUID,
"Nexmo",
VonageChannelID,
VonageChannelUUID,
"Vonage",
"5789",
[]string{"tel"},
[]assets.ChannelRole{"send", "receive"},
Expand Down
2 changes: 1 addition & 1 deletion core/models/orgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestOrgs(t *testing.T) {
defer tx.Rollback()

tx.MustExec("UPDATE channels_channel SET country = 'FR' WHERE id = $1;", TwitterChannelID)
tx.MustExec("UPDATE channels_channel SET country = 'US' WHERE id IN ($1,$2);", TwilioChannelID, NexmoChannelID)
tx.MustExec("UPDATE channels_channel SET country = 'US' WHERE id IN ($1,$2);", TwilioChannelID, VonageChannelID)
tx.MustExec(`INSERT INTO orgs_language(is_active, created_on, modified_on, name, iso_code, created_by_id, modified_by_id, org_id)
VALUES(TRUE, NOW(), NOW(), 'French', 'fra', 1, 1, 2);`)
tx.MustExec(`INSERT INTO orgs_language(is_active, created_on, modified_on, name, iso_code, created_by_id, modified_by_id, org_id)
Expand Down
4 changes: 2 additions & 2 deletions core/models/test_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ var Org1UUID = uuids.UUID("bf0514a5-9407-44c9-b0f9-3f36f9c18414")
var TwilioChannelID = ChannelID(10000)
var TwilioChannelUUID = assets.ChannelUUID("74729f45-7f29-4868-9dc4-90e491e3c7d8")

var NexmoChannelID = ChannelID(10001)
var NexmoChannelUUID = assets.ChannelUUID("19012bfd-3ce3-4cae-9bb9-76cf92c73d49")
var VonageChannelID = ChannelID(10001)
var VonageChannelUUID = assets.ChannelUUID("19012bfd-3ce3-4cae-9bb9-76cf92c73d49")

var TwitterChannelID = ChannelID(10002)
var TwitterChannelUUID = assets.ChannelUUID("0f661e8b-ea9d-4bd3-9953-d368340acf91")
Expand Down
10 changes: 5 additions & 5 deletions core/tasks/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,12 @@ func TestChannelEvents(t *testing.T) {
VALUES(TRUE, now(), now(), NULL, false, $1, 'N', NULL, 1, 1, 1, $2) RETURNING id`,
models.FavoritesFlowID, models.TwitterChannelID)

// trigger on our nexmo channel for referral and number flow
// trigger on our vonage channel for referral and number flow
db.MustExec(
`INSERT INTO triggers_trigger(is_active, created_on, modified_on, keyword, is_archived,
flow_id, trigger_type, match_type, created_by_id, modified_by_id, org_id, channel_id)
VALUES(TRUE, now(), now(), NULL, false, $1, 'R', NULL, 1, 1, 1, $2) RETURNING id`,
models.PickNumberFlowID, models.NexmoChannelID)
models.PickNumberFlowID, models.VonageChannelID)

// add a URN for cathy so we can test twitter URNs
testdata.InsertContactURN(t, db, models.Org1, models.BobID, urns.URN("twitterid:123456"), 10)
Expand All @@ -259,10 +259,10 @@ func TestChannelEvents(t *testing.T) {
UpdateLastSeen bool
}{
{NewConversationEventType, models.CathyID, models.CathyURNID, models.Org1, models.TwitterChannelID, nil, "What is your favorite color?", true},
{NewConversationEventType, models.CathyID, models.CathyURNID, models.Org1, models.NexmoChannelID, nil, "", true},
{WelcomeMessageEventType, models.CathyID, models.CathyURNID, models.Org1, models.NexmoChannelID, nil, "", false},
{NewConversationEventType, models.CathyID, models.CathyURNID, models.Org1, models.VonageChannelID, nil, "", true},
{WelcomeMessageEventType, models.CathyID, models.CathyURNID, models.Org1, models.VonageChannelID, nil, "", false},
{ReferralEventType, models.CathyID, models.CathyURNID, models.Org1, models.TwitterChannelID, nil, "", true},
{ReferralEventType, models.CathyID, models.CathyURNID, models.Org1, models.NexmoChannelID, nil, "Pick a number between 1-10.", true},
{ReferralEventType, models.CathyID, models.CathyURNID, models.Org1, models.VonageChannelID, nil, "Pick a number between 1-10.", true},
}

models.FlushCache()
Expand Down
Binary file modified mailroom_test.dump
Binary file not shown.
2 changes: 1 addition & 1 deletion web/contact/testdata/resolve.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"name": "Bob",
"status": "active",
"timezone": "America/Los_Angeles",
"created_on": "2021-02-23T18:42:47.152652Z",
"created_on": "2021-04-09T14:51:56.517774Z",
"urns": [
"tel:+16055742222?id=10001&priority=1000"
],
Expand Down
Loading

0 comments on commit 7f7dcb9

Please sign in to comment.