Skip to content

Commit 89f7621

Browse files
authored
Merge branch 'akitasoftware:main' into main (#7)
2 parents 843ce7e + 68d6681 commit 89f7621

File tree

3 files changed

+97
-83
lines changed

3 files changed

+97
-83
lines changed

go.mod

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/OneOfOne/xxhash v1.2.8
88
github.com/Pallinder/go-randomdata v1.2.0
99
github.com/akitasoftware/akita-ir v0.0.0-20220630210013-8926783978fe
10-
github.com/akitasoftware/akita-libs v0.0.0-20240308023724-7593cac5a9f5
10+
github.com/akitasoftware/akita-libs v0.0.0-20240415065826-ff8036138dc1
1111
github.com/akitasoftware/go-utils v0.0.0-20221207014235-6f4c9079488d
1212
github.com/akitasoftware/plugin-flickr v0.2.0
1313
github.com/andybalholm/brotli v1.0.1
@@ -59,15 +59,12 @@ require (
5959
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.8 // indirect
6060
github.com/aws/aws-sdk-go-v2/service/sts v1.17.1 // indirect
6161
github.com/davecgh/go-spew v1.1.1 // indirect
62-
github.com/dukex/mixpanel v1.0.1 // indirect
6362
github.com/fsnotify/fsnotify v1.4.9 // indirect
6463
github.com/go-logr/logr v1.2.3 // indirect
6564
github.com/gogo/protobuf v1.3.2 // indirect
6665
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
6766
github.com/google/gofuzz v1.1.0 // indirect
68-
github.com/hashicorp/errwrap v1.0.0 // indirect
6967
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
70-
github.com/hashicorp/go-multierror v1.1.1 // indirect
7168
github.com/hashicorp/hcl v1.0.0 // indirect
7269
github.com/iancoleman/strcase v0.3.0 // indirect
7370
github.com/inconshreveable/mousetrap v1.0.1 // indirect
@@ -83,8 +80,6 @@ require (
8380
github.com/modern-go/reflect2 v1.0.2 // indirect
8481
github.com/pelletier/go-toml v1.2.0 // indirect
8582
github.com/pmezard/go-difflib v1.0.0 // indirect
86-
github.com/segmentio/analytics-go/v3 v3.2.1 // indirect
87-
github.com/segmentio/backo-go v1.0.0 // indirect
8883
github.com/spf13/afero v1.1.2 // indirect
8984
github.com/spf13/cast v1.3.0 // indirect
9085
github.com/spf13/jwalterweatherman v1.0.0 // indirect

go.sum

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ github.com/akitasoftware/akita-ir v0.0.0-20211020161529-944af4d11d6e/go.mod h1:W
3131
github.com/akitasoftware/akita-ir v0.0.0-20220630210013-8926783978fe h1:0BeBDjLDFPwv2bkk6YuRAPf1r6U4Wby98NHI9+Lddvs=
3232
github.com/akitasoftware/akita-ir v0.0.0-20220630210013-8926783978fe/go.mod h1:WEWPzhZtxlJnov3MxcqSDiZaHHf00vs3aJwCdt3OwzA=
3333
github.com/akitasoftware/akita-libs v0.0.0-20211020162041-fe02207174fb/go.mod h1:YLFCjhwQ0ZFfYWSUD2c9KYKEeBn+R+Cz+A5SitXvJz8=
34-
github.com/akitasoftware/akita-libs v0.0.0-20240308023724-7593cac5a9f5 h1:HPNpEBwi3IKlp+J30YdFA/wbG1x+1+bYYKmpy1oaTaM=
35-
github.com/akitasoftware/akita-libs v0.0.0-20240308023724-7593cac5a9f5/go.mod h1:Wo3rkItMZBjXszdCutVK5I6QNMMEslN+ltRSS7C03jk=
34+
github.com/akitasoftware/akita-libs v0.0.0-20240415065826-ff8036138dc1 h1:z2VL5kA1ogtMU92PTzArL+ipGpZLPAWJYY9ent/0BrE=
35+
github.com/akitasoftware/akita-libs v0.0.0-20240415065826-ff8036138dc1/go.mod h1:kDm2NMNWts5dJHpVMhFznr+G1Hk/zz80l6aOZ+2fPdc=
3636
github.com/akitasoftware/go-utils v0.0.0-20221207014235-6f4c9079488d h1:pN1dbNacZ/mvlU1NcJVDxqmKnrDQDTVaN6iKOarfdYM=
3737
github.com/akitasoftware/go-utils v0.0.0-20221207014235-6f4c9079488d/go.mod h1:+IOXf7l/QCAQECJzjJwhTp1sBkRoJ6WciZwJezUwBa4=
3838
github.com/akitasoftware/gopacket v1.1.18-0.20210730205736-879e93dac35b h1:toBhS5rhCjo/N4YZ1cYtlsdSTGjMFH+gbJGCc+OmZiY=
@@ -96,7 +96,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
9696
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
9797
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
9898
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
99-
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
10099
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
101100
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g=
102101
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE=
@@ -119,8 +118,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
119118
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
120119
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
121120
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
122-
github.com/dukex/mixpanel v1.0.1 h1:IQ3qBjtgltF044jU9+i6MubdDdpc8PKpK9yvfawRgeE=
123-
github.com/dukex/mixpanel v1.0.1/go.mod h1:080BDsRRMzAxViWT3OjlQaMW9nhaIEXDHHtGeDK60b8=
124121
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
125122
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
126123
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -207,7 +204,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
207204
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
208205
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
209206
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
210-
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
211207
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
212208
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
213209
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
@@ -216,8 +212,6 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
216212
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
217213
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
218214
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
219-
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
220-
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
221215
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
222216
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
223217
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
@@ -270,7 +264,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
270264
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
271265
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
272266
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
273-
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
274267
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
275268
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
276269
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -364,10 +357,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
364357
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
365358
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
366359
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
367-
github.com/segmentio/analytics-go/v3 v3.2.1 h1:G+f90zxtc1p9G+WigVyTR0xNfOghOGs/PYAlljLOyeg=
368-
github.com/segmentio/analytics-go/v3 v3.2.1/go.mod h1:p8owAF8X+5o27jmvUognuXxdtqvSGtD0ZrfY2kcS9bE=
369-
github.com/segmentio/backo-go v1.0.0 h1:kbOAtGJY2DqOR0jfRkYEorx/b18RgtepGtY3+Cpe6qA=
370-
github.com/segmentio/backo-go v1.0.0/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M=
371360
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
372361
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
373362
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=

telemetry/telemetry.go

Lines changed: 94 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/akitasoftware/akita-cli/rest"
1616
"github.com/akitasoftware/akita-cli/version"
1717
"github.com/akitasoftware/akita-libs/analytics"
18+
"github.com/akitasoftware/go-utils/maps"
1819
)
1920

2021
var (
@@ -27,14 +28,20 @@ var (
2728
// Client key; set at link-time with -X flag
2829
defaultAmplitudeKey = ""
2930

30-
// Store the distinct ID; run through the process
31+
// Store the user ID and team ID; run through the process
3132
// of getting it only once.
32-
userDistinctID string
33-
userDistinctOnce sync.Once
33+
userID string
34+
teamID string
3435

3536
// Timeout talking to API.
3637
// Shorter than normal because we don't want the CLI to be slow.
3738
userAPITimeout = 2 * time.Second
39+
40+
// Sync.once to ensure that client is initialized before trying to use it.
41+
initClientOnce sync.Once
42+
43+
// Whether to log client init logs to the console
44+
isLoggingEnabled bool = true
3845
)
3946

4047
type nullClient struct{}
@@ -52,8 +59,14 @@ func (_ nullClient) Close() error {
5259
}
5360

5461
// Initialize the telemetry client.
55-
// This should be called once at startup either from the root command or from a subcommand that overrides the default PersistentPreRun.
56-
func Init(isLoggingEnabled bool) {
62+
// This should be called once at startup either from the root command
63+
// or from a subcommand that overrides the default PersistentPreRun.
64+
func Init(loggingEnabled bool) {
65+
isLoggingEnabled = loggingEnabled
66+
initClientOnce.Do(doInit)
67+
}
68+
69+
func doInit() {
5770
// Opt-out mechanism
5871
disableTelemetry := os.Getenv("AKITA_DISABLE_TELEMETRY") + os.Getenv("POSTMAN_INSIGHTS_AGENT_DISABLE_TELEMETRY")
5972
if disableTelemetry != "" {
@@ -107,39 +120,50 @@ func Init(isLoggingEnabled bool) {
107120
printer.Infof("Please send this log message to %s.\n", consts.SupportEmail)
108121
}
109122
analyticsClient = nullClient{}
110-
} else {
111-
analyticsEnabled = true
123+
return
124+
}
125+
126+
analyticsEnabled = true
127+
128+
userID, teamID, err = getUserIdentity() // Initialize user ID and team ID
129+
if err != nil {
130+
if isLoggingEnabled {
131+
printer.Infof("Telemetry unavailable; error getting userID for given API key: %v\n", err)
132+
printer.Infof("Postman support will not be able to see any errors you encounter.\n")
133+
printer.Infof("Please send this log message to %s.\n", consts.SupportEmail)
134+
}
135+
analyticsClient = nullClient{}
136+
return
112137
}
138+
139+
// Set up automatic reporting of all API errors
140+
// (rest can't call telemetry directly because we call rest above!)
141+
rest.SetAPIErrorHandler(APIError)
113142
}
114143

115-
func getDistinctID() string {
116-
// If we have a user email, use that!
144+
func getUserIdentity() (string, string, error) {
145+
// If we can get user details use userID and teamID
117146
// Otherwise use the configured API Key.
118-
// Failing that, try to use the user name and host name?
147+
// Failing that, try to use the user name and host name.
148+
// In latter 2 cases teamID will be empty.
119149

120150
id := os.Getenv("POSTMAN_ANALYTICS_DISTINCT_ID")
121151
if id != "" {
122-
return id
152+
return id, "", nil
123153
}
124154

125155
// If there's no credentials configured, skip the API call and
126156
// do not emit a log message.
127157
// Similarly if telemetry is disabled.
128158
if cfg.CredentialsPresent() && analyticsEnabled {
129-
// Call the REST API to get the user email associated with the configured
159+
// Call the REST API to get the postman user associated with the configured
130160
// API key.
131161
ctx, cancel := context.WithTimeout(context.Background(), userAPITimeout)
132162
defer cancel()
133163
frontClient := rest.NewFrontClient(rest.Domain, GetClientID())
134164
userResponse, err := frontClient.GetUser(ctx)
135165
if err == nil {
136-
if userResponse.Email != "" {
137-
return userResponse.Email
138-
}
139-
140-
// Use the user ID if no email is present;
141-
// this should be fixed in the current backend.
142-
return fmt.Sprint(userResponse.ID)
166+
return fmt.Sprint(userResponse.ID), fmt.Sprint(userResponse.TeamID), nil
143167
}
144168

145169
printer.Infof("Telemetry using temporary ID; GetUser API call failed: %v\n", err)
@@ -148,44 +172,31 @@ func getDistinctID() string {
148172
}
149173

150174
// Try to derive a distinct ID from the credentials, if present, even
151-
// if the /v1/user call failed.
175+
// if the getUser() call failed.
152176
keyID := cfg.DistinctIDFromCredentials()
153177
if keyID != "" {
154-
return keyID
178+
return keyID, "", nil
155179
}
156180

157181
localUser, err := user.Current()
158182
if err != nil {
159-
return "unknown"
183+
return "", "", err
160184
}
161185
localHost, err := os.Hostname()
162186
if err != nil {
163-
return localUser.Username
187+
return localUser.Username, "", nil
164188
}
165-
return localUser.Username + "@" + localHost
166-
}
167-
168-
func distinctID() string {
169-
userDistinctOnce.Do(func() {
170-
userDistinctID = getDistinctID()
171-
172-
// Set up automatic reporting of all API errors
173-
// (rest can't call telemetry directly because we call rest above!)
174-
rest.SetAPIErrorHandler(APIError)
175-
176-
printer.Debugf("Using ID %q for telemetry\n", userDistinctID)
177-
})
178-
return userDistinctID
189+
return localUser.Username + "@" + localHost, "", nil
179190
}
180191

181192
// Report an error in a particular operation (inContext), including
182193
// the text of the error.
183194
func Error(inContext string, e error) {
184-
analyticsClient.Track(distinctID(),
185-
fmt.Sprintf("Error in %s", inContext),
195+
tryTrackingEvent(
196+
"Operation - Errored",
186197
map[string]any{
187-
"error": e.Error(),
188-
"type": "error",
198+
"operation": inContext,
199+
"error": e.Error(),
189200
},
190201
)
191202
}
@@ -235,80 +246,80 @@ func RateLimitError(inContext string, e error) {
235246
rateLimitMap.Store(inContext, newRecord)
236247
}
237248

238-
analyticsClient.Track(distinctID(),
239-
fmt.Sprintf("Error in %s", inContext),
249+
tryTrackingEvent(
250+
"Operation - Rate Limited",
240251
map[string]any{
241-
"error": e.Error(),
242-
"type": "error",
243-
"count": count,
252+
"operation": inContext,
253+
"error": e.Error(),
254+
"count": count,
244255
},
245256
)
246257
}
247258

248259
// Report an error in a particular API, including the text of the error.
249260
func APIError(method string, path string, e error) {
250-
analyticsClient.Track(distinctID(),
251-
"Error calling API",
261+
tryTrackingEvent(
262+
"API Call - Errored",
252263
map[string]any{
253264
"method": method,
254265
"path": path,
255266
"error": e.Error(),
256-
"type": "error",
257267
},
258268
)
259269
}
260270

261271
// Report a failure without a specific error object
262272
func Failure(message string) {
263-
analyticsClient.Track(distinctID(),
264-
fmt.Sprintf("Unknown Error: %s", message),
273+
tryTrackingEvent(
274+
"Operation - Errored",
265275
map[string]any{
266-
"type": "error",
276+
"error": message,
267277
},
268278
)
269279
}
270280

271281
// Report success of an operation
272282
func Success(message string) {
273-
analyticsClient.Track(distinctID(),
274-
fmt.Sprintf("Success in %s", message),
283+
tryTrackingEvent(
284+
"Operation - Succeeded",
275285
map[string]any{
276-
"type": "success",
286+
"operation": message,
277287
},
278288
)
279289
}
280290

281291
// Report a step in a multi-part workflow.
282292
func WorkflowStep(workflow string, message string) {
283-
analyticsClient.Track(distinctID(),
284-
fmt.Sprintf("Executing Step: %s", message),
293+
tryTrackingEvent(
294+
"Workflow Step - Executed",
285295
map[string]any{
286-
"type": "workflow",
296+
"step": message,
287297
"workflow": workflow,
288298
},
289299
)
290300
}
291301

292302
// Report command line flags (before any error checking.)
293303
func CommandLine(command string, commandLine []string) {
294-
analyticsClient.Track(distinctID(),
295-
fmt.Sprintf("Executed %s", command),
304+
tryTrackingEvent(
305+
"Command - Executed",
296306
map[string]any{
307+
"command": command,
297308
"command_line": commandLine,
298309
},
299310
)
300311
}
301312

302313
// Report the platform and version of an attempted integration
303314
func InstallIntegrationVersion(integration, arch, platform, version string) {
304-
analyticsClient.Track(distinctID(),
305-
fmt.Sprintf("Install %s", integration),
315+
tryTrackingEvent(
316+
"Integration - Installed",
306317
map[string]any{
318+
"integration": integration,
307319
"architecture": arch,
308320
"version": version,
309321
"platform": platform,
310-
},
311-
)
322+
})
312323
}
313324

314325
// Flush the telemetry to its endpoint
@@ -321,3 +332,22 @@ func Shutdown() {
321332
printer.Infof("Please send the CLI output to %s.\n", consts.SupportEmail)
322333
}
323334
}
335+
336+
// Attempts to track an event using the provided event name and properties. It adds the user ID
337+
// and team ID to the event properties, and then sends the event to the analytics client.
338+
// If there is an error sending the event, a warning message is printed.
339+
func tryTrackingEvent(eventName string, eventProperties maps.Map[string, any]) {
340+
// precondition: analyticsClient is initialized
341+
initClientOnce.Do(doInit)
342+
343+
eventProperties.Upsert("user_id", userID, func(v, newV any) any { return v })
344+
345+
if teamID != "" {
346+
eventProperties.Upsert("team_id", teamID, func(v, newV any) any { return v })
347+
}
348+
349+
err := analyticsClient.Track(userID, eventName, eventProperties)
350+
if err != nil {
351+
printer.Warningf("Error sending analytics event %q: %v\n", eventName, err)
352+
}
353+
}

0 commit comments

Comments
 (0)