Skip to content

Commit

Permalink
feat: add ability to do mutlipart ordered formdata along with file(s) #…
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Nov 7, 2024
1 parent 13e4cad commit f107bfc
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
7 changes: 7 additions & 0 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,13 @@ func createMultipart(w *multipart.Writer, r *Request) error {
}

for _, mf := range r.multipartFields {
if len(mf.Values) > 0 {
for _, v := range mf.Values {
w.WriteField(mf.Name, v)
}
continue
}

if err := mf.openFileIfRequired(); err != nil {
return err
}
Expand Down
5 changes: 5 additions & 0 deletions multipart.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ type MultipartField struct {
// ProgressCallback feature so that Resty sends the FileSize
// value via [MultipartFieldProgress]
ProgressCallback MultipartFieldCallbackFunc

// Values field is used to provide form field value. (Optional, unless it's a form-data field)
//
// It is primarily added for ordered multipart form-data field use cases
Values []string
}

// Clone method returns the deep copy of m except [io.Reader].
Expand Down
62 changes: 61 additions & 1 deletion multipart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,66 @@ func TestMultipartFieldProgressCallback(t *testing.T) {
assertEqual(t, true, strings.Contains(responseStr, "100mbfile.bin"))
}

func TestMultipartOrderedFormData(t *testing.T) {
ts := createFormPostServer(t)
defer ts.Close()
defer cleanupFiles(".testdata/upload")

jsonStr1 := `{"input": {"name": "Uploaded document 1", "_filename" : ["file1.txt"]}}`
jsonStr2 := `{"input": {"name": "Uploaded document 2", "_filename" : ["file2.txt"]}}`

fields := []*MultipartField{
{
Name: "field1",
Values: []string{"field1value1", "field1value2"},
},
{
Name: "field2",
Values: []string{"field2value1", "field2value2"},
},
{
Name: "uploadManifest1",
FileName: "upload-file-1.json",
ContentType: "application/json",
Reader: bytes.NewBufferString(jsonStr1),
},
{
Name: "field3",
Values: []string{"field3value1", "field3value2"},
},
{
Name: "uploadManifest2",
FileName: "upload-file-2.json",
ContentType: "application/json",
Reader: bytes.NewBufferString(jsonStr2),
},
{
Name: "field4",
Values: []string{"field4value1", "field4value2"},
},
{
Name: "uploadManifest3",
ContentType: "application/json",
Reader: bytes.NewBufferString(jsonStr2),
},
}

c := dcnld().SetBaseURL(ts.URL)

resp, err := c.R().
SetMultipartOrderedFormData("first_name", []string{"Jeevanandam"}).
SetMultipartOrderedFormData("last_name", []string{"M"}).
SetMultipartFields(fields...).
Post("/upload")

responseStr := resp.String()

assertError(t, err)
assertEqual(t, http.StatusOK, resp.StatusCode())
assertEqual(t, true, strings.Contains(responseStr, "upload-file-1.json"))
assertEqual(t, true, strings.Contains(responseStr, "upload-file-2.json"))
}

var errTestErrorReader = errors.New("fake")

type errorReader struct{}
Expand Down Expand Up @@ -484,7 +544,7 @@ func (mwe *mpWriterError) Write(p []byte) (int, error) {
return 0, errors.New("multipart write error")
}

func TestMulipartRequest_createMultipart(t *testing.T) {
func TestMultipartRequest_createMultipart(t *testing.T) {
mw := multipart.NewWriter(&mpWriterError{})

c := dcnl()
Expand Down
11 changes: 11 additions & 0 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,17 @@ func (r *Request) SetMultipartFormData(data map[string]string) *Request {
return r
}

// SetMultipartOrderedFormData method allows add ordered form data to be attached to the request
// as `multipart:form-data`
func (r *Request) SetMultipartOrderedFormData(name string, values []string) *Request {
r.isMultiPart = true
r.multipartFields = append(r.multipartFields, &MultipartField{
Name: name,
Values: values,
})
return r
}

// SetMultipartField method sets custom data with Content-Type using [io.Reader] for multipart upload.
//
// Resty provides an optional multipart live upload progress callback;
Expand Down
3 changes: 3 additions & 0 deletions resty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ func createFormPostServer(t *testing.T) *httptest.Server {
targetPath := filepath.Join(getTestDataPath(), "upload")
_ = os.MkdirAll(targetPath, 0700)

values := r.MultipartForm.Value
t.Logf("%v", values)

for _, fhdrs := range r.MultipartForm.File {
for _, hdr := range fhdrs {
t.Logf("Name: %v", hdr.Filename)
Expand Down

0 comments on commit f107bfc

Please sign in to comment.