-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #276 from svaloumas/feature/add-twilio-service
- Loading branch information
Showing
8 changed files
with
301 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Twilio (Message Service) | ||
|
||
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/nikoksr/notify/service/twilio) | ||
|
||
## Prerequisites | ||
|
||
Navigate to Twilio [console](https://console.twilio.com/), create a new account or login with an existing one. | ||
You will find the `Account SID` and the `Auth Token` under the `Account Info` tab. You may also request a Twilio phone number, if required. | ||
|
||
To test the integration with a phone number you can just use the sample code below. | ||
|
||
## Usage | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
|
||
"github.com/nikoksr/notify" | ||
"github.com/nikoksr/notify/service/twilio" | ||
) | ||
|
||
func main() { | ||
twilioSvc, err := twilio.New("account_sid", "auth_token", "your_phone_number") | ||
if err != nil { | ||
log.Fatalf("twilio.New() failed: %s", err.Error()) | ||
} | ||
|
||
twilioSvc.AddReceivers("recipient_phone_number") | ||
|
||
notifier := notify.New() | ||
notifier.UseServices(twilioSvc) | ||
|
||
err = notifier.Send(context.Background(), "subject", "message") | ||
if err != nil { | ||
log.Fatalf("notifier.Send() failed: %s", err.Error()) | ||
} | ||
|
||
log.Println("notification sent") | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
Package twilio provides message notification integration for Twilio (Message Service). | ||
Usage: | ||
package main | ||
import ( | ||
"context" | ||
"log" | ||
"github.com/nikoksr/notify" | ||
"github.com/nikoksr/notify/service/twilio" | ||
) | ||
func main() { | ||
twilioSvc, err := twilio.New("account_sid", "auth_token", "your_phone_number") | ||
if err != nil { | ||
log.Fatalf("twilio.New() failed: %s", err.Error()) | ||
} | ||
twilioSvc.AddReceivers("recipient_phone_number") | ||
notifier := notify.New() | ||
notifier.UseServices(twilioSvc) | ||
err = notifier.Send(context.Background(), "subject", "message") | ||
if err != nil { | ||
log.Fatalf("notifier.Send() failed: %s", err.Error()) | ||
} | ||
log.Println("notification sent") | ||
} | ||
*/ | ||
package twilio |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package twilio | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
|
||
"github.com/kevinburke/twilio-go" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// Compile-time check that twilio.MessageService satisfies twilioClient interface. | ||
var _ twilioClient = &twilio.MessageService{} | ||
|
||
// twilioClient abstracts twilio-go MessageService for writing unit tests | ||
type twilioClient interface { | ||
SendMessage(from, to, body string, mediaURLs []*url.URL) (*twilio.Message, error) | ||
} | ||
|
||
// Service encapsulates the Twilio Message Service client along with internal state for storing recipient phone numbers. | ||
type Service struct { | ||
client twilioClient | ||
|
||
fromPhoneNumber string | ||
toPhoneNumbers []string | ||
} | ||
|
||
// New returns a new instance of Twilio notification service. | ||
func New(accountSID, authToken, fromPhoneNumber string) (*Service, error) { | ||
client := twilio.NewClient(accountSID, authToken, nil) | ||
|
||
s := &Service{ | ||
client: client.Messages, | ||
fromPhoneNumber: fromPhoneNumber, | ||
toPhoneNumbers: []string{}, | ||
} | ||
return s, nil | ||
} | ||
|
||
// AddReceivers takes strings of recipient phone numbers and appends them to the internal phone numbers slice. | ||
// The Send method will send a given message to all those phone numbers. | ||
func (s *Service) AddReceivers(phoneNumbers ...string) { | ||
s.toPhoneNumbers = append(s.toPhoneNumbers, phoneNumbers...) | ||
} | ||
|
||
// Send takes a message subject and a message body and sends them to all previously set phone numbers. | ||
func (s *Service) Send(ctx context.Context, subject, message string) error { | ||
body := subject + "\n" + message | ||
|
||
for _, toPhoneNumber := range s.toPhoneNumbers { | ||
select { | ||
case <-ctx.Done(): | ||
return ctx.Err() | ||
default: | ||
|
||
_, err := s.client.SendMessage(s.fromPhoneNumber, toPhoneNumber, body, []*url.URL{}) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to send message to phone number '%s' using Twilio", toPhoneNumber) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package twilio | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
testing "testing" | ||
|
||
twilio "github.com/kevinburke/twilio-go" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestAddReceivers(t *testing.T) { | ||
t.Parallel() | ||
|
||
assert := require.New(t) | ||
|
||
svc := &Service{ | ||
toPhoneNumbers: []string{}, | ||
} | ||
toPhoneNumbers := []string{"PhoneNumber1", "PhoneNumber2", "PhoneNumber3"} | ||
svc.AddReceivers(toPhoneNumbers...) | ||
|
||
assert.Equal(svc.toPhoneNumbers, toPhoneNumbers) | ||
} | ||
|
||
func TestSend(t *testing.T) { | ||
t.Parallel() | ||
|
||
assert := require.New(t) | ||
|
||
svc := &Service{ | ||
fromPhoneNumber: "my_phone_number", | ||
toPhoneNumbers: []string{}, | ||
} | ||
|
||
mockPhoneNumber := "recipient_phone_number" | ||
mockBody := "subject\nmessage" | ||
mockError := errors.New("some error") | ||
|
||
// test twilio client send | ||
mockClient := newMockTwilioClient(t) | ||
mockClient.On("SendMessage", | ||
svc.fromPhoneNumber, | ||
mockPhoneNumber, | ||
mockBody, | ||
[]*url.URL{}).Return(&twilio.Message{Body: "a response message"}, nil) | ||
svc.client = mockClient | ||
svc.AddReceivers(mockPhoneNumber) | ||
err := svc.Send(context.Background(), "subject", "message") | ||
assert.Nil(err) | ||
mockClient.AssertExpectations(t) | ||
|
||
// test twilio client send returning error | ||
mockClient = newMockTwilioClient(t) | ||
mockClient.On("SendMessage", | ||
svc.fromPhoneNumber, | ||
mockPhoneNumber, | ||
mockBody, | ||
[]*url.URL{}).Return(nil, mockError) | ||
svc.client = mockClient | ||
svc.AddReceivers(mockPhoneNumber) | ||
err = svc.Send(context.Background(), "subject", "message") | ||
assert.NotNil(err) | ||
assert.Equal( | ||
fmt.Sprintf("failed to send message to phone number '%s' using Twilio: %s", mockPhoneNumber, mockError.Error()), | ||
err.Error()) | ||
mockClient.AssertExpectations(t) | ||
|
||
// test twilio client send multiple receivers | ||
anotherMockPhoneNumber := "another_recipient_phone_number" | ||
mockClient = newMockTwilioClient(t) | ||
mockClient.On("SendMessage", | ||
svc.fromPhoneNumber, | ||
mockPhoneNumber, | ||
mockBody, | ||
[]*url.URL{}).Return(&twilio.Message{Body: "a response message"}, nil) | ||
mockClient.On("SendMessage", | ||
svc.fromPhoneNumber, | ||
anotherMockPhoneNumber, | ||
mockBody, | ||
[]*url.URL{}).Return(&twilio.Message{Body: "a response message"}, nil) | ||
svc.client = mockClient | ||
svc.AddReceivers(mockPhoneNumber) | ||
svc.AddReceivers(anotherMockPhoneNumber) | ||
err = svc.Send(context.Background(), "subject", "message") | ||
assert.Nil(err) | ||
mockClient.AssertExpectations(t) | ||
} |