@@ -51,6 +51,7 @@ func (t *NoOpTracker) Track(event string, metadata map[string]any) error {
5151type DxTracker struct {
5252 mode Mode
5353 testMode bool
54+ product Product
5455
5556 logger zerolog.Logger
5657
@@ -61,7 +62,7 @@ type DxTracker struct {
6162// NewDxTracker initializes a tracker with automatic GitHub CLI integration for authentication.
6263// APITokenVariableName is the name of the GitHub repository variable containing the DX API token.
6364// Each DX project has its own token for tracking purposes.
64- func NewDxTracker (APITokenVariableName , product string ) (Tracker , error ) {
65+ func NewDxTracker (APITokenVariableName string , product Product ) (Tracker , error ) {
6566 t := & DxTracker {}
6667
6768 lvlStr := os .Getenv (EnvVarLogLevel )
@@ -77,7 +78,6 @@ func NewDxTracker(APITokenVariableName, product string) (Tracker, error) {
7778
7879 if os .Getenv (EnvVarDisableTracking ) == "true" {
7980 t .logger .Debug ().Msg ("Tracking disabled by environment variable" )
80-
8181 return & NoOpTracker {}, nil
8282 }
8383
@@ -94,6 +94,7 @@ func NewDxTracker(APITokenVariableName, product string) (Tracker, error) {
9494 return nil , errors .New ("product is required. Each DX project has its own token for tracking purposes" )
9595 }
9696 product = strings .ToLower (product )
97+ t .product = product
9798
9899 c , isConfigAvailable , configErr := openConfig ()
99100 if configErr != nil {
@@ -374,7 +375,7 @@ func openConfig() (*config, bool, error) {
374375 GithubUsername : legacyConfig .GithubUsername ,
375376 APITokens : map [Product ]string {
376377 // it is the only product that uses tracking at the moment
377- "local_CRE " : legacyConfig .DxAPIToken ,
378+ "local_cre " : legacyConfig .DxAPIToken ,
378379 },
379380 }
380381
@@ -407,7 +408,7 @@ func saveConfig(c *config) error {
407408 return errors .Wrap (pathErr , "failed to get config path" )
408409 }
409410
410- mkdirErr := os .MkdirAll (filepath .Dir (configPath ), 0755 )
411+ mkdirErr := os .MkdirAll (filepath .Dir (configPath ), 0o755 )
411412 if mkdirErr != nil {
412413 return errors .Wrap (mkdirErr , "failed to create config directory" )
413414 }
@@ -442,12 +443,12 @@ type event struct {
442443func (t * DxTracker ) saveEvent (name string , timestamp int64 , metadata map [string ]any ) error {
443444 t .logger .Debug ().Msgf ("Saving event. Name: %s, Timestamp: %d, Metadata: %v" , name , timestamp , metadata )
444445
445- storagePath , pathErr := storagePath ()
446+ storagePath , pathErr := storagePath (t . product )
446447 if pathErr != nil {
447448 return errors .Wrap (pathErr , "failed to get storage path" )
448449 }
449450
450- mkdirErr := os .MkdirAll (filepath .Dir (storagePath ), 0755 )
451+ mkdirErr := os .MkdirAll (filepath .Dir (storagePath ), 0o755 )
451452 if mkdirErr != nil {
452453 return errors .Wrap (mkdirErr , "failed to create storage directory" )
453454 }
@@ -476,7 +477,7 @@ func (t *DxTracker) saveEvent(name string, timestamp int64, metadata map[string]
476477 return errors .Wrap (err , "failed to marshal events to JSON" )
477478 }
478479
479- if err := os .WriteFile (storagePath , jsonData , 0600 ); err != nil {
480+ if err := os .WriteFile (storagePath , jsonData , 0o600 ); err != nil {
480481 return errors .Wrap (err , "failed to write event to storage file" )
481482 }
482483
@@ -485,31 +486,62 @@ func (t *DxTracker) saveEvent(name string, timestamp int64, metadata map[string]
485486
486487// sendSavedEvents attempts to send all queued events and clears the queue on success.
487488func (t * DxTracker ) sendSavedEvents () error {
488- storagePath , pathErr := storagePath ()
489+ storagePath , pathErr := storagePath (t . product )
489490 if pathErr != nil {
490491 return errors .Wrap (pathErr , "failed to get storage path" )
491492 }
492493
493- stats , statErr := os .Stat (storagePath )
494- if os .IsNotExist (statErr ) {
495- return nil
496- }
494+ var readEvents = func (path string ) ([]event , error ) {
495+ stats , statErr := os .Stat (path )
496+ if os .IsNotExist (statErr ) {
497+ return nil , nil
498+ }
497499
498- if stats .Size () == 0 {
499- return nil
500+ if stats .Size () == 0 {
501+ return nil , nil
502+ }
503+
504+ storageFile , storageErr := os .OpenFile (path , os .O_RDONLY , 0644 )
505+ if storageErr != nil {
506+ return nil , errors .Wrap (storageErr , "failed to open storage file" )
507+ }
508+ defer storageFile .Close ()
509+
510+ var events []event
511+
512+ decoderErr := json .NewDecoder (storageFile ).Decode (& events )
513+ if decoderErr != nil {
514+ return nil , errors .Wrap (decoderErr , "failed to decode events from storage file" )
515+ }
516+
517+ return events , nil
500518 }
501519
502- storageFile , storageErr := os . OpenFile (storagePath , os . O_RDONLY , 0644 )
503- if storageErr != nil {
504- return errors .Wrap (storageErr , "failed to open storage file " )
520+ events , readErr := readEvents (storagePath )
521+ if readErr != nil {
522+ return errors .Wrap (readErr , "failed to read saved events " )
505523 }
506- defer storageFile .Close ()
507524
508- var events []event
525+ // read from legacy storage path if product is local_CRE
526+ if t .product == "local_cre" {
527+ t .logger .Debug ().Msg ("Checking for legacy saved events" )
509528
510- decoderErr := json .NewDecoder (storageFile ).Decode (& events )
511- if decoderErr != nil {
512- return errors .Wrap (decoderErr , "failed to decode events from storage file" )
529+ legacyPath , legacyErr := legacyStoragePath ()
530+ if legacyErr != nil {
531+ return errors .Wrap (legacyErr , "failed to get legacy storage path" )
532+ }
533+
534+ legacyEvents , legacyReadErr := readEvents (legacyPath )
535+ if legacyReadErr != nil {
536+ return errors .Wrap (legacyReadErr , "failed to read legacy saved events" )
537+ }
538+
539+ events = append (events , legacyEvents ... )
540+ }
541+
542+ if len (events ) == 0 {
543+ t .logger .Debug ().Msg ("No saved events to send" )
544+ return nil
513545 }
514546
515547 t .logger .Debug ().Msgf ("Sending %d saved events" , len (events ))
@@ -547,7 +579,7 @@ func (t *DxTracker) sendSavedEvents() error {
547579
548580// clearSavedEvents removes all queued events after successful transmission.
549581func (t * DxTracker ) clearSavedEvents () error {
550- storagePath , pathErr := storagePath ()
582+ storagePath , pathErr := storagePath (t . product )
551583 if pathErr != nil {
552584 return errors .Wrap (pathErr , "failed to get storage path" )
553585 }
@@ -579,7 +611,18 @@ func validateEvent(event string, timestamp int64, metadata map[string]any) error
579611}
580612
581613// storagePath returns the path to the events queue file in the user's home directory.
582- func storagePath () (string , error ) {
614+ func storagePath (product Product ) (string , error ) {
615+ homeDir , err := os .UserHomeDir ()
616+ if err != nil {
617+ return "" , errors .Wrap (err , "failed to get user home directory" )
618+ }
619+
620+ return filepath .Join (homeDir , ".local" , "share" , "dx" , product + "_events.json" ), nil
621+ }
622+
623+ // legacyStoragePath returns the path to the events queue file in the user's home directory.
624+ // deprecated: legacyStoragePath is used to read old storage files and migrate them to the new format. Use storagePath instead.
625+ func legacyStoragePath () (string , error ) {
583626 homeDir , err := os .UserHomeDir ()
584627 if err != nil {
585628 return "" , errors .Wrap (err , "failed to get user home directory" )
0 commit comments