From c00b355110b2f8de9a8246feff8c7c3914ecd88c Mon Sep 17 00:00:00 2001 From: Brad Murray Date: Tue, 9 Jan 2024 15:27:14 -0500 Subject: [PATCH] Optionally track analytics for hardware versions of providers (#5) * Optionally track analytics for hardware versions of providers * Run pre-commit --- .gitignore | 2 ++ cmd/registration_relay/main.go | 15 +++++++++ internal/analytics/analytics.go | 58 +++++++++++++++++++++++++++++++++ internal/provider/provider.go | 18 ++++++++-- internal/provider/types.go | 10 ++++-- 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 internal/analytics/analytics.go diff --git a/.gitignore b/.gitignore index ba055d0..d96a153 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.idea + /registration_relay diff --git a/cmd/registration_relay/main.go b/cmd/registration_relay/main.go index 338d4af..eaced5d 100644 --- a/cmd/registration_relay/main.go +++ b/cmd/registration_relay/main.go @@ -12,6 +12,7 @@ import ( "github.com/beeper/libserv/pkg/flagenv" + "github.com/beeper/registration-relay/internal/analytics" "github.com/beeper/registration-relay/internal/api" "github.com/beeper/registration-relay/internal/config" "github.com/beeper/registration-relay/internal/metrics" @@ -46,6 +47,17 @@ func main() { "Validate auth header URL", ) + analyticsURL := flag.String( + "analyticsURL", + flagenv.StringEnvWithDefault("ANALYTICS_URL", ""), + "URL to send analytics to, disabled by default", + ) + analyticsToken := flag.String( + "analyticsToken", + flagenv.StringEnvWithDefault("ANALYTICS_TOKEN", ""), + "Write key to auth analytics with, disabled by default", + ) + flag.Parse() if *prettyLogs { @@ -67,6 +79,9 @@ func main() { } cfg.API.ValidateAuthURL = *validateAuthURL + analytics.ConfigURL = *analyticsURL + analytics.ConfigToken = *analyticsToken + log.Info().Str("commit", Commit).Str("build_time", BuildTime).Msg("registration-relay starting") metricsSrv := metrics.NewPrometheusMetricsHandler(*metricsListenAddr) diff --git a/internal/analytics/analytics.go b/internal/analytics/analytics.go new file mode 100644 index 0000000..bd3b258 --- /dev/null +++ b/internal/analytics/analytics.go @@ -0,0 +1,58 @@ +package analytics + +import ( + "bytes" + "encoding/json" + "net/http" + + "github.com/rs/zerolog/log" +) + +var ConfigURL = "" +var ConfigToken = "" +var client http.Client + +var logger = log.With().Str("component", "analytics").Logger() + +func trackImplementation(userId string, event string, properties map[string]any) { + var buf bytes.Buffer + err := json.NewEncoder(&buf).Encode(map[string]interface{}{ + "userId": userId, + "event": event, + "properties": properties, + }) + if err != nil { + logger.Error().Err(err).Msg("error encoding payload") + return + } + + req, err := http.NewRequest(http.MethodPost, ConfigURL, &buf) + if err != nil { + logger.Error().Err(err).Msg("error creating request") + return + } + req.SetBasicAuth(ConfigToken, "") + resp, err := client.Do(req) + if err != nil { + logger.Error().Err(err).Msg("error sending request") + return + } + err = resp.Body.Close() + if err != nil { + logger.Error().Err(err).Msg("error closing request") + } + + logger.Info().Str("event", event).Msg("Tracked event") +} + +func IsEnabled() bool { + return len(ConfigToken) > 0 +} + +func Track(userId string, event string, properties map[string]any) { + if !IsEnabled() { + return + } + + go trackImplementation(userId, event, properties) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 32ff05d..35e4f1a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -14,6 +14,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/beeper/registration-relay/internal/analytics" "github.com/beeper/registration-relay/internal/metrics" "github.com/beeper/registration-relay/internal/util" ) @@ -56,6 +57,11 @@ func RegisterProvider(data registerCommandData, provider *provider) (*registerCo return nil, err } data.Secret = base64.RawStdEncoding.EncodeToString(calculateSecret(provider.globalSecret, data.Code)) + + analytics.Track(data.Code, "Provider Registered", map[string]any{ + "commit": data.Commit, + "hardware_version": data.Versions.HardwareVersion, + }) } else { if len(data.Code) != 19 || len(data.Secret) > 64 { return nil, fmt.Errorf("invalid secret") @@ -67,9 +73,14 @@ func RegisterProvider(data registerCommandData, provider *provider) (*registerCo if existing, exists := codeToProvider[data.Code]; exists { existing.log.Warn(). Str("code", data.Code). - Msg("New provider with same code registering, exiting websocket") + Msg("New provider with same code registering, exiting existing websocket") existing.ws.Close() } + + analytics.Track(data.Code, "Provider Reconnected", map[string]any{ + "commit": data.Commit, + "hardware_version": data.Versions.HardwareVersion, + }) } codeToProvider[data.Code] = provider @@ -156,7 +167,10 @@ Loop: registerCode = response.Code p.log = p.log.With().Str("code", request.Code).Logger() - p.log.Debug().Msg("Registered provider") + p.log.Debug(). + Str("hardware_version", request.Versions.HardwareVersion). + Str("commit", request.Commit). + Msg("Registered provider") // Send back register response before setting the flag (ws is single writer) buf, err := json.Marshal(RawCommand[registerCommandData]{Command: "response", Data: *response, ReqID: rawCommand.ReqID}) diff --git a/internal/provider/types.go b/internal/provider/types.go index 6c9fad8..f2accc2 100644 --- a/internal/provider/types.go +++ b/internal/provider/types.go @@ -6,9 +6,15 @@ type RawCommand[T any] struct { Data T `json:"data"` } +type versions struct { + HardwareVersion string `json:"hardware_version"` +} + type registerCommandData struct { - Code string `json:"code"` - Secret string `json:"secret"` + Code string `json:"code"` + Secret string `json:"secret"` + Commit string `json:"commit,omitempty"` + Versions versions `json:"versions,omitempty"` } type errorData struct {