api.go
changeset 125 2bbb72b9ebf6
child 128 a5a00fad7a32
equal deleted inserted replaced
124:5ee3f23205af 125:2bbb72b9ebf6
       
     1 package gondole
       
     2 
       
     3 import (
       
     4 	"bytes"
       
     5 	"encoding/json"
       
     6 	"fmt"
       
     7 	"net/http"
       
     8 	"net/url"
       
     9 	"strings"
       
    10 
       
    11 	"github.com/sendgrid/rest"
       
    12 )
       
    13 
       
    14 // restAPI actually does the HTTP query
       
    15 // It is a copy of rest.API with better handling of parameters with multiple values
       
    16 func restAPI(request rest.Request) (*rest.Response, error) {
       
    17 	c := &rest.Client{HTTPClient: http.DefaultClient}
       
    18 
       
    19 	// Build the HTTP request object.
       
    20 	if len(request.QueryParams) != 0 {
       
    21 		// Add parameters to the URL
       
    22 		request.BaseURL += "?"
       
    23 		urlp := url.Values{}
       
    24 		for key, value := range request.QueryParams {
       
    25 			// It seems Mastodon doesn't like parameters with index
       
    26 			// numbers, but it needs the brackets.
       
    27 			// Let's check if the key matches '^.+\[.*\]$'
       
    28 			klen := len(key)
       
    29 			if klen == 0 {
       
    30 				continue
       
    31 			}
       
    32 			i := strings.Index(key, "[")
       
    33 			if key[klen-1] == ']' && i > 0 {
       
    34 				// This is an array, let's remove the index number
       
    35 				key = key[:i] + "[]"
       
    36 			}
       
    37 			urlp.Add(key, value)
       
    38 		}
       
    39 		urlpstr := urlp.Encode()
       
    40 		request.BaseURL += urlpstr
       
    41 	}
       
    42 
       
    43 	req, err := http.NewRequest(string(request.Method), request.BaseURL, bytes.NewBuffer(request.Body))
       
    44 	if err != nil {
       
    45 		return nil, err
       
    46 	}
       
    47 
       
    48 	for key, value := range request.Headers {
       
    49 		req.Header.Set(key, value)
       
    50 	}
       
    51 	_, exists := req.Header["Content-Type"]
       
    52 	if len(request.Body) > 0 && !exists {
       
    53 		req.Header.Set("Content-Type", "application/json")
       
    54 	}
       
    55 
       
    56 	// Build the HTTP client and make the request.
       
    57 	res, err := c.MakeRequest(req)
       
    58 	if err != nil {
       
    59 		return nil, err
       
    60 	}
       
    61 
       
    62 	// Build Response object.
       
    63 	response, err := rest.BuildResponse(res)
       
    64 	if err != nil {
       
    65 		return nil, err
       
    66 	}
       
    67 
       
    68 	return response, nil
       
    69 }
       
    70 
       
    71 // prepareRequest inserts all pre-defined stuff
       
    72 func (g *Client) prepareRequest(target string, method rest.Method, params apiCallParams) (req rest.Request) {
       
    73 	endPoint := g.APIBase + "/" + target
       
    74 
       
    75 	// Request headers
       
    76 	hdrs := make(map[string]string)
       
    77 	hdrs["User-Agent"] = fmt.Sprintf("Gondole/%s", GondoleVersion)
       
    78 	if g.UserToken != nil {
       
    79 		hdrs["Authorization"] = fmt.Sprintf("Bearer %s", g.UserToken.AccessToken)
       
    80 	}
       
    81 
       
    82 	req = rest.Request{
       
    83 		BaseURL:     endPoint,
       
    84 		Headers:     hdrs,
       
    85 		Method:      method,
       
    86 		QueryParams: params,
       
    87 	}
       
    88 	return
       
    89 }
       
    90 
       
    91 // apiCall makes a call to the Mastodon API server
       
    92 func (g *Client) apiCall(endPoint string, method rest.Method, params apiCallParams, data interface{}) error {
       
    93 	// Prepare query
       
    94 	req := g.prepareRequest(endPoint, method, params)
       
    95 
       
    96 	// Make API call
       
    97 	r, err := restAPI(req)
       
    98 	if err != nil {
       
    99 		return fmt.Errorf("API query (%s) failed: %s", endPoint, err.Error())
       
   100 	}
       
   101 
       
   102 	// Check for error reply
       
   103 	var errorResult Error
       
   104 	if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil {
       
   105 		// The empty object is not an error
       
   106 		if errorResult.Text != "" {
       
   107 			return fmt.Errorf("%s", errorResult.Text)
       
   108 		}
       
   109 	}
       
   110 
       
   111 	// Not an error reply; let's unmarshal the data
       
   112 	err = json.Unmarshal([]byte(r.Body), &data)
       
   113 	if err != nil {
       
   114 		return fmt.Errorf("cannot decode API response (%s): %s", method, err.Error())
       
   115 	}
       
   116 	return nil
       
   117 }