From 3dc4a48cbe07370053fb9d680e02bf0b003474cb Mon Sep 17 00:00:00 2001 From: Alexei Vainshtein Date: Mon, 26 Nov 2018 22:09:53 +0200 Subject: [PATCH 1/2] Support Artifactory Go API --- artifactory/services/go/go.go | 95 +++++-------------- artifactory/services/go/go_test.go | 41 ++++---- artifactory/services/go/goutils.go | 29 ++++++ artifactory/services/go/publish.go | 27 ++++++ artifactory/services/go/publishmodandzip.go | 65 +++++++++++++ artifactory/services/go/publishwithheaders.go | 49 ++++++++++ .../services/go/publishwithmatrixparams.go | 47 +++++++++ 7 files changed, 263 insertions(+), 90 deletions(-) create mode 100644 artifactory/services/go/goutils.go create mode 100644 artifactory/services/go/publish.go create mode 100644 artifactory/services/go/publishmodandzip.go create mode 100644 artifactory/services/go/publishwithheaders.go create mode 100644 artifactory/services/go/publishwithmatrixparams.go diff --git a/artifactory/services/go/go.go b/artifactory/services/go/go.go index d862b186a..17786f9e8 100644 --- a/artifactory/services/go/go.go +++ b/artifactory/services/go/go.go @@ -1,13 +1,11 @@ package _go import ( - "encoding/base64" + "errors" + "fmt" "github.com/jfrog/jfrog-client-go/artifactory/auth" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" - "github.com/jfrog/jfrog-client-go/errors/httperrors" "github.com/jfrog/jfrog-client-go/httpclient" - "github.com/jfrog/jfrog-client-go/utils/version" - "strings" + "github.com/jfrog/jfrog-client-go/utils/errorutils" ) type GoService struct { @@ -28,68 +26,21 @@ func (gs *GoService) SetArtDetails(artDetails auth.ArtifactoryDetails) { } func (gs *GoService) PublishPackage(params GoParams) error { - url, err := utils.BuildArtifactoryUrl(gs.ArtDetails.GetUrl(), "api/go/"+params.GetTargetRepo(), make(map[string]string)) - clientDetails := gs.ArtDetails.CreateHttpClientDetails() - - utils.AddHeader("X-GO-MODULE-VERSION", params.GetVersion(), &clientDetails.Headers) - utils.AddHeader("X-GO-MODULE-CONTENT", base64.StdEncoding.EncodeToString(params.GetModContent()), &clientDetails.Headers) artifactoryVersion, err := gs.ArtDetails.GetVersion() if err != nil { return err } - if !shouldUseHeaders(artifactoryVersion) { - createUrlPath(params, &url) - } else { - addPropertiesHeaders(params.GetProps(), &clientDetails.Headers) - } - - resp, body, err := gs.client.UploadFile(params.GetZipPath(), url, clientDetails, 0) - if err != nil { - return err - } - return httperrors.CheckResponseStatus(resp, body, 201) -} - -// This is needed when using Artifactory older then 6.5.0 -func addPropertiesHeaders(props string, headers *map[string]string) error { - properties, err := utils.ParseProperties(props, utils.JoinCommas) - if err != nil { - return err - } - headersMap := properties.ToHeadersMap() - for k, v := range headersMap { - utils.AddHeader("X-ARTIFACTORY-PROPERTY-"+k, v, headers) + publisher := GetCompatiblePublisher(artifactoryVersion) + if publisher == nil { + return errorutils.CheckError(errors.New(fmt.Sprintf("Unsupported version of Artifactory: %s", artifactoryVersion))) } - return nil -} -func createUrlPath(params GoParams, url *string) error { - *url = strings.Join([]string{*url, params.GetModuleId(), "@v", params.GetVersion() + ".zip"}, "/") - properties, err := utils.ParseProperties(params.GetProps(), utils.JoinCommas) - if err != nil { - return err - } - *url = strings.Join([]string{*url, properties.ToEncodedString()}, ";") - if strings.HasSuffix(*url, ";") { - tempUrl := *url - tempUrl = tempUrl[:len(tempUrl)-1] - *url = tempUrl - } - return nil -} - -// Returns true if needed to use properties as header (Artifactory version between 6.2.0 and 6.5.0) -// or false if need to use matrix params (Artifactory version 6.5.0 and above). -func shouldUseHeaders(artifactoryVersion string) bool { - propertiesApi := "6.5.0" - if version.Compare(artifactoryVersion, propertiesApi) < 0 && artifactoryVersion != "development" { - return true - } - return false + return publisher.PublishPackage(params, gs.client, gs.ArtDetails) } type GoParams struct { ZipPath string + ModPath string ModContent []byte Version string Props string @@ -97,30 +48,34 @@ type GoParams struct { ModuleId string } -func (gpi *GoParams) GetZipPath() string { - return gpi.ZipPath +func (gp *GoParams) GetZipPath() string { + return gp.ZipPath } -func (gpi *GoParams) GetModContent() []byte { - return gpi.ModContent +func (gp *GoParams) GetModContent() []byte { + return gp.ModContent } -func (gpi *GoParams) GetVersion() string { - return gpi.Version +func (gp *GoParams) GetVersion() string { + return gp.Version } -func (gpi *GoParams) GetProps() string { - return gpi.Props +func (gp *GoParams) GetProps() string { + return gp.Props } -func (gpi *GoParams) GetTargetRepo() string { - return gpi.TargetRepo +func (gp *GoParams) GetTargetRepo() string { + return gp.TargetRepo } -func (gpi *GoParams) GetModuleId() string { - return gpi.ModuleId +func (gp *GoParams) GetModuleId() string { + return gp.ModuleId +} + +func (gp *GoParams) GetModPath() string { + return gp.ModPath } func NewGoParams() GoParams { return GoParams{} -} +} \ No newline at end of file diff --git a/artifactory/services/go/go_test.go b/artifactory/services/go/go_test.go index ef7a9ab6d..5c8575099 100644 --- a/artifactory/services/go/go_test.go +++ b/artifactory/services/go/go_test.go @@ -1,6 +1,7 @@ package _go import ( + "reflect" "strings" "testing" ) @@ -9,16 +10,20 @@ func TestCreateUrlPath(t *testing.T) { tests := []struct { name string - params GoParams + extension string + moduleId string + version string + props string url string expectedUrl string }{ - {"withBuildProperties", GoParams{ZipPath: "path/to/zip/file", Version: "v1.1.1", TargetRepo: "ArtiRepo", ModuleId: "github.com/jfrog/test", Props: "build.name=a;build.number=1"}, "http://test.url/", "http://test.url//github.com/jfrog/test/@v/v1.1.1.zip;build.name=a;build.number=1"}, - {"withoutBuildProperties", GoParams{ZipPath: "path/to/zip/file", Version: "v1.1.1", TargetRepo: "ArtiRepo", ModuleId: "github.com/jfrog/test"}, "http://test.url/", "http://test.url//github.com/jfrog/test/@v/v1.1.1.zip"}, + {"withBuildProperties", ".zip", "github.com/jfrog/test", "v1.1.1", "build.name=a;build.number=1", "http://test.url/", "http://test.url//github.com/jfrog/test/@v/v1.1.1.zip;build.name=a;build.number=1"}, + {"withoutBuildProperties", ".zip", "github.com/jfrog/test", "v1.1.1", "", "http://test.url/", "http://test.url//github.com/jfrog/test/@v/v1.1.1.zip"}, + {"withoutBuildPropertiesModExtension", ".mod", "github.com/jfrog/test", "v1.1.1", "", "http://test.url/", "http://test.url//github.com/jfrog/test/@v/v1.1.1.mod"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - createUrlPath(test.params, &test.url) + createUrlPath(test.moduleId, test.version, test.props, test.extension, &test.url) if !strings.EqualFold(test.url, test.expectedUrl) { t.Error("Expected:", test.expectedUrl, "Got:", test.url) } @@ -29,26 +34,22 @@ func TestCreateUrlPath(t *testing.T) { func TestShouldUseHeaders(t *testing.T) { tests := []struct { artifactoryVersion string - expectedResult bool + expectedResult string }{ - {"6.5.0", false}, - {"6.2.0", true}, - {"5.9.0", true}, - {"6.0.0", true}, - {"6.6.0", false}, - {"development", false}, - {"6.10.2", false}, + {"6.5.0", "*_go.publishWithMatrixParams"}, + {"6.2.0", "*_go.publishWithHeader"}, + {"5.9.0", "*_go.publishWithHeader"}, + {"6.0.0", "*_go.publishWithHeader"}, + {"6.6.0", "*_go.publishWithoutApi"}, + {"development", "*_go.publishWithoutApi"}, + {"6.10.2", "*_go.publishWithoutApi"}, } for _, test := range tests { t.Run(test.artifactoryVersion, func(t *testing.T) { - result := shouldUseHeaders(test.artifactoryVersion) - if result && !test.expectedResult { - t.Error("Expected:", test.expectedResult, "Got:", result) - } - - if !result && test.expectedResult { - t.Error("Expected:", test.expectedResult, "Got:", result) + result := GetCompatiblePublisher(test.artifactoryVersion) + if reflect.TypeOf(result).String() != test.expectedResult { + t.Error("Expected:", test.expectedResult, "Got:", reflect.TypeOf(result).String()) } }) } -} +} \ No newline at end of file diff --git a/artifactory/services/go/goutils.go b/artifactory/services/go/goutils.go new file mode 100644 index 000000000..3958f75df --- /dev/null +++ b/artifactory/services/go/goutils.go @@ -0,0 +1,29 @@ +package _go + +import ( + "encoding/base64" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/utils/io/httputils" + "strings" +) + +func addHeaders(params GoParams, clientDetails *httputils.HttpClientDetails) { + utils.AddHeader("X-GO-MODULE-VERSION", params.GetVersion(), &clientDetails.Headers) + utils.AddHeader("X-GO-MODULE-CONTENT", base64.StdEncoding.EncodeToString(params.GetModContent()), &clientDetails.Headers) +} + +func createUrlPath(moduleId, version, props, extension string, url *string) error { + *url = strings.Join([]string{*url, moduleId, "@v", version + extension}, "/") + properties, err := utils.ParseProperties(props, utils.JoinCommas) + if err != nil { + return err + } + + *url = strings.Join([]string{*url, properties.ToEncodedString()}, ";") + if strings.HasSuffix(*url, ";") { + tempUrl := *url + tempUrl = tempUrl[:len(tempUrl)-1] + *url = tempUrl + } + return nil +} \ No newline at end of file diff --git a/artifactory/services/go/publish.go b/artifactory/services/go/publish.go new file mode 100644 index 000000000..386ee75f9 --- /dev/null +++ b/artifactory/services/go/publish.go @@ -0,0 +1,27 @@ +package _go + +import ( + "github.com/jfrog/jfrog-client-go/artifactory/auth" + "github.com/jfrog/jfrog-client-go/httpclient" +) + +var publishers []PublishGoPackage + +type PublishGoPackage interface { + isCompatible(artifactoryVersion string) bool + PublishPackage(params GoParams, client *httpclient.HttpClient, ArtDetails auth.ArtifactoryDetails) error +} + +func register(publishApi PublishGoPackage) { + publishers = append(publishers, publishApi) +} + +// Returns the compatible publisher to Artifactory +func GetCompatiblePublisher(artifactoryVersion string) PublishGoPackage { + for _, publisher := range publishers { + if publisher.isCompatible(artifactoryVersion) { + return publisher + } + } + return nil +} diff --git a/artifactory/services/go/publishmodandzip.go b/artifactory/services/go/publishmodandzip.go new file mode 100644 index 000000000..60c977098 --- /dev/null +++ b/artifactory/services/go/publishmodandzip.go @@ -0,0 +1,65 @@ +package _go + +import ( + "github.com/jfrog/jfrog-client-go/artifactory/auth" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/errors/httperrors" + "github.com/jfrog/jfrog-client-go/httpclient" + "github.com/jfrog/jfrog-client-go/utils/version" + "net/url" + "strings" +) + +func init() { + register(&publishWithoutApi{}) +} + +// Support for Artifactory 6.6.0 and above API +type publishWithoutApi struct { +} + +func (pwa *publishWithoutApi) isCompatible(artifactoryVersion string) bool { + propertiesApi := "6.6.0" + if version.Compare(artifactoryVersion, propertiesApi) < 0 && artifactoryVersion != "development" { + return false + } + return true +} + +func (pwa *publishWithoutApi) PublishPackage(params GoParams, client *httpclient.HttpClient, ArtDetails auth.ArtifactoryDetails) error { + url, err := utils.BuildArtifactoryUrl(ArtDetails.GetUrl(), params.GetTargetRepo(), make(map[string]string)) + if err != nil { + return err + } + zipUrl := url + moduleId := strings.Split(params.GetModuleId(), ":") + err = createUrlPath(moduleId[0], params.GetVersion(), params.GetProps(), ".zip", &zipUrl) + if err != nil { + return err + } + clientDetails := ArtDetails.CreateHttpClientDetails() + addHeaders(params, &clientDetails) + addGoVersion(params, &zipUrl) + resp, body, err := client.UploadFile(params.GetZipPath(), zipUrl, clientDetails, 0) + if err != nil { + return err + } + err = httperrors.CheckResponseStatus(resp, body, 201) + if err != nil { + return err + } + err = createUrlPath(moduleId[0], params.GetVersion(), params.GetProps(), ".mod", &url) + if err != nil { + return err + } + addGoVersion(params, &url) + resp, body, err = client.UploadFile(params.GetModPath(), url, clientDetails, 0) + if err != nil { + return err + } + return httperrors.CheckResponseStatus(resp, body, 201) +} + +func addGoVersion(params GoParams, urlPath *string) { + *urlPath += ";go.version=" + url.QueryEscape(params.GetVersion()) +} diff --git a/artifactory/services/go/publishwithheaders.go b/artifactory/services/go/publishwithheaders.go new file mode 100644 index 000000000..d3fa2b75d --- /dev/null +++ b/artifactory/services/go/publishwithheaders.go @@ -0,0 +1,49 @@ +package _go + +import ( + "github.com/jfrog/jfrog-client-go/artifactory/auth" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/errors/httperrors" + "github.com/jfrog/jfrog-client-go/httpclient" + "github.com/jfrog/jfrog-client-go/utils/version" +) + +func init() { + register(&publishWithHeader{}) +} + +// Support for Artifactory older then 6.5.0 API +type publishWithHeader struct { +} + +func (pwh *publishWithHeader) isCompatible(artifactoryVersion string) bool { + propertiesApi := "6.5.0" + if version.Compare(artifactoryVersion, propertiesApi) < 0 { + return true + } + return false +} + +func (pwh *publishWithHeader) PublishPackage(params GoParams, client *httpclient.HttpClient, ArtDetails auth.ArtifactoryDetails) error { + url, err := utils.BuildArtifactoryUrl(ArtDetails.GetUrl(), "api/go/"+params.GetTargetRepo(), make(map[string]string)) + clientDetails := ArtDetails.CreateHttpClientDetails() + addHeaders(params, &clientDetails) + addPropertiesHeaders(params.GetProps(), &clientDetails.Headers) + resp, body, err := client.UploadFile(params.GetZipPath(), url, clientDetails, 0) + if err != nil { + return err + } + return httperrors.CheckResponseStatus(resp, body, 201) +} + +func addPropertiesHeaders(props string, headers *map[string]string) error { + properties, err := utils.ParseProperties(props, utils.JoinCommas) + if err != nil { + return err + } + headersMap := properties.ToHeadersMap() + for k, v := range headersMap { + utils.AddHeader("X-ARTIFACTORY-PROPERTY-"+k, v, headers) + } + return nil +} diff --git a/artifactory/services/go/publishwithmatrixparams.go b/artifactory/services/go/publishwithmatrixparams.go new file mode 100644 index 000000000..125f7e1ce --- /dev/null +++ b/artifactory/services/go/publishwithmatrixparams.go @@ -0,0 +1,47 @@ +package _go + +import ( + "github.com/jfrog/jfrog-client-go/artifactory/auth" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/jfrog/jfrog-client-go/errors/httperrors" + "github.com/jfrog/jfrog-client-go/httpclient" + "github.com/jfrog/jfrog-client-go/utils/version" +) + +func init() { + register(&publishWithMatrixParams{}) +} + +// Support for Artifactory version between 6.5.0 and 6.6.0 API +type publishWithMatrixParams struct { +} + +func (pwmp *publishWithMatrixParams) isCompatible(artifactoryVersion string) bool { + propertiesApi := "6.5.0" + withoutApi := "6.6.0" + if version.Compare(artifactoryVersion, propertiesApi) < 0 { + return false + } + + if version.Compare(artifactoryVersion, withoutApi) >= 0 { + return false + } + return true +} + +func (pwmp *publishWithMatrixParams) PublishPackage(params GoParams, client *httpclient.HttpClient, ArtDetails auth.ArtifactoryDetails) error { + url, err := utils.BuildArtifactoryUrl(ArtDetails.GetUrl(), "api/go/"+params.GetTargetRepo(), make(map[string]string)) + clientDetails := ArtDetails.CreateHttpClientDetails() + addHeaders(params, &clientDetails) + + err = createUrlPath(params.GetModuleId(), params.GetVersion(), params.GetProps(), ".zip", &url) + if err != nil { + return err + } + + resp, body, err := client.UploadFile(params.GetZipPath(), url, clientDetails, 0) + if err != nil { + return err + } + return httperrors.CheckResponseStatus(resp, body, 201) +} From 1790cc3df8d22217af4bf04fb5799152feda3f3b Mon Sep 17 00:00:00 2001 From: Alexei Vainshtein Date: Tue, 27 Nov 2018 13:15:49 +0200 Subject: [PATCH 2/2] Support Artifactory Go API - no headers needed for new api --- artifactory/services/go/publishmodandzip.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artifactory/services/go/publishmodandzip.go b/artifactory/services/go/publishmodandzip.go index 60c977098..14e31b61d 100644 --- a/artifactory/services/go/publishmodandzip.go +++ b/artifactory/services/go/publishmodandzip.go @@ -38,7 +38,7 @@ func (pwa *publishWithoutApi) PublishPackage(params GoParams, client *httpclient return err } clientDetails := ArtDetails.CreateHttpClientDetails() - addHeaders(params, &clientDetails) + addGoVersion(params, &zipUrl) resp, body, err := client.UploadFile(params.GetZipPath(), zipUrl, clientDetails, 0) if err != nil {