Skip to content

Commit

Permalink
Xray usage event report (jfrog#1046)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored and guyshe-jfrog committed Nov 30, 2023
1 parent 9f0a39a commit 5baf0b8
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 7 deletions.
68 changes: 62 additions & 6 deletions utils/usage/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import (
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils"
"github.com/jfrog/jfrog-client-go/artifactory/usage"
clientutils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
ecosysusage "github.com/jfrog/jfrog-client-go/utils/usage"
xrayusage "github.com/jfrog/jfrog-client-go/xray/usage"

"golang.org/x/sync/errgroup"
)

Expand Down Expand Up @@ -46,6 +49,7 @@ func NewUsageReporter(productId string, serverDetails *config.ServerDetails) *Us
serverDetails: serverDetails,
reportWaitGroup: new(errgroup.Group),
sendToEcosystem: true,
sendToXray: true,
sendToArtifactory: true,
}
}
Expand Down Expand Up @@ -80,19 +84,31 @@ func (ur *UsageReporter) Report(features ...ReportFeature) {
log.Debug("Usage info is disabled.")
return
}
if len(features) == 0 {
log.Debug(ReportUsagePrefix, "Nothing to send.")
return
}
log.Debug(ReportUsagePrefix, "Sending info...")
if ur.sendToEcosystem {
ur.reportWaitGroup.Go(func() (err error) {
if err = ur.reportToEcosystem(features...); err != nil {
err = fmt.Errorf("ecosystem, %s", err.Error())
err = fmt.Errorf("ecosystem, %w", err)
}
return
})
}
if ur.sendToXray {
ur.reportWaitGroup.Go(func() (err error) {
if err = ur.reportToXray(features...); err != nil {
err = fmt.Errorf("xray, %w", err)
}
return
})
}
if ur.sendToArtifactory {
ur.reportWaitGroup.Go(func() (err error) {
if err = ur.reportToArtifactory(features...); err != nil {
err = fmt.Errorf("artifactory, %s", err.Error())
err = fmt.Errorf("artifactory, %w", err)
}
return
})
Expand Down Expand Up @@ -122,20 +138,37 @@ func (ur *UsageReporter) reportToEcosystem(features ...ReportFeature) (err error
return ecosysusage.SendEcosystemUsageReports(reports...)
}

func (ur *UsageReporter) reportToArtifactory(features ...ReportFeature) (err error) {
if ur.serverDetails.ArtifactoryUrl == "" {
err = errorutils.CheckErrorf("Artifactory URL is not set")
func (ur *UsageReporter) reportToXray(features ...ReportFeature) (err error) {
events := ur.convertAttributesToXrayEvents(features...)
if len(events) == 0 {
err = errorutils.CheckErrorf("Nothing to send.")
return
}
serviceManager, err := utils.CreateServiceManager(ur.serverDetails, -1, 0, false)
if ur.serverDetails.XrayUrl == "" {
err = errorutils.CheckErrorf("Xray Url is not set.")
return
}
serviceManager, err := xrayutils.CreateXrayServiceManager(ur.serverDetails)
if err != nil {
return
}
return xrayusage.SendXrayUsageEvents(*serviceManager, events...)
}

func (ur *UsageReporter) reportToArtifactory(features ...ReportFeature) (err error) {
converted := ur.convertAttributesToArtifactoryFeatures(features...)
if len(converted) == 0 {
err = errorutils.CheckErrorf("nothing to send")
return
}
if ur.serverDetails.ArtifactoryUrl == "" {
err = errorutils.CheckErrorf("Artifactory URL is not set")
return
}
serviceManager, err := utils.CreateServiceManager(ur.serverDetails, -1, 0, false)
if err != nil {
return
}
return usage.ReportUsageToArtifactory(ur.ProductId, serviceManager, converted...)
}

Expand Down Expand Up @@ -164,6 +197,29 @@ func (ur *UsageReporter) convertAttributesToArtifactoryFeatures(reportFeatures .
return
}

func (ur *UsageReporter) convertAttributesToXrayEvents(reportFeatures ...ReportFeature) (events []xrayusage.ReportXrayEventData) {
for _, feature := range reportFeatures {
convertedAttributes := []xrayusage.ReportUsageAttribute{}
for _, attribute := range feature.Attributes {
convertedAttributes = append(convertedAttributes, xrayusage.ReportUsageAttribute{
AttributeName: attribute.AttributeName,
AttributeValue: attribute.AttributeValue,
})
}
if feature.ClientId != "" {
// Add clientId as attribute
convertedAttributes = append(convertedAttributes, xrayusage.ReportUsageAttribute{
AttributeName: clientIdAttributeName,
AttributeValue: feature.ClientId,
})
}
events = append(events, xrayusage.CreateUsageEvent(
ur.ProductId, feature.FeatureId, convertedAttributes...,
))
}
return
}

func (ur *UsageReporter) convertAttributesToEcosystemReports(reportFeatures ...ReportFeature) (reports []ecosysusage.ReportEcosystemUsageData, err error) {
accountId := ur.serverDetails.Url
clientToFeaturesMap := map[string][]string{}
Expand Down
60 changes: 59 additions & 1 deletion utils/usage/usage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/artifactory/usage"
ecosysusage "github.com/jfrog/jfrog-client-go/utils/usage"
xrayusage "github.com/jfrog/jfrog-client-go/xray/usage"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -129,11 +131,67 @@ func createArtifactoryUsageHandler(t *testing.T, productName, commandName string
}
}

func TestReportXrayUsage(t *testing.T) {
const productName = "test-product"
const commandName = "test-command"
const clientName = "test-client"

server := httptest.NewServer(createXrayUsageHandler(t, productName, commandName, clientName))
defer server.Close()
serverDetails := &config.ServerDetails{XrayUrl: server.URL + "/"}

reporter := NewUsageReporter(productName, serverDetails).SetSendToEcosystem(false).SetSendToArtifactory(false)

reporter.Report(ReportFeature{
FeatureId: commandName,
ClientId: clientName,
})
assert.NoError(t, reporter.WaitForResponses())
}

func TestReportXrayError(t *testing.T) {
reporter := NewUsageReporter("", &config.ServerDetails{}).SetSendToEcosystem(false).SetSendToArtifactory(false)
reporter.Report(ReportFeature{})
assert.Error(t, reporter.WaitForResponses())

server := httptest.NewServer(create404UsageHandler(t))
defer server.Close()
reporter = NewUsageReporter("", &config.ServerDetails{ArtifactoryUrl: server.URL + "/"}).SetSendToEcosystem(false).SetSendToArtifactory(false)
reporter.Report(ReportFeature{})
assert.Error(t, reporter.WaitForResponses())
}

func createXrayUsageHandler(t *testing.T, productId, commandName, clientId string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "/api/v1/system/version" {
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`{"xray_version":"6.9.0"}`))
assert.NoError(t, err)
return
}
if r.RequestURI == "/api/v1/usage/events/send" {
// Check request
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(r.Body)
assert.NoError(t, err)
featureId := xrayusage.GetExpectedXrayEventName(productId, commandName)
assert.Equal(t, fmt.Sprintf(`[{"data":{"clientId":"%s"},"product_name":"%s","event_name":"%s","origin":"API_CLI"}]`, clientId, productId, featureId), buf.String())

// Send response OK
w.WriteHeader(http.StatusOK)
_, err = w.Write([]byte("{}"))
assert.NoError(t, err)
return
}
assert.Fail(t, "Unexpected request URI", r.RequestURI)
}
}

func TestReportEcosystemUsageError(t *testing.T) {
// No features
reporter := NewUsageReporter("", &config.ServerDetails{}).SetSendToArtifactory(false).SetSendToXray(false)
reporter.Report()
assert.Error(t, reporter.WaitForResponses())
assert.NoError(t, reporter.WaitForResponses())
// Empty features
reporter.Report(ReportFeature{
FeatureId: "",
Expand Down

0 comments on commit 5baf0b8

Please sign in to comment.