diff --git a/access/services/login.go b/access/services/login.go index 7a6ce32b3..5dd9fdbf1 100644 --- a/access/services/login.go +++ b/access/services/login.go @@ -2,14 +2,16 @@ package services import ( "encoding/json" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "net/http" + "path" + "time" + + artifactoryutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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/utils/io/httputils" - "net/http" - "path" - "time" ) const ( @@ -37,7 +39,7 @@ func NewLoginService(client *jfroghttpclient.JfrogHttpClient) *LoginService { func (ls *LoginService) SendLoginAuthenticationRequest(uuid string) error { restAPI := path.Join(baseClientLoginApi, requestApi) - fullUrl, err := utils.BuildArtifactoryUrl(ls.ServiceDetails.GetUrl(), restAPI, make(map[string]string)) + fullUrl, err := utils.BuildUrl(ls.ServiceDetails.GetUrl(), restAPI, make(map[string]string)) if err != nil { return err } @@ -49,7 +51,7 @@ func (ls *LoginService) SendLoginAuthenticationRequest(uuid string) error { return errorutils.CheckError(err) } httpClientsDetails := ls.ServiceDetails.CreateHttpClientDetails() - utils.SetContentType("application/json", &httpClientsDetails.Headers) + artifactoryutils.SetContentType("application/json", &httpClientsDetails.Headers) resp, body, err := ls.client.SendPost(fullUrl, requestContent, &httpClientsDetails) if err != nil { return err @@ -89,7 +91,7 @@ func (ls *LoginService) GetLoginAuthenticationToken(uuid string) (token auth.Com func (ls *LoginService) getLoginAuthenticationToken(uuid string) (resp *http.Response, body []byte, err error) { restAPI := path.Join(baseClientLoginApi, tokenApi, uuid) - fullUrl, err := utils.BuildArtifactoryUrl(ls.ServiceDetails.GetUrl(), restAPI, make(map[string]string)) + fullUrl, err := utils.BuildUrl(ls.ServiceDetails.GetUrl(), restAPI, make(map[string]string)) if err != nil { return } diff --git a/artifactory/services/delete.go b/artifactory/services/delete.go index 58c1ec3b4..887eb264e 100644 --- a/artifactory/services/delete.go +++ b/artifactory/services/delete.go @@ -10,6 +10,7 @@ import ( "github.com/jfrog/jfrog-client-go/auth" "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" clientutils "github.com/jfrog/jfrog-client-go/utils" + urlutil "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/content" "github.com/jfrog/jfrog-client-go/utils/log" @@ -98,7 +99,7 @@ func (ds *DeleteService) createFileHandlerFunc(result *utils.Result) fileDeleteH return func(threadId int) error { result.TotalCount[threadId]++ logMsgPrefix := clientutils.GetLogMsgPrefix(threadId, ds.DryRun) - deletePath, e := utils.BuildArtifactoryUrl(ds.GetArtifactoryDetails().GetUrl(), resultItem.GetItemRelativePath(), make(map[string]string)) + deletePath, e := urlutil.BuildUrl(ds.GetArtifactoryDetails().GetUrl(), resultItem.GetItemRelativePath(), make(map[string]string)) if e != nil { return e } diff --git a/artifactory/services/discardBuilds.go b/artifactory/services/discardBuilds.go index 6316fba17..7a4085d3a 100644 --- a/artifactory/services/discardBuilds.go +++ b/artifactory/services/discardBuilds.go @@ -2,16 +2,18 @@ package services import ( "encoding/json" - buildinfo "github.com/jfrog/build-info-go/entities" "net/http" "path" "strconv" "strings" "time" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + buildinfo "github.com/jfrog/build-info-go/entities" + + artifactoryutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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/utils/log" ) @@ -30,7 +32,7 @@ func (ds *DiscardBuildsService) DiscardBuilds(params DiscardBuildsParams) error discardUrl := ds.ArtDetails.GetUrl() restApi := path.Join("api/build/retention/", params.GetBuildName()) - requestFullUrl, err := utils.BuildArtifactoryUrl(discardUrl, restApi, make(map[string]string)) + requestFullUrl, err := utils.BuildUrl(discardUrl, restApi, make(map[string]string)) if err != nil { return err } @@ -63,7 +65,7 @@ func (ds *DiscardBuildsService) DiscardBuilds(params DiscardBuildsParams) error } httpClientsDetails := ds.getArtifactoryDetails().CreateHttpClientDetails() - utils.SetContentType("application/json", &httpClientsDetails.Headers) + artifactoryutils.SetContentType("application/json", &httpClientsDetails.Headers) resp, body, err := ds.client.SendPost(requestFullUrl, requestContent, &httpClientsDetails) if err != nil { diff --git a/artifactory/services/distribute.go b/artifactory/services/distribute.go index 83e2c89e5..e0dd48d6b 100644 --- a/artifactory/services/distribute.go +++ b/artifactory/services/distribute.go @@ -6,9 +6,10 @@ import ( "path" "strings" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + artifactoryutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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/utils/log" ) @@ -41,7 +42,7 @@ func (ds *DistributeService) BuildDistribute(params BuildDistributionParams) err distributeUrl := ds.ArtDetails.GetUrl() restApi := path.Join("api/build/distribute/", params.GetBuildName(), params.GetBuildNumber()) - requestFullUrl, err := utils.BuildArtifactoryUrl(distributeUrl, restApi, make(map[string]string)) + requestFullUrl, err := utils.BuildUrl(distributeUrl, restApi, make(map[string]string)) if err != nil { return err } @@ -70,7 +71,7 @@ func (ds *DistributeService) BuildDistribute(params BuildDistributionParams) err } httpClientsDetails := ds.getArtifactoryDetails().CreateHttpClientDetails() - utils.SetContentType("application/json", &httpClientsDetails.Headers) + artifactoryutils.SetContentType("application/json", &httpClientsDetails.Headers) resp, body, err := ds.client.SendPost(requestFullUrl, requestContent, &httpClientsDetails) if err != nil { diff --git a/artifactory/services/dockerpromote.go b/artifactory/services/dockerpromote.go index e1f7d359e..e51a573bd 100644 --- a/artifactory/services/dockerpromote.go +++ b/artifactory/services/dockerpromote.go @@ -5,9 +5,10 @@ import ( "net/http" "path" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + artifactoryutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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/utils/log" ) @@ -36,7 +37,7 @@ func (ps *DockerPromoteService) IsDryRun() bool { func (ps *DockerPromoteService) PromoteDocker(params DockerPromoteParams) error { // Create URL restApi := path.Join("api/docker", params.SourceRepo, "v2", "promote") - url, err := utils.BuildArtifactoryUrl(ps.GetArtifactoryDetails().GetUrl(), restApi, nil) + url, err := utils.BuildUrl(ps.GetArtifactoryDetails().GetUrl(), restApi, nil) if err != nil { return err } @@ -57,7 +58,7 @@ func (ps *DockerPromoteService) PromoteDocker(params DockerPromoteParams) error // Send POST request httpClientsDetails := ps.GetArtifactoryDetails().CreateHttpClientDetails() - utils.SetContentType("application/json", &httpClientsDetails.Headers) + artifactoryutils.SetContentType("application/json", &httpClientsDetails.Headers) resp, body, err := ps.client.SendPost(url, requestContent, &httpClientsDetails) if err != nil { return err diff --git a/artifactory/services/download.go b/artifactory/services/download.go index 941c7234c..c7e76df71 100644 --- a/artifactory/services/download.go +++ b/artifactory/services/download.go @@ -502,7 +502,7 @@ func (ds *DownloadService) createFileHandlerFunc(downloadParams DownloadParams, return func(downloadData DownloadData) parallel.TaskFunc { return func(threadId int) error { logMsgPrefix := clientutils.GetLogMsgPrefix(threadId, ds.DryRun) - downloadPath, e := utils.BuildArtifactoryUrl(ds.GetArtifactoryDetails().GetUrl(), downloadData.Dependency.GetItemRelativePath(), make(map[string]string)) + downloadPath, e := clientutils.BuildUrl(ds.GetArtifactoryDetails().GetUrl(), downloadData.Dependency.GetItemRelativePath(), make(map[string]string)) if e != nil { return e } diff --git a/artifactory/services/go/publish.go b/artifactory/services/go/publish.go index 3e89c94cf..63426812d 100644 --- a/artifactory/services/go/publish.go +++ b/artifactory/services/go/publish.go @@ -8,9 +8,10 @@ import ( "github.com/jfrog/gofrog/version" "github.com/jfrog/jfrog-client-go/utils/log" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + artifactoryutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "github.com/jfrog/jfrog-client-go/auth" "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" + "github.com/jfrog/jfrog-client-go/utils" clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/content" @@ -37,8 +38,8 @@ func (gpc *GoPublishCommand) verifyCompatibleVersion(artifactoryVersion string) return nil } -func (gpc *GoPublishCommand) PublishPackage(params GoParams, client *jfroghttpclient.JfrogHttpClient, artDetails auth.ServiceDetails) (*utils.OperationSummary, error) { - goApiUrl, err := utils.BuildArtifactoryUrl(artDetails.GetUrl(), "api/go/", make(map[string]string)) +func (gpc *GoPublishCommand) PublishPackage(params GoParams, client *jfroghttpclient.JfrogHttpClient, artDetails auth.ServiceDetails) (*artifactoryutils.OperationSummary, error) { + goApiUrl, err := utils.BuildUrl(artDetails.GetUrl(), "api/go/", make(map[string]string)) if err != nil { return nil, err } @@ -72,7 +73,7 @@ func (gpc *GoPublishCommand) PublishPackage(params GoParams, client *jfroghttpcl return nil, err } - return &utils.OperationSummary{TotalSucceeded: totalSucceed, TotalFailed: totalFailed, TransferDetailsReader: content.NewContentReader(fileTransferDetailsTempFile, "files")}, nil + return &artifactoryutils.OperationSummary{TotalSucceeded: totalSucceed, TotalFailed: totalFailed, TransferDetailsReader: content.NewContentReader(fileTransferDetailsTempFile, "files")}, nil } func (gpc *GoPublishCommand) uploadFile(params GoParams, filePath string, moduleId, ext, goApiUrl string, filesDetails *[]clientutils.FileTransferDetails, pwa *GoPublishCommand) (success, failed int, err error) { @@ -108,7 +109,7 @@ func (gpc *GoPublishCommand) upload(localPath, pathInArtifactory, version, props if err != nil { return nil, err } - utils.AddChecksumHeaders(gpc.clientDetails.Headers, details) + artifactoryutils.AddChecksumHeaders(gpc.clientDetails.Headers, details) resp, body, err := gpc.client.UploadFile(localPath, goApiUrl, "", &gpc.clientDetails, nil) if err != nil { return nil, err diff --git a/artifactory/services/movecopy.go b/artifactory/services/movecopy.go index ed7a27bd9..1a9112566 100644 --- a/artifactory/services/movecopy.go +++ b/artifactory/services/movecopy.go @@ -237,7 +237,7 @@ func (mc *MoveCopyService) moveOrCopyFile(sourcePath, destPath, logMsgPrefix str } else { log.Info(logMsgPrefix + message) } - requestFullUrl, err := utils.BuildArtifactoryUrl(moveUrl, restApi, params) + requestFullUrl, err := clientutils.BuildUrl(moveUrl, restApi, params) if err != nil { return false, err } @@ -268,7 +268,7 @@ func (mc *MoveCopyService) createPathForMoveAction(destPath, logMsgPrefix string func (mc *MoveCopyService) createPathInArtifactory(destPath, logMsgPrefix string) (bool, error) { rtUrl := mc.GetArtifactoryDetails().GetUrl() - requestFullUrl, err := utils.BuildArtifactoryUrl(rtUrl, destPath, map[string]string{}) + requestFullUrl, err := clientutils.BuildUrl(rtUrl, destPath, map[string]string{}) if err != nil { return false, err } diff --git a/artifactory/services/ping.go b/artifactory/services/ping.go index dc2b5e3a7..45a798d18 100644 --- a/artifactory/services/ping.go +++ b/artifactory/services/ping.go @@ -3,9 +3,9 @@ package services import ( "net/http" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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/utils/log" ) @@ -32,7 +32,7 @@ func (ps *PingService) IsDryRun() bool { } func (ps *PingService) Ping() ([]byte, error) { - url, err := utils.BuildArtifactoryUrl(ps.GetArtifactoryDetails().GetUrl(), "api/system/ping", nil) + url, err := utils.BuildUrl(ps.GetArtifactoryDetails().GetUrl(), "api/system/ping", nil) if err != nil { return nil, err } diff --git a/artifactory/services/promote.go b/artifactory/services/promote.go index 794f0d7d2..a56c297f1 100644 --- a/artifactory/services/promote.go +++ b/artifactory/services/promote.go @@ -8,6 +8,7 @@ import ( "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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" "github.com/jfrog/jfrog-client-go/utils/log" ) @@ -41,7 +42,7 @@ func (ps *PromoteService) BuildPromote(promotionParams PromotionParams) error { queryParams["project"] = promotionParams.ProjectKey } - requestFullUrl, err := utils.BuildArtifactoryUrl(promoteUrl, restApi, queryParams) + requestFullUrl, err := clientutils.BuildUrl(promoteUrl, restApi, queryParams) if err != nil { return err } diff --git a/artifactory/services/props.go b/artifactory/services/props.go index c716f865c..54c94b190 100644 --- a/artifactory/services/props.go +++ b/artifactory/services/props.go @@ -108,7 +108,7 @@ func (ps *PropsService) performRequest(propsParams PropsParams, isDelete bool) ( logMsgPrefix := clientutils.GetLogMsgPrefix(threadId, ps.IsDryRun()) restAPI := path.Join("api", "storage", relativePath) - setPropertiesURL, err := utils.BuildArtifactoryUrl(ps.GetArtifactoryDetails().GetUrl(), restAPI, make(map[string]string)) + setPropertiesURL, err := clientutils.BuildUrl(ps.GetArtifactoryDetails().GetUrl(), restAPI, make(map[string]string)) if err != nil { return err } @@ -166,7 +166,7 @@ func NewPropsParams() PropsParams { func (ps *PropsService) GetItemProperties(relativePath string) (*utils.ItemProperties, error) { restAPI := path.Join("api", "storage", path.Clean(relativePath)) - propertiesURL, err := utils.BuildArtifactoryUrl(ps.GetArtifactoryDetails().GetUrl(), restAPI, make(map[string]string)) + propertiesURL, err := clientutils.BuildUrl(ps.GetArtifactoryDetails().GetUrl(), restAPI, make(map[string]string)) if err != nil { return nil, err } diff --git a/artifactory/services/readfile.go b/artifactory/services/readfile.go index 413890f66..75015304e 100644 --- a/artifactory/services/readfile.go +++ b/artifactory/services/readfile.go @@ -4,9 +4,9 @@ import ( "io" "net/http" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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" ) @@ -39,7 +39,7 @@ func (ds *ReadFileService) SetDryRun(isDryRun bool) { } func (ds *ReadFileService) ReadRemoteFile(downloadPath string) (io.ReadCloser, error) { - readPath, err := utils.BuildArtifactoryUrl(ds.GetArtifactoryDetails().GetUrl(), downloadPath, make(map[string]string)) + readPath, err := utils.BuildUrl(ds.GetArtifactoryDetails().GetUrl(), downloadPath, make(map[string]string)) if err != nil { return nil, err } diff --git a/artifactory/services/security.go b/artifactory/services/security.go index bc837e248..78153ec33 100644 --- a/artifactory/services/security.go +++ b/artifactory/services/security.go @@ -8,7 +8,6 @@ import ( "strconv" "strings" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "github.com/jfrog/jfrog-client-go/auth" "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" clientutils "github.com/jfrog/jfrog-client-go/utils" @@ -34,7 +33,7 @@ func (ss *SecurityService) getArtifactoryDetails() auth.ServiceDetails { // Create an API key for the current user. Returns an error if API key already exists - use regenerate API key instead. func (ss *SecurityService) CreateAPIKey() (string, error) { httpClientDetails := ss.ArtDetails.CreateHttpClientDetails() - reqURL, err := utils.BuildArtifactoryUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil) + reqURL, err := clientutils.BuildUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil) if err != nil { return "", err } @@ -55,7 +54,7 @@ func (ss *SecurityService) CreateAPIKey() (string, error) { func (ss *SecurityService) RegenerateAPIKey() (string, error) { httpClientDetails := ss.ArtDetails.CreateHttpClientDetails() - reqURL, err := utils.BuildArtifactoryUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil) + reqURL, err := clientutils.BuildUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil) if err != nil { return "", err } @@ -75,7 +74,7 @@ func (ss *SecurityService) RegenerateAPIKey() (string, error) { // Returns empty string if API Key wasn't generated. func (ss *SecurityService) GetAPIKey() (string, error) { httpClientDetails := ss.ArtDetails.CreateHttpClientDetails() - reqURL, err := utils.BuildArtifactoryUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil) + reqURL, err := clientutils.BuildUrl(ss.ArtDetails.GetUrl(), APIKeyPath, nil) if err != nil { return "", err } diff --git a/artifactory/services/storage.go b/artifactory/services/storage.go index 97489e0fe..51c4453c8 100644 --- a/artifactory/services/storage.go +++ b/artifactory/services/storage.go @@ -2,14 +2,16 @@ package services import ( "encoding/json" + "net/http" + "path" + "strconv" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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" "github.com/jfrog/jfrog-client-go/utils/log" - "net/http" - "path" - "strconv" ) type StorageService struct { @@ -34,7 +36,7 @@ func (s *StorageService) GetJfrogHttpClient() *jfroghttpclient.JfrogHttpClient { func (s *StorageService) FolderInfo(relativePath string) (*utils.FolderInfo, error) { client := s.GetJfrogHttpClient() restAPI := path.Join(StorageRestApi, path.Clean(relativePath)) - folderUrl, err := utils.BuildArtifactoryUrl(s.GetArtifactoryDetails().GetUrl(), restAPI, make(map[string]string)) + folderUrl, err := clientutils.BuildUrl(s.GetArtifactoryDetails().GetUrl(), restAPI, make(map[string]string)) if err != nil { return nil, err } @@ -69,7 +71,7 @@ func (s *StorageService) FileList(relativePath string, optionalParams utils.File params["depth"] = strconv.Itoa(optionalParams.Depth) } - folderUrl, err := utils.BuildArtifactoryUrl(s.GetArtifactoryDetails().GetUrl(), restAPI, params) + folderUrl, err := clientutils.BuildUrl(s.GetArtifactoryDetails().GetUrl(), restAPI, params) if err != nil { return nil, err } diff --git a/artifactory/services/upload.go b/artifactory/services/upload.go index 118267609..0abf34585 100644 --- a/artifactory/services/upload.go +++ b/artifactory/services/upload.go @@ -4,6 +4,16 @@ import ( "archive/zip" "errors" "fmt" + "io" + "net/http" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "github.com/jfrog/build-info-go/entities" biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/gofrog/parallel" @@ -18,15 +28,6 @@ import ( "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/io/httputils" "github.com/jfrog/jfrog-client-go/utils/log" - "io" - "net/http" - "os" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" - "sync" ) type UploadService struct { @@ -742,7 +743,7 @@ func (us *UploadService) postUpload(uploadResult *utils.Result, threadId int, ar } func (us *UploadService) createFolderInArtifactory(artifact UploadData) error { - url, err := utils.BuildArtifactoryUrl(us.ArtDetails.GetUrl(), artifact.Artifact.TargetPath, make(map[string]string)) + url, err := clientutils.BuildUrl(us.ArtDetails.GetUrl(), artifact.Artifact.TargetPath, make(map[string]string)) url = clientutils.AddTrailingSlashIfNeeded(url) if err != nil { return err @@ -912,7 +913,7 @@ func (us *UploadService) addFileToZip(artifact *clientutils.Artifact, progressPr } func buildUploadUrls(artifactoryUrl, targetPath, buildProps, debianConfig string, targetProps *utils.Properties) (targetUrlWithProps string, err error) { - targetUrl, err := utils.BuildArtifactoryUrl(artifactoryUrl, targetPath, make(map[string]string)) + targetUrl, err := clientutils.BuildUrl(artifactoryUrl, targetPath, make(map[string]string)) if err != nil { return } diff --git a/artifactory/services/utils/artifactoryutils.go b/artifactory/services/utils/artifactoryutils.go index 9c51b8188..f89c3805d 100644 --- a/artifactory/services/utils/artifactoryutils.go +++ b/artifactory/services/utils/artifactoryutils.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "net/http" - "net/url" "path" "strings" "sync" @@ -97,26 +96,6 @@ func AddHeader(headerName, headerValue string, headers *map[string]string) { (*headers)[headerName] = headerValue } -// Builds a URL for Artifactory requests. -// Pay attention: semicolons are escaped! -func BuildArtifactoryUrl(baseUrl, path string, params map[string]string) (string, error) { - u := url.URL{Path: path} - parsedUrl, err := url.Parse(baseUrl + u.String()) - err = errorutils.CheckError(err) - if err != nil { - return "", err - } - q := parsedUrl.Query() - for k, v := range params { - q.Set(k, v) - } - parsedUrl.RawQuery = q.Encode() - - // Semicolons are reserved as separators in some Artifactory APIs, so they'd better be encoded when used for other purposes - encodedUrl := strings.ReplaceAll(parsedUrl.String(), ";", url.QueryEscape(";")) - return encodedUrl, nil -} - func IsWildcardPattern(pattern string) bool { return strings.Contains(pattern, "*") || strings.HasSuffix(pattern, "/") || !strings.Contains(pattern, "/") } @@ -580,7 +559,7 @@ func GetBuildInfo(buildName, buildNumber, projectKey string, flags CommonConf) ( queryParams["project"] = projectKey } - requestFullUrl, err := BuildArtifactoryUrl(flags.GetArtifactoryDetails().GetUrl(), restApi, queryParams) + requestFullUrl, err := utils.BuildUrl(flags.GetArtifactoryDetails().GetUrl(), restApi, queryParams) if err != nil { return nil, false, err } diff --git a/artifactory/services/xrayscan.go b/artifactory/services/xrayscan.go index b2e6a897f..99542adbe 100644 --- a/artifactory/services/xrayscan.go +++ b/artifactory/services/xrayscan.go @@ -38,7 +38,7 @@ func NewXrayScanService(client *jfroghttpclient.JfrogHttpClient) *XrayScanServic // Deprecated legacy scan build. The new build scan command is in "/xray/commands/scan/buildscan" func (ps *XrayScanService) ScanBuild(scanParams XrayScanParams) ([]byte, error) { url := ps.ArtDetails.GetUrl() - requestFullUrl, err := utils.BuildArtifactoryUrl(url, apiUri, make(map[string]string)) + requestFullUrl, err := clientutils.BuildUrl(url, apiUri, make(map[string]string)) if err != nil { return []byte{}, err } diff --git a/artifactory/usage/reportusage.go b/artifactory/usage/reportusage.go index 7c9b5882f..4f0839369 100644 --- a/artifactory/usage/reportusage.go +++ b/artifactory/usage/reportusage.go @@ -3,17 +3,17 @@ package usage import ( "encoding/json" "errors" - "fmt" - versionutil "github.com/jfrog/gofrog/version" + "net/http" + "github.com/jfrog/jfrog-client-go/artifactory" "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/io/httputils" "github.com/jfrog/jfrog-client-go/utils/log" - "net/http" ) const minArtifactoryVersion = "6.9.0" -const ReportUsagePrefix = "Usage Report: " type ReportUsageAttribute struct { AttributeName string @@ -24,58 +24,81 @@ func (rua *ReportUsageAttribute) isEmpty() bool { return rua.AttributeName == "" } -func SendReportUsage(productId, commandName string, serviceManager artifactory.ArtifactoryServicesManager, attributes ...ReportUsageAttribute) error { +func validateAndGetUsageServerInfo(serviceManager artifactory.ArtifactoryServicesManager) (url string, clientDetails httputils.HttpClientDetails, err error) { config := serviceManager.GetConfig() if config == nil { - return errorutils.CheckErrorf(ReportUsagePrefix + "Expected full config, but no configuration exists.") + err = errorutils.CheckErrorf("Expected full config, but no configuration exists.") + return } rtDetails := config.GetServiceDetails() if rtDetails == nil { - return errorutils.CheckErrorf(ReportUsagePrefix + "Artifactory details not configured.") + err = errorutils.CheckErrorf("Artifactory details not configured.") + return } - url, err := utils.BuildArtifactoryUrl(rtDetails.GetUrl(), "api/system/usage", make(map[string]string)) - if err != nil { - return errors.New(ReportUsagePrefix + err.Error()) - } - clientDetails := rtDetails.CreateHttpClientDetails() // Check Artifactory version artifactoryVersion, err := rtDetails.GetVersion() if err != nil { - return errors.New(ReportUsagePrefix + "Couldn't get Artifactory version. Error: " + err.Error()) + err = errors.New("Couldn't get Artifactory version. Error: " + err.Error()) + return } - if !isVersionCompatible(artifactoryVersion) { - log.Debug(fmt.Sprintf(ReportUsagePrefix+"Expected Artifactory version %s or above, got %s", minArtifactoryVersion, artifactoryVersion)) - return nil + if e := clientutils.ValidateMinimumVersion(clientutils.Artifactory, artifactoryVersion, minArtifactoryVersion); e != nil { + log.Debug("Usage Report:", e.Error()) + return } - - bodyContent, err := reportUsageToJson(productId, commandName, attributes...) + url, err = clientutils.BuildUrl(rtDetails.GetUrl(), "api/system/usage", make(map[string]string)) if err != nil { - return errors.New(ReportUsagePrefix + err.Error()) + err = errors.New(err.Error()) + return } + clientDetails = rtDetails.CreateHttpClientDetails() + return +} + +func sendReport(url string, serviceManager artifactory.ArtifactoryServicesManager, clientDetails httputils.HttpClientDetails, bodyContent []byte) error { utils.AddHeader("Content-Type", "application/json", &clientDetails.Headers) resp, body, err := serviceManager.Client().SendPost(url, bodyContent, &clientDetails) if err != nil { - return errors.New(ReportUsagePrefix + "Couldn't send usage info. Error: " + err.Error()) + return errors.New("Couldn't send usage info. Error: " + err.Error()) } - err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK, http.StatusAccepted) if err != nil { return err } - - log.Debug(ReportUsagePrefix+"Usage info sent successfully.", "Artifactory response:", resp.Status) return nil } -// Returns an error if the Artifactory version is not compatible -func isVersionCompatible(artifactoryVersion string) bool { - // API exists from Artifactory version 6.9.0 and above: - version := versionutil.NewVersion(artifactoryVersion) - return version.AtLeast(minArtifactoryVersion) +func ReportUsageToArtifactory(productId string, serviceManager artifactory.ArtifactoryServicesManager, features ...Feature) error { + url, clientDetails, err := validateAndGetUsageServerInfo(serviceManager) + if err != nil || url == "" { + return err + } + bodyContent, err := usageFeaturesToJson(productId, features...) + if err != nil { + return err + } + return sendReport(url, serviceManager, clientDetails, bodyContent) +} + +func SendReportUsage(productId, commandName string, serviceManager artifactory.ArtifactoryServicesManager, attributes ...ReportUsageAttribute) error { + url, clientDetails, err := validateAndGetUsageServerInfo(serviceManager) + if err != nil || url == "" { + return err + } + bodyContent, err := reportUsageToJson(productId, commandName, attributes...) + if err != nil { + return err + } + return sendReport(url, serviceManager, clientDetails, bodyContent) +} + +func usageFeaturesToJson(productId string, features ...Feature) ([]byte, error) { + params := reportUsageParams{ProductId: productId, Features: features} + bodyContent, err := json.Marshal(params) + return bodyContent, errorutils.CheckError(err) } func reportUsageToJson(productId, commandName string, attributes ...ReportUsageAttribute) ([]byte, error) { - featureInfo := feature{FeatureId: commandName} + featureInfo := Feature{FeatureId: commandName} if len(attributes) > 0 { featureInfo.Attributes = make(map[string]string, len(attributes)) for _, attribute := range attributes { @@ -84,17 +107,16 @@ func reportUsageToJson(productId, commandName string, attributes ...ReportUsageA } } } - params := reportUsageParams{ProductId: productId, Features: []feature{featureInfo}} - bodyContent, err := json.Marshal(params) - return bodyContent, errorutils.CheckError(err) + return usageFeaturesToJson(productId, featureInfo) } type reportUsageParams struct { ProductId string `json:"productId"` - Features []feature `json:"features,omitempty"` + Features []Feature `json:"features,omitempty"` } -type feature struct { +type Feature struct { FeatureId string `json:"featureId,omitempty"` Attributes map[string]string `json:"attributes,omitempty"` + ClientId string `json:"uniqueClientId,omitempty"` } diff --git a/artifactory/usage/reportusage_test.go b/artifactory/usage/reportusage_test.go index 211af4a3a..acc74cfad 100644 --- a/artifactory/usage/reportusage_test.go +++ b/artifactory/usage/reportusage_test.go @@ -24,9 +24,11 @@ func TestIsVersionCompatible(t *testing.T) { } for _, test := range tests { t.Run(test.artifactoryVersion, func(t *testing.T) { - result := isVersionCompatible(test.artifactoryVersion) - if test.expectedResult != result { - t.Error(fmt.Errorf("expected %t, got %t", test.expectedResult, result)) + err := utils.ValidateMinimumVersion(utils.Xray, test.artifactoryVersion, minArtifactoryVersion) + if test.expectedResult { + assert.NoError(t, err) + } else { + assert.ErrorContains(t, err, fmt.Sprintf(utils.MinimumVersionMsg, utils.Xray, test.artifactoryVersion, minArtifactoryVersion)) } }) } diff --git a/lifecycle/services/delete.go b/lifecycle/services/delete.go index 0910712ee..1596c949d 100644 --- a/lifecycle/services/delete.go +++ b/lifecycle/services/delete.go @@ -1,7 +1,7 @@ package services import ( - rtUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "net/http" "path" @@ -12,7 +12,7 @@ func (rbs *ReleaseBundlesService) DeleteReleaseBundle(rbDetails ReleaseBundleDet queryParams := getProjectQueryParam(params.ProjectKey) queryParams[async] = strconv.FormatBool(params.Async) restApi := path.Join(releaseBundleBaseApi, records, rbDetails.ReleaseBundleName, rbDetails.ReleaseBundleVersion) - requestFullUrl, err := rtUtils.BuildArtifactoryUrl(rbs.GetLifecycleDetails().GetUrl(), restApi, queryParams) + requestFullUrl, err := utils.BuildUrl(rbs.GetLifecycleDetails().GetUrl(), restApi, queryParams) if err != nil { return err } diff --git a/lifecycle/services/operation.go b/lifecycle/services/operation.go index 539c47e3f..3d44f6db6 100644 --- a/lifecycle/services/operation.go +++ b/lifecycle/services/operation.go @@ -5,6 +5,7 @@ import ( rtUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "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/utils/log" "net/http" @@ -36,7 +37,7 @@ type ReleaseBundleOperation interface { func (rbs *ReleaseBundlesService) doOperation(operation ReleaseBundleOperation) ([]byte, error) { queryParams := getProjectQueryParam(operation.getOperationParams().ProjectKey) queryParams[async] = strconv.FormatBool(operation.getOperationParams().Async) - requestFullUrl, err := rtUtils.BuildArtifactoryUrl(rbs.GetLifecycleDetails().GetUrl(), operation.getOperationRestApi(), queryParams) + requestFullUrl, err := utils.BuildUrl(rbs.GetLifecycleDetails().GetUrl(), operation.getOperationRestApi(), queryParams) if err != nil { return []byte{}, err } diff --git a/lifecycle/services/status.go b/lifecycle/services/status.go index ba696f3ce..35364a79a 100644 --- a/lifecycle/services/status.go +++ b/lifecycle/services/status.go @@ -3,7 +3,7 @@ package services import ( "encoding/json" "fmt" - rtUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/httputils" "net/http" @@ -52,7 +52,7 @@ func (rbs *ReleaseBundlesService) getReleaseBundleOperationStatus(restApi string } func (rbs *ReleaseBundlesService) getReleaseBundleStatus(restApi string, projectKey string) (statusResp ReleaseBundleStatusResponse, body []byte, err error) { - requestFullUrl, err := rtUtils.BuildArtifactoryUrl(rbs.GetLifecycleDetails().GetUrl(), restApi, getProjectQueryParam(projectKey)) + requestFullUrl, err := utils.BuildUrl(rbs.GetLifecycleDetails().GetUrl(), restApi, getProjectQueryParam(projectKey)) if err != nil { return } diff --git a/tests/artifactoryupload_test.go b/tests/artifactoryupload_test.go index 1795cba13..c8df9435b 100644 --- a/tests/artifactoryupload_test.go +++ b/tests/artifactoryupload_test.go @@ -368,7 +368,7 @@ func archiveUpload(t *testing.T) { } // Check for timezone offset for each file in the zip - r, err := zip.OpenReader(downloadTarget+"test.zip") + r, err := zip.OpenReader(downloadTarget + "test.zip") assert.NoError(t, err) defer func() { assert.NoError(t, r.Close()) }() _, sysTimezoneOffset := time.Now().Zone() diff --git a/utils/usage/reportusage.go b/utils/usage/reportusage.go new file mode 100644 index 000000000..765486f8c --- /dev/null +++ b/utils/usage/reportusage.go @@ -0,0 +1,61 @@ +package usage + +import ( + "encoding/json" + "errors" + "net/http" + + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/http/httpclient" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/io/httputils" +) + +const ( + ecosystemUsageApiPath = "https://usage-ecosystem.jfrog.io/api/usage/report" +) + +type ReportEcosystemUsageData struct { + ProductId string `json:"productId"` + AccountId string `json:"accountId"` + ClientId string `json:"clientId,omitempty"` + Features []string `json:"features"` +} + +func SendEcosystemUsageReports(reports ...ReportEcosystemUsageData) error { + if len(reports) == 0 { + return errorutils.CheckErrorf("Nothing to send.") + } + bodyContent, err := json.Marshal(reports) + if err != nil { + return errorutils.CheckError(err) + } + resp, body, err := sendRequestToEcosystemService(bodyContent) + if err != nil { + return errors.New("Couldn't send usage info to Ecosystem. Error: " + err.Error()) + } + return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK, http.StatusAccepted) +} + +func CreateUsageData(productId, accountId, clientId string, features ...string) (reportInfo ReportEcosystemUsageData, err error) { + reportInfo = ReportEcosystemUsageData{ProductId: productId, AccountId: accountId, ClientId: clientId, Features: []string{}} + for _, feature := range features { + if feature != "" { + reportInfo.Features = append(reportInfo.Features, feature) + } + } + if len(reportInfo.Features) == 0 { + err = errorutils.CheckErrorf("expected at least one feature to report usage on") + } + return +} + +func sendRequestToEcosystemService(content []byte) (resp *http.Response, respBody []byte, err error) { + var client *httpclient.HttpClient + if client, err = httpclient.ClientBuilder().Build(); err != nil { + return + } + details := httputils.HttpClientDetails{} + utils.AddHeader("Content-Type", "application/json", &details.Headers) + return client.SendPost(ecosystemUsageApiPath, content, details, "Ecosystem-Usage") +} diff --git a/utils/usage/reportusage_test.go b/utils/usage/reportusage_test.go new file mode 100644 index 000000000..f0a71b4c6 --- /dev/null +++ b/utils/usage/reportusage_test.go @@ -0,0 +1,64 @@ +package usage + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +type reportUsageTestCase struct { + ProductId string + AccountId string + ClientId string + Features []string + JsonPattern string +} + +var cases = []reportUsageTestCase{ + {"jfrog-cli-go", "platform.jfrog.io", "", []string{}, `[{"productId":"%s","accountId":"%s","features":[]}]`}, + {"jfrog-cli-go", "platform.jfrog.io", "", []string{"generic_audit"}, `[{"productId":"%s","accountId":"%s","features":["%s"]}]`}, + {"frogbot", "platform.jfrog.io", "repo1", []string{"scan_pull_request"}, `[{"productId":"%s","accountId":"%s","clientId":"%s","features":["%s"]}]`}, + {"frogbot", "platform.jfrog.io", "repo1", []string{"scan_pull_request", "npm-dep"}, `[{"productId":"%s","accountId":"%s","clientId":"%s","features":["%s","%s"]}]`}, +} + +func TestEcosystemReportUsageToJson(t *testing.T) { + // Create the expected json + for _, test := range cases { + // Create the expected json + var expectedResult string + switch len(test.Features) { + case 1: + if test.ClientId != "" { + expectedResult = fmt.Sprintf(test.JsonPattern, test.ProductId, test.AccountId, test.ClientId, test.Features[0]) + } else { + expectedResult = fmt.Sprintf(test.JsonPattern, test.ProductId, test.AccountId, test.Features[0]) + } + case 2: + if test.ClientId != "" { + expectedResult = fmt.Sprintf(test.JsonPattern, test.ProductId, test.AccountId, test.ClientId, test.Features[0], test.Features[1]) + } else { + expectedResult = fmt.Sprintf(test.JsonPattern, test.ProductId, test.AccountId, test.Features[0], test.Features[1]) + } + default: + if test.ClientId != "" { + expectedResult = fmt.Sprintf(test.JsonPattern, test.ProductId, test.AccountId, test.ClientId) + } else { + expectedResult = fmt.Sprintf(test.JsonPattern, test.ProductId, test.AccountId) + } + } + // Run test + t.Run("Features: "+strings.Join(test.Features, ","), func(t *testing.T) { + if data, err := CreateUsageData(test.ProductId, test.AccountId, test.ClientId, test.Features...); len(test.Features) > 0 { + assert.NoError(t, err) + body, err := json.Marshal([]ReportEcosystemUsageData{data}) + assert.NoError(t, err) + assert.Equal(t, expectedResult, string(body)) + } else { + assert.ErrorContains(t, err, "expected at least one feature to report usage on") + } + }) + } +} diff --git a/utils/utils.go b/utils/utils.go index af9281426..152eb8a6a 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,6 +16,7 @@ import ( "github.com/jfrog/build-info-go/entities" "github.com/jfrog/gofrog/stringutils" + "github.com/jfrog/gofrog/version" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" @@ -29,6 +30,18 @@ const ( Version = "1.31.5" ) +type MinVersionProduct string + +const ( + Artifactory MinVersionProduct = "JFrog Artifactory" + Xray MinVersionProduct = "JFrog Xray" + DataTransfer MinVersionProduct = "Data Transfer" + DockerApi MinVersionProduct = "Docker API" + Projects MinVersionProduct = "JFrog Projects" + + MinimumVersionMsg = "You are using %s version %s, while this operation requires version %s or higher." +) + // In order to limit the number of items loaded from a reader into the memory, we use a buffers with this size limit. var ( MaxBufferSize = 50000 @@ -52,6 +65,13 @@ func getDefaultUserAgent() string { return fmt.Sprintf("%s/%s", Agent, getVersion()) } +func ValidateMinimumVersion(product MinVersionProduct, currentVersion, minimumVersion string) error { + if !version.NewVersion(currentVersion).AtLeast(minimumVersion) { + return errorutils.CheckErrorf(MinimumVersionMsg, product, currentVersion, minimumVersion) + } + return nil +} + // Get the local root path, from which to start collecting artifacts to be used for: // 1. Uploaded to Artifactory, // 2. Adding to the local build-info, to be later published to Artifactory. @@ -210,6 +230,25 @@ func cleanPath(path string) string { return path } +// Builds a URL for Artifactory/Xray requests. +// Pay attention: semicolons are escaped! +func BuildUrl(baseUrl, path string, params map[string]string) (string, error) { + u := url.URL{Path: path} + parsedUrl, err := url.Parse(baseUrl + u.String()) + if err = errorutils.CheckError(err); err != nil { + return "", err + } + q := parsedUrl.Query() + for k, v := range params { + q.Set(k, v) + } + parsedUrl.RawQuery = q.Encode() + + // Semicolons are reserved as separators in some Artifactory APIs, so they'd better be encoded when used for other purposes + encodedUrl := strings.ReplaceAll(parsedUrl.String(), ";", url.QueryEscape(";")) + return encodedUrl, nil +} + // BuildTargetPath Replaces matched regular expression from path to corresponding placeholder {i} at target. // Example 1: // diff --git a/xray/usage/reportusage.go b/xray/usage/reportusage.go new file mode 100644 index 000000000..365486c91 --- /dev/null +++ b/xray/usage/reportusage.go @@ -0,0 +1,90 @@ +package usage + +import ( + "encoding/json" + "errors" + "net/http" + + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + clientutils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/jfrog/jfrog-client-go/xray" +) + +const ( + minXrayVersion = "3.81.4" + xrayUsageApiPath = "api/v1/usage/events/send" +) + +type ReportUsageAttribute struct { + AttributeName string + AttributeValue string +} + +func (rua *ReportUsageAttribute) isEmpty() bool { + return rua.AttributeName == "" +} + +type ReportXrayEventData struct { + Attributes map[string]string `json:"data,omitempty"` + ProductId string `json:"product_name"` + EventId string `json:"event_name"` + Origin string `json:"origin,omitempty"` +} + +func SendXrayUsageEvents(serviceManager xray.XrayServicesManager, events ...ReportXrayEventData) error { + if len(events) == 0 { + return errorutils.CheckErrorf("Nothing to send.") + } + config := serviceManager.Config() + if config == nil { + return errorutils.CheckErrorf("Expected full config, but no configuration exists.") + } + xrDetails := config.GetServiceDetails() + if xrDetails == nil { + return errorutils.CheckErrorf("Xray details not configured.") + } + xrayVersion, err := xrDetails.GetVersion() + if err != nil { + return errors.New("Couldn't get Xray version. Error: " + err.Error()) + } + if e := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, minXrayVersion); e != nil { + log.Debug("Usage Report:", e.Error()) + return nil + } + url, err := clientutils.BuildUrl(xrDetails.GetUrl(), xrayUsageApiPath, make(map[string]string)) + if err != nil { + return errors.New(err.Error()) + } + clientDetails := xrDetails.CreateHttpClientDetails() + + bodyContent, err := json.Marshal(events) + if errorutils.CheckError(err) != nil { + return errors.New(err.Error()) + } + utils.AddHeader("Content-Type", "application/json", &clientDetails.Headers) + resp, body, err := serviceManager.Client().SendPost(url, bodyContent, &clientDetails) + if err != nil { + return errors.New("Couldn't send usage info. Error: " + err.Error()) + } + return errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK, http.StatusAccepted) +} + +func CreateUsageEvent(productId, featureId string, additionalAttributes ...ReportUsageAttribute) (event ReportXrayEventData) { + event = ReportXrayEventData{ProductId: productId, EventId: GetExpectedXrayEventName(productId, featureId), Origin: "API_CLI"} + if len(additionalAttributes) == 0 { + return + } + event.Attributes = make(map[string]string, len(additionalAttributes)) + for _, attribute := range additionalAttributes { + if !attribute.isEmpty() { + event.Attributes[attribute.AttributeName] = attribute.AttributeValue + } + } + return +} + +func GetExpectedXrayEventName(productId, commandName string) string { + return "server_" + productId + "_" + commandName +} diff --git a/xray/usage/reportusage_test.go b/xray/usage/reportusage_test.go new file mode 100644 index 000000000..fb1d269cc --- /dev/null +++ b/xray/usage/reportusage_test.go @@ -0,0 +1,70 @@ +package usage + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/jfrog/jfrog-client-go/utils" + "github.com/stretchr/testify/assert" +) + +func TestIsXrayVersionCompatible(t *testing.T) { + tests := []struct { + xrayVersion string + compatible bool + }{ + {"1.2.0", false}, + {"2.9.0", false}, + {"2.0.0", false}, + {"3.80.3", false}, + {"3.81.4", true}, + {utils.Development, true}, + {"3.83.2", true}, + {"4.15.2", true}, + } + for _, test := range tests { + t.Run(test.xrayVersion, func(t *testing.T) { + err := utils.ValidateMinimumVersion(utils.Xray, test.xrayVersion, minXrayVersion) + if test.compatible { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} + +type reportUsageTestCase struct { + productId string + EventId string + Attributes []ReportUsageAttribute + jsonPattern string +} + +var reportCases = []reportUsageTestCase{ + {"jfrog-cli-go", "generic_audit", []ReportUsageAttribute{}, `[{"product_name":"%s","event_name":"%s","origin":"API_CLI"}]`}, + {"frogbot", "scan_pull_request", []ReportUsageAttribute{{AttributeName: "clientId", AttributeValue: "repo1"}}, `[{"data":{"%s":"%s"},"product_name":"%s","event_name":"%s","origin":"API_CLI"}]`}, + {"jfrog-idea-plugin", "ci", []ReportUsageAttribute{{AttributeName: "buildNumber", AttributeValue: "1023456"}, {AttributeName: "clientId", AttributeValue: "user-hash"}}, `[{"data":{"%s":"%s","%s":"%s"},"product_name":"%s","event_name":"%s","origin":"API_CLI"}]`}, +} + +func TestXrayUsageEventToJson(t *testing.T) { + for _, test := range reportCases { + // Create the expected json + var expectedResult string + switch len(test.Attributes) { + case 1: + expectedResult = fmt.Sprintf(test.jsonPattern, test.Attributes[0].AttributeName, test.Attributes[0].AttributeValue, test.productId, GetExpectedXrayEventName(test.productId, test.EventId)) + case 2: + expectedResult = fmt.Sprintf(test.jsonPattern, test.Attributes[0].AttributeName, test.Attributes[0].AttributeValue, test.Attributes[1].AttributeName, test.Attributes[1].AttributeValue, test.productId, GetExpectedXrayEventName(test.productId, test.EventId)) + default: + expectedResult = fmt.Sprintf(test.jsonPattern, test.productId, GetExpectedXrayEventName(test.productId, test.EventId)) + } + // Run test + t.Run(test.EventId, func(t *testing.T) { + body, err := json.Marshal([]ReportXrayEventData{CreateUsageEvent(test.productId, test.EventId, test.Attributes...)}) + assert.NoError(t, err) + assert.Equal(t, expectedResult, string(body)) + }) + } +}