github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/analytics/dimensions/dimensions.go (about)

     1  package dimensions
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/ActiveState/cli/internal/constants"
     9  	"github.com/ActiveState/cli/internal/errs"
    10  	"github.com/ActiveState/cli/internal/installation/storage"
    11  	"github.com/ActiveState/cli/internal/instanceid"
    12  	"github.com/ActiveState/cli/internal/logging"
    13  	"github.com/ActiveState/cli/internal/multilog"
    14  	"github.com/ActiveState/cli/internal/osutils"
    15  	"github.com/ActiveState/cli/internal/output"
    16  	"github.com/ActiveState/cli/internal/rollbar"
    17  	"github.com/ActiveState/cli/internal/rtutils/ptr"
    18  	"github.com/ActiveState/cli/internal/singleton/uniqid"
    19  	"github.com/ActiveState/cli/pkg/platform/authentication"
    20  	"github.com/ActiveState/cli/pkg/sysinfo"
    21  )
    22  
    23  type Values struct {
    24  	Version          *string
    25  	ChannelName      *string
    26  	UserID           *string
    27  	OSName           *string
    28  	OSVersion        *string
    29  	InstallSource    *string
    30  	UniqID           *string
    31  	SessionToken     *string
    32  	UpdateTag        *string
    33  	ProjectNameSpace *string
    34  	OutputType       *string
    35  	ProjectID        *string
    36  	Flags            *string
    37  	Trigger          *string
    38  	Headless         *string
    39  	InstanceID       *string
    40  	CommitID         *string
    41  	Command          *string
    42  	Sequence         *int
    43  	TargetVersion    *string
    44  	Error            *string
    45  	Message          *string
    46  	CI               *bool
    47  	Interactive      *bool
    48  	ActiveStateCI    *bool
    49  
    50  	preProcessor func(*Values) error
    51  }
    52  
    53  func NewDefaultDimensions(pjNamespace, sessionToken, updateTag string, auth *authentication.Auth) *Values {
    54  	installSource, err := storage.InstallSource()
    55  	if err != nil {
    56  		multilog.Error("Could not detect installSource: %s", errs.JoinMessage(err))
    57  	}
    58  
    59  	deviceID := uniqid.Text()
    60  
    61  	var userIDString string
    62  	userID := auth.UserID()
    63  	if userID != nil {
    64  		userIDString = userID.String()
    65  	}
    66  
    67  	osName := sysinfo.OS().String()
    68  	osVersion := "unknown"
    69  	osvInfo, err := sysinfo.OSVersion()
    70  	if err != nil {
    71  		multilog.Log(logging.ErrorNoStacktrace, rollbar.Error)("Could not detect osVersion: %v", err)
    72  	}
    73  	if osvInfo != nil {
    74  		osVersion = osvInfo.Version
    75  	}
    76  
    77  	return &Values{
    78  		ptr.To(constants.Version),
    79  		ptr.To(constants.ChannelName),
    80  		ptr.To(userIDString),
    81  		ptr.To(osName),
    82  		ptr.To(osVersion),
    83  		ptr.To(installSource),
    84  		ptr.To(deviceID),
    85  		ptr.To(sessionToken),
    86  		ptr.To(updateTag),
    87  		ptr.To(pjNamespace),
    88  		ptr.To(string(output.PlainFormatName)),
    89  		ptr.To(""),
    90  		ptr.To(CalculateFlags()),
    91  		ptr.To(""),
    92  		ptr.To(""),
    93  		ptr.To(instanceid.ID()),
    94  		ptr.To(""),
    95  		ptr.To(osutils.ExecutableName()),
    96  		ptr.To(0),
    97  		ptr.To(""),
    98  		ptr.To(""),
    99  		ptr.To(""),
   100  		ptr.To(false),
   101  		ptr.To(false),
   102  		ptr.To(false),
   103  		nil,
   104  	}
   105  }
   106  
   107  func (v *Values) Clone() *Values {
   108  	return &Values{
   109  		Version:          ptr.Clone(v.Version),
   110  		ChannelName:      ptr.Clone(v.ChannelName),
   111  		UserID:           ptr.Clone(v.UserID),
   112  		OSName:           ptr.Clone(v.OSName),
   113  		OSVersion:        ptr.Clone(v.OSVersion),
   114  		InstallSource:    ptr.Clone(v.InstallSource),
   115  		UniqID:           ptr.Clone(v.UniqID),
   116  		SessionToken:     ptr.Clone(v.SessionToken),
   117  		UpdateTag:        ptr.Clone(v.UpdateTag),
   118  		ProjectNameSpace: ptr.Clone(v.ProjectNameSpace),
   119  		OutputType:       ptr.Clone(v.OutputType),
   120  		ProjectID:        ptr.Clone(v.ProjectID),
   121  		Flags:            ptr.Clone(v.Flags),
   122  		Trigger:          ptr.Clone(v.Trigger),
   123  		Headless:         ptr.Clone(v.Headless),
   124  		InstanceID:       ptr.Clone(v.InstanceID),
   125  		CommitID:         ptr.Clone(v.CommitID),
   126  		Command:          ptr.Clone(v.Command),
   127  		Sequence:         ptr.Clone(v.Sequence),
   128  		TargetVersion:    ptr.Clone(v.TargetVersion),
   129  		Error:            ptr.Clone(v.Error),
   130  		Message:          ptr.Clone(v.Message),
   131  		CI:               ptr.Clone(v.CI),
   132  		Interactive:      ptr.Clone(v.Interactive),
   133  		ActiveStateCI:    ptr.Clone(v.ActiveStateCI),
   134  		preProcessor:     v.preProcessor,
   135  	}
   136  }
   137  
   138  func (m *Values) Merge(mergeWith ...*Values) {
   139  	// This is awkward and long, but using mergo was not an option here because it cannot differentiate between
   140  	// falsy values and nil pointers
   141  	for _, dim := range mergeWith {
   142  		if dim.Version != nil {
   143  			m.Version = dim.Version
   144  		}
   145  		if dim.ChannelName != nil {
   146  			m.ChannelName = dim.ChannelName
   147  		}
   148  		if dim.UserID != nil {
   149  			m.UserID = dim.UserID
   150  		}
   151  		if dim.OSName != nil {
   152  			m.OSName = dim.OSName
   153  		}
   154  		if dim.OSVersion != nil {
   155  			m.OSVersion = dim.OSVersion
   156  		}
   157  		if dim.InstallSource != nil {
   158  			m.InstallSource = dim.InstallSource
   159  		}
   160  		if dim.UniqID != nil {
   161  			m.UniqID = dim.UniqID
   162  		}
   163  		if dim.SessionToken != nil {
   164  			m.SessionToken = dim.SessionToken
   165  		}
   166  		if dim.UpdateTag != nil {
   167  			m.UpdateTag = dim.UpdateTag
   168  		}
   169  		if dim.ProjectNameSpace != nil {
   170  			m.ProjectNameSpace = dim.ProjectNameSpace
   171  		}
   172  		if dim.OutputType != nil {
   173  			m.OutputType = dim.OutputType
   174  		}
   175  		if dim.ProjectID != nil {
   176  			m.ProjectID = dim.ProjectID
   177  		}
   178  		if dim.Flags != nil {
   179  			m.Flags = dim.Flags
   180  		}
   181  		if dim.Trigger != nil {
   182  			m.Trigger = dim.Trigger
   183  		}
   184  		if dim.Headless != nil {
   185  			m.Headless = dim.Headless
   186  		}
   187  		if dim.InstanceID != nil {
   188  			m.InstanceID = dim.InstanceID
   189  		}
   190  		if dim.CommitID != nil {
   191  			m.CommitID = dim.CommitID
   192  		}
   193  		if dim.Command != nil {
   194  			m.Command = dim.Command
   195  		}
   196  		if dim.Sequence != nil {
   197  			m.Sequence = dim.Sequence
   198  		}
   199  		if dim.TargetVersion != nil {
   200  			m.TargetVersion = dim.TargetVersion
   201  		}
   202  		if dim.Error != nil {
   203  			m.Error = dim.Error
   204  		}
   205  		if dim.Message != nil {
   206  			m.Message = dim.Message
   207  		}
   208  		if dim.CI != nil {
   209  			m.CI = dim.CI
   210  		}
   211  		if dim.Interactive != nil {
   212  			m.Interactive = dim.Interactive
   213  		}
   214  		if dim.ActiveStateCI != nil {
   215  			m.ActiveStateCI = dim.ActiveStateCI
   216  		}
   217  		if dim.preProcessor != nil {
   218  			m.preProcessor = dim.preProcessor
   219  		}
   220  	}
   221  }
   222  
   223  func (v *Values) RegisterPreProcessor(f func(*Values) error) {
   224  	v.preProcessor = f
   225  }
   226  
   227  func (v *Values) PreProcess() error {
   228  	if v.preProcessor != nil {
   229  		if err := v.preProcessor(v); err != nil {
   230  			return errs.Wrap(err, "PreProcessor failed: %s", errs.JoinMessage(err))
   231  		}
   232  	}
   233  
   234  	if ptr.From(v.UniqID, "") == "" {
   235  		return errs.New("device id is unset when creating analytics event")
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  func (v *Values) Marshal() (string, error) {
   242  	dimMarshalled, err := json.Marshal(v)
   243  	if err != nil {
   244  		return "", errs.Wrap(err, "Could not marshal dimensions")
   245  	}
   246  	return string(dimMarshalled), nil
   247  }
   248  
   249  func CalculateFlags() string {
   250  	flags := []string{}
   251  	for _, arg := range os.Args {
   252  		if strings.HasPrefix(arg, "-") {
   253  			flags = append(flags, arg)
   254  		}
   255  	}
   256  	return strings.Join(flags, " ")
   257  }