2021-03-23 15:52:34 +00:00
|
|
|
// 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 (
|
2021-04-12 17:58:36 +01:00
|
|
|
eventAccountCreated = "Account Created"
|
|
|
|
eventSignedIn = "Signed In"
|
|
|
|
eventProjectCreated = "Project Created"
|
|
|
|
eventAccessGrantCreated = "Access Grant Created"
|
|
|
|
eventAccountVerified = "Account Verified"
|
|
|
|
eventGatewayCredentialsCreated = "Credentials Created"
|
|
|
|
eventPassphraseCreated = "Passphrase Created"
|
|
|
|
eventExternalLinkClicked = "External Link Clicked"
|
2021-04-19 17:44:25 +01:00
|
|
|
eventPathSelected = "Path Selected"
|
2021-06-01 22:44:37 +01:00
|
|
|
eventLinkShared = "Link Shared"
|
2021-03-23 15:52:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Config is a configuration struct for analytics Service.
|
|
|
|
type Config struct {
|
|
|
|
SegmentWriteKey string `help:"segment write key" default:""`
|
2021-03-27 22:43:22 +00:00
|
|
|
Enabled bool `help:"enable analytics reporting" default:"false"`
|
2021-03-23 15:52:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Service for sending analytics.
|
|
|
|
//
|
|
|
|
// architecture: Service
|
|
|
|
type Service struct {
|
|
|
|
log *zap.Logger
|
|
|
|
config Config
|
|
|
|
satelliteName string
|
2021-03-31 19:34:44 +01:00
|
|
|
clientEvents map[string]bool
|
2021-03-23 15:52:34 +00:00
|
|
|
|
|
|
|
segment segment.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewService creates new service for creating sending analytics.
|
|
|
|
func NewService(log *zap.Logger, config Config, satelliteName string) *Service {
|
2021-03-27 22:43:22 +00:00
|
|
|
service := &Service{
|
2021-03-23 15:52:34 +00:00
|
|
|
log: log,
|
|
|
|
config: config,
|
|
|
|
satelliteName: satelliteName,
|
2021-03-31 19:34:44 +01:00
|
|
|
clientEvents: make(map[string]bool),
|
2021-03-23 15:52:34 +00:00
|
|
|
}
|
2021-03-27 22:43:22 +00:00
|
|
|
if config.Enabled {
|
|
|
|
service.segment = segment.New(config.SegmentWriteKey)
|
|
|
|
}
|
2021-06-01 22:44:37 +01:00
|
|
|
for _, name := range []string{eventGatewayCredentialsCreated, eventPassphraseCreated, eventExternalLinkClicked, eventPathSelected, eventLinkShared} {
|
2021-03-31 19:34:44 +01:00
|
|
|
service.clientEvents[name] = true
|
|
|
|
}
|
2021-03-27 22:43:22 +00:00
|
|
|
return service
|
2021-03-23 15:52:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the Segment client.
|
|
|
|
func (service *Service) Close() error {
|
2021-03-27 22:43:22 +00:00
|
|
|
if !service.config.Enabled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-23 15:52:34 +00:00
|
|
|
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 {
|
2021-04-27 19:40:03 +01:00
|
|
|
ID uuid.UUID
|
|
|
|
AnonymousID string
|
|
|
|
FullName string
|
|
|
|
Email string
|
|
|
|
Type UserType
|
|
|
|
EmployeeCount string
|
|
|
|
CompanyName string
|
|
|
|
JobTitle string
|
|
|
|
HaveSalesContact bool
|
2021-03-23 15:52:34 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 22:43:22 +00:00
|
|
|
func (service *Service) enqueueMessage(message segment.Message) {
|
|
|
|
if !service.config.Enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err := service.segment.Enqueue(message)
|
|
|
|
if err != nil {
|
|
|
|
service.log.Error("Error enqueueing message", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 15:52:34 +00:00
|
|
|
// 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)
|
|
|
|
|
2021-03-27 22:43:22 +00:00
|
|
|
service.enqueueMessage(segment.Identify{
|
2021-04-08 22:04:44 +01:00
|
|
|
UserId: fields.ID.String(),
|
|
|
|
AnonymousId: fields.AnonymousID,
|
|
|
|
Traits: traits,
|
2021-03-23 15:52:34 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
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)
|
2021-04-27 19:40:03 +01:00
|
|
|
props.Set("have_sales_contact", fields.HaveSalesContact)
|
2021-03-23 15:52:34 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 22:43:22 +00:00
|
|
|
service.enqueueMessage(segment.Track{
|
2021-04-08 22:04:44 +01:00
|
|
|
UserId: fields.ID.String(),
|
|
|
|
AnonymousId: fields.AnonymousID,
|
|
|
|
Event: eventAccountCreated,
|
|
|
|
Properties: props,
|
2021-03-23 15:52:34 +00:00
|
|
|
})
|
|
|
|
}
|
2021-03-31 19:34:44 +01:00
|
|
|
|
2021-04-08 18:34:23 +01:00
|
|
|
// 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,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-12 17:58:36 +01:00
|
|
|
// TrackAccountVerified sends an "Account Verified" event to Segment.
|
|
|
|
func (service *Service) TrackAccountVerified(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: eventAccountVerified,
|
|
|
|
Properties: props,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-03-31 19:34:44 +01:00
|
|
|
// 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) {
|
|
|
|
// do not track if the event name is an invalid client-side event
|
|
|
|
if !service.clientEvents[eventName] {
|
|
|
|
service.log.Error("Invalid client-triggered event", zap.String("eventName", eventName))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
service.enqueueMessage(segment.Track{
|
|
|
|
UserId: userID.String(),
|
|
|
|
Event: eventName,
|
|
|
|
})
|
|
|
|
}
|
2021-04-12 17:58:36 +01:00
|
|
|
|
|
|
|
// TrackLinkEvent sends an arbitrary event and link associated with user ID to Segment.
|
|
|
|
// It is used for tracking occurrences of client-side events.
|
|
|
|
func (service *Service) TrackLinkEvent(eventName string, userID uuid.UUID, link string) {
|
|
|
|
|
|
|
|
// do not track if the event name is an invalid client-side event
|
|
|
|
if !service.clientEvents[eventName] {
|
|
|
|
service.log.Error("Invalid client-triggered event", zap.String("eventName", eventName))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
props := segment.NewProperties()
|
|
|
|
props.Set("link", link)
|
|
|
|
|
|
|
|
service.enqueueMessage(segment.Track{
|
|
|
|
UserId: userID.String(),
|
|
|
|
Event: eventName,
|
|
|
|
Properties: props,
|
|
|
|
})
|
|
|
|
}
|