Skip to content

Commit

Permalink
feat: Added circleci telemetry enable and `circleci telemetry disab…
Browse files Browse the repository at this point in the history
…le` commands
  • Loading branch information
JulesFaucherre committed Aug 1, 2023
1 parent a68d639 commit 91caef4
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 1 deletion.
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func MakeCommands() *cobra.Command {
rootCmd.AddCommand(newAdminCommand(rootOptions))
rootCmd.AddCommand(newCompletionCommand())
rootCmd.AddCommand(newEnvCmd())
rootCmd.AddCommand(newTelemetryCommand(rootOptions))

flags := rootCmd.PersistentFlags()

Expand Down
2 changes: 1 addition & 1 deletion cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var _ = Describe("Root", func() {
Describe("subcommands", func() {
It("can create commands", func() {
commands := cmd.MakeCommands()
Expect(len(commands.Commands())).To(Equal(24))
Expect(len(commands.Commands())).To(Equal(25))
})
})

Expand Down
73 changes: 73 additions & 0 deletions cmd/telemetry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package cmd

import (
"os"

"github.com/CircleCI-Public/circleci-cli/api/rest"
"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

func newTelemetryCommand(config *settings.Config) *cobra.Command {
apiClient := telemetryCircleCIAPI{
cli: rest.NewFromConfig(config.Host, config),
}

telemetryEnable := &cobra.Command{
Use: "enable",
Short: "Allow telemetry events to be sent to CircleCI servers",
RunE: func(_ *cobra.Command, _ []string) error {
return setIsTelemetryActive(apiClient, true)
},
Args: cobra.ExactArgs(0),
}

telemetryDisable := &cobra.Command{
Use: "disable",
Short: "Make sure no telemetry events is sent to CircleCI servers",
RunE: func(_ *cobra.Command, _ []string) error {
return setIsTelemetryActive(apiClient, false)
},
Args: cobra.ExactArgs(0),
}

telemetryCommand := &cobra.Command{
Use: "telemetry",
Short: "Configure telemetry preferences",
Long: `Configure telemetry preferences.
Note: If you have not configured your telemetry preferences and call the CLI with a closed stdin, telemetry will be disabled`,
}

telemetryCommand.AddCommand(telemetryEnable)
telemetryCommand.AddCommand(telemetryDisable)

return telemetryCommand
}

func setIsTelemetryActive(apiClient telemetryAPIClient, isActive bool) error {
settings := settings.TelemetrySettings{}
if err := settings.Load(); err != nil && !os.IsNotExist(err) {
return errors.Wrap(err, "Loading telemetry configuration")
}

settings.HasAnsweredPrompt = true
settings.IsActive = isActive

if settings.UniqueID == "" {
settings.UniqueID = createUUID()
}

if settings.UserID == "" {
if myID, err := apiClient.getMyUserId(); err == nil {
settings.UserID = myID
}
}

if err := settings.Write(); err != nil {
return errors.Wrap(err, "Writing telemetry configuration")
}

return nil
}
143 changes: 143 additions & 0 deletions cmd/telemetry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package cmd

import (
"path/filepath"
"testing"

"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/spf13/afero"
"gotest.tools/v3/assert"
)

func TestSetIsTelemetryActive(t *testing.T) {
type args struct {
apiClient telemetryAPIClient
isActive bool
settings *settings.TelemetrySettings
}
type want struct {
settings *settings.TelemetrySettings
}

type testCase struct {
name string
args args
want want
}

userId := "user-id"
uniqueId := "unique-id"

testCases := []testCase{
{
name: "Enabling telemetry with settings should just update the is active field",
args: args{
apiClient: telemetryTestAPIClient{},
isActive: true,
settings: &settings.TelemetrySettings{
IsActive: false,
HasAnsweredPrompt: true,
UniqueID: uniqueId,
UserID: userId,
},
},
want: want{
settings: &settings.TelemetrySettings{
IsActive: true,
HasAnsweredPrompt: true,
UniqueID: uniqueId,
UserID: userId,
},
},
},
{
name: "Enabling telemetry without settings should fill the settings fields",
args: args{
apiClient: telemetryTestAPIClient{id: userId, err: nil},
isActive: true,
settings: nil,
},
want: want{
settings: &settings.TelemetrySettings{
IsActive: true,
HasAnsweredPrompt: true,
UniqueID: uniqueId,
UserID: userId,
},
},
},
{
name: "Disabling telemetry with settings should just update the is active field",
args: args{
apiClient: telemetryTestAPIClient{},
isActive: false,
settings: &settings.TelemetrySettings{
IsActive: true,
HasAnsweredPrompt: true,
UniqueID: uniqueId,
UserID: userId,
},
},
want: want{
settings: &settings.TelemetrySettings{
IsActive: false,
HasAnsweredPrompt: true,
UniqueID: uniqueId,
UserID: userId,
},
},
},
{
name: "Enabling telemetry without settings should fill the settings fields",
args: args{
apiClient: telemetryTestAPIClient{id: userId, err: nil},
isActive: false,
settings: nil,
},
want: want{
settings: &settings.TelemetrySettings{
IsActive: false,
HasAnsweredPrompt: true,
UniqueID: uniqueId,
UserID: userId,
},
},
},
}

// Mock create UUID
oldUUIDCreate := createUUID
createUUID = func() string { return uniqueId }
defer (func() { createUUID = oldUUIDCreate })()

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
// Mock FS
oldFS := settings.FS.Fs
settings.FS.Fs = afero.NewMemMapFs()
defer (func() { settings.FS.Fs = oldFS })()

if tt.args.settings != nil {
err := tt.args.settings.Write()
assert.NilError(t, err)
}

err := setIsTelemetryActive(tt.args.apiClient, tt.args.isActive)
assert.NilError(t, err)

exist, err := settings.FS.Exists(filepath.Join(settings.SettingsPath(), "telemetry.yml"))
assert.NilError(t, err)
if tt.want.settings == nil {
assert.Equal(t, exist, false)
} else {
assert.Equal(t, exist, true)

loadedSettings := &settings.TelemetrySettings{}
err := loadedSettings.Load()
assert.NilError(t, err)

assert.DeepEqual(t, tt.want.settings, loadedSettings)
}
})
}
}

0 comments on commit 91caef4

Please sign in to comment.