1
0
mirror of https://github.com/nikoksr/notify.git synced 2024-11-24 08:22:18 +02:00

feat(service): add fcm

This commit is contained in:
Stefanos Valoumas 2022-05-08 12:56:01 +03:00
parent 4564e0229e
commit a38dbb0c2c
7 changed files with 477 additions and 33 deletions

5
go.mod
View File

@ -33,7 +33,10 @@ require (
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
)
require github.com/google/go-cmp v0.5.8
require (
github.com/appleboy/go-fcm v0.1.5
github.com/google/go-cmp v0.5.8
)
require (
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 // indirect

34
go.sum
View File

@ -48,54 +48,32 @@ github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210506200512-3b3352a919f0 h1:1
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210506200512-3b3352a919f0/go.mod h1:rjP7sIipbZcagro/6TCk6X0ZeFT2eyudH5+fve/cbBA=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/appleboy/go-fcm v0.1.5 h1:fKbcZf/7vwGsvDkcop8a+kCHnK+tt4wXX0X7uEzwI6E=
github.com/appleboy/go-fcm v0.1.5/go.mod h1:MSxZ4LqGRsnywOjnlXJXMqbjZrG4vf+0oHitfC9HRH0=
github.com/atc0005/go-teams-notify/v2 v2.6.1 h1:t22ybzQuaQs4UJe4ceF5VYGsPhs6ir3nZOId/FBy6Go=
github.com/atc0005/go-teams-notify/v2 v2.6.1/go.mod h1:xo6GejLDHn3tWBA181F8LrllIL0xC1uRsRxq7YNXaaY=
github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA=
github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2 v1.16.3 h1:0W1TSJ7O6OzwuEvIXAtJGvOeQ0SGAhcpxPN2/NK5EhM=
github.com/aws/aws-sdk-go-v2 v1.16.3/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2/config v1.15.3 h1:5AlQD0jhVXlGzwo+VORKiUuogkG7pQcLJNzIzK7eodw=
github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg=
github.com/aws/aws-sdk-go-v2/config v1.15.4 h1:P4mesY1hYUxru4f9SU0XxNKXmzfxsD0FtMIPRBjkH7Q=
github.com/aws/aws-sdk-go-v2/config v1.15.4/go.mod h1:ZijHHh0xd/A+ZY53az0qzC5tT46kt4JVCePf2NX9Lk4=
github.com/aws/aws-sdk-go-v2/credentials v1.11.2 h1:RQQ5fzclAKJyY5TvF+fkjJEwzK4hnxQCLOu5JXzDmQo=
github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g=
github.com/aws/aws-sdk-go-v2/credentials v1.12.0 h1:4R/NqlcRFSkR0wxOhgHi+agGpbEr5qMCjn7VqUIJY+E=
github.com/aws/aws-sdk-go-v2/credentials v1.12.0/go.mod h1:9YWk7VW+eyKsoIL6/CljkTrNVWBSK9pkqOPUuijid4A=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3 h1:LWPg5zjHV9oz/myQr4wMs0gi4CjnDN/ILmyZUFYXZsU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 h1:FP8gquGeGHHdfY6G5llaMQDF+HAf20VKc8opRwmjf04=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4/go.mod h1:u/s5/Z+ohUQOPXl00m2yJVyioWDECsbpXTQlaqSlufc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9 h1:onz/VaaxZ7Z4V+WIN9Txly9XLTmoOh1oJ8XcAC3pako=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 h1:uFWgo6mGJI1n17nbcvSc6fxVuR3xLNqvXt12JCnEcT8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10/go.mod h1:F+EZtuIwjlv35kRJPyBGcsA4f7bnSoz15zOQ2lJq1Z4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3 h1:9stUQR/u2KXU6HkFJYlqnZEjBnbgrVbG6I5HN09xZh0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4 h1:cnsvEKSoHN4oAN7spMMr0zhEW2MHnhAVpmqQg8E6UcM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4/go.mod h1:8glyUqVIM4AmeenIsPo0oVh3+NUwnsQml2OFupfQW+0=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10 h1:by9P+oy3P/CwggN4ClnW2D4oL91QV7pBzBICi1chZvQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11 h1:6cZRymlLEIlDTEB0+5+An6Zj1CKt6rSE69tOmFeu1nk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11/go.mod h1:0MR+sS1b/yxsfAPvAESrw8NfwUoxMinDyw6EYR9BS2U=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3 h1:Gh1Gpyh01Yvn7ilO/b/hr01WgNpaszfbKMUgqM186xQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 h1:b16QW0XWl0jWjLABFc1A+uh145Oqv+xDcObNk0iQgUk=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4/go.mod h1:uKkN7qmSIsNJVyMtxNQoCEYMvFEXbOg9fwCJPdfp2u8=
github.com/aws/aws-sdk-go-v2/service/ses v1.14.3 h1:SNFIa2NvkZ46nkkUS6DlTjgQItjjYCjrh+UCpD5mF4Y=
github.com/aws/aws-sdk-go-v2/service/ses v1.14.3/go.mod h1:VPOcsfmvq+FyaT47LT8ReFGD0NXnPEbcJGbxm7mC3yg=
github.com/aws/aws-sdk-go-v2/service/ses v1.14.4 h1:ke4Kp6GolWjw9xTtE9PQPw+jN/zcBi9pcunAh9apAW8=
github.com/aws/aws-sdk-go-v2/service/ses v1.14.4/go.mod h1:YPeSVmu3+2iUSCUIspo+vvFe3LKCIW8x8Dxnuadh6AU=
github.com/aws/aws-sdk-go-v2/service/sns v1.17.4 h1:7TdmoJJBwLFyakXjfrGztejwY5Ie1JEto7YFfznCmAw=
github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw=
github.com/aws/aws-sdk-go-v2/service/sns v1.17.5 h1:g04C27W8hBB+8T4q7t3TAF4i9ZbTYLr3i9bhFAERIzM=
github.com/aws/aws-sdk-go-v2/service/sns v1.17.5/go.mod h1:U7g4gHRcOB0H1VYv2yNzAOmGZS8dTEUZ/HkSSO6Pggg=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.3 h1:frW4ikGcxfAEDfmQqWgMLp+F1n4nRo9sF39OcIb5BkQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.4 h1:Uw5wBybFQ1UeA9ts0Y07gbv0ncZnIAyw858tDW0NP2o=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.4/go.mod h1:cPDwJwsP4Kff9mldCXAmddjJL6JGQqtA3Mzer2zyr88=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.3 h1:cJGRyzCSVwZC7zZZ1xbx9m32UnrKydRYhOvcD1NYP9Q=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4 h1:+xtV90n3abQmgzk1pS++FdxZTrPEDgQng6e4/56WR2A=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4/go.mod h1:lfSYenAXtavyX2A1LsViglqlG9eEFYxNryTZS5rn3QE=
github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE=
@ -122,8 +100,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/dghubble/go-twitter v0.0.0-20220413154426-14d8abde2e80 h1:pq636dbda5rkQyrFUOeADrGeKQGlCHBvcpGKqsVZ4ZQ=
github.com/dghubble/go-twitter v0.0.0-20220413154426-14d8abde2e80/go.mod h1:7vl8uM4He/dTj20TDHBwvkKEBPUm60re8htWb7YRo+Q=
github.com/dghubble/go-twitter v0.0.0-20220428155120-ee736133298b h1:jNYx4ME9CcTvFinBz3T8p/5nZwhggUA7iXaWH0t2ffo=
github.com/dghubble/go-twitter v0.0.0-20220428155120-ee736133298b/go.mod h1:rJg8kU8GQWeH9lutdlROcjIGIgLab0tZfn7jgZ6FeBU=
github.com/dghubble/oauth1 v0.7.1 h1:JjbOVSVVkms9A4h/sTQy5Jb2nFuAAVb2qVYgenJPyrE=
@ -194,7 +170,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -277,8 +252,6 @@ github.com/pkg/errors v0.8.1-0.20161029093637-248dadf4e906/go.mod h1:bwawxfHBFNV
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/plivo/plivo-go/v7 v7.8.0 h1:qa5SXRduZEgAYb6Uv2ZeYjm1ZnlQzCprS0xjHxB8EAU=
github.com/plivo/plivo-go/v7 v7.8.0/go.mod h1:Jw1e16x0WjW334botVeKQT4hcMXMN55r4HH0XoGsR6Q=
github.com/plivo/plivo-go/v7 v7.9.0 h1:wpSG28GoygIZ+kaYKYGmlyF6dihT1wbNAUmE5y72Kh8=
github.com/plivo/plivo-go/v7 v7.9.0/go.mod h1:Jw1e16x0WjW334botVeKQT4hcMXMN55r4HH0XoGsR6Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -296,8 +269,6 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/slack-go/slack v0.10.2 h1:KMN/h2sgUninHXvQI8PrR/PHBUuWp2NPvz2Kr66tki4=
github.com/slack-go/slack v0.10.2/go.mod h1:5FLdBRv7VW/d9EBxx/eEktOptWygbA9K2QK/KW7ds1s=
github.com/slack-go/slack v0.10.3 h1:kKYwlKY73AfSrtAk9UHWCXXfitudkDztNI9GYBviLxw=
github.com/slack-go/slack v0.10.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
@ -520,7 +491,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=

57
service/fcm/README.md Normal file
View File

@ -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")
}
```

48
service/fcm/doc.go Normal file
View File

@ -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

104
service/fcm/fcm.go Normal file
View File

@ -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
}

214
service/fcm/fcm_test.go Normal file
View File

@ -0,0 +1,214 @@
package fcm
import (
"context"
"errors"
"testing"
"github.com/appleboy/go-fcm"
"github.com/stretchr/testify/require"
)
func TestAddReceivers(t *testing.T) {
t.Parallel()
assert := require.New(t)
svc := &Service{
deviceTokens: []string{},
}
deviceTokens := []string{"Token1", "Token2", "Token3"}
svc.AddReceivers(deviceTokens...)
assert.Equal(svc.deviceTokens, deviceTokens)
}
func TestSend(t *testing.T) {
t.Parallel()
assert := require.New(t)
svc := &Service{
deviceTokens: []string{},
}
mockToken := "deviceToken"
mockData := map[string]interface{}{
"foo": "bar",
}
// test fcm client send
mockClient := newMockFCMClient(t)
mockClient.On("SendWithRetry", &fcm.Message{
To: mockToken,
Notification: &fcm.Notification{
Title: "subject",
Body: "message",
},
}, 0).Return(&fcm.Response{Success: 1}, nil)
svc.client = mockClient
svc.AddReceivers(mockToken)
ctx := context.Background()
err := svc.Send(ctx, "subject", "message")
assert.Nil(err)
mockClient.AssertExpectations(t)
// test fcm client send with data
mockClient = newMockFCMClient(t)
mockClient.On("SendWithRetry", &fcm.Message{
To: mockToken,
Data: mockData,
Notification: &fcm.Notification{
Title: "subject",
Body: "message",
},
}, 0).Return(&fcm.Response{Success: 1}, nil)
svc.client = mockClient
svc.AddReceivers(mockToken)
ctx = context.Background()
ctxWithData := context.WithValue(ctx, DataKey, mockData)
err = svc.Send(ctxWithData, "subject", "message")
assert.Nil(err)
mockClient.AssertExpectations(t)
// test fcm client send with data and retries
mockClient = newMockFCMClient(t)
mockClient.On("SendWithRetry", &fcm.Message{
To: mockToken,
Data: mockData,
Notification: &fcm.Notification{
Title: "subject",
Body: "message",
},
}, 3).Return(&fcm.Response{Success: 1}, nil)
svc.client = mockClient
svc.AddReceivers(mockToken)
ctx = context.Background()
ctxWithData = context.WithValue(ctx, DataKey, mockData)
ctxWithDataAndRetries := context.WithValue(ctxWithData, RetriesKey, 3)
err = svc.Send(ctxWithDataAndRetries, "subject", "message")
assert.Nil(err)
mockClient.AssertExpectations(t)
// test fcm client returning error
mockClient = newMockFCMClient(t)
mockClient.On("SendWithRetry", &fcm.Message{
To: mockToken,
Data: mockData,
Notification: &fcm.Notification{
Title: "subject",
Body: "message",
},
}, 3).Return(nil, errors.New("some error"))
svc.client = mockClient
svc.AddReceivers(mockToken)
ctx = context.Background()
ctxWithData = context.WithValue(ctx, DataKey, mockData)
ctxWithDataAndRetries = context.WithValue(ctxWithData, RetriesKey, 3)
err = svc.Send(ctxWithDataAndRetries, "subject", "message")
assert.NotNil(err)
mockClient.AssertExpectations(t)
// test fcm client multiple receivers
anotherMockToken := "another_device_token"
mockClient = newMockFCMClient(t)
mockClient.On("SendWithRetry", &fcm.Message{
To: mockToken,
Data: mockData,
Notification: &fcm.Notification{
Title: "subject",
Body: "message",
},
}, 3).Return(&fcm.Response{Success: 1}, nil)
mockClient.On("SendWithRetry", &fcm.Message{
To: anotherMockToken,
Data: mockData,
Notification: &fcm.Notification{
Title: "subject",
Body: "message",
},
}, 3).Return(&fcm.Response{Success: 1}, nil)
svc.client = mockClient
svc.AddReceivers(mockToken, anotherMockToken)
ctx = context.Background()
ctxWithData = context.WithValue(ctx, DataKey, mockData)
ctxWithDataAndRetries = context.WithValue(ctxWithData, RetriesKey, 3)
err = svc.Send(ctxWithDataAndRetries, "subject", "message")
assert.Nil(err)
mockClient.AssertExpectations(t)
}
func TestGetMessageData(t *testing.T) {
t.Parallel()
assert := require.New(t)
mockData := map[string]interface{}{
"foo": "bar",
}
// test without data
ctx := context.Background()
data, ok := getMessageData(ctx)
assert.Nil(data)
assert.False(ok)
// test with invalid type of data
ctx = context.Background()
ctxWithInvalidTypeOfData := context.WithValue(ctx, DataKey, "invalid_type_of_data")
data, ok = getMessageData(ctxWithInvalidTypeOfData)
assert.Nil(data)
assert.False(ok)
// test with invalid data key
ctx = context.Background()
invalidDataKey := struct {
Key string
}{
Key: "invalid_data_key",
}
ctxWithInvalidDataKey := context.WithValue(ctx, invalidDataKey, mockData)
data, ok = getMessageData(ctxWithInvalidDataKey)
assert.Nil(data)
assert.False(ok)
// test with data
ctx = context.Background()
ctxWithData := context.WithValue(ctx, DataKey, mockData)
data, ok = getMessageData(ctxWithData)
assert.Equal(mockData, data)
assert.True(ok)
}
func TestGetMessageRetryAttempts(t *testing.T) {
t.Parallel()
assert := require.New(t)
// test without retries
ctx := context.Background()
retryAttempts := getMessageRetryAttempts(ctx)
assert.Equal(0, retryAttempts)
// test with invalid type of retries
ctx = context.Background()
ctxWithInvalidTypeOfRetries := context.WithValue(ctx, RetriesKey, "invalid_type_of_retries")
retryAttempts = getMessageRetryAttempts(ctxWithInvalidTypeOfRetries)
assert.Equal(0, retryAttempts)
// test with invalid retries key
ctx = context.Background()
invalidRetriesKey := struct {
Key string
}{
Key: "invalid_retries_key",
}
ctxWithInvalidRetriesKey := context.WithValue(ctx, invalidRetriesKey, 3)
retryAttempts = getMessageRetryAttempts(ctxWithInvalidRetriesKey)
assert.Equal(0, retryAttempts)
// test with retries
ctx = context.Background()
ctxWithRetries := context.WithValue(ctx, RetriesKey, 3)
retryAttempts = getMessageRetryAttempts(ctxWithRetries)
assert.Equal(3, retryAttempts)
}

View File

@ -0,0 +1,48 @@
// Code generated by mockery v2.12.2. DO NOT EDIT.
package fcm
import (
fcm "github.com/appleboy/go-fcm"
mock "github.com/stretchr/testify/mock"
testing "testing"
)
// mockFCMClient is an autogenerated mock type for the mockFCMClient type
type mockFCMClient struct {
mock.Mock
}
// SendWithRetry provides a mock function with given fields: _a0, _a1
func (_m *mockFCMClient) SendWithRetry(_a0 *fcm.Message, _a1 int) (*fcm.Response, error) {
ret := _m.Called(_a0, _a1)
var r0 *fcm.Response
if rf, ok := ret.Get(0).(func(*fcm.Message, int) *fcm.Response); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*fcm.Response)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*fcm.Message, int) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// newMockFCMClient creates a new instance of mockFCMClient. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func newMockFCMClient(t testing.TB) *mockFCMClient {
mock := &mockFCMClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}