Skip to content

Commit

Permalink
use the alerts client provided by platform
Browse files Browse the repository at this point in the history
It made more sense for the alerts client to live in platform, where its
API endpoints are implemented. Now we use that version of the client instead
of this hand-rolled version.

BACK-2500
  • Loading branch information
ewollesen committed Nov 7, 2023
1 parent 7be0ec2 commit 822402c
Show file tree
Hide file tree
Showing 81 changed files with 6,971 additions and 89 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/tidepool-org/clinic/client v0.0.0-20230815132146-bd6c2982ff6d
github.com/tidepool-org/go-common v0.10.1-0.20230508194719-72b56b95a79a
github.com/tidepool-org/platform v1.33.1-0.20231013005639-3b2d96d57243
github.com/tidepool-org/platform v1.33.1-0.20231102192306-a5ca88faae46
go.mongodb.org/mongo-driver v1.11.1
go.uber.org/fx v1.13.1
go.uber.org/zap v1.22.0
Expand Down Expand Up @@ -65,3 +65,5 @@ require (
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
)

replace github.com/tidepool-org/platform => ../platform
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,6 @@ github.com/tidepool-org/clinic/client v0.0.0-20230815132146-bd6c2982ff6d h1:tlkq
github.com/tidepool-org/clinic/client v0.0.0-20230815132146-bd6c2982ff6d/go.mod h1:eduhUZw6oOhrtt2C57RGn4rYq9CoCX8ucwDV0PmxSF4=
github.com/tidepool-org/go-common v0.10.1-0.20230508194719-72b56b95a79a h1:pTkq6PU/+YbW40UgRh9tI0kRGmwxsAwOnzb2nvFG4eg=
github.com/tidepool-org/go-common v0.10.1-0.20230508194719-72b56b95a79a/go.mod h1:hJ7gk9U6QhIJsVspA8EHN8YuKZuCd/HCP25II+D63w0=
github.com/tidepool-org/platform v1.33.1-0.20231013005639-3b2d96d57243 h1:SzVkolzTYAN9wthEst1OJna6xQDszubD5XRkalNeWH4=
github.com/tidepool-org/platform v1.33.1-0.20231013005639-3b2d96d57243/go.mod h1:MmUL8WfxdJtmRPHxh4byfEFrbdOvYIB9BriUaosQkLk=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
Expand Down
87 changes: 30 additions & 57 deletions hydrophone.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package main

import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"path"
"time"

"github.com/gorilla/mux"
Expand All @@ -28,6 +24,11 @@ import (
"github.com/tidepool-org/hydrophone/models"
"github.com/tidepool-org/hydrophone/templates"
"github.com/tidepool-org/platform/alerts"
"github.com/tidepool-org/platform/auth"
authclient "github.com/tidepool-org/platform/auth/client"
"github.com/tidepool-org/platform/client"
platformlog "github.com/tidepool-org/platform/log"
"github.com/tidepool-org/platform/platform"
)

var defaultStopTimeout = 60 * time.Second
Expand Down Expand Up @@ -89,68 +90,32 @@ func seagullProvider(config OutboundConfig, httpClient *http.Client) clients.Sea
Build()
}

