-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapi.go
138 lines (124 loc) · 3.58 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package b2
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
// B2 is an API client for Backblaze's B2. It contains all user account state
// and a private http client used to make requests.
type B2 struct {
AccountID string
ApplicationKey string
AuthorizationToken string
APIURL string
DownloadURL string
client client
}
// The client interface is satisfied by an http.Client and a testClient.
type client interface {
Do(*http.Request) (*http.Response, error)
}
// authResponse contains a successful B2 authentication response.
type authResponse struct {
AccountID string `json:"accountId"`
AuthorizationToken string `json:"authorizationToken"`
APIURL string `json:"apiUrl"`
DownloadURL string `json:"downloadUrl"`
}
// APIError contains an error generated by the B2 API.
//
// All errors that the B2 API returns will be in this format, though not all
// errors returned by this libraries function calls will be APIErrors.
type APIError struct {
Status int64 `json:"status"`
Code string `json:"code"`
Message string `json:"message"`
}
func (e APIError) Error() string {
return fmt.Sprintf("Status: %d, Code: %s, Message: %s", e.Status, e.Code, e.Message)
}
// CreateB2 makes a new B2 client and authorizes it.
func CreateB2(accountID, appKey string) (*B2, error) {
b2 := &B2{
AccountID: accountID,
ApplicationKey: appKey,
client: http.DefaultClient,
}
return b2.createB2()
}
// createB2 executes the authorization of a B2 client.
func (b2 *B2) createB2() (*B2, error) {
req, err := CreateRequest("GET", "https://api.backblaze.com/b2api/v1/b2_authorize_account", nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(b2.AccountID, b2.ApplicationKey)
resp, err := b2.client.Do(req)
if err != nil {
return nil, err
}
return b2.parseCreateB2(resp)
}
func (b2 *B2) parseCreateB2(resp *http.Response) (*B2, error) {
ar := &authResponse{}
err := parseResponse(resp, ar)
if err != nil {
return nil, err
}
b2.AuthorizationToken = ar.AuthorizationToken
b2.APIURL = ar.APIURL
b2.DownloadURL = ar.DownloadURL
return b2, nil
}
// CreateRequest makes a http.Request that can be passed to http's Client.Do.
func CreateRequest(method, url string, request interface{}) (*http.Request, error) {
body, err := json.Marshal(request)
if err != nil {
return nil, err
}
return http.NewRequest(method, url, bytes.NewReader(body))
}
// GetBzInfoHeaders returns a map of headers in a response that start with
// "X-Bz-Info-", which are file metadata that were uploaded with the file.
func GetBzInfoHeaders(resp *http.Response) map[string]string {
out := map[string]string{}
for k, v := range resp.Header {
if strings.HasPrefix(k, "X-Bz-Info-") {
// strip Bz prefix and grab first header
out[k[10:]] = v[0]
}
}
return out
}
// parseResponse routes a response to parse the body or an error.
func parseResponse(resp *http.Response, body interface{}) error {
defer resp.Body.Close()
if resp.StatusCode == 200 {
return parseResponseBody(resp, body)
}
return parseAPIError(resp)
}
// parseResponseBody parses a successful JSON response into the body param.
func parseResponseBody(resp *http.Response, body interface{}) error {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return json.Unmarshal(b, body)
}
// parseAPIError parses and returns an APIError.
func parseAPIError(resp *http.Response) error {
e := &APIError{}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(b, e)
if err != nil {
return err
}
return e
}