From 8292671b7cc4525898218232e6424c293005b94f Mon Sep 17 00:00:00 2001 From: Gai Lazar Date: Wed, 3 Apr 2024 13:03:35 +0300 Subject: [PATCH] New XSC analytics metrics capabilities. (#928) --- .github/workflows/tests.yml | 4 +- README.md | 98 +++++++++++++++++++--- tests/utils_test.go | 12 +++ tests/xsc_test.go | 36 ++++++++ tests/xscanalyticsevent_test.go | 79 ++++++++++++++++++ utils/utils.go | 16 ++++ utils/utils_test.go | 46 +++++++++++ xray/manager.go | 13 --- xsc/auth/xscdetails.go | 45 ++++++++++ xsc/manager.go | 71 ++++++++++++++++ xsc/services/analytics.go | 140 ++++++++++++++++++++++++++++++++ xsc/services/version.go | 51 ++++++++++++ 12 files changed, 586 insertions(+), 25 deletions(-) create mode 100644 tests/xsc_test.go create mode 100644 tests/xscanalyticsevent_test.go create mode 100644 xsc/auth/xscdetails.go create mode 100644 xsc/manager.go create mode 100644 xsc/services/analytics.go create mode 100644 xsc/services/version.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1329c7992..b0302c2ec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -91,7 +91,7 @@ jobs: strategy: fail-fast: false matrix: - suite: [ distribution, xray, mpu ] + suite: [ distribution, xray, xsc, mpu ] os: [ ubuntu, windows, macos ] runs-on: ${{ matrix.os }}-latest steps: @@ -114,7 +114,7 @@ jobs: restore-keys: ${{ runner.os }}-go- - name: ${{ matrix.suite }} tests - run: go test -v github.com/jfrog/jfrog-client-go/tests --timeout 0 --test.${{ matrix.suite }} --rt.url=${{ secrets.PLATFORM_URL }}/artifactory --ds.url=${{ secrets.PLATFORM_URL }}/distribution --xr.url=${{ secrets.PLATFORM_URL }}/xray --access.url=${{ secrets.PLATFORM_URL }}/access --rt.user=${{ secrets.PLATFORM_USER }} --rt.password=${{ secrets.PLATFORM_PASSWORD }} --access.token=${{ secrets.PLATFORM_ADMIN_TOKEN }} --ci.runId=${{ runner.os }}-${{ matrix.suite }} + run: go test -v github.com/jfrog/jfrog-client-go/tests --timeout 0 --test.${{ matrix.suite }} --rt.url=${{ secrets.PLATFORM_URL }}/artifactory --ds.url=${{ secrets.PLATFORM_URL }}/distribution --xr.url=${{ secrets.PLATFORM_URL }}/xray --xsc.url=${{ secrets.PLATFORM_URL }}/xsc --access.url=${{ secrets.PLATFORM_URL }}/access --rt.user=${{ secrets.PLATFORM_USER }} --rt.password=${{ secrets.PLATFORM_PASSWORD }} --access.token=${{ secrets.PLATFORM_ADMIN_TOKEN }} --ci.runId=${{ runner.os }}-${{ matrix.suite }} JFrog-Client-Go-Pipelines-Tests: needs: Pretest diff --git a/README.md b/README.md index 545377094..80fe1d640 100644 --- a/README.md +++ b/README.md @@ -170,9 +170,17 @@ - [Delete Violations Report](#delete-violations-report) - [Get Artifact Summary](#get-artifact-summary) - [Get Entitlement info](#get-entitlement-info) - - [Using XSC Service](#using-xsc-service) - - [Check if xsc is enabled](#check-if-xsc-is-enabled) - - [Send git info details to xsc](#send-git-info-details-to-xsc) + - [XSC APIs](#xsc-apis) + - [Creating XSC Service Manager](#creating-xray-service-manager) + - [Creating XSC Details](#creating-xsc-details) + - [Creating XSC Service Config](#creating-xsc-service-config) + - [Creating New XSC Service Manager](#creating-new-xsc-service-manager) + - [Using XSC Services](#using-xsc-services) + - [Fetching XSC's Version](#fetching-xscs-version) + - [Report XSC analytics metrics](#report-xsc-analytics-metrics) + - [Add analytics general event](#add-analytics-general-event) + - [Update analytics general event](#update-analytics-general-event) + - [Get analytics general event](#get-analytics-general-event) - [Pipelines APIs](#pipelines-apis) - [Creating Pipelines Service Manager](#creating-pipelines-service-manager) - [Creating Pipelines Details](#creating-pipelines-details) @@ -265,6 +273,7 @@ content of this repository is deleted. | `-test.artifactory` | Artifactory tests | Artifactory Pro | | `-test.distribution` | Distribution tests | Artifactory with Distribution | | `-test.xray` | Xray tests | Artifactory with Xray | +| `-test.xsc` | Xsc tests | Xray with Xsc | | `-test.pipelines` | Pipelines tests | JFrog Pipelines | | `-test.access` | Access tests | Artifactory Pro | | `-test.repositories` | Repositories tests | Artifactory Pro | @@ -277,6 +286,7 @@ content of this repository is deleted. | `-rt.url` | [Default: http://localhost:8081/artifactory] Artifactory URL. | | `-ds.url` | [Optional] JFrog Distribution URL. | | `-xr.url` | [Optional] JFrog Xray URL. | +| `-xsc.url` | [Optional] JFrog Xsc URL. | | `-pipe.url` | [Optional] JFrog Pipelines URL. | | `-access.url` | [Optional] JFrog Access URL. | | `-rt.user` | [Default: admin] Artifactory username. | @@ -2233,20 +2243,88 @@ artifactSummary, err := xrayManager.ArtifactSummary(artifactSummaryRequest) isEntitled, err := xrayManager.IsEntitled(featureId) ``` -### Using XSC Service -#### Check if xsc is enabled +## XSC APIs + +### Creating XSC Service Manager + +#### Creating XSC Details + +```go +xscDetails := auth.NewXscDetails() +xscDetails.SetUrl("http://localhost:8081/xsc") +xscDetails.SetSshKeyPath("path/to/.ssh/") +xscDetails.SetApiKey("apikey") +xscDetails.SetUser("user") +xscDetails.SetPassword("password") +xscDetails.SetAccessToken("accesstoken") +// if client certificates are required +xscDetails.SetClientCertPath("path/to/.cer") +xscDetails.SetClientCertKeyPath("path/to/.key") +``` + +#### Creating XSC Service Config + +```go +serviceConfig, err := config.NewConfigBuilder(). + SetServiceDetails(xscDetails). + SetCertificatesPath(certPath). + // Optionally overwrite the default HTTP retries, which is set to 3. + SetHttpRetries(8). + Build() +``` + +#### Creating New XSC Service Manager ```go -// Will try to get XSC version. If route is not available, user is not entitled for XSC. -xscVersion, err := scanService.IsXscEnabled() +xscManager, err := xsc.New(serviceConfig) ``` -#### Send git info details to xsc +### Using XSC Services + +#### Fetching XSC's Version + +```go +version, err := xscManager.GetVersion() +``` + +#### Report XSC analytics metrics +##### Add analytics general event +Sent XSC a new event which contains analytics data, and get multi-scan id back from XSC. +```go +event := services.XscAnalyticsGeneralEvent{ + XscAnalyticsBasicGeneralEvent: services.XscAnalyticsBasicGeneralEvent{ + EventType: services.CliEventType, + Product: services.CliProduct, + ProductVersion: "2.53.1", + IsDefaultConfig: false, + JfrogUser: "gail", + OsPlatform: "mac", + OsArchitecture: "arm64", + AnalyzerManagerVersion: "1.1.1", + EventStatus: services.Started, +}} +msi, err := xscManager.AddAnalyticsGeneralEvent(event) +``` +##### Update analytics general event +Sent XSC a finalized analytics metrics event with information matching an existing event's msi. +```go +finalizeEvent := services.XscAnalyticsGeneralEventFinalize{ + MultiScanId: msi, + XscAnalyticsBasicGeneralEvent: services.XscAnalyticsBasicGeneralEvent{ + EventStatus: services.Completed, + TotalFindings: 10, + TotalIgnoredFindings: 5, + TotalScanDuration: "15s", + }, +} +err := xscManager.UpdateAnalyticsGeneralEvent(finalizeEvent) +``` +##### Get analytics general event +Get a general event from XSC matching the provided msi. ```go -// Details are the git info details (gitRepoUrl, branchName, commitHash are required fields). Returns multi scan id. -multiScanId, err := scanService.SendScanGitInfoContext(details) +event, err := xscManager.GetAnalyticsGeneralEvent(msi) ``` ## Pipelines APIs diff --git a/tests/utils_test.go b/tests/utils_test.go index 7a0ff6829..16fd2010b 100644 --- a/tests/utils_test.go +++ b/tests/utils_test.go @@ -42,6 +42,7 @@ import ( "github.com/jfrog/jfrog-client-go/utils/log" xrayAuth "github.com/jfrog/jfrog-client-go/xray/auth" xrayServices "github.com/jfrog/jfrog-client-go/xray/services" + xscAuth "github.com/jfrog/jfrog-client-go/xsc/auth" "github.com/stretchr/testify/assert" ) @@ -49,6 +50,7 @@ var ( TestArtifactory *bool TestDistribution *bool TestXray *bool + TestXsc *bool TestPipelines *bool TestAccess *bool TestRepositories *bool @@ -56,6 +58,7 @@ var ( RtUrl *string DistUrl *string XrayUrl *string + XscUrl *string PipelinesUrl *string RtUser *string RtPassword *string @@ -145,6 +148,7 @@ func init() { TestArtifactory = flag.Bool("test.artifactory", false, "Test Artifactory") TestDistribution = flag.Bool("test.distribution", false, "Test distribution") TestXray = flag.Bool("test.xray", false, "Test xray") + TestXsc = flag.Bool("test.xsc", false, "Test xsc") TestPipelines = flag.Bool("test.pipelines", false, "Test pipelines") TestAccess = flag.Bool("test.access", false, "Test access") TestRepositories = flag.Bool("test.repositories", false, "Test repositories in Artifactory") @@ -152,6 +156,7 @@ func init() { RtUrl = flag.String("rt.url", "http://localhost:8081/artifactory", "Artifactory url") DistUrl = flag.String("ds.url", "", "Distribution url") XrayUrl = flag.String("xr.url", "", "Xray url") + XscUrl = flag.String("xsc.url", "", "Xsc url") PipelinesUrl = flag.String("pipe.url", "", "Pipelines url") AccessUrl = flag.String("access.url", "http://localhost:8081/access", "Access url") RtUser = flag.String("rt.user", "admin", "Artifactory username") @@ -563,6 +568,13 @@ func GetXrayDetails() auth.ServiceDetails { return xrayDetails } +func GetXscDetails() auth.ServiceDetails { + xscDetails := xscAuth.NewXscDetails() + xscDetails.SetUrl(clientutils.AddTrailingSlashIfNeeded(*XscUrl)) + setAuthenticationDetail(xscDetails) + return xscDetails +} + func GetPipelinesDetails() auth.ServiceDetails { pDetails := pipelinesAuth.NewPipelinesDetails() pDetails.SetUrl(clientutils.AddTrailingSlashIfNeeded(*PipelinesUrl)) diff --git a/tests/xsc_test.go b/tests/xsc_test.go new file mode 100644 index 000000000..bc2178a31 --- /dev/null +++ b/tests/xsc_test.go @@ -0,0 +1,36 @@ +package tests + +import ( + clientUtils "github.com/jfrog/jfrog-client-go/utils" + "testing" +) + +func TestXscVersion(t *testing.T) { + initXscTest(t, "") + version, err := GetXscDetails().GetVersion() + if err != nil { + t.Error(err) + } + if version == "" { + t.Error("Expected a version, got empty string") + } +} + +func initXscTest(t *testing.T, minVersion string) { + if !*TestXsc { + t.Skip("Skipping xsc test. To run xsc test add the '-test.xsc=true' option.") + } + validateXscVersion(t, minVersion) +} +func validateXscVersion(t *testing.T, minVersion string) { + // Validate active XSC server. + version, err := GetXscDetails().GetVersion() + if err != nil { + t.Skip(err) + } + // Validate minimum XSC version. + err = clientUtils.ValidateMinimumVersion(clientUtils.Xsc, version, minVersion) + if err != nil { + t.Skip(err) + } +} diff --git a/tests/xscanalyticsevent_test.go b/tests/xscanalyticsevent_test.go new file mode 100644 index 000000000..bfe83792d --- /dev/null +++ b/tests/xscanalyticsevent_test.go @@ -0,0 +1,79 @@ +package tests + +import ( + "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + "github.com/jfrog/jfrog-client-go/xsc/services" + "github.com/stretchr/testify/assert" + "regexp" + "testing" +) + +var testsEventService *services.AnalyticsEventService + +func TestXscAddAndUpdateGeneralEvent(t *testing.T) { + xscDetails, client := initXscEventTest(t) + testsEventService = services.NewAnalyticsEventService(client) + testsEventService.XscDetails = xscDetails + + event := services.XscAnalyticsGeneralEvent{XscAnalyticsBasicGeneralEvent: services.XscAnalyticsBasicGeneralEvent{ + EventType: services.CliEventType, + EventStatus: services.Started, + Product: services.CliProduct, + ProductVersion: "2.53.1", + IsDefaultConfig: false, + JfrogUser: "gail", + OsPlatform: "mac", + OsArchitecture: "arm64", + MachineId: "id", + AnalyzerManagerVersion: "1.1.1", + }} + msi, err := testsEventService.AddGeneralEvent(event) + assert.NoError(t, err) + assert.True(t, isValidUUID(msi)) + + // Validate that the event sent and saved properly in XSC. + resp, err := testsEventService.GetGeneralEvent(msi) + assert.NoError(t, err) + assert.Equal(t, event, *resp) + + finalizeEvent := services.XscAnalyticsGeneralEventFinalize{ + MultiScanId: msi, + XscAnalyticsBasicGeneralEvent: services.XscAnalyticsBasicGeneralEvent{ + EventStatus: services.Completed, + TotalFindings: 10, + TotalIgnoredFindings: 5, + TotalScanDuration: "15s", + }, + } + + err = testsEventService.UpdateGeneralEvent(finalizeEvent) + assert.NoError(t, err) + + // Validate that the event's update sent and saved properly in XSC. + resp, err = testsEventService.GetGeneralEvent(msi) + assert.NoError(t, err) + event.EventStatus = services.Completed + event.TotalFindings = 10 + event.TotalIgnoredFindings = 5 + event.TotalScanDuration = "15s" + assert.Equal(t, event, *resp) +} + +func isValidUUID(str string) bool { + uuidRegex := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$`) + return uuidRegex.MatchString(str) +} + +func initXscEventTest(t *testing.T) (xscDetails auth.ServiceDetails, client *jfroghttpclient.JfrogHttpClient) { + var err error + initXscTest(t, services.AnalyticsMetricsMinXscVersion) + xscDetails = GetXscDetails() + client, err = jfroghttpclient.JfrogClientBuilder(). + SetClientCertPath(xscDetails.GetClientCertPath()). + SetClientCertKeyPath(xscDetails.GetClientCertKeyPath()). + AppendPreRequestInterceptor(xscDetails.RunPreRequestFunctions). + Build() + assert.NoError(t, err) + return +} diff --git a/utils/utils.go b/utils/utils.go index 8c232e616..3b9e7d55e 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -36,6 +36,7 @@ type MinVersionProduct string const ( Artifactory MinVersionProduct = "JFrog Artifactory" Xray MinVersionProduct = "JFrog Xray" + Xsc MinVersionProduct = "JFrog Xsc" DataTransfer MinVersionProduct = "Data Transfer" DockerApi MinVersionProduct = "Docker API" Projects MinVersionProduct = "JFrog Projects" @@ -601,3 +602,18 @@ func ExtractSha256FromResponseBody(body []byte) (string, error) { func Pointer[K any](val K) *K { return &val } + +func SetEnvWithResetCallback(key, value string) (func() error, error) { + oldValue, exist := os.LookupEnv(key) + if err := os.Setenv(key, value); err != nil { + return func() error { return nil }, errorutils.CheckError(err) + } + if exist { + return func() error { + return errorutils.CheckError(os.Setenv(key, oldValue)) + }, nil + } + return func() error { + return errorutils.CheckError(os.Unsetenv(key)) + }, nil +} diff --git a/utils/utils_test.go b/utils/utils_test.go index ac61ad82c..ee58e29b4 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "os" "reflect" "sort" "testing" @@ -287,3 +288,48 @@ func TestValidateMinimumVersion(t *testing.T) { }) } } + +func TestSetEnvWithResetCallback(t *testing.T) { + type args struct { + key string + value string + } + tests := []struct { + name string + args args + init func() + finish func() + }{ + { + name: "existing environment variable", + args: args{key: "TEST_KEY", value: "test_value"}, + init: func() { + assert.NoError(t, os.Setenv("TEST_KEY", "test-init-value")) + }, + finish: func() { + assert.Equal(t, os.Getenv("TEST_KEY"), "test-init-value") + }, + }, + { + name: "non-existing environment variable", + args: args{key: "NEW_TEST_KEY", value: "test_value"}, + init: func() { + + }, + finish: func() { + _, exist := os.LookupEnv("NEW_TEST_KEY") + assert.False(t, exist) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.init() + resetCallback, err := SetEnvWithResetCallback(tt.args.key, tt.args.value) + assert.NoError(t, err) + assert.Equal(t, tt.args.value, os.Getenv(tt.args.key)) + assert.NoError(t, resetCallback()) + tt.finish() + }) + } +} diff --git a/xray/manager.go b/xray/manager.go index 33fa30c66..d1eb6eca8 100644 --- a/xray/manager.go +++ b/xray/manager.go @@ -198,16 +198,3 @@ func (sm *XrayServicesManager) IsEntitled(featureId string) (bool, error) { entitlementsService.XrayDetails = sm.config.GetServiceDetails() return entitlementsService.IsEntitled(featureId) } - -func (sm *XrayServicesManager) XscEnabled() (string, error) { - scanService := services.NewScanService(sm.client) - scanService.XrayDetails = sm.config.GetServiceDetails() - return scanService.IsXscEnabled() -} - -// SendXscGitInfoRequest sends git info details to xsc service and gets multi scan id -func (sm *XrayServicesManager) SendXscGitInfoRequest(details *services.XscGitInfoContext) (multiScanId string, err error) { - scanService := services.NewScanService(sm.client) - scanService.XrayDetails = sm.config.GetServiceDetails() - return scanService.SendScanGitInfoContext(details) -} diff --git a/xsc/auth/xscdetails.go b/xsc/auth/xscdetails.go new file mode 100644 index 000000000..a41a80d80 --- /dev/null +++ b/xsc/auth/xscdetails.go @@ -0,0 +1,45 @@ +package auth + +import ( + "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/config" + "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/jfrog/jfrog-client-go/xsc" +) + +// NewXscDetails creates a struct of the Xsc details +func NewXscDetails() *XscDetails { + return &XscDetails{} +} + +type XscDetails struct { + auth.CommonConfigFields +} + +func (ds *XscDetails) GetVersion() (string, error) { + var err error + if ds.Version == "" { + ds.Version, err = ds.getXscVersion() + if err != nil { + return "", err + } + log.Debug("The Xsc version is:", ds.Version) + } + return ds.Version, nil +} + +func (ds *XscDetails) getXscVersion() (string, error) { + cd := auth.ServiceDetails(ds) + serviceConfig, err := config.NewConfigBuilder(). + SetServiceDetails(cd). + SetCertificatesPath(cd.GetClientCertPath()). + Build() + if err != nil { + return "", err + } + sm, err := xsc.New(serviceConfig) + if err != nil { + return "", err + } + return sm.GetVersion() +} diff --git a/xsc/manager.go b/xsc/manager.go new file mode 100644 index 000000000..7985ac2ba --- /dev/null +++ b/xsc/manager.go @@ -0,0 +1,71 @@ +package xsc + +import ( + "github.com/jfrog/jfrog-client-go/config" + "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + "github.com/jfrog/jfrog-client-go/xsc/services" +) + +// XscServicesManager defines the http client and general configuration +type XscServicesManager struct { + client *jfroghttpclient.JfrogHttpClient + config config.Config +} + +// New creates a service manager to interact with Xsc +func New(config config.Config) (*XscServicesManager, error) { + details := config.GetServiceDetails() + var err error + manager := &XscServicesManager{config: config} + manager.client, err = jfroghttpclient.JfrogClientBuilder(). + SetCertificatesPath(config.GetCertificatesPath()). + SetInsecureTls(config.IsInsecureTls()). + SetContext(config.GetContext()). + SetDialTimeout(config.GetDialTimeout()). + SetOverallRequestTimeout(config.GetOverallRequestTimeout()). + SetClientCertPath(details.GetClientCertPath()). + SetClientCertKeyPath(details.GetClientCertKeyPath()). + AppendPreRequestInterceptor(details.RunPreRequestFunctions). + SetRetries(config.GetHttpRetries()). + SetRetryWaitMilliSecs(config.GetHttpRetryWaitMilliSecs()). + Build() + return manager, err +} + +// Client will return the http client +func (sm *XscServicesManager) Client() *jfroghttpclient.JfrogHttpClient { + return sm.client +} + +func (sm *XscServicesManager) Config() config.Config { + return sm.config +} + +// GetVersion will return the Xsc version +func (sm *XscServicesManager) GetVersion() (string, error) { + versionService := services.NewVersionService(sm.client) + versionService.XscDetails = sm.config.GetServiceDetails() + return versionService.GetVersion() +} + +// AddAnalyticsGeneralEvent will send an analytics metrics general event to Xsc and return MSI (multi scan id) generated by Xsc. +func (sm *XscServicesManager) AddAnalyticsGeneralEvent(event services.XscAnalyticsGeneralEvent) (string, error) { + eventService := services.NewAnalyticsEventService(sm.client) + eventService.XscDetails = sm.config.GetServiceDetails() + return eventService.AddGeneralEvent(event) +} + +// UpdateAnalyticsGeneralEvent upon completion of the scan and we have all the results to report on, +// we send a finalized analytics metrics event with information matching an existing event's msi. +func (sm *XscServicesManager) UpdateAnalyticsGeneralEvent(event services.XscAnalyticsGeneralEventFinalize) error { + eventService := services.NewAnalyticsEventService(sm.client) + eventService.XscDetails = sm.config.GetServiceDetails() + return eventService.UpdateGeneralEvent(event) +} + +// GetAnalyticsGeneralEvent returns general event that match the msi provided. +func (sm *XscServicesManager) GetAnalyticsGeneralEvent(msi string) (*services.XscAnalyticsGeneralEvent, error) { + eventService := services.NewAnalyticsEventService(sm.client) + eventService.XscDetails = sm.config.GetServiceDetails() + return eventService.GetGeneralEvent(msi) +} diff --git a/xsc/services/analytics.go b/xsc/services/analytics.go new file mode 100644 index 000000000..4162a30f5 --- /dev/null +++ b/xsc/services/analytics.go @@ -0,0 +1,140 @@ +package services + +import ( + "encoding/json" + "fmt" + "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/xray/services" + "net/http" +) + +const ( + AnalyticsMetricsMinXscVersion = "1.7.1" + xscEventApi = "api/v1/event" +) + +type AnalyticsEventService struct { + client *jfroghttpclient.JfrogHttpClient + XscDetails auth.ServiceDetails +} + +func NewAnalyticsEventService(client *jfroghttpclient.JfrogHttpClient) *AnalyticsEventService { + return &AnalyticsEventService{client: client} +} + +// GetXscDetails returns the Xsc details +func (vs *AnalyticsEventService) GetXscDetails() auth.ServiceDetails { + return vs.XscDetails +} + +// AddGeneralEvent add general event in Xsc and returns msi generated by Xsc. +func (vs *AnalyticsEventService) AddGeneralEvent(event XscAnalyticsGeneralEvent) (string, error) { + httpDetails := vs.XscDetails.CreateHttpClientDetails() + requestContent, err := json.Marshal(event) + if err != nil { + return "", errorutils.CheckError(err) + } + resp, body, err := vs.client.SendPost(vs.XscDetails.GetUrl()+xscEventApi, requestContent, &httpDetails) + if err != nil { + return "", err + } + if err = errorutils.CheckResponseStatus(resp, http.StatusCreated); err != nil { + return "", errorutils.CheckError(errorutils.GenerateResponseError(resp.Status, utils.IndentJson(body))) + } + var response XscAnalyticsGeneralEventResponse + err = json.Unmarshal(body, &response) + return response.MultiScanId, errorutils.CheckError(err) +} + +// UpdateGeneralEvent update finalized analytics metrics info of an existing event. +func (vs *AnalyticsEventService) UpdateGeneralEvent(event XscAnalyticsGeneralEventFinalize) error { + httpDetails := vs.XscDetails.CreateHttpClientDetails() + requestContent, err := json.Marshal(event) + if err != nil { + return errorutils.CheckError(err) + } + resp, body, err := vs.client.SendPut(vs.XscDetails.GetUrl()+xscEventApi, requestContent, &httpDetails) + if err != nil { + return err + } + if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil { + return errorutils.CheckError(errorutils.GenerateResponseError(resp.Status, utils.IndentJson(body))) + } + return nil +} + +// GetGeneralEvent returns event's data matching the provided multi scan id. +func (vs *AnalyticsEventService) GetGeneralEvent(msi string) (*XscAnalyticsGeneralEvent, error) { + httpDetails := vs.XscDetails.CreateHttpClientDetails() + resp, body, _, err := vs.client.SendGet(fmt.Sprintf("%s%s/%s", vs.XscDetails.GetUrl(), xscEventApi, msi), true, &httpDetails) + if err != nil { + return nil, err + } + if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil { + return nil, errorutils.CheckError(errorutils.GenerateResponseError(resp.Status, utils.IndentJson(body))) + } + var response XscAnalyticsGeneralEvent + err = json.Unmarshal(body, &response) + return &response, errorutils.CheckError(err) +} + +// XscAnalyticsGeneralEvent extend the basic struct with Frogbot related info. +type XscAnalyticsGeneralEvent struct { + XscAnalyticsBasicGeneralEvent + GitInfo *services.XscGitInfoContext `json:"gitinfo,omitempty"` + IsGitInfoFlow bool `json:"is_gitinfo_flow,omitempty"` +} + +type XscAnalyticsGeneralEventFinalize struct { + XscAnalyticsBasicGeneralEvent + MultiScanId string `json:"multi_scan_id,omitempty"` +} + +type XscAnalyticsBasicGeneralEvent struct { + EventType EventType `json:"event_type,omitempty"` + EventStatus EventStatus `json:"event_status,omitempty"` + Product ProductName `json:"product,omitempty"` + ProductVersion string `json:"product_version,omitempty"` + TotalFindings int `json:"total_findings,omitempty"` + TotalIgnoredFindings int `json:"total_ignored_findings,omitempty"` + IsDefaultConfig bool `json:"is_default_config,omitempty"` + JfrogUser string `json:"jfrog_user,omitempty"` + OsPlatform string `json:"os_platform,omitempty"` + OsArchitecture string `json:"os_architecture,omitempty"` + MachineId string `json:"machine_id,omitempty"` + AnalyzerManagerVersion string `json:"analyzer_manager_version,omitempty"` + JpdVersion string `json:"jpd_version,omitempty"` + TotalScanDuration string `json:"total_scan_duration,omitempty"` + FrogbotScanType string `json:"frogbot_scan_type,omitempty"` + FrogbotCiProvider string `json:"frogbot_ci_provider,omitempty"` +} + +type XscAnalyticsGeneralEventResponse struct { + MultiScanId string `json:"multi_scan_id,omitempty"` +} + +type EventStatus string + +const ( + Started EventStatus = "started" + Completed EventStatus = "completed" + Cancelled EventStatus = "cancelled" + Failed EventStatus = "failed" +) + +type ProductName string + +const ( + CliProduct ProductName = "cli" + FrogbotProduct ProductName = "frogbot" +) + +type EventType int + +const ( + CliEventType EventType = 1 + FrogbotType EventType = 8 +) diff --git a/xsc/services/version.go b/xsc/services/version.go new file mode 100644 index 000000000..1f4a62f1f --- /dev/null +++ b/xsc/services/version.go @@ -0,0 +1,51 @@ +package services + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" +) + +// VersionService returns the https client and Xsc details +type VersionService struct { + client *jfroghttpclient.JfrogHttpClient + XscDetails auth.ServiceDetails +} + +// NewVersionService creates a new service to retrieve the version of Xsc +func NewVersionService(client *jfroghttpclient.JfrogHttpClient) *VersionService { + return &VersionService{client: client} +} + +// GetXscDetails returns the Xsc details +func (vs *VersionService) GetXscDetails() auth.ServiceDetails { + return vs.XscDetails +} + +// GetVersion returns the version of Xsc +func (vs *VersionService) GetVersion() (string, error) { + httpDetails := vs.XscDetails.CreateHttpClientDetails() + resp, body, _, err := vs.client.SendGet(vs.XscDetails.GetUrl()+"api/v1/system/version", true, &httpDetails) + if err != nil { + return "", err + } + if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil { + return "", errorutils.CheckError(errorutils.GenerateResponseError(resp.Status, utils.IndentJson(body))) + } + var version xscVersion + err = json.Unmarshal(body, &version) + if err != nil { + return "", errorutils.CheckError(err) + } + return strings.TrimSpace(version.Version), nil +} + +type xscVersion struct { + Version string `json:"xsc_version,omitempty"` + Revision string `json:"xray_version,omitempty"` +}