diff --git a/cmd/quickstart.go b/cmd/quickstart.go index 51a38f67..acac70a4 100644 --- a/cmd/quickstart.go +++ b/cmd/quickstart.go @@ -11,25 +11,28 @@ import ( "ldcli/cmd/cliflags" "ldcli/cmd/validators" + "ldcli/internal/analytics" "ldcli/internal/environments" "ldcli/internal/flags" "ldcli/internal/quickstart" ) func NewQuickStartCmd( + analyticsTracker analytics.Tracker, environmentsClient environments.Client, flagsClient flags.Client, ) *cobra.Command { return &cobra.Command{ Args: validators.Validate(), Long: "", - RunE: runQuickStart(environmentsClient, flagsClient), + RunE: runQuickStart(analyticsTracker, environmentsClient, flagsClient), Short: "Setup guide to create your first feature flag", Use: "setup", } } func runQuickStart( + analyticsTracker analytics.Tracker, environmentsClient environments.Client, flagsClient flags.Client, ) func(*cobra.Command, []string) error { @@ -42,9 +45,11 @@ func runQuickStart( defer f.Close() _, err = tea.NewProgram(quickstart.NewContainerModel( + analyticsTracker, environmentsClient, flagsClient, viper.GetString(cliflags.AccessTokenFlag), + viper.GetBool(cliflags.AnalyticsOptOut), viper.GetString(cliflags.BaseURIFlag), )).Run() if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 2e922b8b..5f353066 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -142,7 +142,7 @@ func NewRootCommand( cmd.AddCommand(flagsCmd) cmd.AddCommand(membersCmd) cmd.AddCommand(projectsCmd) - cmd.AddCommand(NewQuickStartCmd(clients.EnvironmentsClient, clients.FlagsClient)) + cmd.AddCommand(NewQuickStartCmd(analyticsTracker, clients.EnvironmentsClient, clients.FlagsClient)) addAllResourceCmds(cmd, clients.GenericClient, analyticsTracker) diff --git a/internal/analytics/client.go b/internal/analytics/client.go index 0bce52c3..4c2d33e2 100644 --- a/internal/analytics/client.go +++ b/internal/analytics/client.go @@ -24,6 +24,12 @@ type Tracker interface { optOut bool, outcome string, ) + SendSetupStepStartedEvent( + accessToken, + baseURI string, + optOut bool, + step string, + ) } type Client struct { @@ -128,6 +134,23 @@ func (c *Client) SendCommandCompletedEvent( } } +func (c *Client) SendSetupStepStartedEvent( + accessToken, + baseURI string, + optOut bool, + step string, +) { + c.sendEvent( + accessToken, + baseURI, + optOut, + "CLI Setup Step Started", + map[string]interface{}{ + "step": step, + }, + ) +} + func (a *Client) Wait() { a.wg.Wait() } @@ -150,6 +173,14 @@ func (c *NoopClient) SendCommandCompletedEvent( ) { } +func (c *NoopClient) SendSetupStepStartedEvent( + accessToken, + baseURI string, + optOut bool, + step string, +) { +} + type MockTracker struct { mock.Mock ID string @@ -197,3 +228,20 @@ func (m *MockTracker) SendCommandCompletedEvent( }, ) } + +func (m *MockTracker) SendSetupStepStartedEvent( + accessToken, + baseURI string, + optOut bool, + step string, +) { + m.sendEvent( + accessToken, + baseURI, + optOut, + "CLI Setup Step Started", + map[string]interface{}{ + "step": step, + }, + ) +} diff --git a/internal/quickstart/container.go b/internal/quickstart/container.go index 9562bc50..5f4b5729 100644 --- a/internal/quickstart/container.go +++ b/internal/quickstart/container.go @@ -9,6 +9,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/muesli/reflow/wordwrap" + "ldcli/internal/analytics" "ldcli/internal/environments" "ldcli/internal/flags" ) @@ -18,21 +19,35 @@ const ( defaultEnvKey = "test" ) +type step int + const ( - _ = iota + _ step = iota stepCreateFlag stepChooseSDK stepShowSDKInstructions stepToggleFlag ) +func (s step) String() string { + return []string{ + "_", + "1 - feature flag name", + "2 - sdk selection", + "3 - sdk installation", + "4 - flag toggle", + }[s] +} + // ContainerModel is a high level container model that controls the nested models where each // represents a step in the quick-start flow. type ContainerModel struct { accessToken string + analyticsOptOut bool + analyticsTracker analytics.Tracker baseUri string currentModel tea.Model - currentStep int + currentStep step environment *environment environmentsClient environments.Client err error @@ -46,13 +61,17 @@ type ContainerModel struct { } func NewContainerModel( + analyticsTracker analytics.Tracker, environmentsClient environments.Client, flagsClient flags.Client, accessToken string, + analyticsOptOut bool, baseUri string, ) tea.Model { return ContainerModel{ accessToken: accessToken, + analyticsOptOut: analyticsOptOut, + analyticsTracker: analyticsTracker, baseUri: baseUri, currentModel: NewCreateFlagModel(flagsClient, accessToken, baseUri), currentStep: 1, @@ -64,11 +83,18 @@ func NewContainerModel( } func (m ContainerModel) Init() tea.Cmd { - return nil + return trackSetupStepStartedEvent( + m.analyticsTracker, + m.accessToken, + m.baseUri, + m.analyticsOptOut, + m.currentStep.String(), + ) } func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd + var sendEvent bool switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width @@ -103,11 +129,18 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.environment, ) cmd = m.currentModel.Init() + sendEvent = true } default: // delegate all other input to the current model m.currentModel, cmd = m.currentModel.Update(msg) } + case confirmedFlagMsg: + m.currentModel = NewChooseSDKModel(0) + m.flagKey = msg.flag.key + m.currentStep += 1 + m.err = nil + sendEvent = true case choseSDKMsg: m.currentModel = NewShowSDKInstructionsModel( m.environmentsClient, @@ -122,11 +155,7 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = m.currentModel.Init() m.sdk = msg.sdk m.currentStep += 1 - case confirmedFlagMsg: - m.currentModel = NewChooseSDKModel(0) - m.flagKey = msg.flag.key - m.currentStep += 1 - m.err = nil + sendEvent = true case errMsg: m.currentModel, cmd = m.currentModel.Update(msg) case fetchedEnvMsg: @@ -150,10 +179,21 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { ) cmd = m.currentModel.Init() m.currentStep += 1 + sendEvent = true default: log.Printf("container default: %T\n", msg) } + if sendEvent { + cmd = tea.Batch(cmd, trackSetupStepStartedEvent( + m.analyticsTracker, + m.accessToken, + m.baseUri, + m.analyticsOptOut, + m.currentStep.String(), + )) + } + return m, cmd } diff --git a/internal/quickstart/messages.go b/internal/quickstart/messages.go index d0cb77b9..9b8bf2fd 100644 --- a/internal/quickstart/messages.go +++ b/internal/quickstart/messages.go @@ -7,6 +7,7 @@ import ( tea "github.com/charmbracelet/bubbletea" + "ldcli/internal/analytics" "ldcli/internal/environments" "ldcli/internal/errors" "ldcli/internal/flags" @@ -240,3 +241,13 @@ func selectedSDK(index int) tea.Cmd { return selectedSDKMsg{index: index} } } + +type eventTrackedMsg struct{} + +func trackSetupStepStartedEvent(tracker analytics.Tracker, accessToken, baseURI string, optOut bool, step string) tea.Cmd { + return func() tea.Msg { + tracker.SendSetupStepStartedEvent(accessToken, baseURI, optOut, step) + + return eventTrackedMsg{} + } +}