From 0d296a672eb94a359eabecf2effd4b534e9f2f1b Mon Sep 17 00:00:00 2001 From: Andre Medeiros Date: Mon, 26 Jul 2021 13:05:09 -0400 Subject: [PATCH] add test notification endpoint --- go.mod | 1 + go.sum | 2 ++ internal/api/api.go | 19 ++++++++++++- internal/api/devices.go | 63 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9a8a815..9ccb6bd 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/DataDog/datadog-go v4.8.1+incompatible github.com/Microsoft/go-winio v0.5.0 // indirect github.com/adjust/rmq/v4 v4.0.1 + github.com/dustin/go-humanize v1.0.0 // indirect github.com/go-co-op/gocron v1.6.2 github.com/go-redis/redis/v8 v8.11.0 github.com/heroku/x v0.0.31 diff --git a/go.sum b/go.sum index 366d097..d9bf387 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,8 @@ github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fp github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= diff --git a/internal/api/api.go b/internal/api/api.go index d4f83df..ffcfa11 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -9,6 +9,7 @@ import ( "github.com/DataDog/datadog-go/statsd" "github.com/jackc/pgx/v4/pgxpool" "github.com/julienschmidt/httprouter" + "github.com/sideshow/apns2/token" "github.com/sirupsen/logrus" "github.com/christianselig/apollo-backend/internal/data" @@ -21,6 +22,7 @@ type api struct { db *pgxpool.Pool reddit *reddit.Client models *data.Models + apns *token.Token } func NewAPI(ctx context.Context, logger *logrus.Logger, statsd *statsd.Client, db *pgxpool.Pool) *api { @@ -31,9 +33,23 @@ func NewAPI(ctx context.Context, logger *logrus.Logger, statsd *statsd.Client, d 16, ) + var apns *token.Token + { + authKey, err := token.AuthKeyFromFile(os.Getenv("APPLE_KEY_PATH")) + if err != nil { + panic(err) + } + + apns = &token.Token{ + AuthKey: authKey, + KeyID: os.Getenv("APPLE_KEY_ID"), + TeamID: os.Getenv("APPLE_TEAM_ID"), + } + } + models := data.NewModels(ctx, db) - return &api{logger, statsd, db, reddit, models} + return &api{logger, statsd, db, reddit, models, apns} } func (a *api) Server(port int) *http.Server { @@ -49,6 +65,7 @@ func (a *api) Routes() *httprouter.Router { router.GET("/v1/health", a.healthCheckHandler) router.POST("/v1/device", a.upsertDeviceHandler) + router.POST("/v1/device/test", a.testDeviceHandler) router.POST("/v1/device/:apns/account", a.upsertAccountHandler) return router diff --git a/internal/api/devices.go b/internal/api/devices.go index 2f6eafd..b0cd243 100644 --- a/internal/api/devices.go +++ b/internal/api/devices.go @@ -1,10 +1,16 @@ package api import ( + "context" "encoding/json" + "fmt" "net/http" + "github.com/dustin/go-humanize/english" "github.com/julienschmidt/httprouter" + "github.com/sideshow/apns2" + "github.com/sideshow/apns2/payload" + "github.com/sirupsen/logrus" "github.com/christianselig/apollo-backend/internal/data" ) @@ -23,3 +29,60 @@ func (a *api) upsertDeviceHandler(w http.ResponseWriter, r *http.Request, _ http w.WriteHeader(http.StatusOK) } + +func (a *api) testDeviceHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + ctx := context.Background() + + d, err := a.models.Devices.GetByAPNSToken(ps.ByName("apns")) + if err != nil { + a.logger.WithFields(logrus.Fields{ + "err": err, + }).Info("failed fetching device from database") + a.errorResponse(w, r, 500, err.Error()) + return + } + + stmt := ` + SELECT username + FROM accounts + INNER JOIN devices_accounts ON devices.account_id = accounts.id + WHERE devices_accounts.device = $1` + rows, err := a.db.Query(ctx, stmt, d.ID) + if err != nil { + a.logger.WithFields(logrus.Fields{ + "apns": ps.ByName("apns"), + "err": err, + }).Error("failed to fetch device accounts") + return + } + defer rows.Close() + + var users []string + for rows.Next() { + var user string + rows.Scan(&user) + users = append(users, user) + } + + body := fmt.Sprintf("Active usernames are: %s", english.OxfordWordSeries(users, "and")) + notification := &apns2.Notification{} + notification.Topic = "com.christianselig.Apollo" + notification.Payload = payload. + NewPayload(). + Category("test-notification"). + AlertBody(body) + + client := apns2.NewTokenClient(a.apns) + if !d.Sandbox { + client = client.Production() + } + + if _, err := client.Push(notification); err != nil { + a.logger.WithFields(logrus.Fields{ + "err": err, + }).Info("failed to send test notification") + a.errorResponse(w, r, 500, err.Error()) + return + } + w.WriteHeader(http.StatusOK) +}