Skip to content

Commit 694a644

Browse files
committed
Fix: Resolve customer identity for sim command tracking
The sim command's PersistentPreRunE overrides the root command's hook, preventing the customer ID resolution logic from running. This resulted in all sim subcommands tracking as 'anonymous' instead of with the actual customer ID. Added customer ID resolution to simPreRun to ensure sim commands properly identify users for analytics tracking, matching the behavior of other CLI commands.
1 parent 04fc3f9 commit 694a644

2 files changed

Lines changed: 68 additions & 3 deletions

File tree

cli/root.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ var rootCmd = &cobra.Command{
8080

8181
// Resolve customer identity for analytics (best-effort, never blocks the CLI).
8282
if tr := cmdutil.TrackerFromCmd(cmd); tr != nil {
83-
if customerID := resolveCustomerID(client, env.APIKey); customerID != "" {
83+
if customerID := ResolveCustomerID(client, env.APIKey); customerID != "" {
8484
tr.SetUserID(customerID)
8585
}
8686
}
@@ -149,11 +149,11 @@ func Execute(version, commit, date, amplitudeKey string) {
149149
}
150150
}
151151

152-
// resolveCustomerID returns the customer_id associated with the given API key.
152+
// ResolveCustomerID returns the customer_id associated with the given API key.
153153
// The customer_id may represent a user ("user_123") or a team ("team_456").
154154
// It uses a local cache to avoid calling /api/whoami on every invocation.
155155
// On any error it returns "" silently — analytics should never block the CLI.
156-
func resolveCustomerID(client dune.DuneClient, apiKey string) string {
156+
func ResolveCustomerID(client dune.DuneClient, apiKey string) string {
157157
keyHash := authconfig.HashAPIKey(apiKey)
158158

159159
// Try the cache first.

cmd/sim/sim.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88

99
"github.com/spf13/cobra"
1010

11+
"github.com/duneanalytics/duneapi-client-go/config"
12+
"github.com/duneanalytics/duneapi-client-go/dune"
13+
1114
"github.com/duneanalytics/cli/authconfig"
1215
"github.com/duneanalytics/cli/cmd/sim/evm"
1316
"github.com/duneanalytics/cli/cmd/sim/svm"
@@ -50,6 +53,17 @@ func simPreRun(cmd *cobra.Command, _ []string) error {
5053
// correct duration for telemetry.
5154
cmdutil.SetStartTime(cmd, time.Now())
5255

56+
// Resolve customer identity for analytics (best-effort, never blocks the CLI).
57+
// We try to resolve the Dune API key (not the Sim API key) to identify the user.
58+
if tr := cmdutil.TrackerFromCmd(cmd); tr != nil {
59+
if env := resolveDuneEnv(); env != nil {
60+
client := dune.NewDuneClient(env)
61+
if customerID := resolveCustomerIDForSim(client, env.APIKey); customerID != "" {
62+
tr.SetUserID(customerID)
63+
}
64+
}
65+
}
66+
5367
// Commands like `sim evm supported-chains` that hit public endpoints
5468
// don't require an API key. Provide a bare (unauthenticated) client so
5569
// they can still use the shared HTTP infrastructure and error handling.
@@ -103,3 +117,54 @@ func SimClientFromCmd(cmd *cobra.Command) *SimClient {
103117
}
104118
return v.(*SimClient)
105119
}
120+
121+
// resolveDuneEnv attempts to resolve the Dune API key from environment variables
122+
// or the config file, returning a Dune environment configuration.
123+
// Returns nil if no Dune API key is available (this is not an error for sim commands).
124+
func resolveDuneEnv() *config.Env {
125+
// Try environment variable first.
126+
env, err := config.FromEnvVars()
127+
if err == nil {
128+
return env
129+
}
130+
131+
// Try config file.
132+
cfg, err := authconfig.Load()
133+
if err != nil || cfg == nil {
134+
return nil
135+
}
136+
137+
key := strings.TrimSpace(cfg.APIKey)
138+
if key == "" {
139+
return nil
140+
}
141+
142+
return config.FromAPIKey(key)
143+
}
144+
145+
// resolveCustomerIDForSim is a wrapper around cli.ResolveCustomerID that
146+
// can be called from the sim package. It must be declared here to avoid
147+
// a circular import between cli and cmd/sim.
148+
func resolveCustomerIDForSim(client dune.DuneClient, apiKey string) string {
149+
keyHash := authconfig.HashAPIKey(apiKey)
150+
151+
// Try the cache first.
152+
cached, err := authconfig.LoadIdentity()
153+
if err == nil && cached != nil && cached.APIKeyHash == keyHash && cached.CustomerID != "" {
154+
return cached.CustomerID
155+
}
156+
157+
// Cache miss or stale — call the API.
158+
resp, err := client.WhoAmI()
159+
if err != nil || resp == nil || resp.CustomerID == "" {
160+
return ""
161+
}
162+
163+
// Persist for next time (best-effort).
164+
_ = authconfig.SaveIdentity(&authconfig.UserIdentity{
165+
CustomerID: resp.CustomerID,
166+
APIKeyHash: keyHash,
167+
})
168+
169+
return resp.CustomerID
170+
}

0 commit comments

Comments
 (0)