diff --git a/artifactory/usage/reportusage.go b/artifactory/usage/call_home.go similarity index 72% rename from artifactory/usage/reportusage.go rename to artifactory/usage/call_home.go index bda41b225..cdcb76eae 100644 --- a/artifactory/usage/reportusage.go +++ b/artifactory/usage/call_home.go @@ -18,11 +18,17 @@ type ReportUsageAttribute struct { AttributeValue string } +type ArtifactoryCallHome struct{} + +func NewArtifactoryCallHome() *ArtifactoryCallHome { + return &ArtifactoryCallHome{} +} + func (rua *ReportUsageAttribute) isEmpty() bool { return rua.AttributeName == "" } -func validateAndGetUsageServerInfo(serviceManager artifactory.ArtifactoryServicesManager) (url string, clientDetails httputils.HttpClientDetails, err error) { +func (ach *ArtifactoryCallHome) validateAndGetUsageServerInfo(serviceManager artifactory.ArtifactoryServicesManager) (url string, clientDetails httputils.HttpClientDetails, err error) { config := serviceManager.GetConfig() if config == nil { err = errorutils.CheckErrorf("expected full config, but no configuration exists.") @@ -50,7 +56,7 @@ func validateAndGetUsageServerInfo(serviceManager artifactory.ArtifactoryService return } -func sendReport(url string, serviceManager artifactory.ArtifactoryServicesManager, clientDetails httputils.HttpClientDetails, bodyContent []byte) error { +func (ach *ArtifactoryCallHome) sendReport(url string, serviceManager artifactory.ArtifactoryServicesManager, clientDetails httputils.HttpClientDetails, bodyContent []byte) error { clientDetails.SetContentTypeApplicationJson() resp, body, err := serviceManager.Client().SendPost(url, bodyContent, &clientDetails) if err != nil { @@ -63,8 +69,8 @@ func sendReport(url string, serviceManager artifactory.ArtifactoryServicesManage return nil } -func ReportUsageToArtifactory(productId string, serviceManager artifactory.ArtifactoryServicesManager, features ...Feature) error { - url, clientDetails, err := validateAndGetUsageServerInfo(serviceManager) +func (ach *ArtifactoryCallHome) SendUsageToArtifactory(productId string, serviceManager artifactory.ArtifactoryServicesManager, features ...Feature) error { + url, clientDetails, err := ach.validateAndGetUsageServerInfo(serviceManager) if err != nil || url == "" { return err } @@ -72,11 +78,11 @@ func ReportUsageToArtifactory(productId string, serviceManager artifactory.Artif if err != nil { return err } - return sendReport(url, serviceManager, clientDetails, bodyContent) + return ach.sendReport(url, serviceManager, clientDetails, bodyContent) } -func SendReportUsage(productId, commandName string, serviceManager artifactory.ArtifactoryServicesManager, attributes ...ReportUsageAttribute) error { - url, clientDetails, err := validateAndGetUsageServerInfo(serviceManager) +func (ach *ArtifactoryCallHome) SendUsage(productId, commandName string, serviceManager artifactory.ArtifactoryServicesManager, attributes ...ReportUsageAttribute) error { + url, clientDetails, err := ach.validateAndGetUsageServerInfo(serviceManager) if err != nil || url == "" { return err } @@ -84,7 +90,7 @@ func SendReportUsage(productId, commandName string, serviceManager artifactory.A if err != nil { return err } - return sendReport(url, serviceManager, clientDetails, bodyContent) + return ach.sendReport(url, serviceManager, clientDetails, bodyContent) } func usageFeaturesToJson(productId string, features ...Feature) ([]byte, error) { diff --git a/artifactory/usage/reportusage_test.go b/artifactory/usage/call_home_test.go similarity index 100% rename from artifactory/usage/reportusage_test.go rename to artifactory/usage/call_home_test.go diff --git a/go.mod b/go.mod index 603c96263..25d108b10 100644 --- a/go.mod +++ b/go.mod @@ -10,14 +10,14 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.1 github.com/gookit/color v1.5.4 github.com/jfrog/archiver/v3 v3.6.1 - github.com/jfrog/build-info-go v1.10.5 + github.com/jfrog/build-info-go v1.10.7 github.com/jfrog/gofrog v1.7.6 github.com/minio/sha256-simd v1.0.1 github.com/stretchr/testify v1.9.0 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f - golang.org/x/term v0.26.0 + golang.org/x/term v0.27.0 ) require ( @@ -52,7 +52,7 @@ require ( golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.31.0 // indirect golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/tools v0.27.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 1461379f1..d0fc3425f 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI= github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw= -github.com/jfrog/build-info-go v1.10.5 h1:cW03JlPlKv7RMUU896uLUxyLWXAmCgR5Y5QX0fwgz0Q= -github.com/jfrog/build-info-go v1.10.5/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= +github.com/jfrog/build-info-go v1.10.7 h1:10NVHYg0193gJpQft+S4WQfvYMtj5jlwwhJRvkFJtBE= +github.com/jfrog/build-info-go v1.10.7/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s= github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -124,8 +124,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= @@ -142,14 +142,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= diff --git a/http/httpclient/client.go b/http/httpclient/client.go index bc829797e..93800932e 100644 --- a/http/httpclient/client.go +++ b/http/httpclient/client.go @@ -133,6 +133,10 @@ func (jc *HttpClient) Send(method, url string, content []byte, followRedirect, c } resp, respBody, redirectUrl, err = jc.doRequest(req, content, followRedirect, closeBody, httpClientsDetails) if err != nil { + if strings.Contains(err.Error(), "unsupported protocol scheme") { + // Wrong URL, so no need to retry + return false, fmt.Errorf("%w\nThe recieved error indicats an invalid URL: %q, Please ensure the URL includes a valid scheme like 'http://' or 'https://'.", err, url) + } return true, err } // Response must not be nil diff --git a/jfconnect/auth/jfconnectdetails.go b/jfconnect/auth/jfconnectdetails.go new file mode 100644 index 000000000..734eb37e0 --- /dev/null +++ b/jfconnect/auth/jfconnectdetails.go @@ -0,0 +1,17 @@ +package auth + +import ( + "github.com/jfrog/jfrog-client-go/auth" +) + +func NewJfConnectDetails() auth.ServiceDetails { + return &jfConnectDetails{} +} + +type jfConnectDetails struct { + auth.CommonConfigFields +} + +func (jc *jfConnectDetails) GetVersion() (string, error) { + panic("Failed: Method is not implemented") +} diff --git a/jfconnect/manager.go b/jfconnect/manager.go new file mode 100644 index 000000000..08fffb052 --- /dev/null +++ b/jfconnect/manager.go @@ -0,0 +1,45 @@ +package jfconnect + +import ( + "github.com/jfrog/jfrog-client-go/config" + "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + "github.com/jfrog/jfrog-client-go/jfconnect/services" +) + +type Manager interface { + PostMetric([]byte) error +} + +type jfConnectManager struct { + client *jfroghttpclient.JfrogHttpClient + config config.Config +} + +func NewManager(config config.Config) (Manager, error) { + details := config.GetServiceDetails() + var err error + manager := &jfConnectManager{config: config} + manager.client, err = jfroghttpclient.JfrogClientBuilder(). + SetCertificatesPath(config.GetCertificatesPath()). + SetInsecureTls(config.IsInsecureTls()). + SetClientCertPath(details.GetClientCertPath()). + SetClientCertKeyPath(details.GetClientCertKeyPath()). + AppendPreRequestInterceptor(details.RunPreRequestFunctions). + SetContext(config.GetContext()). + SetDialTimeout(config.GetDialTimeout()). + SetOverallRequestTimeout(config.GetOverallRequestTimeout()). + SetRetries(config.GetHttpRetries()). + SetRetryWaitMilliSecs(config.GetHttpRetryWaitMilliSecs()). + Build() + + return manager, err +} + +func (jm *jfConnectManager) Client() *jfroghttpclient.JfrogHttpClient { + return jm.client +} + +func (jm *jfConnectManager) PostMetric(metric []byte) error { + jfConnectService := services.NewJfConnectService(jm.config.GetServiceDetails(), jm.client) + return jfConnectService.PostMetric(metric) +} diff --git a/jfconnect/services/metrics.go b/jfconnect/services/metrics.go new file mode 100644 index 000000000..cb3fa9ecb --- /dev/null +++ b/jfconnect/services/metrics.go @@ -0,0 +1,39 @@ +package services + +import ( + "net/http" + + "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + clientutils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" +) + +const LogMetricApiEndpoint = "jfconnect/api/v1/backoffice/metrics/log" + +type JfConnectService struct { + client *jfroghttpclient.JfrogHttpClient + serviceDetails *auth.ServiceDetails +} + +func NewJfConnectService(serviceDetails auth.ServiceDetails, client *jfroghttpclient.JfrogHttpClient) *JfConnectService { + return &JfConnectService{serviceDetails: &serviceDetails, client: client} +} + +func (jcs *JfConnectService) GetJfConnectDetails() auth.ServiceDetails { + return *jcs.serviceDetails +} + +func (jcs *JfConnectService) PostMetric(metric []byte) error { + details := jcs.GetJfConnectDetails() + httpClientDetails := details.CreateHttpClientDetails() + httpClientDetails.SetContentTypeApplicationJson() + + url := clientutils.AddTrailingSlashIfNeeded(details.GetUrl()) + url += LogMetricApiEndpoint + resp, body, err := jcs.client.SendPost(url, metric, &httpClientDetails) + if err != nil { + return err + } + return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusCreated, http.StatusOK) +} diff --git a/tests/xsc_test.go b/tests/xsc_test.go index bc2178a31..7096f4dc2 100644 --- a/tests/xsc_test.go +++ b/tests/xsc_test.go @@ -6,7 +6,7 @@ import ( ) func TestXscVersion(t *testing.T) { - initXscTest(t, "") + initXscTest(t, "", "") version, err := GetXscDetails().GetVersion() if err != nil { t.Error(err) @@ -16,21 +16,38 @@ func TestXscVersion(t *testing.T) { } } -func initXscTest(t *testing.T, minVersion string) { +func initXscTest(t *testing.T, minXscVersion string, minXrayVersion string) { if !*TestXsc { t.Skip("Skipping xsc test. To run xsc test add the '-test.xsc=true' option.") } - validateXscVersion(t, minVersion) + validateXscAndXrayVersion(t, minXscVersion, minXrayVersion) } -func validateXscVersion(t *testing.T, minVersion string) { - // Validate active XSC server. - version, err := GetXscDetails().GetVersion() + +// This func validates minimal Xsc version. +// Since Xsc was migrated into Xray from version 3.107.13, we need to check minimal Xray version from this version forward instead of Xsc version. +// For features that are available before the migration we pass minXscVersion to check. If the utilized Xray version >= 3.107.13, the returned Xsc version will always suffice the check. +// For features that were introduced only after the migration we pass only minXrayVersion to check and can leave minXscVersion blank. +// In general minXscVersion should be provided only for features that were introduced before Xsc migration to Xray +func validateXscAndXrayVersion(t *testing.T, minXscVersion string, minXrayVersion string) { + // Validate active Xsc server + currentXscVersion, 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) + + if minXscVersion != "" { + if err = clientUtils.ValidateMinimumVersion(clientUtils.Xsc, currentXscVersion, minXscVersion); err != nil { + t.Skip(err) + } + } + + if minXrayVersion != "" { + var currentXrayVersion string + if currentXrayVersion, err = GetXrayDetails().GetVersion(); err != nil { + t.Skip(err) + } + if err = clientUtils.ValidateMinimumVersion(clientUtils.Xsc, currentXrayVersion, minXrayVersion); err != nil { + t.Skip(err) + } } } diff --git a/tests/xscanalyticsevent_test.go b/tests/xscanalyticsevent_test.go index bfe83792d..0aa223e5b 100644 --- a/tests/xscanalyticsevent_test.go +++ b/tests/xscanalyticsevent_test.go @@ -67,7 +67,7 @@ func isValidUUID(str string) bool { func initXscEventTest(t *testing.T) (xscDetails auth.ServiceDetails, client *jfroghttpclient.JfrogHttpClient) { var err error - initXscTest(t, services.AnalyticsMetricsMinXscVersion) + initXscTest(t, services.AnalyticsMetricsMinXscVersion, "") xscDetails = GetXscDetails() client, err = jfroghttpclient.JfrogClientBuilder(). SetClientCertPath(xscDetails.GetClientCertPath()). diff --git a/tests/xscconfigprofile_test.go b/tests/xscconfigprofile_test.go index 44c5932ba..206c141f0 100644 --- a/tests/xscconfigprofile_test.go +++ b/tests/xscconfigprofile_test.go @@ -4,20 +4,48 @@ import ( "encoding/json" "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" "github.com/jfrog/jfrog-client-go/xsc/services" + xscutils "github.com/jfrog/jfrog-client-go/xsc/services/utils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "net/http" "net/http/httptest" "os" + "strings" "testing" ) -func TestGetConfigurationProfile(t *testing.T) { - initXscTest(t, services.ConfigProfileMinXscVersion) +const configProfileWithoutRepo = "default-test-profile" - mockServer, configProfileService := createXscMockServerForConfigProfile(t) +func TestGetConfigurationProfileByName(t *testing.T) { + initXscTest(t, services.ConfigProfileMinXscVersion, "") + + xrayVersion, err := GetXrayDetails().GetVersion() + require.NoError(t, err) + + mockServer, configProfileService := createXscMockServerForConfigProfile(t, xrayVersion) + defer mockServer.Close() + + configProfile, err := configProfileService.GetConfigurationProfileByName(configProfileWithoutRepo) + assert.NoError(t, err) + + profileFileContent, err := os.ReadFile("testdata/configprofile/configProfileExample.json") + assert.NoError(t, err) + var configProfileForComparison services.ConfigProfile + err = json.Unmarshal(profileFileContent, &configProfileForComparison) + assert.NoError(t, err) + assert.Equal(t, &configProfileForComparison, configProfile) +} + +func TestGetConfigurationProfileByUrl(t *testing.T) { + initXscTest(t, "", services.ConfigProfileByUrlMinXrayVersion) + + xrayVersion, err := GetXrayDetails().GetVersion() + require.NoError(t, err) + + mockServer, configProfileService := createXscMockServerForConfigProfile(t, xrayVersion) defer mockServer.Close() - configProfile, err := configProfileService.GetConfigurationProfile("default-test-profile") + configProfile, err := configProfileService.GetConfigurationProfileByUrl(mockServer.URL) assert.NoError(t, err) profileFileContent, err := os.ReadFile("testdata/configprofile/configProfileExample.json") @@ -26,17 +54,26 @@ func TestGetConfigurationProfile(t *testing.T) { err = json.Unmarshal(profileFileContent, &configProfileForComparison) assert.NoError(t, err) assert.Equal(t, &configProfileForComparison, configProfile) + } -func createXscMockServerForConfigProfile(t *testing.T) (mockServer *httptest.Server, configProfileService *services.ConfigurationProfileService) { +func createXscMockServerForConfigProfile(t *testing.T, xrayVersion string) (mockServer *httptest.Server, configProfileService *services.ConfigurationProfileService) { mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.RequestURI == "/xsc/api/v1/profile/default-test-profile" && r.Method == http.MethodGet { + apiUrlPart := "api/v1/" + var isXrayAfterXscMigration bool + if isXrayAfterXscMigration = xscutils.IsXscXrayInnerService(xrayVersion); isXrayAfterXscMigration { + apiUrlPart = "" + } + + switch { + case (strings.Contains(r.RequestURI, "/xsc/"+apiUrlPart+"profile/"+configProfileWithoutRepo) && r.Method == http.MethodGet) || + strings.Contains(r.RequestURI, "xray/api/v1/xsc/profile_repos") && r.Method == http.MethodPost && isXrayAfterXscMigration: w.WriteHeader(http.StatusOK) content, err := os.ReadFile("testdata/configprofile/configProfileExample.json") assert.NoError(t, err) _, err = w.Write(content) assert.NoError(t, err) - } else { + default: assert.Fail(t, "received an unexpected request") } })) @@ -45,10 +82,15 @@ func createXscMockServerForConfigProfile(t *testing.T) (mockServer *httptest.Ser xscDetails.SetUrl(mockServer.URL + "/xsc") xscDetails.SetAccessToken("") + xrayDetails := GetXrayDetails() + xrayDetails.SetUrl(mockServer.URL + "/xray") + xrayDetails.SetAccessToken("") + client, err := jfroghttpclient.JfrogClientBuilder().Build() assert.NoError(t, err) configProfileService = services.NewConfigurationProfileService(client) configProfileService.XscDetails = xscDetails + configProfileService.XrayDetails = xrayDetails return } diff --git a/tests/xsclogerrorevent_test.go b/tests/xsclogerrorevent_test.go index c56273633..ff441bb2f 100644 --- a/tests/xsclogerrorevent_test.go +++ b/tests/xsclogerrorevent_test.go @@ -13,7 +13,7 @@ import ( const errorMessageContentForTest = "THIS IS NOT A REAL ERROR! This Error is posted as part of TestXscSendLogErrorEvent test" func TestXscSendLogErrorEvent(t *testing.T) { - initXscTest(t, services.LogErrorMinXscVersion) + initXscTest(t, services.LogErrorMinXscVersion, "") mockServer, logErrorService := createXscMockServerForLogEvent(t) defer mockServer.Close() diff --git a/utils/utils.go b/utils/utils.go index 17bec088e..b9c13f3c6 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -28,7 +28,7 @@ import ( const ( Development = "development" Agent = "jfrog-client-go" - Version = "1.48.1" + Version = "1.48.4" ) const xrayDevVersion = "3.x-dev" diff --git a/xray/services/xsc/xsc.go b/xray/services/xsc/xsc.go index cc5119a99..1e3003ed4 100644 --- a/xray/services/xsc/xsc.go +++ b/xray/services/xsc/xsc.go @@ -47,8 +47,14 @@ func (xs *XscInnerService) GetAnalyticsGeneralEvent(msi string) (*services.XscAn return eventService.GetGeneralEvent(msi) } -func (xs *XscInnerService) GetConfigProfile(profileName string) (*services.ConfigProfile, error) { +func (xs *XscInnerService) GetConfigProfileByName(profileName string) (*services.ConfigProfile, error) { configProfileService := services.NewConfigurationProfileService(xs.client) configProfileService.XrayDetails = xs.XrayDetails - return configProfileService.GetConfigurationProfile(profileName) + return configProfileService.GetConfigurationProfileByName(profileName) +} + +func (xs *XscInnerService) GetConfigProfileByUrl(repoUrl string) (*services.ConfigProfile, error) { + configProfileService := services.NewConfigurationProfileService(xs.client) + configProfileService.XrayDetails = xs.XrayDetails + return configProfileService.GetConfigurationProfileByUrl(repoUrl) } diff --git a/xsc/manager.go b/xsc/manager.go index 04423674b..f14f55d41 100644 --- a/xsc/manager.go +++ b/xsc/manager.go @@ -77,8 +77,13 @@ func (sm *XscServicesManager) GetAnalyticsGeneralEvent(msi string) (*services.Xs return eventService.GetGeneralEvent(msi) } -func (sm *XscServicesManager) GetConfigProfile(profileName string) (*services.ConfigProfile, error) { +func (sm *XscServicesManager) GetConfigProfileByName(profileName string) (*services.ConfigProfile, error) { configProfileService := services.NewConfigurationProfileService(sm.client) configProfileService.XscDetails = sm.config.GetServiceDetails() - return configProfileService.GetConfigurationProfile(profileName) + return configProfileService.GetConfigurationProfileByName(profileName) +} + +func (sm *XscServicesManager) GetConfigProfileByUrl(_ string) (*services.ConfigProfile, error) { + // Empty implementation required for alignment with interface, implemented only at the new service inside the Xray service + return nil, nil } diff --git a/xsc/service.go b/xsc/service.go index 6094b5b0d..9fccf85da 100644 --- a/xsc/service.go +++ b/xsc/service.go @@ -15,6 +15,8 @@ type XscService interface { UpdateAnalyticsGeneralEvent(event services.XscAnalyticsGeneralEventFinalize) error // GetAnalyticsGeneralEvent returns general event that match the msi provided. GetAnalyticsGeneralEvent(msi string) (*services.XscAnalyticsGeneralEvent, error) - // GetConfigProfile returns the configuration profile that match the profile name provided. - GetConfigProfile(profileName string) (*services.ConfigProfile, error) + // GetConfigProfileByName returns the configuration profile that match the profile name provided. + GetConfigProfileByName(profileName string) (*services.ConfigProfile, error) + // GetConfigProfileByUrl returns the configuration profile related to the provided repository url. + GetConfigProfileByUrl(profileUrl string) (*services.ConfigProfile, error) } diff --git a/xsc/services/profile.go b/xsc/services/profile.go index 2db33d7d7..0f646f38b 100644 --- a/xsc/services/profile.go +++ b/xsc/services/profile.go @@ -2,6 +2,7 @@ package services import ( "encoding/json" + "errors" "fmt" "net/http" @@ -13,9 +14,12 @@ import ( ) const ( - ConfigProfileMinXscVersion = "1.11.0" - xscConfigProfileApi = "profile" - xscDeprecatedConfigProfileApiSuffix = "api/v1/" + xscConfigProfileApi + ConfigProfileMinXscVersion = "1.11.0" + ConfigProfileByUrlMinXrayVersion = "3.110.0" + xscConfigProfileByNameApi = "profile" + xscConfigProfileByUrlApi = "profile_repos" + xscDeprecatedConfigProfileByNameApiSuffix = "api/v1/" + xscConfigProfileByNameApi + getProfileByUrlBody = "{\"repo_url\":\"%s\"}" ) type ConfigurationProfileService struct { @@ -100,22 +104,22 @@ type ServicesScannerConfig struct { ExcludePatterns []string `json:"exclude_patterns,omitempty"` } -func (cp *ConfigurationProfileService) sendConfigProfileRequest(profileName string) (url string, resp *http.Response, body []byte, err error) { +func (cp *ConfigurationProfileService) sendConfigProfileByNameRequest(profileName string) (url string, resp *http.Response, body []byte, err error) { if cp.XrayDetails != nil { httpDetails := cp.XrayDetails.CreateHttpClientDetails() - url = fmt.Sprintf("%s%s%s/%s", utils.AddTrailingSlashIfNeeded(cp.XrayDetails.GetUrl()), xscutils.XscInXraySuffix, xscConfigProfileApi, profileName) + url = fmt.Sprintf("%s%s%s/%s", utils.AddTrailingSlashIfNeeded(cp.XrayDetails.GetUrl()), xscutils.XscInXraySuffix, xscConfigProfileByNameApi, profileName) resp, body, _, err = cp.client.SendGet(url, true, &httpDetails) return } // Backward compatibility httpDetails := cp.XscDetails.CreateHttpClientDetails() - url = fmt.Sprintf("%s%s/%s", utils.AddTrailingSlashIfNeeded(cp.XscDetails.GetUrl()), xscDeprecatedConfigProfileApiSuffix, profileName) + url = fmt.Sprintf("%s%s/%s", utils.AddTrailingSlashIfNeeded(cp.XscDetails.GetUrl()), xscDeprecatedConfigProfileByNameApiSuffix, profileName) resp, body, _, err = cp.client.SendGet(url, true, &httpDetails) return } -func (cp *ConfigurationProfileService) GetConfigurationProfile(profileName string) (*ConfigProfile, error) { - url, res, body, err := cp.sendConfigProfileRequest(profileName) +func (cp *ConfigurationProfileService) GetConfigurationProfileByName(profileName string) (*ConfigProfile, error) { + url, res, body, err := cp.sendConfigProfileByNameRequest(profileName) if err != nil { return nil, fmt.Errorf("failed to send GET query to '%s': %q", url, err) } @@ -127,3 +131,29 @@ func (cp *ConfigurationProfileService) GetConfigurationProfile(profileName strin err = errorutils.CheckError(json.Unmarshal(body, &profile)) return &profile, err } + +func (cp *ConfigurationProfileService) sendConfigProfileByUrlRequest(repoUrl string) (url string, resp *http.Response, body []byte, err error) { + if cp.XrayDetails == nil { + err = errors.New("received empty Xray details") + return + } + httpDetails := cp.XrayDetails.CreateHttpClientDetails() + url = fmt.Sprintf("%s%s%s", utils.AddTrailingSlashIfNeeded(cp.XrayDetails.GetUrl()), xscutils.XscInXraySuffix, xscConfigProfileByUrlApi) + requestContent := []byte(fmt.Sprintf(getProfileByUrlBody, repoUrl)) + resp, body, err = cp.client.SendPost(url, requestContent, &httpDetails) + return +} + +func (cp *ConfigurationProfileService) GetConfigurationProfileByUrl(url string) (*ConfigProfile, error) { + url, res, body, err := cp.sendConfigProfileByUrlRequest(url) + if err != nil { + return nil, fmt.Errorf("failed to send POST query to '%s': %q", url, err) + } + if err = errorutils.CheckResponseStatusWithBody(res, body, http.StatusOK); err != nil { + return nil, err + } + + var profile ConfigProfile + err = errorutils.CheckError(json.Unmarshal(body, &profile)) + return &profile, err +}