diff --git a/README.md b/README.md index 1b66c4a4..0e515314 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Yes, please! Contributions of all kinds are very welcome! Feel free to check our | [WeChat](https://www.wechat.com) | [service/wechat](service/wechat) | [silenceper/wechat](https://github.com/silenceper/wechat) | :heavy_check_mark: | | [Webpush Notification](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) | [service/webpush](service/webpush) | [SherClockHolmes/webpush-go](https://github.com/SherClockHolmes/webpush-go/) | :heavy_check_mark: | | [WhatsApp](https://www.whatsapp.com) | [service/whatsapp](service/whatsapp) | [Rhymen/go-whatsapp](https://github.com/Rhymen/go-whatsapp) | :x: | +| [APNS2 Notification] | [service/apns2](service/apns2) | [sideshow/apns2](https://github.com/sideshow/apns2) | :heavy_check_mark: | ## Special Thanks diff --git a/go.mod b/go.mod index 5685947f..2689370c 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/go-chi/chi/v5 v5.0.8 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect @@ -61,6 +62,7 @@ require ( github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/rs/zerolog v1.30.0 // indirect + github.com/sideshow/apns2 v0.23.0 // indirect go.mau.fi/util v0.1.0 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect @@ -114,10 +116,10 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect github.com/ttacon/libphonenumber v1.2.1 // indirect - golang.org/x/crypto v0.13.0 // indirect - golang.org/x/net v0.15.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.16.0 // indirect golang.org/x/oauth2 v0.12.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index 84f24cff..d268a494 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20221121042443-a3fd332d56d9 h1:v github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20221121042443-a3fd332d56d9/go.mod h1:rjP7sIipbZcagro/6TCk6X0ZeFT2eyudH5+fve/cbBA= github.com/SherClockHolmes/webpush-go v1.2.0 h1:sGv0/ZWCvb1HUH+izLqrb2i68HuqD/0Y+AmGQfyqKJA= github.com/SherClockHolmes/webpush-go v1.2.0/go.mod h1:w6X47YApe/B9wUz2Wh8xukxlyupaxSSEbu6yKJcHN2w= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis/v2 v2.30.0 h1:uA3uhDbCxfO9+DI/DuGeAMr9qI+noVWwGPNTFuKID5M= @@ -128,6 +130,9 @@ github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZg github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -263,6 +268,8 @@ github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekuei github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= github.com/sendgrid/sendgrid-go v3.13.0+incompatible h1:HZrzc06/QfBGesY9o3n1lvBrRONA+57rbDRKet7plos= github.com/sendgrid/sendgrid-go v3.13.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= +github.com/sideshow/apns2 v0.23.0 h1:lpkikaZ995GIcKk6AFsYzHyezCrsrfEDvUWcWkEGErY= +github.com/sideshow/apns2 v0.23.0/go.mod h1:7Fceu+sL0XscxrfLSkAoH6UtvKefq3Kq1n4W3ayQZqE= github.com/silenceper/wechat/v2 v2.1.5 h1:eIlv61v2bAFBG9ZE75zuRC0ALHEZEUq8JlJ9tfKvatg= github.com/silenceper/wechat/v2 v2.1.5/go.mod h1:7Iu3EhQYVtDUJAj+ZVRy8yom75ga7aDWv8RurLkVm0s= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= @@ -280,6 +287,7 @@ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -319,6 +327,7 @@ go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE= go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +golang.org/x/crypto v0.0.0-20170512130425-ab89591268e0/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -328,6 +337,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= @@ -354,10 +365,13 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= @@ -397,6 +411,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -456,6 +471,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= diff --git a/service/apns2/README.md b/service/apns2/README.md new file mode 100644 index 00000000..9be2b70f --- /dev/null +++ b/service/apns2/README.md @@ -0,0 +1,27 @@ +# APNS2 + +[APNS2](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns) sending notifications through the apns server directly to iphone devices + +## Usage + +```go +// Create a apns2 service. `service.p12` or `service.pem` is generated when you install the application. +apns2Service := apns2.New(P12File("/cert/service.p12",""),"") +apns2Service = apns2.New(P12Bytes([]byte{},""),"") +apns2Service = apns2.New(PemFile("/cert/service.pem",""),"") +apns2Service = apns2.New(PemBytes([]byte{},""),"") + +// Add devices +apns2Service.AddReceivers("","") + +// Tell our notifier to use the apns2 service. +notify.UseServices(apns2Service) + +// Send a test message. +_ = notify.Send( + context.Background(), + "Subject/Title", + "The actual message - Hello, you awesome gophers! :)", +) +``` + diff --git a/service/apns2/apns2.go b/service/apns2/apns2.go new file mode 100644 index 00000000..85f702bf --- /dev/null +++ b/service/apns2/apns2.go @@ -0,0 +1,126 @@ +package apns2 + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + apnsSvc "github.com/sideshow/apns2" + "github.com/sideshow/apns2/certificate" +) + +// Compile-time check that fcm.Client satisfies fcmClient interface. +var _ apns2Client = &apnsSvc.Client{} + +//go:generate mockery --name=apns2Client --output=. --case=underscore --inpackage +type apns2Client interface { + Push(n *apnsSvc.Notification) (*apnsSvc.Response, error) +} + +// hook to parse p12 bytes for credentials +func P12Bytes(bytes []byte, password string) func() (apns2Client, error) { + return func() (apns2Client, error) { + cert, err := certificate.FromP12Bytes(bytes, password) + if err != nil { + return nil, errors.Wrapf(err, "invalid certificates %s %s", bytes, password) + } + + client := apnsSvc.NewClient(cert).Production() + return client, nil + } +} + +// hook to parse p12 file for credentials +func P12File(filename, password string) func() (apns2Client, error) { + return func() (apns2Client, error) { + cert, err := certificate.FromP12File(filename, password) + if err != nil { + return nil, errors.Wrapf(err, "invalid certificates %s %s", filename, password) + } + + client := apnsSvc.NewClient(cert).Production() + return client, nil + } +} + +// hook to parse pem file for credentials +func PemFile(filename, password string) func() (apns2Client, error) { + return func() (apns2Client, error) { + cert, err := certificate.FromPemFile(filename, password) + if err != nil { + return nil, errors.Wrapf(err, "invalid certificates %s %s", filename, password) + } + + client := apnsSvc.NewClient(cert).Production() + return client, nil + } +} + +// hook to parse pem bytes for credentials +func PemBytes(bytes []byte, password string) func() (apns2Client, error) { + return func() (apns2Client, error) { + cert, err := certificate.FromPemBytes(bytes, password) + if err != nil { + return nil, errors.Wrapf(err, "invalid certificates %s %s", bytes, password) + } + + client := apnsSvc.NewClient(cert).Production() + return client, nil + } +} + +func buildNotification(token, topic, msg string) *apnsSvc.Notification { + notification := &apnsSvc.Notification{} + notification.DeviceToken = token + notification.Topic = topic + notification.Payload = []byte(fmt.Sprintf(`{"aps":{"alert":"%s"}}`, msg)) + + return notification +} + +// Service encapsulates the APNS2 client along with internal state for storing device tokens. +type Service struct { + client apns2Client + topic string + deviceTokens []string +} + +// New returns a new instance of a APNS2 notification service +func New(makeClient func() (apns2Client, error), topic string) (*Service, error) { + apnsClient, err := makeClient() + if err != nil { + return nil, err + } + + client := &Service{ + apnsClient, + topic, + make([]string, 0), + } + return client, nil +} + +// AddReceivers takes APNS2 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 { + for _, deviceToken := range s.deviceTokens { + select { + case <-ctx.Done(): + return ctx.Err() + default: + notification := buildNotification(deviceToken, s.topic, subject+" "+message) + + _, err := s.client.Push(notification) + if err != nil { + return errors.Wrapf(err, "failed to send notification to %s", deviceToken) + } + } + } + + return nil +} diff --git a/service/apns2/apns2_test.go b/service/apns2/apns2_test.go new file mode 100644 index 00000000..49ffbd35 --- /dev/null +++ b/service/apns2/apns2_test.go @@ -0,0 +1,58 @@ +package apns2 + +import ( + "context" + "testing" + + apnsSvc "github.com/sideshow/apns2" + "github.com/stretchr/testify/require" +) + +func TestAPNS2_AddReceivers(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 TestBuildNotification(t *testing.T) { + t.Parallel() + + assert := require.New(t) + notification := buildNotification("", "", "test notification") + assert.IsType(new(apnsSvc.Notification), notification) + + assert.Equal(notification.Topic, "") + assert.Equal(notification.DeviceToken, "") +} + +func TestSend(t *testing.T) { + t.Parallel() + + assert := require.New(t) + + client := &mockApns2Client{} + notification := buildNotification("", "", "subject message") + client.On("Push", notification).Return(&apnsSvc.Response{ + StatusCode: 200, + Reason: "", + ApnsID: "", + Timestamp: apnsSvc.Time{}, + }, nil) + + svc := &Service{ + client: client, + topic: "", + deviceTokens: []string{""}, + } + err := svc.Send(context.Background(), "subject", "message") + + assert.Nil(err) +} diff --git a/service/apns2/doc.go b/service/apns2/doc.go new file mode 100644 index 00000000..8e5f5b97 --- /dev/null +++ b/service/apns2/doc.go @@ -0,0 +1,39 @@ +/* +Package apns2 provides a service for sending notifications to ios. + +Usage: + + package main + + import ( + "context" + "log" + + "github.com/nikoksr/notify" + "github.com/nikoksr/notify/service/apns2" + ) + + func main() { + + // Create a apns2 service. `service.p12` or `service.pem` is generated when you install the application. + apns2Service := apns2.New(P12File("/cert/service.p12",""),"") + apns2Service = apns2.New(P12Bytes([]byte{},""),"") + apns2Service = apns2.New(PemFile("/cert/service.pem",""),"") + apns2Service = apns2.New(PemBytes([]byte{},""),"") + + // Add devices + apns2Service.AddReceivers("","") + + // Tell our notifier to use the apns2 service. + notify.UseServices(apns2Service) + + // Send a test message. + _ = notify.Send( + context.Background(), + "Subject/Title", + "The actual message - Hello, you awesome gophers! :)", + ) + + } +*/ +package apns2 diff --git a/service/apns2/mock_apns2_client.go b/service/apns2/mock_apns2_client.go new file mode 100644 index 00000000..61feb754 --- /dev/null +++ b/service/apns2/mock_apns2_client.go @@ -0,0 +1,53 @@ +// Code generated by mockery v2.35.1. DO NOT EDIT. + +package apns2 + +import ( + sideshowapns2 "github.com/sideshow/apns2" + mock "github.com/stretchr/testify/mock" +) + +// mockApns2Client is an autogenerated mock type for the apns2Client type +type mockApns2Client struct { + mock.Mock +} + +// Push provides a mock function with given fields: n +func (_m *mockApns2Client) Push(n *sideshowapns2.Notification) (*sideshowapns2.Response, error) { + ret := _m.Called(n) + + var r0 *sideshowapns2.Response + var r1 error + if rf, ok := ret.Get(0).(func(*sideshowapns2.Notification) (*sideshowapns2.Response, error)); ok { + return rf(n) + } + if rf, ok := ret.Get(0).(func(*sideshowapns2.Notification) *sideshowapns2.Response); ok { + r0 = rf(n) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*sideshowapns2.Response) + } + } + + if rf, ok := ret.Get(1).(func(*sideshowapns2.Notification) error); ok { + r1 = rf(n) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// newMockApns2Client creates a new instance of mockApns2Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockApns2Client(t interface { + mock.TestingT + Cleanup(func()) +}) *mockApns2Client { + mock := &mockApns2Client{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}