Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
add Group import export
Browse files Browse the repository at this point in the history
Signed-off-by: previ <[email protected]>
  • Loading branch information
previ committed Apr 3, 2021
1 parent 85dd177 commit 7148ba2
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 8 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
name: Lint and Test - ${{ matrix.go-version }}
strategy:
matrix:
go-version: [1.14.x, 1.15.x, 1.x]
go-version: [1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.x]
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
Expand All @@ -25,7 +25,7 @@ jobs:
- name: Lint package
uses: golangci/golangci-lint-action@v2
with:
version: v1.35
version: v1.39.0

- name: Test package
run: go test -v ./...
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-retryablehttp v0.6.8
github.com/stretchr/testify v1.4.0
golang.org/x/net v0.0.0-20181108082009-03003ca0c849 // indirect
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
google.golang.org/appengine v1.3.0 // indirect
)
Expand Down
16 changes: 12 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
163 changes: 163 additions & 0 deletions groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
package gitlab

import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"time"
)

Expand Down Expand Up @@ -770,3 +774,162 @@ func (s *GroupsService) DeleteGroupPushRule(gid interface{}, options ...RequestO

return s.client.Do(req, nil)
}

// ImportExportGroupStatus represent Group Export / Import return status
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_import_export.html
type ImportExportGroupStatus struct {
Message *string `json:"message,omitempty"`
}

// GroupExportRequest gets all details of a group.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_import_export.html
func (s *GroupsService) GroupExportRequest(gid interface{}, options ...RequestOptionFunc) (*ImportExportGroupStatus, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/export", pathEscape(group))

req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
if err != nil {
return nil, nil, err
}

status := new(ImportExportGroupStatus)
resp, err := s.client.Do(req, status)
if err != nil {
return nil, resp, err
}

return status, resp, err
}

// GroupExportDownload gets all details of a group.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_import_export.html
func (s *GroupsService) GroupExportDownload(gid interface{}, options ...RequestOptionFunc) ([]byte, *Response, error) {
group, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/export/download", pathEscape(group))

req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
if err != nil {
return nil, nil, err
}

var b bytes.Buffer
resp, err := s.client.Do(req, &b)
if err != nil {
return nil, resp, err
}

return b.Bytes(), resp, err
}

// GroupImportOptions represents parameters for GroupImport.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_import_export.html
type GroupImportOptions struct {
Name *string `url:"name,omitempty" json:"name,omitempty"`
File *string `url:"file,omitempty" json:"file,omitempty"`
Path *string `url:"path,omitempty" json:"path,omitempty"`
ParentID *string `url:"parent_id,omitempty" json:"parent_id,omitempty"`
}

// GroupImport gets all details of a group.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/group_import_export.html
func (s *GroupsService) GroupImport(opt *GroupImportOptions, options ...RequestOptionFunc) (*ImportExportGroupStatus, *Response, error) {
// Open the file
file, err := os.Open(*opt.File)
if err != nil {
fmt.Println(err)
}
// Close the file later
defer file.Close()

// Buffer to store our request body as bytes
var requestBody bytes.Buffer

// Create a multipart writer
multiPartWriter := multipart.NewWriter(&requestBody)

// Initialize the file field
fileWriter, err := multiPartWriter.CreateFormFile("file", "grpup..tar.gz")
if err != nil {
fmt.Println(err)
return nil, nil, err
}

// Copy the actual file content to the field field's writer
_, err = io.Copy(fileWriter, file)
if err != nil {
fmt.Println(err)
return nil, nil, err
}

// Populate other fields
fw, err := multiPartWriter.CreateFormField("name")
if err != nil {
fmt.Println(err)
return nil, nil, err
}

_, err = fw.Write([]byte(*opt.Name))
if err != nil {
fmt.Println(err)
return nil, nil, err
}

fw, err = multiPartWriter.CreateFormField("path")
if err != nil {
fmt.Println(err)
return nil, nil, err
}

_, err = fw.Write([]byte(*opt.Path))
if err != nil {
fmt.Println(err)
return nil, nil, err
}

fw, err = multiPartWriter.CreateFormField("parent_id")
if err != nil {
fmt.Println(err)
}

_, err = fw.Write([]byte(*opt.ParentID))
if err != nil {
fmt.Println(err)
}

// We completed adding the file and the fields, let's close the multipart writer
// So it writes the ending boundary
multiPartWriter.Close()

req, err := s.client.NewRequest(http.MethodPost, "groups/import", nil, options)
if err != nil {
return nil, nil, err
}

// Set the buffer as the request body.
if err = req.SetBody(&requestBody); err != nil {
return nil, nil, err
}

// We need to set the content type from the writer, it includes necessary boundary as well
req.Header.Set("Content-Type", multiPartWriter.FormDataContentType())

// Do the request
var status = new(ImportExportGroupStatus)
resp, err := s.client.Do(req, status)
if err != nil {
return nil, resp, err
}

return status, resp, err
}
89 changes: 89 additions & 0 deletions groups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package gitlab

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"reflect"
"testing"
)
Expand Down Expand Up @@ -376,3 +379,89 @@ func TestUnshareGroupFromGroup(t *testing.T) {
t.Errorf("Groups.UnshareGroupFromGroup returned status code %d", r.StatusCode)
}
}

func TestGroupExportRequest(t *testing.T) {
mux, server, client := setup(t)
defer teardown(server)

mux.HandleFunc("/api/v4/groups/1/export",
func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
fmt.Fprint(w, `{"message": "202 Accepted"}`)
})

status, _, err := client.Groups.GroupExportRequest(1)
if err != nil {
t.Errorf("Groups.GroupExportRequest returned error: %v", err)
}

want := &ImportExportGroupStatus{Message: String("202 Accepted")}
if !reflect.DeepEqual(want, status) {
t.Errorf("Groups.GroupExportRequest returned %+v, want %+v", status, want)
}
}

func TestGroupExportDownload(t *testing.T) {
mux, server, client := setup(t)
defer teardown(server)
content := []byte("fake content")

mux.HandleFunc("/api/v4/groups/1/export/download",
func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
w.Write(content)
})

data, _, err := client.Groups.GroupExportDownload(1)
if err != nil {
t.Errorf("Groups.GroupExportDownload returned error: %v", err)
}

want := []byte("fake content")
if !reflect.DeepEqual(want, data) {
t.Errorf("Groups.GroupExportDownload returned %+v, want %+v", data, want)
}
}

func TestGroupImport(t *testing.T) {
mux, server, client := setup(t)
defer teardown(server)

content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example.*.tar.gz")
if err != nil {
tmpfile.Close()
log.Fatal(err)
}
if _, err := tmpfile.Write(content); err != nil {
tmpfile.Close()
log.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
log.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up

mux.HandleFunc("/api/v4/groups/import",
func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
fmt.Fprint(w, `{"message": "202 Accepted"}`)
})

opt := GroupImportOptions{
Name: String("test"),
Path: String("path"),
ParentID: String("1"),
File: String(tmpfile.Name()),
}

r, _, err := client.Groups.GroupImport(&opt)
if err != nil {
t.Errorf("Groups.GroupExportImport returned error: %v", err)
}

want := &ImportExportGroupStatus{Message: String("202 Accepted")}
if !reflect.DeepEqual(want, r) {
t.Errorf("Groups.GroupExportDownload returned %+v, want %+v", r, want)
}
}

0 comments on commit 7148ba2

Please sign in to comment.