13 // A Client is an HTTP client with an API endpoint and a set of
14 // Arvados credentials.
16 // It offers methods for accessing individual Arvados APIs, and
17 // methods that implement common patterns like fetching multiple pages
18 // of results using List APIs.
20 // HTTP client used to make requests. If nil,
21 // http.DefaultClient or InsecureHTTPClient will be used.
24 // Hostname (or host:port) of Arvados API server.
27 // User authentication token.
30 // Accept unverified certificates. This works only if the
31 // Client field is nil: otherwise, it has no effect.
35 // The default http.Client used by a Client with Insecure==true and
37 var InsecureHTTPClient = &http.Client{
38 Transport: &http.Transport{
39 TLSClientConfig: &tls.Config{
40 InsecureSkipVerify: true}}}
42 // NewClientFromEnv creates a new Client that uses the default HTTP
43 // client with the API endpoint and credentials given by the
44 // ARVADOS_API_* environment variables.
45 func NewClientFromEnv() *Client {
47 APIHost: os.Getenv("ARVADOS_API_HOST"),
48 AuthToken: os.Getenv("ARVADOS_API_TOKEN"),
49 Insecure: os.Getenv("ARVADOS_API_HOST_INSECURE") != "",
53 // Do adds authentication headers and then calls (*http.Client)Do().
54 func (c *Client) Do(req *http.Request) (*http.Response, error) {
55 if c.AuthToken != "" {
56 req.Header.Add("Authorization", "OAuth2 "+c.AuthToken)
58 return c.httpClient().Do(req)
61 // DoAndDecode performs req and unmarshals the response (which must be
62 // JSON) into dst. Use this instead of RequestAndDecode if you need
63 // more control of the http.Request object.
64 func (c *Client) DoAndDecode(dst interface{}, req *http.Request) error {
65 resp, err := c.Do(req)
69 defer resp.Body.Close()
70 buf, err := ioutil.ReadAll(resp.Body)
74 if resp.StatusCode != 200 {
75 return newTransactionError(req, resp, buf)
80 return json.Unmarshal(buf, dst)
83 // RequestAndDecode performs an API request and unmarshals the
84 // response (which must be JSON) into dst. Method and body arguments
85 // are the same as for http.NewRequest(). The given path is added to
86 // the server's scheme/host/port to form the request URL. The given
87 // params are passed via POST form or query string.
89 // path must not contain a query string.
90 func (c *Client) RequestAndDecode(dst interface{}, method, path string, body io.Reader, params interface{}) error {
91 urlString := c.apiURL(path)
92 var urlValues url.Values
93 if v, ok := params.(url.Values); ok {
95 } else if params != nil {
96 // Convert an arbitrary struct to url.Values. For
97 // example, Foo{Bar: []int{1,2,3}, Baz: "waz"} becomes
98 // url.Values{`bar`:`{"a":[1,2,3]}`,`Baz`:`waz`}
100 // TODO: Do this more efficiently, possibly using
101 // json.Decode/Encode, so the whole thing doesn't have
102 // to get encoded, decoded, and re-encoded.
103 j, err := json.Marshal(params)
107 var generic map[string]interface{}
108 err = json.Unmarshal(j, &generic)
112 urlValues = url.Values{}
113 for k, v := range generic {
114 if v, ok := v.(string); ok {
118 j, err := json.Marshal(v)
122 urlValues.Set(k, string(j))
125 if (method == "GET" || body != nil) && urlValues != nil {
126 // FIXME: what if params don't fit in URL
127 u, err := url.Parse(urlString)
131 u.RawQuery = urlValues.Encode()
132 urlString = u.String()
134 req, err := http.NewRequest(method, urlString, body)
138 return c.DoAndDecode(dst, req)
141 func (c *Client) httpClient() *http.Client {
143 case c.Client != nil:
146 return InsecureHTTPClient
148 return http.DefaultClient
152 func (c *Client) apiURL(path string) string {
153 return "https://" + c.APIHost + "/" + path
156 // DiscoveryDocument is the Arvados server's description of itself.
157 type DiscoveryDocument struct {
158 DefaultCollectionReplication int `json:"defaultCollectionReplication"`
159 BlobSignatureTTL int64 `json:"blobSignatureTtl"`
162 // DiscoveryDocument returns a *DiscoveryDocument. The returned object
163 // should not be modified: the same object may be returned by
165 func (c *Client) DiscoveryDocument() (*DiscoveryDocument, error) {
166 var dd DiscoveryDocument
167 return &dd, c.RequestAndDecode(&dd, "GET", "discovery/v1/apis/arvados/v1/rest", nil, nil)