瀏覽代碼

add analytics to refactor

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

+ 16 - 1
api/server/handlers/cluster/create_candidate.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
@@ -56,15 +57,29 @@ func (c *CreateClusterCandidateHandler) ServeHTTP(w http.ResponseWriter, r *http
 			return
 		}
 
+		c.Config().AnalyticsClient.Track(analytics.ClusterConnectionStartTrack(
+			&analytics.ClusterConnectionStartTrackOpts{
+				ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+				ClusterCandidateID:     cc.ID,
+			},
+		))
+
 		// if the ClusterCandidate does not have any actions to perform, create the Cluster
 		// automatically
 		if len(cc.Resolvers) == 0 {
-			_, cc, err = createClusterFromCandidate(c.Repo(), proj, user, cc, &types.ClusterResolverAll{})
+			cluster, cc, err := createClusterFromCandidate(c.Repo(), proj, user, cc, &types.ClusterResolverAll{})
 
 			if err != nil {
 				c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 				return
 			}
+
+			c.Config().AnalyticsClient.Track(analytics.ClusterConnectionSuccessTrack(
+				&analytics.ClusterConnectionSuccessTrackOpts{
+					ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(user.ID, proj.ID, cluster.ID),
+					ClusterCandidateID:     cc.ID,
+				},
+			))
 		}
 
 		res = append(res, cc.ToClusterCandidateType())

+ 8 - 0
api/server/handlers/cluster/resolve_candidate.go

@@ -9,6 +9,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 )
 
@@ -52,5 +53,12 @@ func (c *ResolveClusterCandidateHandler) ServeHTTP(w http.ResponseWriter, r *htt
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.ClusterConnectionSuccessTrack(
+		&analytics.ClusterConnectionSuccessTrackOpts{
+			ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(user.ID, proj.ID, cluster.ID),
+			ClusterCandidateID:     cc.ID,
+		},
+	))
+
 	c.WriteResult(w, r, cluster.ToClusterType())
 }

+ 7 - 69
api/server/handlers/gitinstallation/oauth_callback.go

@@ -9,6 +9,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/models/integrations"
 	"golang.org/x/oauth2"
@@ -75,78 +76,15 @@ func (c *GithubAppOAuthCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.GithubConnectionSuccessTrack(
+		&analytics.GithubConnectionSuccessTrackOpts{
+			UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+		},
+	))
+
 	if session.Values["query_params"] != "" {
 		http.Redirect(w, r, fmt.Sprintf("/dashboard?%s", session.Values["query_params"]), 302)
 	} else {
 		http.Redirect(w, r, "/dashboard", 302)
 	}
 }
-
-// func (app *App) HandleGithubAppOAuthCallback(w http.ResponseWriter, r *http.Request) {
-// 	session, err := app.Store.Get(r, app.ServerConf.CookieName)
-
-// 	if err != nil {
-// 		app.handleErrorDataRead(err, w)
-// 		return
-// 	}
-
-// 	token, err := app.GithubAppConf.Exchange(oauth2.NoContext, r.URL.Query().Get("code"))
-
-// 	if err != nil || !token.Valid() {
-// 		if session.Values["query_params"] != "" {
-// 			http.Redirect(w, r, fmt.Sprintf("/dashboard?%s", session.Values["query_params"]), 302)
-// 		} else {
-// 			http.Redirect(w, r, "/dashboard", 302)
-// 		}
-// 		return
-// 	}
-
-// 	fmt.Println("exchange happaned")
-// 	fmt.Println(token.AccessToken)
-// 	fmt.Println(token.RefreshToken)
-
-// 	userID, err := app.getUserIDFromRequest(r)
-
-// 	if err != nil {
-// 		app.handleErrorInternal(err, w)
-// 		return
-// 	}
-
-// 	user, err := app.Repo.User().ReadUser(userID)
-
-// 	if err != nil {
-// 		app.handleErrorInternal(err, w)
-// 		return
-// 	}
-
-// 	oauthInt := &integrations.GithubAppOAuthIntegration{
-// 		SharedOAuthModel: integrations.SharedOAuthModel{
-// 			AccessToken:  []byte(token.AccessToken),
-// 			RefreshToken: []byte(token.RefreshToken),
-// 			Expiry:       token.Expiry,
-// 		},
-// 		UserID: user.ID,
-// 	}
-
-// 	oauthInt, err = app.Repo.GithubAppOAuthIntegration().CreateGithubAppOAuthIntegration(oauthInt)
-
-// 	if err != nil {
-// 		app.handleErrorInternal(err, w)
-// 		return
-// 	}
-
-// 	user.GithubAppIntegrationID = oauthInt.ID
-
-// 	user, err = app.Repo.User().UpdateUser(user)
-
-// 	if err != nil {
-// 		app.handleErrorInternal(err, w)
-// 		return
-// 	}
-
-// 	if session.Values["query_params"] != "" {
-// 		http.Redirect(w, r, fmt.Sprintf("/dashboard?%s", session.Values["query_params"]), 302)
-// 	} else {
-// 		http.Redirect(w, r, "/dashboard", 302)
-// 	}
-// }

