123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- package multipartestutils
- import (
- "encoding/json"
- "fmt"
- "io"
- "mime/multipart"
- "net/http"
- "net/http/httptest"
- "net/url"
- "os"
- "path/filepath"
- )
- // Builder is a multipart builder.
- // It is not thread-safe.
- type Builder struct {
- pageURL string
- eb eventBody
- queries url.Values
- cbs []func(*multipart.Writer) error
- }
- // New constructs new multipart Builder.
- func NewMultipartBuilder() *Builder {
- return &Builder{
- queries: url.Values{},
- }
- }
- // AddField adds multipart field.
- func (b *Builder) AddField(fieldName, value string) *Builder {
- b.cbs = append(b.cbs, func(mw *multipart.Writer) error {
- if err := mw.WriteField(fieldName, value); err != nil {
- return fmt.Errorf("multipartbuilder: failed to write field %s=%s: %s", fieldName, value, err.Error())
- }
- return nil
- })
- return b
- }
- // AddReader adds multipart file field from provided reader.
- func (b *Builder) AddReader(fieldName, fileName string, reader io.Reader) *Builder {
- b.cbs = append(b.cbs, func(mw *multipart.Writer) error {
- w, err := mw.CreateFormFile(fieldName, fileName)
- if err != nil {
- return fmt.Errorf("multipartbuilder: failed to create form file %s (%s) for reader: %s", fieldName, fileName, err.Error())
- }
- _, err = io.Copy(w, reader)
- if err != nil {
- return fmt.Errorf("multipartbuilder: failed to copy form file %s (%s) for reader: %s", fieldName, fileName, err.Error())
- }
- return nil
- })
- return b
- }
- // AddFile adds multipart file field from specified file path.
- func (b *Builder) AddFile(fieldName, filePath string) *Builder {
- b.cbs = append(b.cbs, func(mw *multipart.Writer) error {
- f, err := os.Open(filePath)
- if err != nil {
- return fmt.Errorf("multipartbuilder: failed to open file %s (%s): %s", fieldName, filePath, err.Error())
- }
- defer f.Close()
- w, err := mw.CreateFormFile(fieldName, filepath.Base(filePath))
- if err != nil {
- return fmt.Errorf("multipartbuilder: failed to create form file %s (%s): %s", fieldName, filePath, err.Error())
- }
- _, err = io.Copy(w, f)
- if err != nil {
- return fmt.Errorf("multipartbuilder: failed to copy form file %s (%s): %s", fieldName, filePath, err.Error())
- }
- return nil
- })
- return b
- }
- // Build finalizes Builder, returning Content-Type and multipart reader.
- // It should be called only once for Builder.
- // Returned reader should be used (Read/Close) at least once to clean up properly.
- // Any errors are bound to returned reader (will be returned on Read/Close).
- func (b *Builder) Build() (string, io.ReadCloser) {
- r, w := io.Pipe()
- mw := multipart.NewWriter(w)
- go func() {
- for _, cb := range b.cbs {
- if err := cb(mw); err != nil {
- _ = w.CloseWithError(err)
- return
- }
- }
- _ = w.CloseWithError(mw.Close())
- }()
- return mw.FormDataContentType(), r
- }
- type EventFuncID struct {
- ID string `json:"id,omitempty"`
- Params []string `json:"params,omitempty"`
- }
- type Event struct {
- Checked bool `json:"checked,omitempty"` // For Checkbox
- From string `json:"from,omitempty"` // For DatePicker
- To string `json:"to,omitempty"` // For DatePicker
- Value string `json:"value,omitempty"` // For Input, DatePicker
- }
- type eventBody struct {
- Event Event `json:"event,omitempty"`
- }
- func (b *Builder) EventFunc(id string) *Builder {
- b.queries["__execute_event__"] = []string{id}
- return b
- }
- func (b *Builder) Query(key string, value string) *Builder {
- b.queries[key] = []string{value}
- return b
- }
- func (b *Builder) Queries(v url.Values) *Builder {
- b.queries = v
- return b
- }
- func (b *Builder) Event(evt Event) *Builder {
- b.eb.Event = evt
- return b
- }
- func (b *Builder) PageURL(url string) *Builder {
- b.pageURL = url
- return b
- }
- func (b *Builder) BuildEventFuncRequest() (r *http.Request) {
- eventBody, _ := json.Marshal(b.eb)
- b.AddField("__event_data__", string(eventBody))
- contentType, body := b.Build()
- pu := b.pageURL
- if len(b.pageURL) == 0 {
- pu = "/"
- }
- r = httptest.NewRequest("POST", fmt.Sprintf("%s?%s", pu, b.queries.Encode()), body)
- r.Header.Add("Content-Type", contentType)
- return
- }
|