瀏覽代碼

temp analytics overhaul

Alexander Belanger 4 年之前
父節點
當前提交
e7dc43a3ae

+ 4 - 4
internal/analytics/identifiers.go

@@ -18,14 +18,14 @@ type segmentIdentifyNewUser struct {
 	isGithub  bool
 }
 
-// Creates a segment Identifier struct for new users. As we handle registration with github, it also accepts a param
-// to check if the new user has registered with github or not.
-func CreateSegmentIdentifyNewUser(user *models.User, registeredViaGithub bool) *segmentIdentifyNewUser {
+// CreateSegmentIdentifyUser creates an identifier for users
+func CreateSegmentIdentifyUser(user *models.User) *segmentIdentifyNewUser {
 	userId := fmt.Sprintf("%v", user.ID)
+
 	return &segmentIdentifyNewUser{
 		userId:    userId,
 		userEmail: user.Email,
-		isGithub:  registeredViaGithub,
+		isGithub:  user.GithubUserID != 0,
 	}
 }
 

+ 26 - 3
internal/analytics/track_events.go

@@ -3,7 +3,30 @@ package analytics
 type SegmentEvent string
 
 const (
-	NewUser            SegmentEvent = "New User"
-	RedeployViaWebhook SegmentEvent = "Triggered Re-deploy via Webhook"
-	NewClusterEvent    SegmentEvent = "New Cluster Event"
+	// onboarding flow
+	UserCreate    SegmentEvent = "New User"
+	ProjectCreate SegmentEvent = "New Project Event"
+
+	ClusterProvisioningStart   SegmentEvent = "Cluster Provisioning Started"
+	ClusterProvisioningError   SegmentEvent = "Cluster Provisioning Error"
+	ClusterProvisioningSuccess SegmentEvent = "Cluster Provisioning Success"
+
+	RegistryProvisioningStart   SegmentEvent = "Registry Provisioning Started"
+	RegistryProvisioningError   SegmentEvent = "Registry Provisioning Error"
+	RegistryProvisioningSuccess SegmentEvent = "Registry Provisioning Success"
+
+	ClusterConnectedStart   SegmentEvent = "Cluster Connection Started"
+	ClusterConnectedError   SegmentEvent = "Cluster Connection Started"
+	ClusterConnectedSuccess SegmentEvent = "Cluster Connection Error"
+
+	RegistryConnectedSuccess SegmentEvent = "Registry Connection Success"
+	RegistryConnectedError   SegmentEvent = "Registry Connection Error"
+
+	GithubConnectedSuccess SegmentEvent = "Github Connection Success"
+	GithubConnectedError   SegmentEvent = "Github Connection Error"
+
+	// launch flow
+	ApplicationLaunch            SegmentEvent = "New Application Launched"
+	ApplicationLaunchError       SegmentEvent = "Application Deployment Error"
+	ApplicationDeploymentWebhook SegmentEvent = "Triggered Re-deploy via Webhook"
 )

+ 388 - 62
internal/analytics/tracks.go

@@ -3,107 +3,433 @@ package analytics
 import (
 	"fmt"
 
-	"github.com/porter-dev/porter/internal/models"
 	segment "gopkg.in/segmentio/analytics-go.v3"
 )
 
+// TRACK FUNCTIONS
+type UserCreateTrackOpts struct {
+	*UserScopedTrackOpts
+}
+
+func UserCreateTrack(opts *UserCreateTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+
+	return getSegmentUserTrack(
+		opts.UserScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, UserCreate),
+	)
+}
+
+type ProjectCreateTrackOpts struct {
+	*ProjectScopedTrackOpts
+}
+
+func ProjectCreateTrack(opts *ProjectCreateTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+
+	return getSegmentProjectTrack(
+		opts.ProjectScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ProjectCreate),
+	)
+}
+
+type ClusterProvisioningStartTrackOpts struct {
+	*ProjectScopedTrackOpts
+
+	ClusterType string // EKS, DOKS, or GKE
+}
+
+func ClusterProvisioningStartTrack(opts *ClusterProvisioningStartTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["cluster_type"] = opts.ClusterType
+
+	return getSegmentProjectTrack(
+		opts.ProjectScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ClusterProvisioningStart),
+	)
+}
+
+type ClusterProvisioningErrorTrackOpts struct {
+	*ClusterScopedTrackOpts
+
+	ClusterType string // EKS, DOKS, or GKE
+	Errors      []string
+}
+
+func ClusterProvisioningErrorTrack(opts *ClusterProvisioningErrorTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["cluster_type"] = opts.ClusterType
+	additionalProps["errors"] = opts.Errors
+
+	return getSegmentClusterTrack(
+		opts.ClusterScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ClusterProvisioningError),
+	)
+}
+
+type ClusterProvisioningSuccessTrackOpts struct {
+	*ClusterScopedTrackOpts
+
+	ClusterType   string // EKS, DOKS, or GKE
+	ClusterServer string
+}
+
+func ClusterProvisioningSuccessTrack(opts *ClusterProvisioningSuccessTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["cluster_type"] = opts.ClusterType
+	additionalProps["cluster_server"] = opts.ClusterServer
+
+	return getSegmentClusterTrack(
+		opts.ClusterScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ClusterProvisioningSuccess),
+	)
+}
+
+type ApplicationDeploymentWebhookTrackOpts struct {
+	*ApplicationScopedTrackOpts
+
+	ImageURI string
+}
+
+func ApplicationDeploymentWebhookTrack(opts *ApplicationDeploymentWebhookTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["image_uri"] = opts.ImageURI
+
+	return getSegmentApplicationTrack(
+		opts.ApplicationScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ApplicationDeploymentWebhook),
+	)
+}
+
+// HELPERS
+
 type segmentTrack interface {
 	getUserId() string
 	getEvent() SegmentEvent
 	getProperties() segment.Properties
 }
 
