From efee481fbc3c5948639669c4d6fb1b22035fb860 Mon Sep 17 00:00:00 2001 From: Sune Keller Date: Tue, 20 Jul 2021 10:14:00 -1000 Subject: [PATCH 1/8] Add GenericPackagesService Signed-off-by: Sune Keller --- generic_packages.go | 105 +++++++++++++++++++++++++++++++++++++++ generic_packages_test.go | 78 +++++++++++++++++++++++++++++ gitlab.go | 2 + request_options.go | 10 ++++ 4 files changed, 195 insertions(+) create mode 100644 generic_packages.go create mode 100644 generic_packages_test.go diff --git a/generic_packages.go b/generic_packages.go new file mode 100644 index 000000000..e86ed8c76 --- /dev/null +++ b/generic_packages.go @@ -0,0 +1,105 @@ +// +// Copyright 2021, Sune Keller +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "bytes" + "fmt" + "io" + "net/http" +) + +// GenericPackagesService handles communication with the packages related methods +// of the GitLab API. +// +// GitLab docs: https://docs.gitlab.com/ee/user/packages/generic_packages/index.html +type GenericPackagesService struct { + client *Client +} + +// DownloadPackageFile allows you to download the package file. +// +// GitLab docs: +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file +func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageName, packageVersion, fileName string, options ...RequestOptionFunc) ([]byte, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf( + "/projects/%s/packages/generic/%s/%s/%s", + pathEscape(project), + pathEscape(packageName), + pathEscape(packageVersion), + pathEscape(fileName), + ) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + var f bytes.Buffer + resp, err := s.client.Do(req, &f) + if err != nil { + return nil, resp, err + } + + return f.Bytes(), resp, err +} + +// PublishPackageFileOptions represents the available PublishPackageFile() options. +// +// GitLab docs: +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file +type PublishPackageFileOptions struct { + Status *GenericPackageStateValue `url:"status,omitempty" json:"status,omitempty"` +} + +// PublishPackageFile allows you to download the package file. +// +// GitLab docs: +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file +func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.ReadCloser, opt *PublishPackageFileOptions, options ...RequestOptionFunc) ([]byte, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf( + "/projects/%s/packages/generic/%s/%s/%s", + pathEscape(project), + pathEscape(packageName), + pathEscape(packageVersion), + pathEscape(fileName), + ) + + // This is currently the only way to use a PUT request to upload a non-JSON file + options = append(options, WithUploadFile(content)) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var f bytes.Buffer + resp, err := s.client.Do(req, &f) + if err != nil { + return nil, resp, err + } + + return f.Bytes(), resp, err +} diff --git a/generic_packages_test.go b/generic_packages_test.go new file mode 100644 index 000000000..a0abadc82 --- /dev/null +++ b/generic_packages_test.go @@ -0,0 +1,78 @@ +// +// Copyright 2021, Sune Keller +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestDownloadPackageFile(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, strings.TrimSpace(` + bar = baz + `)) + }) + + packageBytes, _, err := client.GenericPackages.DownloadPackageFile(1234, "foo", "0.1.2", "bar-baz.txt") + if err != nil { + t.Errorf("GenericPackages.DownloadPackageFile returned error: %v", err) + } + + want := []byte("bar = baz") + if !reflect.DeepEqual(want, packageBytes) { + t.Errorf("GenericPackages.DownloadPackageFile returned %+v, want %+v", packageBytes, want) + } +} + +func TestPublishPackageFile(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, ` + { + "message": "201 Created" + } + `) + }) + + result, _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", io.NopCloser(strings.NewReader("bar = baz")), &PublishPackageFileOptions{}) + if err != nil { + t.Errorf("GenericPackages.PublishPackageFile returned error: %v", err) + } + + body := map[string]interface{}{} + if err := json.Unmarshal(result, &body); err != nil { + t.Errorf("Error decoding body: %v", err) + } + + want := map[string]interface{}{"message": "201 Created"} + if !reflect.DeepEqual(body, want) { + t.Errorf("GenericPackages.PublishPackageFile response code was %+v, want %+v", body, want) + } +} diff --git a/gitlab.go b/gitlab.go index 5f585cb87..ddfee949f 100644 --- a/gitlab.go +++ b/gitlab.go @@ -122,6 +122,7 @@ type Client struct { ExternalStatusChecks *ExternalStatusChecksService Features *FeaturesService FreezePeriods *FreezePeriodsService + GenericPackages *GenericPackagesService GeoNodes *GeoNodesService GitIgnoreTemplates *GitIgnoreTemplatesService GroupBadges *GroupBadgesService @@ -312,6 +313,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.ExternalStatusChecks = &ExternalStatusChecksService{client: c} c.Features = &FeaturesService{client: c} c.FreezePeriods = &FreezePeriodsService{client: c} + c.GenericPackages = &GenericPackagesService{client: c} c.GeoNodes = &GeoNodesService{client: c} c.GitIgnoreTemplates = &GitIgnoreTemplatesService{client: c} c.GroupBadges = &GroupBadgesService{client: c} diff --git a/request_options.go b/request_options.go index 75ae044a5..fd3e9634d 100644 --- a/request_options.go +++ b/request_options.go @@ -18,6 +18,8 @@ package gitlab import ( "context" + "io" + "net/http" retryablehttp "github.com/hashicorp/go-retryablehttp" ) @@ -59,3 +61,11 @@ func WithToken(authType AuthType, token string) RequestOptionFunc { return nil } } + +func WithUploadFile(content io.ReadCloser) RequestOptionFunc { + return func(req *retryablehttp.Request) error { + req.Body = content + req.Method = http.MethodPut + return nil + } +} From 9314bbc2eaa27f6b582b86473c0a33287dd596b2 Mon Sep 17 00:00:00 2001 From: Sune Keller Date: Tue, 9 Mar 2021 15:45:29 +0100 Subject: [PATCH 2/8] Add Generic Packages and Package to README Signed-off-by: Sune Keller --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1df039a19..568fffcab 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ to add new and/or missing endpoints. Currently the following services are suppor - [x] Events - [x] Feature Flags - [ ] Geo Nodes +- [x] Generic Packages - [x] GitLab CI Config Templates - [x] Gitignores Templates - [x] Group Access Requests @@ -57,6 +58,7 @@ to add new and/or missing endpoints. Currently the following services are suppor - [x] Notes (comments) - [x] Notification Settings - [x] Open Source License Templates +- [x] Packages - [x] Pages - [x] Pages Domains - [x] Personal Access Tokens From f686a548477a4fa223e3f2b0dd95a34a5adaa8c4 Mon Sep 17 00:00:00 2001 From: Evan Wies Date: Wed, 21 Jul 2021 13:23:51 -1000 Subject: [PATCH 3/8] Continue Generic Packages support * PublishPackageFile now returns the download URL * removed forward slash from Generic Pacakge API endpoint urls * Publishing package is a PUT operation * add GenericPackageStatusValue enums * rebased to upstream master Signed-off-by: Evan Wies --- generic_packages.go | 32 ++++++++++++++++++++++---------- generic_packages_test.go | 9 +++++++-- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/generic_packages.go b/generic_packages.go index e86ed8c76..4919200a2 100644 --- a/generic_packages.go +++ b/generic_packages.go @@ -23,6 +23,15 @@ import ( "net/http" ) +// GenericPackageStatusValue represents a GitLab Package Status. +type GenericPackageStatusValue string + +// These constants represent all valid package statuses. +const ( + Default GenericPackageStatusValue = "default" + Hidden GenericPackageStatusValue = "hidden" +) + // GenericPackagesService handles communication with the packages related methods // of the GitLab API. // @@ -41,7 +50,7 @@ func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageNam return nil, nil, err } u := fmt.Sprintf( - "/projects/%s/packages/generic/%s/%s/%s", + "projects/%s/packages/generic/%s/%s/%s", pathEscape(project), pathEscape(packageName), pathEscape(packageVersion), @@ -67,20 +76,21 @@ func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageNam // GitLab docs: // https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file type PublishPackageFileOptions struct { - Status *GenericPackageStateValue `url:"status,omitempty" json:"status,omitempty"` + Status *GenericPackageStatusValue `url:"status,omitempty" json:"status,omitempty"` } -// PublishPackageFile allows you to download the package file. +// PublishPackageFile uploads a file to a project's Package Registry. +// Returns the package URL, the response body, the Response, and any error. // // GitLab docs: // https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file -func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.ReadCloser, opt *PublishPackageFileOptions, options ...RequestOptionFunc) ([]byte, *Response, error) { +func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.ReadCloser, opt *PublishPackageFileOptions, options ...RequestOptionFunc) (string, []byte, *Response, error) { project, err := parseID(pid) if err != nil { - return nil, nil, err + return "", nil, nil, err } u := fmt.Sprintf( - "/projects/%s/packages/generic/%s/%s/%s", + "projects/%s/packages/generic/%s/%s/%s", pathEscape(project), pathEscape(packageName), pathEscape(packageVersion), @@ -90,16 +100,18 @@ func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName // This is currently the only way to use a PUT request to upload a non-JSON file options = append(options, WithUploadFile(content)) - req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + req, err := s.client.NewRequest(http.MethodPut, u, opt, options) if err != nil { - return nil, nil, err + return "", nil, nil, err } var f bytes.Buffer resp, err := s.client.Do(req, &f) if err != nil { - return nil, resp, err + return "", nil, resp, err } - return f.Bytes(), resp, err + // ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt' + downloadURL := fmt.Sprintf("%s%s", s.client.BaseURL(), u) + return downloadURL, f.Bytes(), resp, err } diff --git a/generic_packages_test.go b/generic_packages_test.go index a0abadc82..fa6a45696 100644 --- a/generic_packages_test.go +++ b/generic_packages_test.go @@ -53,7 +53,7 @@ func TestPublishPackageFile(t *testing.T) { defer teardown(server) mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) + testMethod(t, r, http.MethodPut) fmt.Fprint(w, ` { "message": "201 Created" @@ -61,11 +61,16 @@ func TestPublishPackageFile(t *testing.T) { `) }) - result, _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", io.NopCloser(strings.NewReader("bar = baz")), &PublishPackageFileOptions{}) + url, result, _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", io.NopCloser(strings.NewReader("bar = baz")), &PublishPackageFileOptions{}) if err != nil { t.Errorf("GenericPackages.PublishPackageFile returned error: %v", err) } + goldenURL := client.BaseURL().String() + "projects/1234/packages/generic/foo/0%2E1%2E2/bar-baz%2Etxt" + if url != goldenURL { + t.Errorf("GenericPackages.PublishPackageFile URL was %+v, want %+v", url, goldenURL) + } + body := map[string]interface{}{} if err := json.Unmarshal(result, &body); err != nil { t.Errorf("Error decoding body: %v", err) From 69a6faf66259258836be64442ec489a2bbe26c86 Mon Sep 17 00:00:00 2001 From: Evan Wies Date: Fri, 23 Jul 2021 23:25:04 -1000 Subject: [PATCH 4/8] Generic Packages fixes. Signed-off-by: Evan Wies --- generic_packages.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/generic_packages.go b/generic_packages.go index 4919200a2..742d659d2 100644 --- a/generic_packages.go +++ b/generic_packages.go @@ -97,10 +97,11 @@ func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName pathEscape(fileName), ) - // This is currently the only way to use a PUT request to upload a non-JSON file + // This is currently the only way to use a PUT request to upload a non-JSON file. + // We invoke NewRequest with MethodGet so the body is not marshalled to JSON; + // WithUploadFile modifies the request to MethodPut options = append(options, WithUploadFile(content)) - - req, err := s.client.NewRequest(http.MethodPut, u, opt, options) + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { return "", nil, nil, err } From 9dc1b191aeb20cf49d7e18738e1c716af20f1e39 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Fri, 12 Nov 2021 10:13:15 +0100 Subject: [PATCH 5/8] Update the PR --- generic_packages.go | 77 +++++++++++++++++----------------------- generic_packages_test.go | 53 ++++++++++----------------- request_options.go | 10 ------ types.go | 17 +++++++++ 4 files changed, 67 insertions(+), 90 deletions(-) diff --git a/generic_packages.go b/generic_packages.go index 742d659d2..a8b84cc39 100644 --- a/generic_packages.go +++ b/generic_packages.go @@ -23,31 +23,32 @@ import ( "net/http" ) -// GenericPackageStatusValue represents a GitLab Package Status. -type GenericPackageStatusValue string - -// These constants represent all valid package statuses. -const ( - Default GenericPackageStatusValue = "default" - Hidden GenericPackageStatusValue = "hidden" -) - -// GenericPackagesService handles communication with the packages related methods -// of the GitLab API. +// GenericPackagesService handles communication with the packages related +// methods of the GitLab API. // -// GitLab docs: https://docs.gitlab.com/ee/user/packages/generic_packages/index.html +// GitLab docs: +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html type GenericPackagesService struct { client *Client } -// DownloadPackageFile allows you to download the package file. +// PublishPackageFileOptions represents the available PublishPackageFile() +// options. // // GitLab docs: // https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file -func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageName, packageVersion, fileName string, options ...RequestOptionFunc) ([]byte, *Response, error) { +type PublishPackageFileOptions struct { + Status *GenericPackageStatusValue `url:"status,omitempty" json:"status,omitempty"` +} + +// PublishPackageFile uploads a file to a project's package registry. +// +// GitLab docs: +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file +func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.Reader, opt *PublishPackageFileOptions, options ...RequestOptionFunc) (*Response, error) { project, err := parseID(pid) if err != nil { - return nil, nil, err + return nil, err } u := fmt.Sprintf( "projects/%s/packages/generic/%s/%s/%s", @@ -57,37 +58,29 @@ func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageNam pathEscape(fileName), ) - req, err := s.client.NewRequest(http.MethodGet, u, nil, options) - if err != nil { - return nil, nil, err - } - - var f bytes.Buffer - resp, err := s.client.Do(req, &f) + // We need to create the request as a GET request to make sure the options + // are set correctly. After the request is created we will overwrite both + // the method and the body. + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { - return nil, resp, err + return nil, err } - return f.Bytes(), resp, err -} + // Overwrite the method and body. + req.Method = http.MethodPut + req.SetBody(content) -// PublishPackageFileOptions represents the available PublishPackageFile() options. -// -// GitLab docs: -// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file -type PublishPackageFileOptions struct { - Status *GenericPackageStatusValue `url:"status,omitempty" json:"status,omitempty"` + return s.client.Do(req, nil) } -// PublishPackageFile uploads a file to a project's Package Registry. -// Returns the package URL, the response body, the Response, and any error. +// DownloadPackageFile allows you to download the package file. // // GitLab docs: // https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file -func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.ReadCloser, opt *PublishPackageFileOptions, options ...RequestOptionFunc) (string, []byte, *Response, error) { +func (s *GenericPackagesService) DownloadPackageFile(pid interface{}, packageName, packageVersion, fileName string, options ...RequestOptionFunc) ([]byte, *Response, error) { project, err := parseID(pid) if err != nil { - return "", nil, nil, err + return nil, nil, err } u := fmt.Sprintf( "projects/%s/packages/generic/%s/%s/%s", @@ -97,22 +90,16 @@ func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName pathEscape(fileName), ) - // This is currently the only way to use a PUT request to upload a non-JSON file. - // We invoke NewRequest with MethodGet so the body is not marshalled to JSON; - // WithUploadFile modifies the request to MethodPut - options = append(options, WithUploadFile(content)) - req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) if err != nil { - return "", nil, nil, err + return nil, nil, err } var f bytes.Buffer resp, err := s.client.Do(req, &f) if err != nil { - return "", nil, resp, err + return nil, resp, err } - // ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt' - downloadURL := fmt.Sprintf("%s%s", s.client.BaseURL(), u) - return downloadURL, f.Bytes(), resp, err + return f.Bytes(), resp, err } diff --git a/generic_packages_test.go b/generic_packages_test.go index fa6a45696..be64a9260 100644 --- a/generic_packages_test.go +++ b/generic_packages_test.go @@ -17,37 +17,13 @@ package gitlab import ( - "encoding/json" "fmt" - "io" "net/http" "reflect" "strings" "testing" ) -func TestDownloadPackageFile(t *testing.T) { - mux, server, client := setup(t) - defer teardown(server) - - mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodGet) - fmt.Fprint(w, strings.TrimSpace(` - bar = baz - `)) - }) - - packageBytes, _, err := client.GenericPackages.DownloadPackageFile(1234, "foo", "0.1.2", "bar-baz.txt") - if err != nil { - t.Errorf("GenericPackages.DownloadPackageFile returned error: %v", err) - } - - want := []byte("bar = baz") - if !reflect.DeepEqual(want, packageBytes) { - t.Errorf("GenericPackages.DownloadPackageFile returned %+v, want %+v", packageBytes, want) - } -} - func TestPublishPackageFile(t *testing.T) { mux, server, client := setup(t) defer teardown(server) @@ -61,23 +37,30 @@ func TestPublishPackageFile(t *testing.T) { `) }) - url, result, _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", io.NopCloser(strings.NewReader("bar = baz")), &PublishPackageFileOptions{}) + _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", strings.NewReader("bar = baz"), &PublishPackageFileOptions{}) if err != nil { t.Errorf("GenericPackages.PublishPackageFile returned error: %v", err) } +} - goldenURL := client.BaseURL().String() + "projects/1234/packages/generic/foo/0%2E1%2E2/bar-baz%2Etxt" - if url != goldenURL { - t.Errorf("GenericPackages.PublishPackageFile URL was %+v, want %+v", url, goldenURL) - } +func TestDownloadPackageFile(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/1234/packages/generic/foo/0.1.2/bar-baz.txt", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, strings.TrimSpace(` + bar = baz + `)) + }) - body := map[string]interface{}{} - if err := json.Unmarshal(result, &body); err != nil { - t.Errorf("Error decoding body: %v", err) + packageBytes, _, err := client.GenericPackages.DownloadPackageFile(1234, "foo", "0.1.2", "bar-baz.txt") + if err != nil { + t.Errorf("GenericPackages.DownloadPackageFile returned error: %v", err) } - want := map[string]interface{}{"message": "201 Created"} - if !reflect.DeepEqual(body, want) { - t.Errorf("GenericPackages.PublishPackageFile response code was %+v, want %+v", body, want) + want := []byte("bar = baz") + if !reflect.DeepEqual(want, packageBytes) { + t.Errorf("GenericPackages.DownloadPackageFile returned %+v, want %+v", packageBytes, want) } } diff --git a/request_options.go b/request_options.go index fd3e9634d..75ae044a5 100644 --- a/request_options.go +++ b/request_options.go @@ -18,8 +18,6 @@ package gitlab import ( "context" - "io" - "net/http" retryablehttp "github.com/hashicorp/go-retryablehttp" ) @@ -61,11 +59,3 @@ func WithToken(authType AuthType, token string) RequestOptionFunc { return nil } } - -func WithUploadFile(content io.ReadCloser) RequestOptionFunc { - return func(req *retryablehttp.Request) error { - req.Body = content - req.Method = http.MethodPut - return nil - } -} diff --git a/types.go b/types.go index 9b95aadd3..bf6d53472 100644 --- a/types.go +++ b/types.go @@ -199,6 +199,23 @@ func FileAction(v FileActionValue) *FileActionValue { return p } +// GenericPackageStatusValue represents a GitLab Package Status. +type GenericPackageStatusValue string + +// The available generic package statuses. +const ( + PackageDefault GenericPackageStatusValue = "default" + PackageHidden GenericPackageStatusValue = "hidden" +) + +// GenericPackageStatus is a helper routine that allocates a new GenericPackageStatusValue +// value to store v and returns a pointer to it. +func GenericPackageStatus(v GenericPackageStatusValue) *GenericPackageStatusValue { + p := new(GenericPackageStatusValue) + *p = v + return p +} + // ISOTime represents an ISO 8601 formatted date type ISOTime time.Time From 15d4f084ab9f897a937e3b8880260511d630c849 Mon Sep 17 00:00:00 2001 From: Evan Wies Date: Sun, 21 Nov 2021 10:59:40 -0500 Subject: [PATCH 6/8] Add select support to Generic Packages --- generic_packages.go | 1 + types.go | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/generic_packages.go b/generic_packages.go index a8b84cc39..f4317a8b6 100644 --- a/generic_packages.go +++ b/generic_packages.go @@ -39,6 +39,7 @@ type GenericPackagesService struct { // https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file type PublishPackageFileOptions struct { Status *GenericPackageStatusValue `url:"status,omitempty" json:"status,omitempty"` + Select *GenericPackageSelectValue `url:"select,omitempty" json:"select,omitempty"` } // PublishPackageFile uploads a file to a project's package registry. diff --git a/types.go b/types.go index bf6d53472..eaa3217b7 100644 --- a/types.go +++ b/types.go @@ -199,6 +199,22 @@ func FileAction(v FileActionValue) *FileActionValue { return p } +// GenericPackageSelectValue represents a GitLab Package Status. +type GenericPackageSelectValue string + +// The available generic package statuses. +const ( + SelectPackageFile GenericPackageSelectValue = "package_file" +) + +// GenericPackageStatus is a helper routine that allocates a new GenericPackageStatusValue +// value to store v and returns a pointer to it. +func GenericPackageSelect(v GenericPackageSelectValue) *GenericPackageSelectValue { + p := new(GenericPackageSelectValue) + *p = v + return p +} + // GenericPackageStatusValue represents a GitLab Package Status. type GenericPackageStatusValue string From 42bf3d93936a56ea6b4803b0ee389d5928e1a158 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Mon, 22 Nov 2021 09:14:11 +0100 Subject: [PATCH 7/8] Add response type as otherwise the select has no use --- generic_packages.go | 47 +++++++++++++++++++++++++++++++++++----- generic_packages_test.go | 2 +- types.go | 14 ++++++------ 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/generic_packages.go b/generic_packages.go index f4317a8b6..a2db4b154 100644 --- a/generic_packages.go +++ b/generic_packages.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "net/http" + "time" ) // GenericPackagesService handles communication with the packages related @@ -32,11 +33,39 @@ type GenericPackagesService struct { client *Client } +// GenericPackagesFile represents a GitLab generic package file. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-package-file +type GenericPackagesFile struct { + ID int `json:"id"` + PackageID int `json:"package_id"` + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` + Size int `json:"size"` + FileStore int `json:"file_store"` + FileMD5 string `json:"file_md5"` + FileSHA1 string `json:"file_sha1"` + FileName string `json:"file_name"` + File struct { + URL string `json:"url"` + } `json:"file"` + FileSHA256 string `json:"file_sha256"` + VerificationRetryAt *time.Time `json:"verification_retry_at"` + VerifiedAt *time.Time `json:"verified_at"` + VerificationFailure bool `json:"verification_failure"` + VerificationRetryCount int `json:"verification_retry_count"` + VerificationChecksum string `json:"verification_checksum"` + VerificationState int `json:"verification_state"` + VerificationStartedAt *time.Time `json:"verification_started_at"` + NewFilePath string `json:"new_file_path"` +} + // PublishPackageFileOptions represents the available PublishPackageFile() // options. // // GitLab docs: -// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-package-file type PublishPackageFileOptions struct { Status *GenericPackageStatusValue `url:"status,omitempty" json:"status,omitempty"` Select *GenericPackageSelectValue `url:"select,omitempty" json:"select,omitempty"` @@ -45,11 +74,11 @@ type PublishPackageFileOptions struct { // PublishPackageFile uploads a file to a project's package registry. // // GitLab docs: -// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file -func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.Reader, opt *PublishPackageFileOptions, options ...RequestOptionFunc) (*Response, error) { +// https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#publish-a-package-file +func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName, packageVersion, fileName string, content io.Reader, opt *PublishPackageFileOptions, options ...RequestOptionFunc) (*GenericPackagesFile, *Response, error) { project, err := parseID(pid) if err != nil { - return nil, err + return nil, nil, err } u := fmt.Sprintf( "projects/%s/packages/generic/%s/%s/%s", @@ -64,14 +93,20 @@ func (s *GenericPackagesService) PublishPackageFile(pid interface{}, packageName // the method and the body. req, err := s.client.NewRequest(http.MethodGet, u, opt, options) if err != nil { - return nil, err + return nil, nil, err } // Overwrite the method and body. req.Method = http.MethodPut req.SetBody(content) - return s.client.Do(req, nil) + f := new(GenericPackagesFile) + resp, err := s.client.Do(req, f) + if err != nil { + return nil, resp, err + } + + return f, resp, err } // DownloadPackageFile allows you to download the package file. diff --git a/generic_packages_test.go b/generic_packages_test.go index be64a9260..e227f268f 100644 --- a/generic_packages_test.go +++ b/generic_packages_test.go @@ -37,7 +37,7 @@ func TestPublishPackageFile(t *testing.T) { `) }) - _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", strings.NewReader("bar = baz"), &PublishPackageFileOptions{}) + _, _, err := client.GenericPackages.PublishPackageFile(1234, "foo", "0.1.2", "bar-baz.txt", strings.NewReader("bar = baz"), &PublishPackageFileOptions{}) if err != nil { t.Errorf("GenericPackages.PublishPackageFile returned error: %v", err) } diff --git a/types.go b/types.go index eaa3217b7..679cb9ef1 100644 --- a/types.go +++ b/types.go @@ -199,23 +199,23 @@ func FileAction(v FileActionValue) *FileActionValue { return p } -// GenericPackageSelectValue represents a GitLab Package Status. +// GenericPackageSelectValue represents a generic package select value. type GenericPackageSelectValue string -// The available generic package statuses. +// The available generic package select values. const ( SelectPackageFile GenericPackageSelectValue = "package_file" ) -// GenericPackageStatus is a helper routine that allocates a new GenericPackageStatusValue -// value to store v and returns a pointer to it. +// GenericPackageSelect is a helper routine that allocates a new +// GenericPackageSelectValue value to store v and returns a pointer to it. func GenericPackageSelect(v GenericPackageSelectValue) *GenericPackageSelectValue { p := new(GenericPackageSelectValue) *p = v return p } -// GenericPackageStatusValue represents a GitLab Package Status. +// GenericPackageStatusValue represents a generic package status. type GenericPackageStatusValue string // The available generic package statuses. @@ -224,8 +224,8 @@ const ( PackageHidden GenericPackageStatusValue = "hidden" ) -// GenericPackageStatus is a helper routine that allocates a new GenericPackageStatusValue -// value to store v and returns a pointer to it. +// GenericPackageStatus is a helper routine that allocates a new +// GenericPackageStatusValue value to store v and returns a pointer to it. func GenericPackageStatus(v GenericPackageStatusValue) *GenericPackageStatusValue { p := new(GenericPackageStatusValue) *p = v From 260e7db7768c854635aac013fee48a80d9997cb0 Mon Sep 17 00:00:00 2001 From: Evan Wies Date: Mon, 22 Nov 2021 10:32:49 -0500 Subject: [PATCH 8/8] Add FormatPackageURL public helper --- generic_packages.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/generic_packages.go b/generic_packages.go index a2db4b154..ba875fa35 100644 --- a/generic_packages.go +++ b/generic_packages.go @@ -61,6 +61,23 @@ type GenericPackagesFile struct { NewFilePath string `json:"new_file_path"` } +// FormatPackageURL returns the GitLab Package Registry URL for the given artifact metadata, without the BaseURL. +// This does not make a GitLab API request, but rather computes it based on their documentation. +func (s *GenericPackagesService) FormatPackageURL(pid interface{}, packageName, packageVersion, fileName string) (string, error) { + project, err := parseID(pid) + if err != nil { + return "", err + } + u := fmt.Sprintf( + "projects/%s/packages/generic/%s/%s/%s", + pathEscape(project), + pathEscape(packageName), + pathEscape(packageVersion), + pathEscape(fileName), + ) + return u, nil +} + // PublishPackageFileOptions represents the available PublishPackageFile() // options. //