+ 11 - 0
api/server/handlers/gitinstallation/oauth_start.go

@@ -5,6 +5,9 @@ import (
 
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
+	"github.com/porter-dev/porter/internal/models"
 	"golang.org/x/oauth2"
 )
 
@@ -21,5 +24,13 @@ func NewGithubAppOAuthStartHandler(
 }
 
 func (c *GithubAppOAuthStartHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
+
+	c.Config().AnalyticsClient.Track(analytics.GithubConnectionStartTrack(
+		&analytics.GithubConnectionStartTrackOpts{
+			UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+		},
+	))
+
 	http.Redirect(w, r, c.Config().GithubAppConf.AuthCodeURL("", oauth2.AccessTypeOffline), 302)
 }

+ 5 - 0
api/server/handlers/project/create.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
 )
@@ -50,6 +51,10 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	p.Config().AnalyticsClient.Track(analytics.ProjectCreateTrack(&analytics.ProjectCreateTrackOpts{
+		ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+	}))
+
 	p.WriteResult(w, r, proj.ToProjectType())
 }
 

+ 11 - 1
api/server/handlers/provision/provision_docr.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
@@ -30,7 +31,8 @@ func NewProvisionDOCRHandler(
 }
 
 func (c *ProvisionDOCRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	// read the project from context
+	// read the user and project from context
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
 	request := &types.CreateDOCRInfraRequest{}
@@ -100,5 +102,13 @@ func (c *ProvisionDOCRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.RegistryProvisioningStartTrack(
+		&analytics.RegistryProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			RegistryType:           types.InfraDOCR,
+			InfraID:                infra.ID,
+		},
+	))
+
 	c.WriteResult(w, r, infra.ToInfraType())
 }

+ 11 - 1
api/server/handlers/provision/provision_doks.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
@@ -30,7 +31,8 @@ func NewProvisionDOKSHandler(
 }
 
 func (c *ProvisionDOKSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	// read the project from context
+	// read the user and project from context
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
 	request := &types.CreateDOKSInfraRequest{}
@@ -100,5 +102,13 @@ func (c *ProvisionDOKSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.ClusterProvisioningStartTrack(
+		&analytics.ClusterProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			ClusterType:            types.InfraDOKS,
+			InfraID:                infra.ID,
+		},
+	))
+
 	c.WriteResult(w, r, infra.ToInfraType())
 }

+ 11 - 1
api/server/handlers/provision/provision_ecr.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
@@ -30,7 +31,8 @@ func NewProvisionECRHandler(
 }
 
 func (c *ProvisionECRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	// read the project from context
+	// read the user and project from context
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
 	request := &types.CreateECRInfraRequest{}
@@ -98,5 +100,13 @@ func (c *ProvisionECRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.RegistryProvisioningStartTrack(
+		&analytics.RegistryProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			RegistryType:           types.InfraECR,
+			InfraID:                infra.ID,
+		},
+	))
+
 	c.WriteResult(w, r, infra.ToInfraType())
 }

+ 11 - 97
api/server/handlers/provision/provision_eks.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
@@ -30,7 +31,8 @@ func NewProvisionEKSHandler(
 }
 
 func (c *ProvisionEKSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	// read the project from context
+	// read the user and project from context
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
 	request := &types.CreateEKSInfraRequest{}
@@ -99,101 +101,13 @@ func (c *ProvisionEKSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.ClusterProvisioningStartTrack(
+		&analytics.ClusterProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			ClusterType:            types.InfraEKS,
+			InfraID:                infra.ID,
+		},
+	))
+
 	c.WriteResult(w, r, infra.ToInfraType())
 }