-type segmentNewUserTrack struct {
-	userId    string
-	userEmail string
+type defaultTrackOpts struct {
+	AdditionalProps map[string]interface{}
+}
+
+type defaultSegmentTrack struct {
+	event      SegmentEvent
+	properties segmentProperties
+}
+
+func getDefaultSegmentTrack(additionalProps map[string]interface{}, event SegmentEvent) *defaultSegmentTrack {
+	props := newSegmentProperties()
+	props.addAdditionalProperties(additionalProps)
+
+	return &defaultSegmentTrack{
+		event:      event,
+		properties: props,
+	}
+}
+
+func (t *defaultSegmentTrack) getEvent() SegmentEvent {
+	return t.event
 }
 
-// CreateSegmentNewUserTrack creates a track of type "New User", which
-// tracks when a user has registered
-func CreateSegmentNewUserTrack(user *models.User) *segmentNewUserTrack {
-	userId := fmt.Sprintf("%v", user.ID)
+func (t *defaultSegmentTrack) getProperties() segment.Properties {
+	props := segment.NewProperties()
 
-	return &segmentNewUserTrack{
-		userId:    userId,
-		userEmail: user.Email,
+	for key, val := range t.properties {
+		props = props.Set(key, val)
 	}
+
+	return props
 }
 
-func (t *segmentNewUserTrack) getUserId() string {
-	return t.userId
+type segmentProperties map[string]interface{}
+
+func newSegmentProperties() segmentProperties {
+	props := make(map[string]interface{})
+
+	return props
 }
 
-func (t *segmentNewUserTrack) getEvent() SegmentEvent {
-	return NewUser
+func (p segmentProperties) addProjectProperties(opts *ProjectScopedTrackOpts) {
+	p["proj_id"] = opts.ProjectID
 }
 
-func (t *segmentNewUserTrack) getProperties() segment.Properties {
-	return segment.NewProperties().Set("email", t.userEmail)
+func (p segmentProperties) addClusterProperties(opts *ClusterScopedTrackOpts) {
+	p["cluster_id"] = opts.ClusterID
 }
 
-type segmentRedeployViaWebhookTrack struct {
-	userId     string
-	repository string
+func (p segmentProperties) addApplicationProperties(opts *ApplicationScopedTrackOpts) {
+	p["app_name"] = opts.Name
+	p["app_namespace"] = opts.Namespace
 }
 
-// CreateSegmentRedeployViaWebhookTrack creates a track of type "Triggered Re-deploy via Webhook", which
-// tracks whenever a repository is redeployed via webhook call
-func CreateSegmentRedeployViaWebhookTrack(userId string, repository string) *segmentRedeployViaWebhookTrack {
-	return &segmentRedeployViaWebhookTrack{
-		userId:     userId,
-		repository: repository,
+func (p segmentProperties) addAdditionalProperties(props map[string]interface{}) {
+	for key, val := range props {
+		p[key] = val
 	}
 }
 
-func (t *segmentRedeployViaWebhookTrack) getUserId() string {
-	return t.userId
+type UserScopedTrack struct {
+	*defaultSegmentTrack
+
+	userID uint
 }
 
-func (t *segmentRedeployViaWebhookTrack) getEvent() SegmentEvent {
-	return RedeployViaWebhook
+type UserScopedTrackOpts struct {
+	*defaultTrackOpts
+
+	UserID uint
 }
 
-func (t *segmentRedeployViaWebhookTrack) getProperties() segment.Properties {
-	return segment.NewProperties().Set("repository", t.repository)
+func GetUserScopedTrackOpts(userID uint) *UserScopedTrackOpts {
+	return &UserScopedTrackOpts{
+		UserID: userID,
+	}
 }
 
-type segmentNewClusterEventTrack struct {
-	userId      string
-	projId      string
-	clusterName string
-	clusterType string // EKS, DOKS, or GKE
-	eventType   string // connected, provisioned, or destroyed
+func (u *UserScopedTrack) getUserId() string {
+	return fmt.Sprintf("%d", u.userID)
 }
 
-// NewClusterEventOpts are the parameters for creating a "New Cluster Event" track
-type NewClusterEventOpts struct {
-	UserId      string
-	ProjId      string
-	ClusterName string
-	ClusterType string // EKS, DOKS, or GKE
-	EventType   string // connected, provisioned, or destroyed
-}
-
-// CreateSegmentNewClusterEvent creates a track of type "New Cluster Event", which
-// tracks whenever a cluster is newly provisioned, connected, or destroyed.
-func CreateSegmentNewClusterEvent(opts *NewClusterEventOpts) *segmentNewClusterEventTrack {
-	return &segmentNewClusterEventTrack{
-		userId:      opts.UserId,
-		projId:      opts.ProjId,
-		clusterName: opts.ClusterName,
-		clusterType: opts.ClusterType,
-		eventType:   opts.EventType,
+func getSegmentUserTrack(opts *UserScopedTrackOpts, track *defaultSegmentTrack) *UserScopedTrack {
+	return &UserScopedTrack{
+		defaultSegmentTrack: track,
+		userID:              opts.UserID,
 	}
 }
 
-func (t *segmentNewClusterEventTrack) getUserId() string {
-	return t.userId
+type ProjectScopedTrack struct {
+	*UserScopedTrack
+
+	projectID uint
 }
 
-func (t *segmentNewClusterEventTrack) getEvent() SegmentEvent {
-	return NewClusterEvent
+type ProjectScopedTrackOpts struct {
+	*UserScopedTrackOpts
+
+	ProjectID uint
 }
 
-func (t *segmentNewClusterEventTrack) getProperties() segment.Properties {
-	return segment.NewProperties().Set("Project ID", t.projId).Set("Cluster Name", t.clusterName).Set("Cluster Type", t.clusterType).Set("Event Type", t.eventType)
+func GetProjectScopedTrackOpts(userID, projID uint) *ProjectScopedTrackOpts {
+	return &ProjectScopedTrackOpts{
+		UserScopedTrackOpts: GetUserScopedTrackOpts(userID),
+		ProjectID:           projID,
+	}
 }
+
+func getSegmentProjectTrack(opts *ProjectScopedTrackOpts, track *defaultSegmentTrack) *ProjectScopedTrack {
+	track.properties.addProjectProperties(opts)
+
+	return &ProjectScopedTrack{
+		UserScopedTrack: getSegmentUserTrack(opts.UserScopedTrackOpts, track),
+		projectID:       opts.ProjectID,
+	}
+}
+
+type ClusterScopedTrack struct {
+	*ProjectScopedTrack
+
+	clusterID uint
+}
+
+type ClusterScopedTrackOpts struct {
+	*ProjectScopedTrackOpts
+
+	ClusterID uint
+}
+
+func GetClusterScopedTrackOpts(userID, projID, clusterID uint) *ClusterScopedTrackOpts {
+	return &ClusterScopedTrackOpts{
+		ProjectScopedTrackOpts: GetProjectScopedTrackOpts(userID, projID),
+		ClusterID:              clusterID,
+	}
+}
+
+func getSegmentClusterTrack(opts *ClusterScopedTrackOpts, track *defaultSegmentTrack) *ClusterScopedTrack {
+	track.properties.addClusterProperties(opts)
+
+	return &ClusterScopedTrack{
+		ProjectScopedTrack: getSegmentProjectTrack(opts.ProjectScopedTrackOpts, track),
+		clusterID:          opts.ClusterID,
+	}
+}
+
+type ApplicationScopedTrack struct {
+	*ClusterScopedTrack
+
+	name      string
+	namespace string
+}
+
+type ApplicationScopedTrackOpts struct {
+	*ClusterScopedTrackOpts
+
+	Name      string
+	Namespace string
+}
+
+func GetApplicationScopedTrackOpts(userID, projID, clusterID uint, name, namespace string) *ApplicationScopedTrackOpts {
+	return &ApplicationScopedTrackOpts{
+		ClusterScopedTrackOpts: GetClusterScopedTrackOpts(userID, projID, clusterID),
+		Name:                   name,
+		Namespace:              namespace,
+	}
+}
+
+func getSegmentApplicationTrack(opts *ApplicationScopedTrackOpts, track *defaultSegmentTrack) *ApplicationScopedTrack {
+	track.properties.addApplicationProperties(opts)
+
+	return &ApplicationScopedTrack{
+		ClusterScopedTrack: getSegmentClusterTrack(opts.ClusterScopedTrackOpts, track),
+		name:               opts.Name,
+		namespace:          opts.Namespace,
+	}
+}
+
+// type segmentProjectScopedTrack struct {
+// 	*UserScopedTrack
+
+// 	ProjID uint
+// 	ProjName string
+// }
+
+// type segmentClusterScopedTrack struct {
+// 	*segmentProjectScopedTrack
+
+// 	clusterID uint
+// 	clusterName string
+// }
+
+// type segmentApplicationScopedTrack struct {
+// 	*segmentClusterScopedTrack
+
+// 	appName string
+// 	appNamespace string
+// }
+
+// // CreateSegmentNewUserTrack creates a track of type "New User", which
+// // tracks when a user has registered
+// func CreateSegmentNewUserTrack(user *models.User) *segmentNewUserTrack {
+// 	userId := fmt.Sprintf("%v", user.ID)
+
+// 	return &segmentNewUserTrack{
+// 		userId:    userId,
+// 		userEmail: user.Email,
+// 	}
+// }
+
+// func (t *segmentNewUserTrack) getUserId() string {
+// 	return t.userId
+// }
+
+// func (t *segmentNewUserTrack) getEvent() SegmentEvent {
+// 	return NewUser
+// }
+
+// func (t *segmentNewUserTrack) getProperties() segment.Properties {
+// 	return segment.NewProperties().Set("email", t.userEmail)
+// }
+
+// type segmentRedeployViaWebhookTrack struct {
+// 	userId     string
+// 	repository string
+// }
+
+// // CreateSegmentRedeployViaWebhookTrack creates a track of type "Triggered Re-deploy via Webhook", which
+// // tracks whenever a repository is redeployed via webhook call
+// func CreateSegmentRedeployViaWebhookTrack(userId string, repository string) *segmentRedeployViaWebhookTrack {
+// 	return &segmentRedeployViaWebhookTrack{
+// 		userId:     userId,
+// 		repository: repository,
+// 	}
+// }
+
+// func (t *segmentRedeployViaWebhookTrack) getUserId() string {
+// 	return t.userId
+// }
+
+// func (t *segmentRedeployViaWebhookTrack) getEvent() SegmentEvent {
+// 	return RedeployViaWebhook
+// }
+
+// func (t *segmentRedeployViaWebhookTrack) getProperties() segment.Properties {
+// 	return segment.NewProperties().Set("repository", t.repository)
+// }
+
+// type segmentNewProjectEventTrack struct {
+// 	userId   string
+// 	projId   string
+// 	projName string
+// }
+
+// // NewProjectEventOpts are the parameters for creating a "New Project Event" track
+// type NewProjectEventOpts struct {
+// 	UserID   string
+// 	ProjID   string
+// 	ProjName string
+// }
+
+// // CreateSegmentNewProjectEvent creates a track of type "New Project Event", which
+// // tracks whenever a cluster is newly provisioned, connected, or destroyed.
+// func CreateSegmentNewProjectEvent(opts *NewProjectEventOpts) *segmentNewProjectEventTrack {
+// 	return &segmentNewProjectEventTrack{
+// 		userId:   opts.UserID,
+// 		projId:   opts.ProjID,
+// 		projName: opts.ProjName,
+// 	}
+// }
+
+// func (t *segmentNewProjectEventTrack) getUserId() string {
+// 	return t.userId
+// }
+
+// func (t *segmentNewProjectEventTrack) getEvent() SegmentEvent {
+// 	return NewProjectEvent
+// }
+
+// func (t *segmentNewProjectEventTrack) getProperties() segment.Properties {
+// 	return segment.NewProperties().
+// 		Set("Project ID", t.projId).
+// 		Set("Project Name", t.projName)
+// }
+
+// type segmentNewClusterEventTrack struct {
+// 	userId      string
+// 	projId      string
+// 	clusterName string
+// 	clusterType string // EKS, DOKS, or GKE
+// 	eventType   string // connected, provisioned, or destroyed
+// }
+
+// // NewClusterEventOpts are the parameters for creating a "New Cluster Event" track
+// type NewClusterEventOpts struct {
+// 	UserId      string
+// 	ProjId      string
+// 	ClusterName string
+// 	ClusterType string // EKS, DOKS, or GKE
+// 	EventType   string // connected, provisioned, or destroyed
+// }
+
+// // CreateSegmentNewClusterEvent creates a track of type "New Cluster Event", which
+// // tracks whenever a cluster is newly provisioned, connected, or destroyed.
+// func CreateSegmentNewClusterEvent(opts *NewClusterEventOpts) *segmentNewClusterEventTrack {
+// 	return &segmentNewClusterEventTrack{
+// 		userId:      opts.UserId,
+// 		projId:      opts.ProjId,
+// 		clusterName: opts.ClusterName,
+// 		clusterType: opts.ClusterType,
+// 		eventType:   opts.EventType,
+// 	}
+// }
+
+// func (t *segmentNewClusterEventTrack) getUserId() string {
+// 	return t.userId
+// }
+
+// func (t *segmentNewClusterEventTrack) getEvent() SegmentEvent {
+// 	return NewClusterEvent
+// }
+
+// func (t *segmentNewClusterEventTrack) getProperties() segment.Properties {
+// 	return segment.NewProperties().Set("Project ID", t.projId).Set("Cluster Name", t.clusterName).Set("Cluster Type", t.clusterType).Set("Event Type", t.eventType)
+// }

+ 1 - 21
server/api/cluster_handler.go

@@ -2,12 +2,10 @@ package api
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"strconv"
 
 	"github.com/go-chi/chi"
-	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/forms"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/domain"
@@ -17,7 +15,7 @@ import (
 // HandleCreateProjectCluster creates a new cluster
 func (app *App) HandleCreateProjectCluster(w http.ResponseWriter, r *http.Request) {
 	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
-	userID, err := app.getUserIDFromRequest(r)
+	// userID, err := app.getUserIDFromRequest(r)
 
 	if err != nil || projID == 0 {
 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
@@ -57,15 +55,6 @@ func (app *App) HandleCreateProjectCluster(w http.ResponseWriter, r *http.Reques
 	}
 
 	app.Logger.Info().Msgf("New cluster created: %d", cluster.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", projID),
-			ClusterName: cluster.Name,
-			ClusterType: "EKS",
-			EventType:   "connected",
-		},
-	))
 
 	w.WriteHeader(http.StatusCreated)
 
@@ -447,15 +436,6 @@ func (app *App) HandleResolveClusterCandidate(w http.ResponseWriter, r *http.Req
 	}
 
 	app.Logger.Info().Msgf("New cluster created: %d", cluster.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", projID),
-			ClusterName: cluster.Name,
-			ClusterType: "",
-			EventType:   "connected",
-		},
-	))
 
 	clusterExt := cluster.Externalize()
 

+ 5 - 2
server/api/oauth_github_handler.go

@@ -131,8 +131,11 @@ func (app *App) HandleGithubOAuthCallback(w http.ResponseWriter, r *http.Request
 		}
 
 		// send to segment
-		app.analyticsClient.Identify(analytics.CreateSegmentIdentifyNewUser(user, true))
-		app.analyticsClient.Track(analytics.CreateSegmentNewUserTrack(user))
+		app.analyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
+
+		app.analyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
+			UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+		}))
 
 		// log the user in
 		app.Logger.Info().Msgf("New user created: %d", user.ID)

+ 4 - 2
server/api/oauth_google_handler.go

@@ -95,9 +95,11 @@ func (app *App) HandleGoogleOAuthCallback(w http.ResponseWriter, r *http.Request
 	}
 
 	// send to segment
-	app.analyticsClient.Identify(analytics.CreateSegmentIdentifyNewUser(user, true))
+	app.analyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
 
-	app.analyticsClient.Track(analytics.CreateSegmentNewUserTrack(user))
+	app.analyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
+		UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+	}))
 
 	// log the user in
 	app.Logger.Info().Msgf("New user created: %d", user.ID)

+ 17 - 53
server/api/provision_handler.go

@@ -7,8 +7,6 @@ import (
 
 	"github.com/go-chi/chi"
 
-	"fmt"
-
 	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/forms"
 	"github.com/porter-dev/porter/internal/kubernetes"
@@ -378,13 +376,11 @@ func (app *App) HandleProvisionAWSEKSInfra(w http.ResponseWriter, r *http.Reques
 	}
 
 	app.Logger.Info().Msgf("New aws eks infra created: %d", infra.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", infra.ProjectID),
-			ClusterName: form.EKSName,
-			ClusterType: "EKS",
-			EventType:   "provisioned",
+
+	app.analyticsClient.Track(analytics.ClusterProvisioningStartTrack(
+		&analytics.ClusterProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(userID, uint(projID)),
+			ClusterType:            "EKS",
 		},
 	))
 
@@ -402,7 +398,7 @@ func (app *App) HandleProvisionAWSEKSInfra(w http.ResponseWriter, r *http.Reques
 func (app *App) HandleDestroyAWSEKSInfra(w http.ResponseWriter, r *http.Request) {
 	// get path parameters
 	infraID, err := strconv.ParseUint(chi.URLParam(r, "infra_id"), 10, 64)
-	userID, err := app.getUserIDFromRequest(r)
+	// userID, err := app.getUserIDFromRequest(r)
 
 	if err != nil {
 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
@@ -469,15 +465,6 @@ func (app *App) HandleDestroyAWSEKSInfra(w http.ResponseWriter, r *http.Request)
 	}
 
 	app.Logger.Info().Msgf("AWS EKS infra marked for destruction: %d", infra.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", infra.ProjectID),
-			ClusterName: form.EKSName,
-			ClusterType: "EKS",
-			EventType:   "destroyed",
-		},
-	))
 
 	w.WriteHeader(http.StatusOK)
 }
@@ -641,13 +628,11 @@ func (app *App) HandleProvisionGCPGKEInfra(w http.ResponseWriter, r *http.Reques
 	}
 
 	app.Logger.Info().Msgf("New gcp gke infra created: %d", infra.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", infra.ProjectID),
-			ClusterName: form.GKEName,
-			ClusterType: "GKE",
-			EventType:   "provisioned",
+
+	app.analyticsClient.Track(analytics.ClusterProvisioningStartTrack(
+		&analytics.ClusterProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(userID, uint(projID)),
+			ClusterType:            "GKE",
 		},
 	))
 
@@ -665,7 +650,7 @@ func (app *App) HandleProvisionGCPGKEInfra(w http.ResponseWriter, r *http.Reques
 func (app *App) HandleDestroyGCPGKEInfra(w http.ResponseWriter, r *http.Request) {
 	// get path parameters
 	infraID, err := strconv.ParseUint(chi.URLParam(r, "infra_id"), 10, 64)
-	userID, err := app.getUserIDFromRequest(r)
+	// userID, err := app.getUserIDFromRequest(r)
 
 	if err != nil {
 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
@@ -731,15 +716,6 @@ func (app *App) HandleDestroyGCPGKEInfra(w http.ResponseWriter, r *http.Request)
 	}
 
 	app.Logger.Info().Msgf("GCP GKE infra marked for destruction: %d", infra.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", infra.ProjectID),
-			ClusterName: form.GKEName,
-			ClusterType: "GKE",
-			EventType:   "destroyed",
-		},
-	))
 
 	w.WriteHeader(http.StatusOK)
 }
@@ -1027,13 +1003,11 @@ func (app *App) HandleProvisionDODOKSInfra(w http.ResponseWriter, r *http.Reques
 	}
 
 	app.Logger.Info().Msgf("New do doks infra created: %d", infra.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", infra.ProjectID),
-			ClusterName: form.DOKSName,
-			ClusterType: "DOKS",
-			EventType:   "provisioned",
+
+	app.analyticsClient.Track(analytics.ClusterProvisioningStartTrack(
+		&analytics.ClusterProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(userID, uint(projID)),
+			ClusterType:            "DOKS",
 		},
 	))
 
@@ -1051,7 +1025,6 @@ func (app *App) HandleProvisionDODOKSInfra(w http.ResponseWriter, r *http.Reques
 func (app *App) HandleDestroyDODOKSInfra(w http.ResponseWriter, r *http.Request) {
 	// get path parameters
 	infraID, err := strconv.ParseUint(chi.URLParam(r, "infra_id"), 10, 64)
-	userID, err := app.getUserIDFromRequest(r)
 
 	if err != nil {
 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
@@ -1119,15 +1092,6 @@ func (app *App) HandleDestroyDODOKSInfra(w http.ResponseWriter, r *http.Request)
 	}
 
 	app.Logger.Info().Msgf("DO DOKS infra marked for destruction: %d", infra.ID)
-	app.analyticsClient.Track(analytics.CreateSegmentNewClusterEvent(
-		&analytics.NewClusterEventOpts{
-			UserId:      fmt.Sprintf("%d", userID),
-			ProjId:      fmt.Sprintf("%d", infra.ProjectID),
-			ClusterName: form.DOKSName,
-			ClusterType: "DOKS",
-			EventType:   "destroyed",
-		},
-	))
 
 	w.WriteHeader(http.StatusOK)
 }

+ 13 - 3
server/api/release_handler.go

@@ -1059,8 +1059,7 @@ func (app *App) HandleUpgradeRelease(w http.ResponseWriter, r *http.Request) {
 		notifyOpts.Status = slack.StatusFailed
 		notifyOpts.Info = upgradeErr.Error()
 
-		slackErr := notifier.Notify(notifyOpts)
-		fmt.Println("SLACK ERROR IS", slackErr)
+		notifier.Notify(notifyOpts)
 
 		app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
 			Code:   ErrReleaseDeploy,
@@ -1316,7 +1315,18 @@ func (app *App) HandleReleaseDeployWebhook(w http.ResponseWriter, r *http.Reques
 
 	notifier.Notify(notifyOpts)
 
-	app.analyticsClient.Track(analytics.CreateSegmentRedeployViaWebhookTrack("anonymous", repository.(string)))
+	userID, _ := app.getUserIDFromRequest(r)
+
+	app.analyticsClient.Track(analytics.ApplicationDeploymentWebhookTrack(&analytics.ApplicationDeploymentWebhookTrackOpts{
+		ImageURI: fmt.Sprintf("%v", repository),
+		ApplicationScopedTrackOpts: analytics.GetApplicationScopedTrackOpts(
+			userID,
+			release.ProjectID,
+			release.ClusterID,
+			release.Name,
+			release.Namespace,
+		),
+	}))
 
 	w.WriteHeader(http.StatusOK)
 }

+ 5 - 2
server/api/user_handler.go

@@ -54,8 +54,11 @@ func (app *App) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
 
 	if err == nil {
 		// send to segment
-		app.analyticsClient.Identify(analytics.CreateSegmentIdentifyNewUser(user, false))
-		app.analyticsClient.Track(analytics.CreateSegmentNewUserTrack(user))
+		app.analyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
+
+		app.analyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
+			UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+		}))
 
 		app.Logger.Info().Msgf("New user created: %d", user.ID)