1
0
mirror of https://github.com/nikoksr/notify.git synced 2025-02-03 13:01:26 +02:00

refactor(send): Add context.Context to parameter list of Send method

This commit is contained in:
Niko Köser 2021-02-18 03:33:30 +01:00
parent d017725536
commit 80cab3dece
No known key found for this signature in database
GPG Key ID: F3F28C118DAA6375
24 changed files with 196 additions and 109 deletions

View File

@ -54,13 +54,16 @@ notifier.UseServices(telegramService)
// Send a test message.
_ = notifier.Send(
context.Background(),
"Subject/Title",
"The actual message. Hello, you awesome gophers! :)",
"The actual message - Hello, you awesome gophers! :)",
)
```
## Supported services <a id="supported_services"></a>
> Please create feature requests for missing services (see #3 for example)
- *Amazon SES*
- *Discord*
- *Email*
@ -74,11 +77,6 @@ _ = notifier.Send(
- *Twitter*
- *WhatsApp*
## Roadmap <a id="roadmap"></a>
- [ ] Add tests (see [#1](https://github.com/nikoksr/notify/issues/1))
- [ ] Add more notification services (Issues and PRs are welcome!)
## Credits <a id="credits"></a>
- Amazon SES support: [aws/aws-sdk-go](https://github.com/aws/aws-sdk-go)

View File

@ -1,9 +1,11 @@
package notify
import "context"
// Notifier defines the behavior for notification services.
//
// The Send function simply sends a subject and a message string to the internal destination Notifier.
// E.g for telegram.Telegram it sends the message to the specified group chat.
type Notifier interface {
Send(string, string) error
Send(context.Context, string, string) error
}

View File

@ -1,12 +1,14 @@
package notify
import (
"context"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
// Send calls the underlying notification services to send the given subject and message to their respective endpoints.
func (n Notify) Send(subject, message string) error {
func (n Notify) Send(ctx context.Context, subject, message string) error {
if n.Disabled {
return nil
}
@ -17,7 +19,7 @@ func (n Notify) Send(subject, message string) error {
if service != nil {
s := service
eg.Go(func() error {
return s.Send(subject, message)
return s.Send(ctx, subject, message)
})
}
}

View File

@ -48,7 +48,7 @@ func (a *AmazonSES) AddReceivers(addresses ...string) {
// Send takes a message subject and a message body and sends them to all previously set chats. Message body supports
// html as markup language.
func (a AmazonSES) Send(subject, message string) error {
func (a AmazonSES) Send(ctx context.Context, subject, message string) error {
input := &ses.SendEmailInput{
Source: a.senderAddress,
Destination: &types.Destination{
@ -69,7 +69,7 @@ func (a AmazonSES) Send(subject, message string) error {
},
}
_, err := a.client.SendEmail(context.Background(), input)
_, err := a.client.SendEmail(ctx, input)
if err != nil {
return errors.Wrap(err, "failed to send mail using Amazon SES service")
}

View File

@ -1,6 +1,8 @@
package discord
import (
"context"
"github.com/bwmarrin/discordgo"
"github.com/pkg/errors"
)
@ -88,13 +90,18 @@ func (d *Discord) AddReceivers(channelIDs ...string) {
}
// Send takes a message subject and a message body and sends them to all previously set chats.
func (d Discord) Send(subject, message string) error {
func (d Discord) Send(ctx context.Context, subject, message string) error {
fullMessage := subject + "\n" + message // Treating subject as message title
for _, channelID := range d.channelIDs {
_, err := d.client.ChannelMessageSend(channelID, fullMessage)
if err != nil {
return errors.Wrapf(err, "failed to send message to Discord channel '%s'", channelID)
select {
case <-ctx.Done():
return nil
default:
_, err := d.client.ChannelMessageSend(channelID, fullMessage)
if err != nil {
return errors.Wrapf(err, "failed to send message to Discord channel '%s'", channelID)
}
}
}

View File

@ -1,6 +1,7 @@
package mail
import (
"context"
"net/smtp"
"net/textproto"
@ -41,7 +42,14 @@ func (m *Mail) AddReceivers(addresses ...string) {
// Send takes a message subject and a message body and sends them to all previously set chats. Message body supports
// html as markup language.
func (m Mail) Send(subject, message string) error {
func (m Mail) Send(ctx context.Context, subject, message string) error {
// TODO: Is this necessary? Or do we just do nothing with ctx?
select {
case <-ctx.Done():
return nil
default:
}
msg := &email.Email{
To: m.receiverAddresses,
From: m.senderAddress,

View File

@ -39,10 +39,10 @@ func (m *Mailgun) AddReceivers(addresses ...string) {
// Send takes a message subject and a message body and sends them to all previously set chats. Message body supports
// html as markup language.
func (m Mailgun) Send(subject, message string) error {
func (m Mailgun) Send(ctx context.Context, subject, message string) error {
mailMessage := m.client.NewMessage(m.senderAddress, subject, message, m.receiverAddresses...)
_, _, err := m.client.Send(context.Background(), mailMessage)
_, _, err := m.client.Send(ctx, mailMessage)
if err != nil {
return errors.Wrap(err, "failed to send mail using Mailgun service")
}

View File

@ -1,6 +1,8 @@
package msteams
import (
"context"
goteamsnotify "github.com/atc0005/go-teams-notify/v2"
"github.com/pkg/errors"
)
@ -43,15 +45,20 @@ func (m *MSTeams) AddReceivers(webHooks ...string) {
// html as markup language.
// For more information about telegram api token:
// -> https://github.com/atc0005/go-teams-notify#example-basic
func (m MSTeams) Send(subject, message string) error {
func (m MSTeams) Send(ctx context.Context, subject, message string) error {
msgCard := goteamsnotify.NewMessageCard()
msgCard.Title = subject
msgCard.Text = message
for _, webHook := range m.webHooks {
err := m.client.Send(webHook, msgCard)
if err != nil {
return errors.Wrapf(err, "failed to send message to Microsoft Teams via webhook '%s'", webHook)
select {
case <-ctx.Done():
return nil
default:
err := m.client.SendWithContext(ctx, webHook, msgCard)
if err != nil {
return errors.Wrapf(err, "failed to send message to Microsoft Teams via webhook '%s'", webHook)
}
}
}

View File

@ -16,34 +16,35 @@ following things:
package main
import (
"log"
"context"
"log"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/plivo"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/plivo"
)
func main() {
plivoSvc, err := plivo.New(
&plivo.ClientOptions{
AuthID: "<Your-Plivo-Auth-Id>",
AuthToken: "<Your-Plivo-Auth-Token>",
}, &plivo.MessageOptions{
Source: "<Your-Plivo-Source-Number>",
})
if err != nil {
log.Fatalf("plivo.New() failed: %s", err.Error())
}
plivoSvc, err := plivo.New(
&plivo.ClientOptions{
AuthID: "<Your-Plivo-Auth-Id>",
AuthToken: "<Your-Plivo-Auth-Token>",
}, &plivo.MessageOptions{
Source: "<Your-Plivo-Source-Number>",
})
if err != nil {
log.Fatalf("plivo.New() failed: %s", err.Error())
}
plivoSvc.AddReceivers("Destination1")
plivoSvc.AddReceivers("Destination1")
notifier := notify.New()
notifier.UseServices(plivoSvc)
notifier := notify.New()
notifier.UseServices(plivoSvc)
err = notifier.Send("subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}
err = notifier.Send(context.Background(), "subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}
log.Printf("notification sent")
log.Printf("notification sent")
}
```

