diff --git a/tests/xsc_test.go b/tests/xsc_test.go index bc2178a31..7b3e9dcc1 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,44 @@ 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) + validateXscVersion(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. +func validateXscVersion(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 minXscVersion is provided we assume we have a Xray version BEFORE Xsc migration to it (i.e. prior to 3.107.13) + // In this case we want to validate minimal required Xsc version + err = clientUtils.ValidateMinimumVersion(clientUtils.Xsc, currentXscVersion, minXscVersion) + if err != nil { + t.Skip(err) + } + } + + if minXrayVersion != "" { + // If minXrayVersion is provided we assume we have a Xray version AFTER Xsc migration to it (3.107.13+) + // In this case we want to validate minimal required Xray version only + var currentXrayVersion string + currentXrayVersion, err = GetXrayDetails().GetVersion() + if err != nil { + t.Skip(err) + } + err = clientUtils.ValidateMinimumVersion(clientUtils.Xsc, currentXrayVersion, minXrayVersion) + if 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..a4e947135 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,32 @@ 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: 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 { + + case 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) + default: assert.Fail(t, "received an unexpected request") } })) @@ -45,10 +88,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/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..30521df52 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 + 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..23184e1a5 100644 --- a/xsc/services/profile.go +++ b/xsc/services/profile.go @@ -14,8 +14,10 @@ import ( const ( ConfigProfileMinXscVersion = "1.11.0" - xscConfigProfileApi = "profile" - xscDeprecatedConfigProfileApiSuffix = "api/v1/" + xscConfigProfileApi + ConfigProfileByUrlMinXrayVersion = "3.110.0" + xscConfigProfileByNameApi = "profile" + xscConfigProfileByUrlApi = "profile_repos" + xscDeprecatedConfigProfileApiSuffix = "api/v1/" + xscConfigProfileByNameApi ) type ConfigurationProfileService struct { @@ -100,10 +102,10 @@ 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 } @@ -114,8 +116,8 @@ func (cp *ConfigurationProfileService) sendConfigProfileRequest(profileName stri 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 +129,33 @@ 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 { + httpDetails := cp.XrayDetails.CreateHttpClientDetails() + url = fmt.Sprintf("%s%s%s", utils.AddTrailingSlashIfNeeded(cp.XrayDetails.GetUrl()), xscutils.XscInXraySuffix, xscConfigProfileByUrlApi) + requestContent := []byte(fmt.Sprintf("{\"repo_url\":\"%s\"}", repoUrl)) + resp, body, err = cp.client.SendPost(url, requestContent, &httpDetails) + return + } + // Backward compatibility + httpDetails := cp.XscDetails.CreateHttpClientDetails() + url = fmt.Sprintf("%s%s/%s", utils.AddTrailingSlashIfNeeded(cp.XscDetails.GetUrl()), xscDeprecatedConfigProfileApiSuffix, xscConfigProfileByUrlApi) + requestContent := []byte(fmt.Sprintf("{\"repo_url\":\"%s\"}", 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 +}