github.com/machinebox/remoto@v0.1.2-0.20191024144331-eff21a7d321f/templates/remotohttp/client.go.plush (about)

     1  // Code generated by Remoto; DO NOT EDIT.
     2  <%
     3  
     4  let serverName = fn(serviceName) {
     5  	return serviceName + "Server"
     6  }
     7  
     8  %>
     9  package <%= def.PackageName %>
    10  
    11  import (
    12  	"context"
    13  	"encoding/json"
    14  	"io"
    15  	"io/ioutil"
    16  	"mime/multipart"
    17  	"net/http"
    18  	"strconv"
    19  
    20  	"github.com/machinebox/remoto/remototypes"
    21  	"github.com/oxtoacart/bpool"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  <%= for (service) in def.Services { %>
    26  // <%= service.Name %>Client accesses remote <%= service.Name %> services.
    27  type <%= service.Name %>Client struct {
    28  	// endpoint is the HTTP endpoint of the remote server.
    29  	endpoint string
    30  	// httpclient is the http.Client to use to make requests.
    31  	httpclient *http.Client
    32  	// bufs is a buffer pool
    33  	bufs *bpool.BufferPool
    34  }
    35  
    36  // New<%= service.Name %>Client makes a new <%= service.Name %>Client that will
    37  // use the specified http.Client to make requests.
    38  func New<%= service.Name %>Client(endpoint string, client *http.Client) *<%= service.Name %>Client {
    39  	return &<%= service.Name %>Client{
    40  		endpoint: endpoint,
    41  		httpclient: client,
    42  		bufs: bpool.NewBufferPool(48),
    43  	}
    44  }
    45  
    46  <%= for (method) in service.Methods { %>
    47  <%= if (method.ResponseStructure.Name == "remototypes.FileResponse") { %>
    48  <%= print_comment(method.Comment) %>func (c *<%= service.Name %>Client) <%= method.Name %>(ctx context.Context, request *<%= method.RequestStructure.Name %>) (io.ReadCloser, error) {
    49  	b, err := json.Marshal([]interface{}{ request })
    50  	if err != nil {
    51  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: encode request")
    52  	}
    53  	buf := c.bufs.Get()
    54  	defer c.bufs.Put(buf)
    55  	w := multipart.NewWriter(buf)
    56  	w.WriteField("json", string(b))
    57  	if files, ok := ctx.Value(contextKeyFiles).(map[string]file); ok {
    58  		for fieldname, file := range files {
    59  			f, err := w.CreateFormFile(fieldname, file.filename)
    60  			if err != nil {
    61  				return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: create form file")
    62  			}
    63  			if _, err := io.Copy(f, file.r); err != nil {
    64  				return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: reading file")
    65  			}
    66  			select {
    67  			case <-ctx.Done():
    68  				return nil, ctx.Err()
    69  			default:
    70  			}
    71  		}
    72  	}
    73  	if err := w.Close(); err != nil {
    74  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: write")
    75  	}
    76  	req, err := http.NewRequest(http.MethodPost, c.endpoint + "/remoto/<%= service.Name %>.<%= method.Name %>", buf)
    77  	if err != nil {
    78  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: new request")
    79  	}
    80  	req.Header.Set("Accept", "application/json; charset=utf-8")
    81  	req.Header.Set("Content-Type", w.FormDataContentType())
    82  	req = req.WithContext(ctx)
    83  	resp, err := c.httpclient.Do(req)
    84  	if err != nil {
    85  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: do")
    86  	}
    87  	if resp.StatusCode != http.StatusOK {
    88  		resp.Body.Close()
    89  		return nil, errors.Errorf("<%= service.Name %>Client.<%= method.Name %>: remote service returned %s", resp.Status)
    90  	}
    91  	return resp.Body, nil
    92  }
    93  <% } else { %>
    94  <%= print_comment(method.Comment) %>func (c *<%= service.Name %>Client) <%= method.Name %>(ctx context.Context, request *<%= method.RequestStructure.Name %>) (*<%= method.ResponseStructure.Name %>, error) {
    95  	resp, err := c.<%= method.Name %>Multi(ctx, []*<%= method.RequestStructure.Name %>{request})
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	if len(resp) == 0 {
   100  		return nil, errors.New("<%= service.Name %>Client.<%= method.Name %>: no response")
   101  	}
   102  	return resp[0], nil
   103  }
   104  
   105  func (c *<%= service.Name %>Client) <%= method.Name %>Multi(ctx context.Context, requests []*<%= method.RequestStructure.Name %>) ([]*<%= method.ResponseStructure.Name %>, error) {
   106  	b, err := json.Marshal(requests)
   107  	if err != nil {
   108  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: encode request")
   109  	}
   110  	buf := c.bufs.Get()
   111  	defer c.bufs.Put(buf)
   112  	w := multipart.NewWriter(buf)
   113  	w.WriteField("json", string(b))
   114  	if files, ok := ctx.Value(contextKeyFiles).(map[string]file); ok {
   115  		for fieldname, file := range files {
   116  			f, err := w.CreateFormFile(fieldname, file.filename)
   117  			if err != nil {
   118  				return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: create form file")
   119  			}
   120  			if _, err := io.Copy(f, file.r); err != nil {
   121  				return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: reading file")
   122  			}
   123  			select {
   124  			case <-ctx.Done():
   125  				return nil, ctx.Err()
   126  			default:
   127  			}
   128  		}
   129  	}
   130  	if err := w.Close(); err != nil {
   131  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: write")
   132  	}
   133  	req, err := http.NewRequest(http.MethodPost, c.endpoint + "/remoto/<%= service.Name %>.<%= method.Name %>", buf)
   134  	if err != nil {
   135  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: new request")
   136  	}
   137  	req.Header.Set("Accept", "application/json; charset=utf-8")
   138  	req.Header.Set("Content-Type", w.FormDataContentType())
   139  	req = req.WithContext(ctx)
   140  	resp, err := c.httpclient.Do(req)
   141  	if err != nil {
   142  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: do")
   143  	}
   144  	if resp.StatusCode != http.StatusOK {
   145  		resp.Body.Close()
   146  		return nil, errors.Errorf("<%= service.Name %>Client.<%= method.Name %>: remote service returned %s", resp.Status)
   147  	}
   148  	b, err = ioutil.ReadAll(resp.Body)
   149  	resp.Body.Close()
   150  	if err != nil {
   151  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: read response body")
   152  	}
   153  	var resps []*<%= method.ResponseStructure.Name %>
   154  	if err := json.Unmarshal(b, &resps); err != nil {
   155  		return nil, errors.Wrap(err, "<%= service.Name %>Client.<%= method.Name %>: decode response body")
   156  	}
   157  	return resps, nil
   158  }
   159  <% } %>
   160  <% } %>
   161  <% } %>
   162  
   163  <%= for (structure) in unique_structures(def) { %>
   164  <%= print_comment(structure.Comment) %>type <%= structure.Name %> struct {
   165  	<%= for (field) in structure.Fields { %>
   166  	<%= print_comment(field.Comment) %><%= field.Name %> <%= go_type_string(field.Type) %> `json:"<%= camelize_down(field.Name) %>"`<% } %>
   167  }
   168  
   169  <%= for (field) in structure.Fields { %>
   170  <%= if (field.Type.Name == "remototypes.File" && !structure.IsResponseObject) { %>
   171  // Set<%= field.Name %> sets the file for the <%= field.Name %> field.
   172  func (s *<%= structure.Name %>) Set<%= field.Name %>(ctx context.Context, filename string, r io.Reader) context.Context {
   173  	files, ok := ctx.Value(contextKeyFiles).(map[string]file)
   174  	if !ok {
   175  		files = make(map[string]file)
   176  	}
   177  	fieldname := "files["+ strconv.Itoa(len(files)) + "]"
   178  	files[fieldname] = file{r: r, filename: filename}
   179  	ctx = context.WithValue(ctx, contextKeyFiles, files)
   180  	s.<%= field.Name %> = remototypes.File{
   181  		Fieldname: fieldname,
   182  		Filename: filename,
   183  	}
   184  	return ctx
   185  }
   186  <% } %>
   187  <%= if (field.Type.Name == "remototypes.File" && !structure.IsRequestObject) { %>
   188  // Open<%= field.Name %> opens the file from the response.
   189  func (s *<%= structure.Name %>) Open<%= field.Name %>(ctx context.Context) (io.Reader, error) {
   190  	return nil, nil
   191  }
   192  <% } %>
   193  <% } %>
   194  
   195  <% } %>
   196  
   197  // contextKey is a local context key type.
   198  // see https://medium.com/@matryer/context-keys-in-go-5312346a868d
   199  type contextKey string
   200  
   201  func (c contextKey) String() string {
   202  	return "remoto context key: " + string(c)
   203  }
   204  
   205  // contextKeyFiles is the context key for the request files.
   206  var contextKeyFiles = contextKey("files")
   207  
   208  // file holds info about a file in the context, including
   209  // the io.Reader where the contents will be read from.
   210  type file struct {
   211  	r io.Reader
   212  	filename string
   213  }
   214  
   215  // this is here so we don't get a compiler complaints.
   216  func init() {
   217  	var _ = remototypes.File{}
   218  	var _ = strconv.Itoa(0)
   219  	var _ = ioutil.Discard
   220  }