-
-// // HandleProvisionAWSEKSInfra provisions a new aws EKS instance for a project
-// func (app *App) HandleProvisionAWSEKSInfra(w http.ResponseWriter, r *http.Request) {
-// 	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
-// 	userID, err := app.getUserIDFromRequest(r)
-
-// 	if err != nil || projID == 0 {
-// 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
-// 		return
-// 	}
-
-// 	form := &forms.CreateEKSInfra{
-// 		ProjectID: uint(projID),
-// 	}
-
-// 	// decode from JSON to form value
-// 	if err := json.NewDecoder(r.Body).Decode(form); err != nil {
-// 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
-// 		return
-// 	}
-
-// 	// validate the form
-// 	if err := app.validator.Struct(form); err != nil {
-// 		app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
-// 		return
-// 	}
-
-// 	// convert the form to an aws infra instance
-// 	infra, err := form.ToInfra()
-
-// 	if err != nil {
-// 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
-// 		return
-// 	}
-
-// 	// handle write to the database
-// 	infra, err = app.Repo.Infra().CreateInfra(infra)
-
-// 	if err != nil {
-// 		app.handleErrorDataWrite(err, w)
-// 		return
-// 	}
-
-// 	awsInt, err := app.Repo.AWSIntegration().ReadAWSIntegration(infra.AWSIntegrationID)
-
-// 	if err != nil {
-// 		infra.Status = types.StatusError
-// 		infra, _ = app.Repo.Infra().UpdateInfra(infra)
-
-// 		app.handleErrorDataRead(err, w)
-// 		return
-// 	}
-
-// 	// launch provisioning pod
-// 	_, err = app.ProvisionerAgent.ProvisionEKS(
-// 		uint(projID),
-// 		awsInt,
-// 		form.EKSName,
-// 		form.MachineType,
-// 		app.Repo,
-// 		infra,
-// 		provisioner.Apply,
-// 		&app.DBConf,
-// 		app.RedisConf,
-// 		app.ServerConf.ProvisionerImageTag,
-// 		app.ServerConf.ProvisionerImagePullSecret,
-// 	)
-
-// 	if err != nil {
-// 		infra.Status = types.StatusError
-// 		infra, _ = app.Repo.Infra().UpdateInfra(infra)
-
-// 		app.handleErrorInternal(err, w)
-// 		return
-// 	}
-
-// 	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",
-// 		},
-// 	))
-
-// 	w.WriteHeader(http.StatusCreated)
-
-// 	infraExt := infra.ToInfraType()
-
-// 	if err := json.NewEncoder(w).Encode(infraExt); err != nil {
-// 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
-// 		return
-// 	}
-// }

+ 11 - 1
api/server/handlers/provision/provision_gcr.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
@@ -30,7 +31,8 @@ func NewProvisionGCRHandler(
 }
 
 func (c *ProvisionGCRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	// read the project from context
+	// read the user and project from context
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
 	request := &types.CreateGCRInfraRequest{}
@@ -97,5 +99,13 @@ func (c *ProvisionGCRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.RegistryProvisioningStartTrack(
+		&analytics.RegistryProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			RegistryType:           types.InfraGCR,
+			InfraID:                infra.ID,
+		},
+	))
+
 	c.WriteResult(w, r, infra.ToInfraType())
 }

+ 11 - 1
api/server/handlers/provision/provision_gke.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
@@ -30,7 +31,8 @@ func NewProvisionGKEHandler(
 }
 
 func (c *ProvisionGKEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	// read the project from context
+	// read the user and project from context
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 
 	request := &types.CreateGKEInfraRequest{}
@@ -98,5 +100,13 @@ func (c *ProvisionGKEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	c.Config().AnalyticsClient.Track(analytics.ClusterProvisioningStartTrack(
+		&analytics.ClusterProvisioningStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			ClusterType:            types.InfraGKE,
+			InfraID:                infra.ID,
+		},
+	))
+
 	c.WriteResult(w, r, infra.ToInfraType())
 }

+ 18 - 0
api/server/handlers/registry/create.go

@@ -8,7 +8,9 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/oauth"
 	"github.com/porter-dev/porter/internal/registry"
 )
 
@@ -27,7 +29,16 @@ func NewRegistryCreateHandler(
 }
 
 func (p *RegistryCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+	operationID := oauth.CreateRandomState()
+
+	p.Config().AnalyticsClient.Track(analytics.RegistryConnectionStartTrack(
+		&analytics.RegistryConnectionStartTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, proj.ID),
+			FlowID:                 operationID,
+		},
+	))
 
 	request := &types.CreateRegistryRequest{}
 
