Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cmd/quickstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -42,6 +45,7 @@ func runQuickStart(
defer f.Close()

_, err = tea.NewProgram(quickstart.NewContainerModel(
analyticsTracker,
environmentsClient,
flagsClient,
viper.GetString(cliflags.AccessTokenFlag),
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
156 changes: 156 additions & 0 deletions internal/analytics/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ type Tracker interface {
optOut bool,
outcome string,
)
SendSetupStartedEvent(
accessToken,
baseURI string,
optOut bool,
step string,
)
SendSetupSDKSelectedEvent(
accessToken,
baseURI string,
optOut bool,
sdk string,
)
SendSetupFlagToggledEvent(
accessToken,
baseURI string,
optOut bool,
on bool,
count int,
duration_ms int64,
)
}

type Client struct {
Expand Down Expand Up @@ -128,6 +148,61 @@ func (c *Client) SendCommandCompletedEvent(
}
}

func (c *Client) SendSetupStartedEvent(
accessToken,
baseURI string,
optOut bool,
step string,
) {
c.sendEvent(
accessToken,
baseURI,
optOut,
"CLI Setup Started",
map[string]interface{}{
"step": step,
},
)
}

func (c *Client) SendSetupSDKSelectedEvent(
accessToken,
baseURI string,
optOut bool,
sdk string,
) {
c.sendEvent(
accessToken,
baseURI,
optOut,
"CLI Setup SDK Selected",
map[string]interface{}{
"sdk": sdk,
},
)
}

func (c *Client) SendSetupFlagToggledEvent(
accessToken,
baseURI string,
optOut bool,
on bool,
count int,
duration_ms int64,
) {
c.sendEvent(
accessToken,
baseURI,
optOut,
"CLI Setup Flag Toggled",
map[string]interface{}{
"on": on,
"count": count,
"duration_ms": duration_ms,
},
)
}

func (a *Client) Wait() {
a.wg.Wait()
}
Expand All @@ -150,6 +225,32 @@ func (c *NoopClient) SendCommandCompletedEvent(
) {
}

func (c *NoopClient) SendSetupStartedEvent(
accessToken,
baseURI string,
optOut bool,
step string,
) {
}

func (c *NoopClient) SendSetupSDKSelectedEvent(
accessToken,
baseURI string,
optOut bool,
sdk string,
) {
}

func (c *NoopClient) SendSetupFlagToggledEvent(
accessToken,
baseURI string,
optOut bool,
on bool,
count int,
duration_ms int64,
) {
}

type MockTracker struct {
mock.Mock
ID string
Expand Down Expand Up @@ -197,3 +298,58 @@ func (m *MockTracker) SendCommandCompletedEvent(
},
)
}

func (m *MockTracker) SendSetupStartedEvent(
accessToken,
baseURI string,
optOut bool,
step string,
) {
m.sendEvent(
accessToken,
baseURI,
optOut,
"CLI Setup Started",
map[string]interface{}{
"step": step,
},
)
}

func (m *MockTracker) SendSetupSDKSelectedEvent(
accessToken,
baseURI string,
optOut bool,
sdk string,
) {
m.sendEvent(
accessToken,
baseURI,
optOut,
"CLI Setup SDK Selected",
map[string]interface{}{
"sdk": sdk,
},
)
}

func (m *MockTracker) SendSetupFlagToggledEvent(
accessToken,
baseURI string,
optOut bool,
on bool,
count int,
duration_ms int64,
) {
m.sendEvent(
accessToken,
baseURI,
optOut,
"CLI Setup Flag Toggled",
map[string]interface{}{
"on": on,
"count": count,
"duration_ms": duration_ms,
},
)
}
24 changes: 17 additions & 7 deletions internal/quickstart/choose_sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package quickstart
import (
"fmt"
"io"
"ldcli/cmd/cliflags"
"ldcli/internal/analytics"
"strings"

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/spf13/viper"
)

var (
Expand All @@ -24,14 +27,17 @@ const (
)

type chooseSDKModel struct {
help help.Model
helpKeys keyMap
list list.Model
selectedIndex int
selectedSDK sdkDetail
accessToken string
analyticsTracker analytics.Tracker
baseUri string
help help.Model
helpKeys keyMap
list list.Model
selectedIndex int
selectedSDK sdkDetail
}

func NewChooseSDKModel(selectedIndex int) tea.Model {
func NewChooseSDKModel(analyticsTracker analytics.Tracker, accessToken, baseUri string, selectedIndex int) tea.Model {
l := list.New(sdksToItems(), sdkDelegate{}, 30, 9)
l.Title = "Select your SDK:\n"
// reset title styles
Expand All @@ -43,7 +49,10 @@ func NewChooseSDKModel(selectedIndex int) tea.Model {
l.SetFilteringEnabled(false) // TODO: try to get filtering working

return chooseSDKModel{
help: help.New(),
analyticsTracker: analyticsTracker,
accessToken: accessToken,
baseUri: baseUri,
help: help.New(),
helpKeys: keyMap{
Back: BindingBack,
CursorUp: BindingCursorUp,
Expand All @@ -64,6 +73,7 @@ func NewChooseSDKModel(selectedIndex int) tea.Model {
// Init sends commands when the model is created that will:
// * select an SDK if it's already been selected
func (m chooseSDKModel) Init() tea.Cmd {
m.analyticsTracker.SendSetupStartedEvent(m.accessToken, m.baseUri, viper.GetBool(cliflags.AnalyticsOptOut), "2 - sdk selection")
return selectedSDK(m.selectedIndex)
}

Expand Down
22 changes: 18 additions & 4 deletions internal/quickstart/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package quickstart
import (
"fmt"
"log"
"time"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/muesli/reflow/wordwrap"

"ldcli/internal/analytics"
"ldcli/internal/environments"
"ldcli/internal/flags"
)
Expand All @@ -30,6 +32,7 @@ const (
// represents a step in the quick-start flow.
type ContainerModel struct {
accessToken string
analyticsTracker analytics.Tracker
baseUri string
currentModel tea.Model
currentStep int
Expand All @@ -41,29 +44,34 @@ type ContainerModel struct {
gettingStarted bool
quitting bool
sdk sdkDetail
startTime time.Time
totalSteps int
width int
}

func NewContainerModel(
analyticsTracker analytics.Tracker,
environmentsClient environments.Client,
flagsClient flags.Client,
accessToken string,
baseUri string,
) tea.Model {
return ContainerModel{
analyticsTracker: analyticsTracker,
accessToken: accessToken,
baseUri: baseUri,
currentModel: NewCreateFlagModel(flagsClient, accessToken, baseUri),
currentModel: NewCreateFlagModel(analyticsTracker, flagsClient, accessToken, baseUri),
currentStep: 1,
environmentsClient: environmentsClient,
flagsClient: flagsClient,
gettingStarted: true,
startTime: time.Now(),
totalSteps: 4,
}
}

func (m ContainerModel) Init() tea.Cmd {
m.currentModel.Init()
return nil
}

Expand All @@ -85,14 +93,16 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.currentModel, cmd = m.currentModel.Update(msg)
case stepChooseSDK:
m.currentStep -= 1
m.currentModel = NewCreateFlagModel(m.flagsClient, m.accessToken, m.baseUri)
m.currentModel = NewCreateFlagModel(m.analyticsTracker, m.flagsClient, m.accessToken, m.baseUri)
cmd = m.currentModel.Init()
case stepShowSDKInstructions:
m.currentStep -= 1
m.currentModel = NewChooseSDKModel(m.sdk.index)
m.currentModel = NewChooseSDKModel(m.analyticsTracker, m.accessToken, m.baseUri, m.sdk.index)
cmd = m.currentModel.Init()
case stepToggleFlag:
m.currentStep -= 1
m.currentModel = NewShowSDKInstructionsModel(
m.analyticsTracker,
m.environmentsClient,
m.accessToken,
m.baseUri,
Expand All @@ -110,6 +120,7 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case choseSDKMsg:
m.currentModel = NewShowSDKInstructionsModel(
m.analyticsTracker,
m.environmentsClient,
m.accessToken,
m.baseUri,
Expand All @@ -123,7 +134,8 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.sdk = msg.sdk
m.currentStep += 1
case confirmedFlagMsg:
m.currentModel = NewChooseSDKModel(0)
m.currentModel = NewChooseSDKModel(m.analyticsTracker, m.accessToken, m.baseUri, 0)
cmd = m.currentModel.Init()
m.flagKey = msg.flag.key
m.currentStep += 1
m.err = nil
Expand All @@ -142,11 +154,13 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.err = nil
case showToggleFlagMsg:
m.currentModel = NewToggleFlagModel(
m.analyticsTracker,
m.flagsClient,
m.accessToken,
m.baseUri,
m.flagKey,
m.sdk.kind,
m.startTime,
)
cmd = m.currentModel.Init()
m.currentStep += 1
Expand Down
Loading