Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for multi file uploads following OAS3 spec #205

Merged
merged 3 commits into from
Jun 5, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 11 additions & 18 deletions httpclient/multipartrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
)

// DoMultiPartRequest creates and executes a multipart/form-data HTTP request for file uploads and form fields.
func (c *Client) DoMultiPartRequest(method, endpoint string, files map[string]string, params map[string]string, contentTypes map[string]string, headersMap map[string]http.Header, out interface{}) (*http.Response, error) {
func (c *Client) DoMultiPartRequest(method, endpoint string, files map[string][]string, params map[string]string, contentTypes map[string]string, headersMap map[string]http.Header, out interface{}) (*http.Response, error) {
log := c.Logger
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Ensure the context is canceled when the function returns
Expand Down Expand Up @@ -107,13 +107,15 @@ func (c *Client) DoMultiPartRequest(method, endpoint string, files map[string]st
}

// createMultipartRequestBody creates a multipart request body with the provided files and form fields, supporting custom content types and headers.
func createMultipartRequestBody(files map[string]string, params map[string]string, contentTypes map[string]string, headersMap map[string]http.Header, log logger.Logger) (*bytes.Buffer, string, error) {
func createMultipartRequestBody(files map[string][]string, params map[string]string, contentTypes map[string]string, headersMap map[string]http.Header, log logger.Logger) (*bytes.Buffer, string, error) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)

for fieldName, filePath := range files {
if err := addFilePart(writer, fieldName, filePath, contentTypes, headersMap, log); err != nil {
return nil, "", err
for fieldName, filePaths := range files {
for _, filePath := range filePaths {
if err := addFilePart(writer, fieldName, filePath, contentTypes, headersMap, log); err != nil {
return nil, "", err
}
}
}

Expand Down Expand Up @@ -145,14 +147,13 @@ func addFilePart(writer *multipart.Writer, fieldName, filePath string, contentTy
contentType = ct
}

var partHeaders textproto.MIMEHeader
var part io.Writer
if h, ok := headersMap[fieldName]; ok {
partHeaders = CustomFormDataHeader(fieldName, filepath.Base(filePath), contentType, h)
part, err = writer.CreatePart(CustomFormDataHeader(fieldName, filepath.Base(filePath), contentType, h))
} else {
partHeaders = FormDataHeader(fieldName, contentType)
part, err = writer.CreateFormFile(fieldName, filepath.Base(filePath))
}

part, err := writer.CreatePart(partHeaders)
if err != nil {
log.Error("Failed to create form file part", zap.String("fieldName", fieldName), zap.Error(err))
return err
Expand All @@ -175,7 +176,7 @@ func addFilePart(writer *multipart.Writer, fieldName, filePath string, contentTy

// addFormField adds a form field to the multipart writer with the provided key and value.
func addFormField(writer *multipart.Writer, key, val string, log logger.Logger) error {
fieldWriter, err := writer.CreatePart(FormDataHeader(key, "text/plain"))
fieldWriter, err := writer.CreateFormField(key)
if err != nil {
log.Error("Failed to create form field", zap.String("key", key), zap.Error(err))
return err
Expand All @@ -187,14 +188,6 @@ func addFormField(writer *multipart.Writer, key, val string, log logger.Logger)
return nil
}

// FormDataHeader creates a textproto.MIMEHeader for a form data field with the provided field name and content type.
func FormDataHeader(fieldname, contentType string) textproto.MIMEHeader {
header := textproto.MIMEHeader{}
header.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"`, fieldname))
header.Set("Content-Type", contentType)
return header
}

// CustomFormDataHeader creates a textproto.MIMEHeader for a form data field with the provided field name, file name, content type, and custom headers.
func CustomFormDataHeader(fieldname, filename, contentType string, customHeaders http.Header) textproto.MIMEHeader {
header := textproto.MIMEHeader{}
Expand Down
Loading