@@ -67,5 +78,12 @@ func (p *RegistryCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 		return
 	}
 
+	p.Config().AnalyticsClient.Track(analytics.RegistryConnectionSuccessTrack(
+		&analytics.RegistryConnectionSuccessTrackOpts{
+			RegistryScopedTrackOpts: analytics.GetRegistryScopedTrackOpts(user.ID, proj.ID, regModel.ID),
+			FlowID:                  operationID,
+		},
+	))
+
 	p.WriteResult(w, r, regModel.ToRegistryType())
 }

+ 24 - 0
api/server/handlers/release/create.go

@@ -11,11 +11,13 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/auth/token"
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/helm/loader"
 	"github.com/porter-dev/porter/internal/integrations/ci/actions"
 	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/oauth"
 	"github.com/porter-dev/porter/internal/registry"
 	"github.com/porter-dev/porter/internal/repository"
 	"gopkg.in/yaml.v2"
@@ -42,6 +44,14 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 	namespace := r.Context().Value(types.NamespaceScope).(string)
+	operationID := oauth.CreateRandomState()
+
+	c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchStartTrack(
+		&analytics.ApplicationLaunchStartTrackOpts{
+			ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(user.ID, cluster.ProjectID, cluster.ID),
+			FlowID:                 operationID,
+		},
+	))
 
 	helmAgent, err := c.GetHelmAgent(r, cluster)
 
@@ -123,6 +133,20 @@ func (c *CreateReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 			return
 		}
 	}
+
+	c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchSuccessTrack(
+		&analytics.ApplicationLaunchSuccessTrackOpts{
+			ApplicationScopedTrackOpts: analytics.GetApplicationScopedTrackOpts(
+				user.ID,
+				cluster.ProjectID,
+				cluster.ID,
+				release.Name,
+				release.Namespace,
+				chart.Metadata.Name,
+			),
+			FlowID: operationID,
+		},
+	))
 }
 
 func createReleaseFromHelmRelease(

+ 26 - 1
api/server/handlers/release/create_addon.go

@@ -10,9 +10,11 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/helm/loader"
 	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/oauth"
 )
 
 type CreateAddonHandler struct {
@@ -32,8 +34,17 @@ func NewCreateAddonHandler(
 }
 
 func (c *CreateAddonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
 	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
 	namespace := r.Context().Value(types.NamespaceScope).(string)
+	operationID := oauth.CreateRandomState()
+
+	c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchStartTrack(
+		&analytics.ApplicationLaunchStartTrackOpts{
+			ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(user.ID, cluster.ProjectID, cluster.ID),
+			FlowID:                 operationID,
+		},
+	))
 
 	helmAgent, err := c.GetHelmAgent(r, cluster)
 
@@ -72,7 +83,7 @@ func (c *CreateAddonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		Registries: registries,
 	}
 
-	_, err = helmAgent.InstallChart(conf, c.Config().DOConf)
+	helmRelease, err := helmAgent.InstallChart(conf, c.Config().DOConf)
 
 	if err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
@@ -82,4 +93,18 @@ func (c *CreateAddonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 		return
 	}
+
+	c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchSuccessTrack(
+		&analytics.ApplicationLaunchSuccessTrackOpts{
+			ApplicationScopedTrackOpts: analytics.GetApplicationScopedTrackOpts(
+				user.ID,
+				cluster.ProjectID,
+				cluster.ID,
+				helmRelease.Name,
+				helmRelease.Namespace,
+				chart.Metadata.Name,
+			),
+			FlowID: operationID,
+		},
+	))
 }

+ 13 - 0
api/server/handlers/release/upgrade_webhook.go

@@ -12,6 +12,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/helm"
 	"github.com/porter-dev/porter/internal/integrations/slack"
 )
@@ -161,4 +162,16 @@ func (c *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	notifyOpts.Version = rel.Version
 
 	notifier.Notify(notifyOpts)
+
+	c.Config().AnalyticsClient.Track(analytics.ApplicationDeploymentWebhookTrack(&analytics.ApplicationDeploymentWebhookTrackOpts{
+		ImageURI: fmt.Sprintf("%v", repository),
+		ApplicationScopedTrackOpts: analytics.GetApplicationScopedTrackOpts(
+			0,
+			release.ProjectID,
+			release.ClusterID,
+			release.Name,
+			release.Namespace,
+			rel.Chart.Metadata.Name,
+		),
+	}))
 }

