github.com/jfrog/jfrog-cli-core/v2@v2.51.0/utils/usage/usage.go (about) 1 package usage 2 3 import ( 4 "fmt" 5 6 "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" 7 "github.com/jfrog/jfrog-cli-core/v2/utils/config" 8 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" 9 xrayutils "github.com/jfrog/jfrog-cli-core/v2/utils/xray" 10 "github.com/jfrog/jfrog-client-go/artifactory/usage" 11 clientutils "github.com/jfrog/jfrog-client-go/utils" 12 "github.com/jfrog/jfrog-client-go/utils/errorutils" 13 "github.com/jfrog/jfrog-client-go/utils/log" 14 ecosysusage "github.com/jfrog/jfrog-client-go/utils/usage" 15 xrayusage "github.com/jfrog/jfrog-client-go/xray/usage" 16 17 "golang.org/x/sync/errgroup" 18 ) 19 20 const ( 21 ReportUsagePrefix = "Usage Report:" 22 clientIdAttributeName = "clientId" 23 ) 24 25 type UsageReporter struct { 26 ProductId string 27 serverDetails *config.ServerDetails 28 reportWaitGroup *errgroup.Group 29 30 sendToEcosystem bool 31 sendToXray bool 32 sendToArtifactory bool 33 } 34 35 type ReportFeature struct { 36 FeatureId string 37 ClientId string 38 Attributes []ReportUsageAttribute 39 } 40 41 type ReportUsageAttribute struct { 42 AttributeName string 43 AttributeValue string 44 } 45 46 func NewUsageReporter(productId string, serverDetails *config.ServerDetails) *UsageReporter { 47 return &UsageReporter{ 48 ProductId: productId, 49 serverDetails: serverDetails, 50 reportWaitGroup: new(errgroup.Group), 51 sendToEcosystem: true, 52 sendToXray: true, 53 sendToArtifactory: true, 54 } 55 } 56 57 func ShouldReportUsage() (reportUsage bool) { 58 reportUsage, err := clientutils.GetBoolEnvValue(coreutils.ReportUsage, true) 59 if err != nil { 60 log.Debug(ReportUsagePrefix + err.Error()) 61 return false 62 } 63 return reportUsage 64 } 65 66 func (ur *UsageReporter) SetSendToEcosystem(send bool) *UsageReporter { 67 ur.sendToEcosystem = send 68 return ur 69 } 70 71 func (ur *UsageReporter) SetSendToXray(send bool) *UsageReporter { 72 ur.sendToXray = send 73 return ur 74 } 75 76 func (ur *UsageReporter) SetSendToArtifactory(send bool) *UsageReporter { 77 ur.sendToArtifactory = send 78 return ur 79 } 80 81 // Report usage to Artifactory, Xray and Ecosystem 82 func (ur *UsageReporter) Report(features ...ReportFeature) { 83 if !ShouldReportUsage() { 84 log.Debug("Usage info is disabled.") 85 return 86 } 87 if len(features) == 0 { 88 log.Debug(ReportUsagePrefix, "Nothing to send.") 89 return 90 } 91 log.Debug(ReportUsagePrefix, "Sending info...") 92 if ur.sendToEcosystem { 93 ur.reportWaitGroup.Go(func() (err error) { 94 if err = ur.reportToEcosystem(features...); err != nil { 95 err = fmt.Errorf("ecosystem, %w", err) 96 } 97 return 98 }) 99 } 100 if ur.sendToXray { 101 ur.reportWaitGroup.Go(func() (err error) { 102 if err = ur.reportToXray(features...); err != nil { 103 err = fmt.Errorf("xray, %w", err) 104 } 105 return 106 }) 107 } 108 if ur.sendToArtifactory { 109 ur.reportWaitGroup.Go(func() (err error) { 110 if err = ur.reportToArtifactory(features...); err != nil { 111 err = fmt.Errorf("artifactory, %w", err) 112 } 113 return 114 }) 115 } 116 } 117 118 func (ur *UsageReporter) WaitForResponses() (err error) { 119 if err = ur.reportWaitGroup.Wait(); err != nil { 120 err = fmt.Errorf("%s %s", ReportUsagePrefix, err.Error()) 121 } 122 return 123 } 124 125 func (ur *UsageReporter) reportToEcosystem(features ...ReportFeature) (err error) { 126 if ur.serverDetails.Url == "" { 127 err = errorutils.CheckErrorf("platform URL is not set") 128 return 129 } 130 reports, err := ur.convertAttributesToEcosystemReports(features...) 131 if err != nil { 132 return 133 } 134 if len(reports) == 0 { 135 err = errorutils.CheckErrorf("nothing to send") 136 return 137 } 138 return ecosysusage.SendEcosystemUsageReports(reports...) 139 } 140 141 func (ur *UsageReporter) reportToXray(features ...ReportFeature) (err error) { 142 events := ur.convertAttributesToXrayEvents(features...) 143 if len(events) == 0 { 144 err = errorutils.CheckErrorf("Nothing to send.") 145 return 146 } 147 if ur.serverDetails.XrayUrl == "" { 148 err = errorutils.CheckErrorf("Xray Url is not set.") 149 return 150 } 151 serviceManager, err := xrayutils.CreateXrayServiceManager(ur.serverDetails) 152 if err != nil { 153 return 154 } 155 return xrayusage.SendXrayUsageEvents(*serviceManager, events...) 156 } 157 158 func (ur *UsageReporter) reportToArtifactory(features ...ReportFeature) (err error) { 159 converted := ur.convertAttributesToArtifactoryFeatures(features...) 160 if len(converted) == 0 { 161 err = errorutils.CheckErrorf("nothing to send") 162 return 163 } 164 if ur.serverDetails.ArtifactoryUrl == "" { 165 err = errorutils.CheckErrorf("Artifactory URL is not set") 166 return 167 } 168 serviceManager, err := utils.CreateServiceManager(ur.serverDetails, -1, 0, false) 169 if err != nil { 170 return 171 } 172 return usage.ReportUsageToArtifactory(ur.ProductId, serviceManager, converted...) 173 } 174 175 func convertAttributesToMap(reportFeature ReportFeature) (converted map[string]string) { 176 if len(reportFeature.Attributes) == 0 { 177 return 178 } 179 converted = make(map[string]string, len(reportFeature.Attributes)) 180 for _, attribute := range reportFeature.Attributes { 181 if attribute.AttributeName != "" { 182 converted[attribute.AttributeName] = attribute.AttributeValue 183 } 184 } 185 return 186 } 187 188 func (ur *UsageReporter) convertAttributesToArtifactoryFeatures(reportFeatures ...ReportFeature) (features []usage.Feature) { 189 for _, feature := range reportFeatures { 190 featureInfo := usage.Feature{ 191 FeatureId: feature.FeatureId, 192 ClientId: feature.ClientId, 193 Attributes: convertAttributesToMap(feature), 194 } 195 features = append(features, featureInfo) 196 } 197 return 198 } 199 200 func (ur *UsageReporter) convertAttributesToXrayEvents(reportFeatures ...ReportFeature) (events []xrayusage.ReportXrayEventData) { 201 for _, feature := range reportFeatures { 202 convertedAttributes := []xrayusage.ReportUsageAttribute{} 203 for _, attribute := range feature.Attributes { 204 convertedAttributes = append(convertedAttributes, xrayusage.ReportUsageAttribute{ 205 AttributeName: attribute.AttributeName, 206 AttributeValue: attribute.AttributeValue, 207 }) 208 } 209 if feature.ClientId != "" { 210 // Add clientId as attribute 211 convertedAttributes = append(convertedAttributes, xrayusage.ReportUsageAttribute{ 212 AttributeName: clientIdAttributeName, 213 AttributeValue: feature.ClientId, 214 }) 215 } 216 events = append(events, xrayusage.CreateUsageEvent( 217 ur.ProductId, feature.FeatureId, convertedAttributes..., 218 )) 219 } 220 return 221 } 222 223 func (ur *UsageReporter) convertAttributesToEcosystemReports(reportFeatures ...ReportFeature) (reports []ecosysusage.ReportEcosystemUsageData, err error) { 224 accountId := ur.serverDetails.Url 225 clientToFeaturesMap := map[string][]string{} 226 // Combine 227 for _, feature := range reportFeatures { 228 if feature.FeatureId == "" { 229 continue 230 } 231 if features, contains := clientToFeaturesMap[feature.ClientId]; contains { 232 clientToFeaturesMap[feature.ClientId] = append(features, feature.FeatureId) 233 } else { 234 clientToFeaturesMap[feature.ClientId] = []string{feature.FeatureId} 235 } 236 } 237 // Create data 238 for clientId, features := range clientToFeaturesMap { 239 var report ecosysusage.ReportEcosystemUsageData 240 if report, err = ecosysusage.CreateUsageData(ur.ProductId, accountId, clientId, features...); err != nil { 241 return 242 } 243 reports = append(reports, report) 244 } 245 return 246 }