func alertsProvider(config OutboundConfig, httpClient *http.Client, shorelineClient shoreline.Client) api.AlertsClient {
return &alertsClient{
client: httpClient,
shoreline: shorelineClient,
host: config.DataClientAddress,
func alertsProvider(config OutboundConfig, tokenProvider auth.ExternalAccessor, logger platformlog.Logger) (api.AlertsClient, error) {
cfg := client.NewConfig()
cfg.Address = config.DataClientAddress
platformCfg := platform.NewConfig()
platformCfg.Config = cfg
platformClient, err := platform.NewClient(platformCfg, platform.AuthorizeAsService)
if err != nil {
return nil, err
}
return alerts.NewClient(platformClient, tokenProvider, logger), nil
}

type alertsClient struct {
client *http.Client
host string
shoreline clients.TokenProvider
func zapPlatformAdapterProvider(zapper *zap.SugaredLogger) platformlog.Logger {
return NewZapPlatformAdapter(zapper)
}

func (c *alertsClient) Upsert(ctx context.Context, cfg *alerts.Config) error {
url := c.urlf("/v1/alerts/%s/%s", cfg.UserID, cfg.FollowedUserID)
body := &bytes.Buffer{}
if err := json.NewEncoder(body).Encode(cfg.Alerts); err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
if err != nil {
return err
}
token := c.shoreline.TokenProvide()
req.Header.Add(api.TP_SESSION_TOKEN, token)

resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("alerts config upsert: unexpected response code: %s", resp.Status)
}

return nil
func externalConfigLoaderProvider(loader platform.ConfigLoader) authclient.ExternalConfigLoader {
return authclient.NewExternalEnvconfigLoader(loader)
}

func (c *alertsClient) urlf(pathFormat string, args ...interface{}) string {
return fmt.Sprintf(c.host+path.Join("/", pathFormat), args...)
func platformConfigLoaderProvider(loader client.ConfigLoader) platform.ConfigLoader {
return platform.NewEnvconfigLoader(loader)
}

func (c *alertsClient) Delete(ctx context.Context, cfg *alerts.Config) error {
url := c.urlf("/v1/alerts/%s/%s", cfg.UserID, cfg.FollowedUserID)
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
if err != nil {
return err
}
token := c.shoreline.TokenProvide()
req.Header.Add(api.TP_SESSION_TOKEN, token)

resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("alerts config upsert: unexpected response code: %s", resp.Status)
}

return nil
func clientConfigLoaderProvider() client.ConfigLoader {
return client.NewEnvconfigLoader()
}

func clinicProvider(config OutboundConfig, shoreline shoreline.Client) (clinicsClient.ClientWithResponsesInterface, error) {
Expand Down Expand Up @@ -307,6 +272,14 @@ func main() {
faultTolerantConsumerProvider,
events.NewHandler,
),
authclient.ExternalClientModule,
authclient.ProvideServiceName("hydrophone"),
fx.Provide(
externalConfigLoaderProvider,
platformConfigLoaderProvider,
clientConfigLoaderProvider,
zapPlatformAdapterProvider,
),
fx.Provide(
seagullProvider,
highwaterProvider,
Expand Down
125 changes: 125 additions & 0 deletions logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package main

import (
"fmt"
"sync"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"

"github.com/tidepool-org/platform/log"
)

// zapPlatformAdapter implements log.Logger for use with a zap.SugaredLogger.
type zapPlatformAdapter struct {
zapper *zap.SugaredLogger
mu sync.Mutex
}

func NewZapPlatformAdapter(zapper *zap.SugaredLogger) *zapPlatformAdapter {
return &zapPlatformAdapter{
zapper: zapper,
}
}

func (a *zapPlatformAdapter) Logf(level log.Level, message string, args ...interface{}) {
formatted := fmt.Sprintf(message, args...)
a.Log(level, formatted)
}

var loggerToZapLevels = map[log.Level]zapcore.Level{
log.DebugLevel: zapcore.DebugLevel,
log.InfoLevel: zapcore.InfoLevel,
log.WarnLevel: zapcore.WarnLevel,
log.ErrorLevel: zapcore.ErrorLevel,
}

func (a *zapPlatformAdapter) Log(level log.Level, message string) {
if zapLevel, found := loggerToZapLevels[level]; found {
a.addOne().zapper.Desugar().Log(zapLevel, message)
return
}
a.zapper.Debugf("zapPlatformHandler: unhandled log.Level %q", level)
a.zapper.With("log.level", level).Info(message)
}

func (a *zapPlatformAdapter) addOne() *zapPlatformAdapter {
return NewZapPlatformAdapter(a.zapper.Desugar().WithOptions(zap.AddCallerSkip(1)).Sugar())
}

func (a *zapPlatformAdapter) Debug(message string) {
a.addOne().Log(log.DebugLevel, message)
}

func (a *zapPlatformAdapter) Info(message string) {
a.addOne().Log(log.InfoLevel, message)
}

func (a *zapPlatformAdapter) Warn(message string) {
a.addOne().Log(log.WarnLevel, message)
}

func (a *zapPlatformAdapter) Error(message string) {
a.addOne().Log(log.ErrorLevel, message)
}

func (a *zapPlatformAdapter) Debugf(message string, args ...interface{}) {
a.addOne().Logf(log.DebugLevel, message, args...)
}

func (a *zapPlatformAdapter) Infof(message string, args ...interface{}) {
a.addOne().Logf(log.InfoLevel, message, args...)
}

func (a *zapPlatformAdapter) Warnf(message string, args ...interface{}) {
a.addOne().Logf(log.WarnLevel, message, args...)
}

func (a *zapPlatformAdapter) Errorf(message string, args ...interface{}) {
a.addOne().Logf(log.ErrorLevel, message, args...)
}

func (a *zapPlatformAdapter) WithError(err error) log.Logger {
return NewZapPlatformAdapter(a.zapper.With(zap.Error(err)))
}

func (a *zapPlatformAdapter) WithField(key string, value interface{}) log.Logger {
return NewZapPlatformAdapter(a.zapper.With(key, value))
}

func (a *zapPlatformAdapter) WithFields(fields log.Fields) log.Logger {
c := a.zapper
for key, value := range fields {
c = c.With(key, value)
}
return NewZapPlatformAdapter(c)
}

func (a *zapPlatformAdapter) WithLevelRank(level log.Level, rank log.Rank) log.Logger {
// There are no docs for LevelRanks, and it's not obvious what effect it
// has, so just skipping for now.
a.zapper.Debugf("zapPlatformAdapter: unimplemented method: WithLevelRank")
return a
}

func (a *zapPlatformAdapter) WithLevelRanks(levelRanks log.LevelRanks) log.Logger {
// There are no docs for LevelRanks, and it's not obvious what effect it
// has, so just skipping for now.
a.zapper.Debugf("zapPlatformAdapter: unimplemented method: WithLevelRanks")
return a
}

func (a *zapPlatformAdapter) WithLevel(level log.Level) log.Logger {
lvl, err := zapcore.ParseLevel(string(level))
if err != nil {
return a
}
return NewZapPlatformAdapter(a.zapper.WithOptions(zap.IncreaseLevel(lvl)))
}

func (a *zapPlatformAdapter) Level() log.Level {
a.zapper.Debugf("zapPlatformAdapter: unimplemented method: Level")
// I don't see a way to retrieve this infromation from a zap logger, and I
// don't see any code that calls this method anyway.
return log.DebugLevel
}
Loading

0 comments on commit 822402c

Please sign in to comment.