satellite/analytics: Add analytics service to satellite
* Set up basic structure of new service. * Implement a basic analytics track event for user creation. Change-Id: Ica8c785540b1ef9d848404af307a22f21d33c6aa
This commit is contained in:
parent
c4b2d76d1c
commit
3db52491ec
3
go.mod
3
go.mod
@ -27,6 +27,7 @@ require (
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
|
||||
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1
|
||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
|
||||
github.com/shopspring/decimal v1.2.0
|
||||
github.com/spacemonkeygo/monkit/v3 v3.0.10
|
||||
github.com/spf13/cobra v1.0.0
|
||||
@ -35,6 +36,7 @@ require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stripe/stripe-go v70.15.0+incompatible
|
||||
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||
github.com/zeebo/assert v1.3.0
|
||||
github.com/zeebo/errs v1.2.2
|
||||
go.etcd.io/bbolt v1.3.5
|
||||
@ -45,6 +47,7 @@ require (
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
|
||||
google.golang.org/api v0.20.0 // indirect
|
||||
gopkg.in/segmentio/analytics-go.v3 v3.1.0
|
||||
storj.io/common v0.0.0-20210324105846-0a39fd4f6781
|
||||
storj.io/drpc v0.0.20
|
||||
storj.io/monkit-jaeger v0.0.0-20210225162224-66fb37637bf6
|
||||
|
7
go.sum
7
go.sum
@ -62,6 +62,7 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCS
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
@ -495,6 +496,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 h1:ZuhckGJ10ulaKkdvJtiAqsLTiPrLaXSdnVgXJKJkTxE=
|
||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
@ -587,6 +590,8 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
||||
@ -914,6 +919,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U=
|
||||
gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
104
satellite/analytics/service.go
Normal file
104
satellite/analytics/service.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package analytics
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
segment "gopkg.in/segmentio/analytics-go.v3"
|
||||
|
||||
"storj.io/common/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
eventAccountCreated = "Account Created"
|
||||
)
|
||||
|
||||
// Config is a configuration struct for analytics Service.
|
||||
type Config struct {
|
||||
SegmentWriteKey string `help:"segment write key" default:""`
|
||||
}
|
||||
|
||||
// Service for sending analytics.
|
||||
//
|
||||
// architecture: Service
|
||||
type Service struct {
|
||||
log *zap.Logger
|
||||
config Config
|
||||
satelliteName string
|
||||
|
||||
segment segment.Client
|
||||
}
|
||||
|
||||
// NewService creates new service for creating sending analytics.
|
||||
func NewService(log *zap.Logger, config Config, satelliteName string) *Service {
|
||||
return &Service{
|
||||
log: log,
|
||||
config: config,
|
||||
satelliteName: satelliteName,
|
||||
|
||||
segment: segment.New(config.SegmentWriteKey),
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the Segment client.
|
||||
func (service *Service) Close() error {
|
||||
return service.segment.Close()
|
||||
}
|
||||
|
||||
// UserType is a type for distinguishing personal vs. professional users.
|
||||
type UserType string
|
||||
|
||||
const (
|
||||
// Professional defines a "professional" user type.
|
||||
Professional UserType = "Professional"
|
||||
// Personal defines a "personal" user type.
|
||||
Personal UserType = "Personal"
|
||||
)
|
||||
|
||||
// TrackCreateUserFields contains input data for tracking a create user event.
|
||||
type TrackCreateUserFields struct {
|
||||
ID uuid.UUID
|
||||
FullName string
|
||||
Email string
|
||||
Type UserType
|
||||
EmployeeCount string
|
||||
CompanyName string
|
||||
JobTitle string
|
||||
}
|
||||
|
||||
// TrackCreateUser sends an "Account Created" event to Segment.
|
||||
func (service *Service) TrackCreateUser(fields TrackCreateUserFields) {
|
||||
traits := segment.NewTraits()
|
||||
traits.SetName(fields.FullName)
|
||||
traits.SetEmail(fields.Email)
|
||||
|
||||
err := service.segment.Enqueue(segment.Identify{
|
||||
UserId: fields.ID.String(),
|
||||
Traits: traits,
|
||||
})
|
||||
if err != nil {
|
||||
service.log.Error("Error with identify event", zap.Error(err))
|
||||
}
|
||||
|
||||
props := segment.NewProperties()
|
||||
props.Set("email", fields.Email)
|
||||
props.Set("name", fields.FullName)
|
||||
props.Set("satellite_selected", service.satelliteName)
|
||||
props.Set("account_type", fields.Type)
|
||||
|
||||
if fields.Type == Professional {
|
||||
props.Set("company_size", fields.EmployeeCount)
|
||||
props.Set("company_name", fields.CompanyName)
|
||||
props.Set("job_title", fields.JobTitle)
|
||||
}
|
||||
|
||||
err = service.segment.Enqueue(segment.Track{
|
||||
UserId: fields.ID.String(),
|
||||
Event: eventAccountCreated,
|
||||
Properties: props,
|
||||
})
|
||||
if err != nil {
|
||||
service.log.Error("Error with track event", zap.Error(err))
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import (
|
||||
"storj.io/storj/private/post/oauth2"
|
||||
"storj.io/storj/private/version/checker"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/console/consoleweb"
|
||||
@ -157,6 +158,10 @@ type API struct {
|
||||
GracefulExit struct {
|
||||
Endpoint *gracefulexit.Endpoint
|
||||
}
|
||||
|
||||
Analytics struct {
|
||||
Service *analytics.Service
|
||||
}
|
||||
}
|
||||
|
||||
// NewAPI creates a new satellite API process.
|
||||
@ -556,6 +561,15 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
})
|
||||
}
|
||||
|
||||
{ // setup analytics service
|
||||
peer.Analytics.Service = analytics.NewService(peer.Log.Named("analytics:service"), config.Analytics, config.Console.SatelliteName)
|
||||
|
||||
peer.Services.Add(lifecycle.Item{
|
||||
Name: "analytics:service",
|
||||
Close: peer.Analytics.Service.Close,
|
||||
})
|
||||
}
|
||||
|
||||
{ // setup console
|
||||
consoleConfig := config.Console
|
||||
peer.Console.Listener, err = net.Listen("tcp", consoleConfig.Address)
|
||||
@ -588,6 +602,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
peer.Console.Service,
|
||||
peer.Mail.Service,
|
||||
peer.Marketing.PartnersService,
|
||||
peer.Analytics.Service,
|
||||
peer.Console.Listener,
|
||||
config.Payments.StripeCoinPayments.StripePublicKey,
|
||||
peer.URL(),
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/private/post"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
||||
"storj.io/storj/satellite/console/consoleweb/consolewebauth"
|
||||
@ -38,13 +39,14 @@ type Auth struct {
|
||||
TermsAndConditionsURL string
|
||||
ContactInfoURL string
|
||||
service *console.Service
|
||||
analytics *analytics.Service
|
||||
mailService *mailservice.Service
|
||||
cookieAuth *consolewebauth.CookieAuth
|
||||
partners *rewards.PartnersService
|
||||
}
|
||||
|
||||
// NewAuth is a constructor for api auth controller.
|
||||
func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice.Service, cookieAuth *consolewebauth.CookieAuth, partners *rewards.PartnersService, externalAddress string, letUsKnowURL string, termsAndConditionsURL string, contactInfoURL string) *Auth {
|
||||
func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice.Service, cookieAuth *consolewebauth.CookieAuth, partners *rewards.PartnersService, analytics *analytics.Service, externalAddress string, letUsKnowURL string, termsAndConditionsURL string, contactInfoURL string) *Auth {
|
||||
return &Auth{
|
||||
log: log,
|
||||
ExternalAddress: externalAddress,
|
||||
@ -55,6 +57,7 @@ func NewAuth(log *zap.Logger, service *console.Service, mailService *mailservice
|
||||
mailService: mailService,
|
||||
cookieAuth: cookieAuth,
|
||||
partners: partners,
|
||||
analytics: analytics,
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,6 +166,20 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
trackCreateUserFields := analytics.TrackCreateUserFields{
|
||||
ID: user.ID,
|
||||
FullName: user.FullName,
|
||||
Email: user.Email,
|
||||
Type: analytics.Personal,
|
||||
}
|
||||
if user.IsProfessional {
|
||||
trackCreateUserFields.Type = analytics.Professional
|
||||
trackCreateUserFields.EmployeeCount = user.EmployeeCount
|
||||
trackCreateUserFields.CompanyName = user.CompanyName
|
||||
trackCreateUserFields.JobTitle = user.Position
|
||||
}
|
||||
a.analytics.TrackCreateUser(trackCreateUserFields)
|
||||
|
||||
token, err := a.service.GenerateActivationToken(ctx, user.ID, user.Email)
|
||||
if err != nil {
|
||||
a.serveJSONError(w, err)
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/private/web"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/console/consoleweb/consoleapi"
|
||||
@ -102,6 +103,7 @@ type Server struct {
|
||||
service *console.Service
|
||||
mailService *mailservice.Service
|
||||
partners *rewards.PartnersService
|
||||
analytics *analytics.Service
|
||||
|
||||
listener net.Listener
|
||||
server http.Server
|
||||
@ -124,7 +126,7 @@ type Server struct {
|
||||
}
|
||||
|
||||
// NewServer creates new instance of console server.
|
||||
func NewServer(logger *zap.Logger, config Config, service *console.Service, mailService *mailservice.Service, partners *rewards.PartnersService, listener net.Listener, stripePublicKey string, nodeURL storj.NodeURL) *Server {
|
||||
func NewServer(logger *zap.Logger, config Config, service *console.Service, mailService *mailservice.Service, partners *rewards.PartnersService, analytics *analytics.Service, listener net.Listener, stripePublicKey string, nodeURL storj.NodeURL) *Server {
|
||||
server := Server{
|
||||
log: logger,
|
||||
config: config,
|
||||
@ -132,6 +134,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
|
||||
service: service,
|
||||
mailService: mailService,
|
||||
partners: partners,
|
||||
analytics: analytics,
|
||||
stripePublicKey: stripePublicKey,
|
||||
rateLimiter: web.NewIPRateLimiter(config.RateLimit),
|
||||
nodeURL: nodeURL,
|
||||
@ -170,7 +173,7 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, mail
|
||||
server.withAuth(http.HandlerFunc(server.projectUsageLimitsHandler)),
|
||||
).Methods(http.MethodGet)
|
||||
|
||||
authController := consoleapi.NewAuth(logger, service, mailService, server.cookieAuth, partners, server.config.ExternalAddress, config.LetUsKnowURL, config.TermsAndConditionsURL, config.ContactInfoURL)
|
||||
authController := consoleapi.NewAuth(logger, service, mailService, server.cookieAuth, partners, server.analytics, server.config.ExternalAddress, config.LetUsKnowURL, config.TermsAndConditionsURL, config.ContactInfoURL)
|
||||
authRouter := router.PathPrefix("/api/v0/auth").Subrouter()
|
||||
authRouter.Handle("/account", server.withAuth(http.HandlerFunc(authController.GetAccount))).Methods(http.MethodGet)
|
||||
authRouter.Handle("/account", server.withAuth(http.HandlerFunc(authController.UpdateAccount))).Methods(http.MethodPatch)
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"storj.io/storj/satellite/accounting/rolluparchive"
|
||||
"storj.io/storj/satellite/accounting/tally"
|
||||
"storj.io/storj/satellite/admin"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/attribution"
|
||||
"storj.io/storj/satellite/audit"
|
||||
"storj.io/storj/satellite/compensation"
|
||||
@ -146,4 +147,6 @@ type Config struct {
|
||||
Compensation compensation.Config
|
||||
|
||||
ProjectLimit accounting.ProjectLimitConfig
|
||||
|
||||
Analytics analytics.Config
|
||||
}
|
||||
|
3
scripts/testdata/satellite-config.yaml.lock
vendored
3
scripts/testdata/satellite-config.yaml.lock
vendored
@ -1,6 +1,9 @@
|
||||
# admin peer http listening address
|
||||
# admin.address: ""
|
||||
|
||||
# segment write key
|
||||
# analytics.segment-write-key: ""
|
||||
|
||||
# how often to run the reservoir chore
|
||||
# audit.chore-interval: 24h0m0s
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user