satellite/analytics: Add analytics for user signed in, project created, and access grant created (#4073)
* satellite/analytics: Add analytics for user signed in, project created and access grant created events Co-authored-by: Moby von Briesen <mobyvb@gmail.com>
This commit is contained in:
parent
a264a4422b
commit
16c98e1ecd
@ -12,6 +12,9 @@ import (
|
||||
|
||||
const (
|
||||
eventAccountCreated = "Account Created"
|
||||
eventSignedIn = "Signed In"
|
||||
eventProjectCreated = "Project Created"
|
||||
eventAccessGrantCreated = "Access Grant Created"
|
||||
gatewayCredentialsCreated = "Credentials Created"
|
||||
)
|
||||
|
||||
@ -121,6 +124,49 @@ func (service *Service) TrackCreateUser(fields TrackCreateUserFields) {
|
||||
})
|
||||
}
|
||||
|
||||
// TrackSignedIn sends an "Signed In" event to Segment.
|
||||
func (service *Service) TrackSignedIn(userID uuid.UUID, email string) {
|
||||
traits := segment.NewTraits()
|
||||
traits.SetEmail(email)
|
||||
|
||||
service.enqueueMessage(segment.Identify{
|
||||
UserId: userID.String(),
|
||||
Traits: traits,
|
||||
})
|
||||
|
||||
props := segment.NewProperties()
|
||||
props.Set("email", email)
|
||||
|
||||
service.enqueueMessage(segment.Track{
|
||||
UserId: userID.String(),
|
||||
Event: eventSignedIn,
|
||||
Properties: props,
|
||||
})
|
||||
}
|
||||
|
||||
// TrackProjectCreated sends an "Project Created" event to Segment.
|
||||
func (service *Service) TrackProjectCreated(userID, projectID uuid.UUID, currentProjectCount int) {
|
||||
|
||||
props := segment.NewProperties()
|
||||
props.Set("project_count", currentProjectCount)
|
||||
props.Set("project_id", projectID.String())
|
||||
|
||||
service.enqueueMessage(segment.Track{
|
||||
UserId: userID.String(),
|
||||
Event: eventProjectCreated,
|
||||
Properties: props,
|
||||
})
|
||||
}
|
||||
|
||||
// TrackAccessGrantCreated sends an "Access Grant Created" event to Segment.
|
||||
func (service *Service) TrackAccessGrantCreated(userID uuid.UUID) {
|
||||
|
||||
service.enqueueMessage(segment.Track{
|
||||
UserId: userID.String(),
|
||||
Event: eventAccessGrantCreated,
|
||||
})
|
||||
}
|
||||
|
||||
// TrackEvent sends an arbitrary event associated with user ID to Segment.
|
||||
// It is used for tracking occurrences of client-side events.
|
||||
func (service *Service) TrackEvent(eventName string, userID uuid.UUID) {
|
||||
|
@ -362,6 +362,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 metainfo
|
||||
peer.Metainfo.Database = pointerDB
|
||||
peer.Metainfo.Metabase = metabaseDB
|
||||
@ -555,15 +564,6 @@ 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)
|
||||
@ -583,6 +583,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
peer.DB.Buckets(),
|
||||
peer.Marketing.PartnersService,
|
||||
peer.Payments.Accounts,
|
||||
peer.Analytics.Service,
|
||||
consoleConfig.Config,
|
||||
config.Payments.MinCoinPayment,
|
||||
)
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"storj.io/storj/satellite"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/accounting/live"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
||||
@ -59,6 +60,8 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
analyticsService := analytics.NewService(log, analytics.Config{}, "test-satellite")
|
||||
|
||||
redis, err := testredis.Mini(ctx)
|
||||
require.NoError(t, err)
|
||||
defer ctx.Check(redis.Close)
|
||||
@ -108,6 +111,7 @@ func TestGraphqlMutation(t *testing.T) {
|
||||
db.Buckets(),
|
||||
partnersService,
|
||||
paymentsService.Accounts(),
|
||||
analyticsService,
|
||||
console.Config{PasswordCost: console.TestPasswordCost, DefaultProjectLimit: 5},
|
||||
5000,
|
||||
)
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"storj.io/storj/satellite"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/accounting/live"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
||||
@ -43,6 +44,8 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
analyticsService := analytics.NewService(log, analytics.Config{}, "test-satellite")
|
||||
|
||||
redis, err := testredis.Mini(ctx)
|
||||
require.NoError(t, err)
|
||||
defer ctx.Check(redis.Close)
|
||||
@ -92,6 +95,7 @@ func TestGraphqlQuery(t *testing.T) {
|
||||
db.Buckets(),
|
||||
partnersService,
|
||||
paymentsService.Accounts(),
|
||||
analyticsService,
|
||||
console.Config{PasswordCost: console.TestPasswordCost, DefaultProjectLimit: 5},
|
||||
5000,
|
||||
)
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"storj.io/common/storj"
|
||||
"storj.io/common/uuid"
|
||||
"storj.io/storj/satellite/accounting"
|
||||
"storj.io/storj/satellite/analytics"
|
||||
"storj.io/storj/satellite/console/consoleauth"
|
||||
"storj.io/storj/satellite/payments"
|
||||
"storj.io/storj/satellite/rewards"
|
||||
@ -93,6 +94,7 @@ type Service struct {
|
||||
buckets Buckets
|
||||
partners *rewards.PartnersService
|
||||
accounts payments.Accounts
|
||||
analytics *analytics.Service
|
||||
|
||||
config Config
|
||||
|
||||
@ -113,7 +115,7 @@ type PaymentsService struct {
|
||||
}
|
||||
|
||||
// NewService returns new instance of Service.
|
||||
func NewService(log *zap.Logger, signer Signer, store DB, projectAccounting accounting.ProjectAccounting, projectUsage *accounting.Service, buckets Buckets, partners *rewards.PartnersService, accounts payments.Accounts, config Config, minCoinPayment int64) (*Service, error) {
|
||||
func NewService(log *zap.Logger, signer Signer, store DB, projectAccounting accounting.ProjectAccounting, projectUsage *accounting.Service, buckets Buckets, partners *rewards.PartnersService, accounts payments.Accounts, analytics *analytics.Service, config Config, minCoinPayment int64) (*Service, error) {
|
||||
if signer == nil {
|
||||
return nil, errs.New("signer can't be nil")
|
||||
}
|
||||
@ -137,6 +139,7 @@ func NewService(log *zap.Logger, signer Signer, store DB, projectAccounting acco
|
||||
buckets: buckets,
|
||||
partners: partners,
|
||||
accounts: accounts,
|
||||
analytics: analytics,
|
||||
config: config,
|
||||
minCoinPayment: minCoinPayment,
|
||||
}, nil
|
||||
@ -431,6 +434,7 @@ func (paymentService PaymentsService) TokenDeposit(ctx context.Context, amount i
|
||||
}
|
||||
|
||||
tx, err := paymentService.service.accounts.StorjTokens().Deposit(ctx, auth.User.ID, amount)
|
||||
|
||||
return tx, Error.Wrap(err)
|
||||
}
|
||||
|
||||
@ -780,6 +784,8 @@ func (s *Service) Token(ctx context.Context, email, password string) (token stri
|
||||
}
|
||||
s.auditLog(ctx, "login", &user.ID, user.Email)
|
||||
|
||||
s.analytics.TrackSignedIn(user.ID, user.Email)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
@ -990,7 +996,7 @@ func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (p
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
|
||||
err = s.checkProjectLimit(ctx, auth.User.ID)
|
||||
currentProjectCount, err := s.checkProjectLimit(ctx, auth.User.ID)
|
||||
if err != nil {
|
||||
return nil, ErrProjLimit.Wrap(err)
|
||||
}
|
||||
@ -1021,6 +1027,7 @@ func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (p
|
||||
}
|
||||
}
|
||||
|
||||
var projectID uuid.UUID
|
||||
err = s.store.WithTx(ctx, func(ctx context.Context, tx DBTx) error {
|
||||
p, err = tx.Projects().Insert(ctx,
|
||||
&Project{
|
||||
@ -1041,12 +1048,17 @@ func (s *Service) CreateProject(ctx context.Context, projectInfo ProjectInfo) (p
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
projectID = p.ID
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
|
||||
s.analytics.TrackProjectCreated(auth.User.ID, projectID, currentProjectCount+1)
|
||||
|
||||
// ToDo: check if this is actually the right place.
|
||||
err = s.accounts.Coupons().AddPromotionalCoupon(ctx, auth.User.ID)
|
||||
if err != nil {
|
||||
@ -1276,6 +1288,8 @@ func (s *Service) CreateAPIKey(ctx context.Context, projectID uuid.UUID, name st
|
||||
return nil, nil, Error.Wrap(err)
|
||||
}
|
||||
|
||||
s.analytics.TrackAccessGrantCreated(auth.User.ID)
|
||||
|
||||
return info, key, nil
|
||||
}
|
||||
|
||||
@ -1598,12 +1612,12 @@ func (s *Service) checkProjectCanBeDeleted(ctx context.Context, project uuid.UUI
|
||||
}
|
||||
|
||||
// checkProjectLimit is used to check if user is able to create a new project.
|
||||
func (s *Service) checkProjectLimit(ctx context.Context, userID uuid.UUID) (err error) {
|
||||
func (s *Service) checkProjectLimit(ctx context.Context, userID uuid.UUID) (currentProjects int, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
limit, err := s.store.Users().GetProjectLimit(ctx, userID)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
return 0, Error.Wrap(err)
|
||||
}
|
||||
if limit == 0 {
|
||||
limit = s.config.DefaultProjectLimit
|
||||
@ -1611,14 +1625,14 @@ func (s *Service) checkProjectLimit(ctx context.Context, userID uuid.UUID) (err
|
||||
|
||||
projects, err := s.GetUsersProjects(ctx)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
return 0, Error.Wrap(err)
|
||||
}
|
||||
|
||||
if len(projects) >= limit {
|
||||
return ErrProjLimit.New(projLimitErrMsg)
|
||||
return 0, ErrProjLimit.New(projLimitErrMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
return len(projects), nil
|
||||
}
|
||||
|
||||
// CreateRegToken creates new registration token. Needed for testing.
|
||||
|
Loading…
Reference in New Issue
Block a user