diff --git a/group_import_export.go b/group_import_export.go index c57880583..f5fbd3335 100644 --- a/group_import_export.go +++ b/group_import_export.go @@ -96,13 +96,13 @@ type GroupImportFileOptions struct { // https://docs.gitlab.com/ce/api/group_import_export.html#import-a-file func (s *GroupImportExportService) ImportFile(opt *GroupImportFileOptions, options ...RequestOptionFunc) (*Response, error) { // First check if we got all required options. - if opt.Name == nil || *opt.Name == "" { + if opt == nil || opt.Name == nil || *opt.Name == "" { return nil, fmt.Errorf("Missing required option: Name") } - if opt.Path == nil || *opt.Path == "" { + if opt == nil || opt.Path == nil || *opt.Path == "" { return nil, fmt.Errorf("Missing required option: Path") } - if opt.File == nil || *opt.File == "" { + if opt == nil || opt.File == nil || *opt.File == "" { return nil, fmt.Errorf("Missing required option: File") } diff --git a/project_import_export.go b/project_import_export.go index 8c89f4e9d..8a430fdf6 100644 --- a/project_import_export.go +++ b/project_import_export.go @@ -19,8 +19,13 @@ package gitlab import ( "bytes" "fmt" + "io" + "mime/multipart" "net/http" + "os" "time" + + "github.com/google/go-querystring/query" ) // ProjectImportExportService handles communication with the project @@ -162,6 +167,7 @@ func (s *ProjectImportExportService) ExportDownload(pid interface{}, options ... // https://docs.gitlab.com/ce/api/project_import_export.html#import-a-file type ImportFileOptions struct { Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"` + Name *string `url:"name,omitempty" json:"name,omitempty"` File *string `url:"file,omitempty" json:"file,omitempty"` Path *string `url:"path,omitempty" json:"path,omitempty"` Overwrite *bool `url:"overwrite,omitempty" json:"overwrite,omitempty"` @@ -173,10 +179,54 @@ type ImportFileOptions struct { // GitLab API docs: // https://docs.gitlab.com/ce/api/project_import_export.html#import-a-file func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options ...RequestOptionFunc) (*ImportStatus, *Response, error) { - req, err := s.client.NewRequest(http.MethodPost, "projects/import", opt, options) + // First check if we got all required options. + if opt == nil || opt.File == nil || *opt.File == "" { + return nil, nil, fmt.Errorf("Missing required option: File") + } + if opt == nil || opt.Path == nil || *opt.Path == "" { + return nil, nil, fmt.Errorf("Missing required option: Path") + } + + file, err := os.Open(*opt.File) + if err != nil { + return nil, nil, err + } + defer file.Close() + + b := &bytes.Buffer{} + w := multipart.NewWriter(b) + + fields, err := query.Values(opt) + if err != nil { + return nil, nil, err + } + + for name := range fields { + if name == "file" { + part, err := w.CreateFormFile("file", file.Name()) + if err != nil { + return nil, nil, err + } + if _, err = io.Copy(part, file); err != nil { + return nil, nil, err + } + } else { + if err = w.WriteField(name, fmt.Sprintf("%v", fields.Get(name))); err != nil { + return nil, nil, err + } + } + } + + err = w.Close() + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodPost, "projects/import", b, options) if err != nil { return nil, nil, err } + req.Header.Set("Content-Type", w.FormDataContentType()) is := new(ImportStatus) resp, err := s.client.Do(req, is) diff --git a/project_import_export_test.go b/project_import_export_test.go index c497b4cd8..d0cae526e 100644 --- a/project_import_export_test.go +++ b/project_import_export_test.go @@ -128,63 +128,6 @@ func TestProjectImportExportService_ExportDownload(t *testing.T) { require.Equal(t, http.StatusNotFound, resp.StatusCode) } -func TestProjectImportExportService_ImportFile(t *testing.T) { - mux, server, client := setup(t) - defer teardown(server) - - mux.HandleFunc("/api/v4/projects/import", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodPost) - fmt.Fprintf(w, ` - { - "id": 1, - "description": null, - "name": "api-project", - "name_with_namespace": "Administrator / api-project", - "path": "api-project", - "path_with_namespace": "root/api-project", - "import_status": "scheduled", - "correlation_id": "mezklWso3Za", - "failed_relations": [] - } - `) - }) - - want := &ImportStatus{ - ID: 1, - Description: "", - Name: "api-project", - NameWithNamespace: "Administrator / api-project", - Path: "api-project", - PathWithNamespace: "root/api-project", - ImportStatus: "scheduled", - } - - es, resp, err := client.ProjectImportExport.ImportFile(nil, nil) - require.NoError(t, err) - require.NotNil(t, resp) - require.Equal(t, want, es) - - es, resp, err = client.ProjectImportExport.ImportFile(nil, errorOption) - require.EqualError(t, err, "RequestOptionFunc returns an error") - require.Nil(t, resp) - require.Nil(t, es) -} - -func TestProjectImportExportService_ImportFile_NotFound(t *testing.T) { - mux, server, client := setup(t) - defer teardown(server) - - mux.HandleFunc("/api/v4/projects/import", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, http.MethodPost) - w.WriteHeader(http.StatusNotFound) - }) - - es, resp, err := client.ProjectImportExport.ImportFile(nil, nil) - require.Error(t, err) - require.Nil(t, es) - require.Equal(t, http.StatusNotFound, resp.StatusCode) -} - func TestProjectImportExportService_ImportStatus(t *testing.T) { mux, server, client := setup(t) defer teardown(server)