mirror of
https://github.com/nikoksr/notify.git
synced 2025-01-10 00:28:36 +02:00
107 lines
2.5 KiB
Go
107 lines
2.5 KiB
Go
package fcm
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/appleboy/go-fcm"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Compile-time check that fcm.Client satisfies fcmClient interface.
|
|
var _ fcmClient = &fcm.Client{}
|
|
|
|
var (
|
|
// DataKey is used as a context.Context key to optionally add data to the message payload.
|
|
DataKey = msgDataKey{}
|
|
// RetriesKey is used as a context.Context key to optionally set a total of retry attempts per each message.
|
|
RetriesKey = msgRetriesKey{}
|
|
)
|
|
|
|
type (
|
|
msgDataKey struct{}
|
|
msgRetriesKey struct{}
|
|
)
|
|
|
|
// fcmClient abstracts go-fcm for writing unit tests
|
|
//
|
|
//go:generate mockery --name=fcmClient --output=. --case=underscore --inpackage
|
|
type fcmClient interface {
|
|
SendWithRetry(*fcm.Message, int) (*fcm.Response, error)
|
|
}
|
|
|
|
// Service encapsulates the FCM client along with internal state for storing device tokens.
|
|
type Service struct {
|
|
client fcmClient
|
|
deviceTokens []string
|
|
}
|
|
|
|
// New returns a new instance of a FCM notification service.
|
|
func New(serverAPIKey string) (*Service, error) {
|
|
client, err := fcm.NewClient(serverAPIKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s := &Service{
|
|
client: client,
|
|
deviceTokens: []string{},
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// AddReceivers takes FCM device tokens and appends them to the internal device tokens slice.
|
|
// The Send method will send a given message to all those devices.
|
|
func (s *Service) AddReceivers(deviceTokens ...string) {
|
|
s.deviceTokens = append(s.deviceTokens, deviceTokens...)
|
|
}
|
|
|
|
// Send takes a message subject and a message body and sends them to all previously set devices.
|
|
func (s *Service) Send(ctx context.Context, subject, message string) error {
|
|
msg := &fcm.Message{
|
|
Notification: &fcm.Notification{
|
|
Title: subject,
|
|
Body: message,
|
|
},
|
|
}
|
|
|
|
if data, ok := getMessageData(ctx); ok {
|
|
msg.Data = data
|
|
}
|
|
|
|
retryAttempts := getMessageRetryAttempts(ctx)
|
|
|
|
for _, deviceToken := range s.deviceTokens {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
msg.To = deviceToken
|
|
|
|
_, err := s.client.SendWithRetry(msg, retryAttempts)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to send message to FCM device with token '%s'", deviceToken)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getMessageData(ctx context.Context) (data map[string]interface{}, ok bool) {
|
|
value := ctx.Value(DataKey)
|
|
if value != nil {
|
|
data, ok = value.(map[string]interface{})
|
|
}
|
|
return
|
|
}
|
|
|
|
func getMessageRetryAttempts(ctx context.Context) int {
|
|
value := ctx.Value(RetriesKey)
|
|
if value != nil {
|
|
if retryAttempts, ok := value.(int); ok {
|
|
return retryAttempts
|
|
}
|
|
}
|
|
return 0
|
|
}
|