+ 8 - 0
api/server/handlers/user/create.go

@@ -10,6 +10,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
 	"golang.org/x/crypto/bcrypt"
@@ -76,6 +77,13 @@ func (u *UserCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	u.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
+
+	u.Config().AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
+		UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+		Email:               user.Email,
+	}))
+
 	u.WriteResult(w, r, user.ToUserType())
 }
 

+ 6 - 0
api/server/handlers/user/email_verify.go

@@ -10,6 +10,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/notifier"
 )
@@ -117,6 +118,11 @@ func (v *VerifyEmailFinalizeHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 		return
 	}
 
+	v.Config().AnalyticsClient.Track(analytics.UserVerifyEmailTrack(&analytics.UserVerifyEmailTrackOpts{
+		UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+		Email:               user.Email,
+	}))
+
 	http.Redirect(w, r, "/dashboard", 302)
 	return
 }

+ 8 - 0
api/server/handlers/user/github_callback.go

@@ -16,6 +16,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 )
 
@@ -74,6 +75,8 @@ func (p *UserOAuthGithubCallbackHandler) ServeHTTP(w http.ResponseWriter, r *htt
 		return
 	}
 
+	p.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
+
 	// save the user as authenticated in the session
 	if err := authn.SaveUserAuthenticated(w, r, p.Config(), user); err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
@@ -138,6 +141,11 @@ func upsertUserFromToken(config *config.Config, tok *oauth2.Token) (*models.User
 			if err != nil {
 				return nil, err
 			}
+
+			config.AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
+				UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+				Email:               user.Email,
+			}))
 		} else if err == nil {
 			return nil, fmt.Errorf("email already registered")
 		} else if err != nil {

+ 8 - 0
api/server/handlers/user/google_callback.go

@@ -16,6 +16,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/models"
 )
 
@@ -77,6 +78,8 @@ func (p *UserOAuthGoogleCallbackHandler) ServeHTTP(w http.ResponseWriter, r *htt
 		return
 	}
 
+	p.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
+
 	// save the user as authenticated in the session
 	if err := authn.SaveUserAuthenticated(w, r, p.Config(), user); err != nil {
 		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
@@ -123,6 +126,11 @@ func upsertGoogleUserFromToken(config *config.Config, tok *oauth2.Token) (*model
 			if err != nil {
 				return nil, err
 			}
+
+			config.AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
+				UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+				Email:               user.Email,
+			}))
 		} else if err == nil {
 			return nil, fmt.Errorf("email already registered")
 		} else if err != nil {

+ 0 - 23
api/server/router/base.go

@@ -335,29 +335,6 @@ func GetBaseRoutes(
 		Router:   r,
 	})
 
-	//  GET /api/integrations/github-app/oauth -> gitinstallation.NewGithubAppOAuthStartHandler
-	githubAppOAuthStartEndpoint := factory.NewAPIEndpoint(
-		&types.APIRequestMetadata{
-			Verb:   types.APIVerbGet,
-			Method: types.HTTPVerbGet,
-			Path: &types.Path{
-				Parent:       basePath,
-				RelativePath: "/integrations/github-app/oauth",
-			},
-			Scopes: []types.PermissionScope{},
-		},
-	)
-
-	githubAppOAuthStartHandler := gitinstallation.NewGithubAppOAuthStartHandler(
-		config,
-	)
-
-	routes = append(routes, &Route{
-		Endpoint: githubAppOAuthStartEndpoint,
-		Handler:  githubAppOAuthStartHandler,
-		Router:   r,
-	})
-
 	//  GET /api/integrations/github-app/install
 	githubAppInstallEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 25 - 0
api/server/router/user.go

@@ -318,6 +318,31 @@ func getUserRoutes(
 		Router:   r,
 	})
 
+	//  GET /api/integrations/github-app/oauth -> gitinstallation.NewGithubAppOAuthStartHandler
+	githubAppOAuthStartEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/integrations/github-app/oauth",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+			},
+		},
+	)
+
+	githubAppOAuthStartHandler := gitinstallation.NewGithubAppOAuthStartHandler(
+		config,
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: githubAppOAuthStartEndpoint,
+		Handler:  githubAppOAuthStartHandler,
+		Router:   r,
+	})
+
 	//  GET /api/oauth/github-app/callback -> gitinstallation.GithubAppOAuthCallbackHandler
 	githubAppOAuthCallbackEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{