-
Notifications
You must be signed in to change notification settings - Fork 210
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 #272 from svaloumas/feature/add-fcm-service
- Loading branch information
Showing
8 changed files
with
478 additions
and
33 deletions.
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,57 @@ | ||
# Firebase Cloud Messaging (FCM) | ||
|
||
[![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/fcm) | ||
|
||
## Prerequisites | ||
|
||
Navigate to Firebase [console](https://console.firebase.google.com/), login with your Google account and create a new project. | ||
You will find the `Server Key` in the project settings screen under `Cloud Messaging` tab. When the server is up and running | ||
you can add Firebase to your applications following the instructions in the `Engage/Cloud Messaging` section. | ||
|
||
To test the integration with a device you can use [FCM toolbox](https://simonmarquis.github.io/FCM-toolbox). You can also download the app | ||
to your mobile, create a device token and test the reachability of your device. | ||
|
||
## Usage | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
|
||
"github.com/nikoksr/notify" | ||
"github.com/nikoksr/notify/service/fcm" | ||
) | ||
|
||
func main() { | ||
fcmSvc, err := fcm.New("server_api_key") | ||
if err != nil { | ||
log.Fatalf("fcm.New() failed: %s", err.Error()) | ||
} | ||
|
||
fcmSvc.AddReceivers("deviceToken1") | ||
|
||
notifier := notify.New() | ||
notifier.UseServices(fcmSvc) | ||
|
||
// Use context.Background() if you want to send a simple notification message. | ||
ctx := context.Background() | ||
|
||
// Optionally, you can include additional data in the message payload by adding the corresponding value to the context. | ||
ctxWithData := context.WithValue(ctx, fcm.DataKey, map[string]interface{}{ | ||
"some-key": "some-value", | ||
"other-key": "other-value", | ||
}) | ||
|
||
// Optionally, you can specify a total of retry attempts per each message by adding the corresponding value to the context. | ||
ctxWithDataAndRetries := context.WithValue(ctxWithData, fcm.RetriesKey, 3) | ||
|
||
err = notifier.Send(ctxWithDataAndRetries, "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,48 @@ | ||
/* | ||
Package fcm provides message notification integration for Firebase Cloud Messaging (FCM). | ||
Usage: | ||
package main | ||
import ( | ||
"context" | ||
"log" | ||
"github.com/nikoksr/notify" | ||
"github.com/nikoksr/notify/service/fcm" | ||
) | ||
func main() { | ||
fcmSvc, err := fcm.New("server_api_key") | ||
if err != nil { | ||
log.Fatalf("fcm.New() failed: %s", err.Error()) | ||
} | ||
fcmSvc.AddReceivers("deviceToken1") | ||
notifier := notify.New() | ||
notifier.UseServices(fcmSvc) | ||
// Use context.Background() if you want to send a simple notification message. | ||
ctx := context.Background() | ||
// Optionally, you can include additional data in the message payload by adding the corresponding value to the context. | ||
ctxWithData := context.WithValue(ctx, fcm.DataKey, map[string]interface{}{ | ||
"some-key": "some-value", | ||
"other-key": "other-value", | ||
}) | ||
// Optionally, you can specify a total of retry attempts per each message by adding the corresponding value to the context. | ||
ctxWithDataAndRetries := context.WithValue(ctxWithData, fcm.RetriesKey, 3) | ||
err = notifier.Send(ctxWithDataAndRetries, "subject", "message") | ||
if err != nil { | ||
log.Fatalf("notifier.Send() failed: %s", err.Error()) | ||
} | ||
log.Println("notification sent") | ||
} | ||
*/ | ||
package fcm |
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,104 @@ | ||
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 | ||
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 | ||
} |
Oops, something went wrong.