package gqlclient
import (
"encoding/json"
"fmt"
"io"
"mime"
"mime/multipart"
"net/textproto"
)
// Upload is a file upload.
//
// See the GraphQL multipart request specification for details:
// https://github.com/jaydenseric/graphql-multipart-request-spec
type Upload struct {
Filename string
MIMEType string
Body io.Reader
}
// MarshalJSON implements json.Marshaler.
func (Upload) MarshalJSON() ([]byte, error) {
return json.Marshal(nil)
}
func writeMultipart(pw *io.PipeWriter, uploads map[string]Upload, operations io.Reader) (contentType string) {
mw := multipart.NewWriter(pw)
go func() {
defer pw.Close()
defer mw.Close()
mapData := make(map[string][]string)
for k := range uploads {
mapData[k] = []string{"variables." + k}
}
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", `form-data; name="operations"`)
h.Set("Content-Type", "application/json")
w, err := mw.CreatePart(h)
if err != nil {
pw.CloseWithError(fmt.Errorf("failed to create operations part: %v", err))
return
}
if _, err := io.Copy(w, operations); err != nil {
pw.CloseWithError(fmt.Errorf("failed to write operations part: %v", err))
return
}
h = make(textproto.MIMEHeader)
h.Set("Content-Disposition", `form-data; name="map"`)
h.Set("Content-Type", "application/json")
w, err = mw.CreatePart(h)
if err != nil {
pw.CloseWithError(fmt.Errorf("failed to create map part: %v", err))
return
}
if err := json.NewEncoder(w).Encode(mapData); err != nil {
pw.CloseWithError(fmt.Errorf("failed to write map part: %v", err))
return
}
for k, upload := range uploads {
dispParams := map[string]string{"name": k}
if upload.Filename != "" {
dispParams["filename"] = upload.Filename
}
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", mime.FormatMediaType("form-data", dispParams))
if upload.MIMEType != "" {
h.Set("Content-Type", upload.MIMEType)
}
w, err := mw.CreatePart(h)
if err != nil {
pw.CloseWithError(fmt.Errorf("failed to create upload %q part: %v", k, err))
return
}
if _, err := io.Copy(w, upload.Body); err != nil {
pw.CloseWithError(fmt.Errorf("failed to write upload %q part: %v", k, err))
return
}
}
}()
return mw.FormDataContentType()
}