View File

@ -29,7 +29,7 @@ Usage:
notifier := notify.New()
notifier.UseServices(plivoSvc)
err = notifier.Send("subject", "message")
err = notifier.Send(context.Background(), "subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
// Code generated by mockery v2.5.1. DO NOT EDIT.
package plivo
@ -7,7 +7,7 @@ import (
mock "github.com/stretchr/testify/mock"
)
// mockPlivoMsgClient is an autogenerated mock type for the plivoMsgClient type
// mockPlivoMsgClient is an autogenerated mock type for the mockPlivoMsgClient type
type mockPlivoMsgClient struct {
mock.Mock
}

View File

@ -1,6 +1,7 @@
package plivo
import (
"context"
"fmt"
"net/http"
"strings"
@ -26,7 +27,7 @@ type MessageOptions struct {
CallbackMethod string // The HTTP method to be used when calling CallbackURL - GET or POST(default)
}
// plivoMsgClient abstracts Plivo SDK for writing unit tests
// mockPlivoMsgClient abstracts Plivo SDK for writing unit tests
type plivoMsgClient interface {
Create(plivo.MessageCreateParams) (*plivo.MessageCreateResponseBody, error)
}
@ -75,7 +76,14 @@ func (s *Service) AddReceivers(phoneNumbers ...string) {
}
// Send sends a SMS via Plivo to all previously added receivers.
func (s *Service) Send(subject, message string) error {
func (s *Service) Send(ctx context.Context, subject, message string) error {
// TODO: Same question here.
select {
case <-ctx.Done():
return nil
default:
}
text := subject + "\n" + message
var dst string

View File

@ -1,6 +1,7 @@
package plivo
import (
"context"
"errors"
"testing"
@ -53,7 +54,8 @@ func TestSend(t *testing.T) {
assert.NotNil(svc)
// no receivers added
err = svc.Send("message", "test")
ctx := context.Background()
err = svc.Send(ctx, "message", "test")
assert.NotNil(err)
// test plivo client returning error
@ -62,7 +64,7 @@ func TestSend(t *testing.T) {
Return(nil, errors.New("some error"))
svc.client = mockClient
svc.AddReceivers("67890")
err = svc.Send("message", "test")
err = svc.Send(ctx, "message", "test")
assert.NotNil(err)
mockClient.AssertExpectations(t)
@ -72,7 +74,7 @@ func TestSend(t *testing.T) {
Return(nil, nil)
svc.client = mockClient
svc.AddReceivers("09876")
err = svc.Send("message", "test")
err = svc.Send(ctx, "message", "test")
assert.Nil(err)
mockClient.AssertExpectations(t)
}

View File

@ -1,6 +1,8 @@
package pushbullet
import (
"context"
"github.com/cschomburg/go-pushbullet"
"github.com/pkg/errors"
)
@ -35,16 +37,21 @@ func (pb *Pushbullet) AddReceivers(deviceNicknames ...string) {
// you will need Pushbullet installed on the relevant devices
// (android, chrome, firefox, windows)
// see https://www.pushbullet.com/apps
func (pb Pushbullet) Send(subject, message string) error {
func (pb Pushbullet) Send(ctx context.Context, subject, message string) error {
for _, deviceNickname := range pb.deviceNicknames {
dev, err := pb.client.Device(deviceNickname)
if err != nil {
return errors.Wrapf(err, "failed to find Pushbullet device with nickname '%s'", deviceNickname)
}
select {
case <-ctx.Done():
return nil
default:
dev, err := pb.client.Device(deviceNickname)
if err != nil {
return errors.Wrapf(err, "failed to find Pushbullet device with nickname '%s'", deviceNickname)
}
err = dev.PushNote(subject, message)
if err != nil {
return errors.Wrapf(err, "failed to send message to Pushbullet device with nickname '%s'", deviceNickname)
err = dev.PushNote(subject, message)
if err != nil {
return errors.Wrapf(err, "failed to send message to Pushbullet device with nickname '%s'", deviceNickname)
}
}
}

View File

@ -1,6 +1,8 @@
package pushbullet
import (
"context"
"github.com/cschomburg/go-pushbullet"
"github.com/pkg/errors"
)
@ -43,7 +45,7 @@ func (sms *SMS) AddReceivers(phoneNumbers ...string) {
// Send takes a message subject and a message body and sends them to all phone numbers.
// see https://help.pushbullet.com/articles/how-do-i-send-text-messages-from-my-computer/
func (sms SMS) Send(subject, message string) error {
func (sms SMS) Send(ctx context.Context, subject, message string) error {
fullMessage := subject + "\n" + message // Treating subject as message title
user, err := sms.client.Me()
if err != nil {
@ -51,9 +53,14 @@ func (sms SMS) Send(subject, message string) error {
}
for _, phoneNumber := range sms.phoneNumbers {
err = sms.client.PushSMS(user.Iden, sms.deviceIdentifier, phoneNumber, fullMessage)
if err != nil {
return errors.Wrapf(err, "failed to send SMS message to %s via Pushbullet", phoneNumber)
select {
case <-ctx.Done():
return nil
default:
err = sms.client.PushSMS(user.Iden, sms.deviceIdentifier, phoneNumber, fullMessage)
if err != nil {
return errors.Wrapf(err, "failed to send SMS message to %s via Pushbullet", phoneNumber)
}
}
}

View File

@ -1,6 +1,7 @@
package sendgrid
import (
"context"
"net/http"
"github.com/pkg/errors"
@ -36,7 +37,7 @@ func (s *SendGrid) AddReceivers(addresses ...string) {
// Send takes a message subject and a message body and sends them to all previously set chats. Message body supports
// html as markup language.
func (s SendGrid) Send(subject, message string) error {
func (s SendGrid) Send(ctx context.Context, subject, message string) error {
from := mail.NewEmail(s.senderName, s.senderAddress)
content := mail.NewContent("text/html", message)
@ -53,6 +54,13 @@ func (s SendGrid) Send(subject, message string) error {
mailMessage.AddContent(content)
mailMessage.SetFrom(from)
// TODO: Should we check here or at the beginning of the func?
select {
case <-ctx.Done():
return nil
default:
}
resp, err := s.client.Send(mailMessage)
if err != nil {
return errors.Wrap(err, "failed to send mail using SendGrid service")

View File

@ -1,6 +1,8 @@
package slack
import (
"context"
"github.com/pkg/errors"
"github.com/slack-go/slack"
)
@ -34,16 +36,22 @@ func (s *Slack) AddReceivers(channelIDs ...string) {
// Send takes a message subject and a message body and sends them to all previously set channels.
// you will need a slack app with the chat:write.public and chat:write permissions.
// see https://api.slack.com/
func (s Slack) Send(subject, message string) error {
func (s Slack) Send(ctx context.Context, subject, message string) error {
fullMessage := subject + "\n" + message // Treating subject as message title
for _, channelID := range s.channelIDs {
id, timestamp, err := s.client.PostMessage(
channelID,
slack.MsgOptionText(fullMessage, false),
)
if err != nil {
return errors.Wrapf(err, "failed to send message to Slack channel '%s' at time '%s'", id, timestamp)
select {
case <-ctx.Done():
return nil
default:
id, timestamp, err := s.client.PostMessageContext(
ctx,
channelID,
slack.MsgOptionText(fullMessage, false),
)
if err != nil {
return errors.Wrapf(err, "failed to send message to Slack channel '%s' at time '%s'", id, timestamp)
}
}
}

View File

@ -1,6 +1,8 @@
package telegram
import (
"context"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/pkg/errors"
)
@ -38,17 +40,22 @@ func (t *Telegram) AddReceivers(chatIDs ...int64) {
// Send takes a message subject and a message body and sends them to all previously set chats. Message body supports
// html as markup language.
func (t Telegram) Send(subject, message string) error {
func (t Telegram) Send(ctx context.Context, subject, message string) error {
fullMessage := subject + "\n" + message // Treating subject as message title
msg := tgbotapi.NewMessage(0, fullMessage)
msg.ParseMode = defaultParseMode
for _, chatID := range t.chatIDs {
msg.ChatID = chatID
_, err := t.client.Send(msg)
if err != nil {
return errors.Wrapf(err, "failed to send message to Telegram chat '%d'", chatID)
select {
case <-ctx.Done():
return nil
default:
msg.ChatID = chatID
_, err := t.client.Send(msg)
if err != nil {
return errors.Wrapf(err, "failed to send message to Telegram chat '%d'", chatID)
}
}
}

View File

@ -1,6 +1,8 @@
package twitter
import (
"context"
"github.com/dghubble/go-twitter/twitter"
"github.com/dghubble/oauth1"
"github.com/pkg/errors"
@ -68,30 +70,35 @@ func (t *Twitter) AddReceivers(twitterIDs ...string) {
// Send takes a message subject and a message body and sends them to all previously set twitterIDs as a DM.
// See https://developer.twitter.com/en/docs/twitter-api/v1/direct-messages/sending-and-receiving/api-reference/new-event
func (t Twitter) Send(subject, message string) error {
func (t Twitter) Send(ctx context.Context, subject, message string) error {
directMessageData := &twitter.DirectMessageData{
Text: subject + "\n" + message,
}
for _, twitterID := range t.twitterIDs {
directMessageTarget := &twitter.DirectMessageTarget{
RecipientID: twitterID,
}
directMessageEvent := &twitter.DirectMessageEvent{
Type: "message_create",
Message: &twitter.DirectMessageEventMessage{
Target: directMessageTarget,
Data: directMessageData,
},
}
select {
case <-ctx.Done():
return nil
default:
directMessageTarget := &twitter.DirectMessageTarget{
RecipientID: twitterID,
}
directMessageEvent := &twitter.DirectMessageEvent{
Type: "message_create",
Message: &twitter.DirectMessageEventMessage{
Target: directMessageTarget,
Data: directMessageData,
},
}
directMessageParams := &twitter.DirectMessageEventsNewParams{
Event: directMessageEvent,
}
directMessageParams := &twitter.DirectMessageEventsNewParams{
Event: directMessageEvent,
}
_, _, err := t.client.DirectMessages.EventsNew(directMessageParams)
if err != nil {
return errors.Wrapf(err, "failed to send direct message to twitter ID '%s'", twitterID)
_, _, err := t.client.DirectMessages.EventsNew(directMessageParams)
if err != nil {
return errors.Wrapf(err, "failed to send direct message to twitter ID '%s'", twitterID)
}
}
}

View File

@ -46,7 +46,7 @@ func main() {
notifier := notify.New()
notifier.UseServices(whatsappSvc)
err = notifier.Send("subject", "message")
err = notifier.Send(context.Background(), "subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}

View File

@ -28,7 +28,7 @@ Usage:
notifier := notify.New()
notifier.UseServices(whatsappSvc)
err = notifier.Send("subject", "message")
err = notifier.Send(context.Background(), "subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}

View File

@ -1,13 +1,13 @@
// Code generated by mockery v2.0.4. DO NOT EDIT.
// Code generated by mockery v2.5.1. DO NOT EDIT.
package whatsapp
import (
"github.com/Rhymen/go-whatsapp"
"github.com/stretchr/testify/mock"
whatsapp "github.com/Rhymen/go-whatsapp"
mock "github.com/stretchr/testify/mock"
)
// mockWhatsappClient is an autogenerated mock type for the whatsappClient type
// mockWhatsappClient is an autogenerated mock type for the mockWhatsappClient type
type mockWhatsappClient struct {
mock.Mock
}

View File

@ -1,6 +1,7 @@
package whatsapp
import (
"context"
"encoding/gob"
"fmt"
"os"
@ -19,7 +20,7 @@ const (
var sessionFilePath = filepath.Join(os.TempDir(), "whatsappSession.gob")
// whatsappClient abstracts go-whatsapp for writing unit tests
// mockWhatsappClient abstracts go-whatsapp for writing unit tests
type whatsappClient interface {
Login(qrChan chan<- string) (whatsapp.Session, error)
RestoreWithSession(session whatsapp.Session) (whatsapp.Session, error)
@ -164,19 +165,24 @@ func (s *Service) AddReceivers(contacts ...string) {
}
// Send takes a message subject and a message body and sends them to all previously set contacts.
func (s *Service) Send(subject, message string) error {
func (s *Service) Send(ctx context.Context, subject, message string) error {
msg := whatsapp.TextMessage{
Text: subject + "\n" + message,
}
for _, contact := range s.contacts {
msg.Info = whatsapp.MessageInfo{
RemoteJid: contact + "@s.whatsapp.net",
}
select {
case <-ctx.Done():
return nil
default:
msg.Info = whatsapp.MessageInfo{
RemoteJid: contact + "@s.whatsapp.net",
}
_, err := s.client.Send(msg)
if err != nil {
return errors.Wrapf(err, "failed to send message to WhatsApp contact '%s'", contact)
_, err := s.client.Send(msg)
if err != nil {
return errors.Wrapf(err, "failed to send message to WhatsApp contact '%s'", contact)
}
}
}

View File

@ -1,6 +1,7 @@
package whatsapp
import (
"context"
"testing"
"github.com/Rhymen/go-whatsapp"
@ -37,7 +38,8 @@ func TestSend(t *testing.T) {
}).Return("", errors.New("some error"))
svc.client = mockClient
svc.AddReceivers("Contact1")
err := svc.Send("subject", "message")
ctx := context.Background()
err := svc.Send(ctx, "subject", "message")
assert.NotNil(err)
mockClient.AssertExpectations(t)
@ -57,7 +59,7 @@ func TestSend(t *testing.T) {
}).Return("", nil)
svc.client = mockClient
svc.AddReceivers("Contact1", "Contact2")
err = svc.Send("subject", "message")
err = svc.Send(ctx, "subject", "message")
assert.Nil(err)
mockClient.AssertExpectations(t)
}