| 1 | + | package api |
| 2 | + | |
| 3 | + | import ( |
| 4 | + | "bytes" |
| 5 | + | "encoding/json" |
| 6 | + | "errors" |
| 7 | + | "fmt" |
| 8 | + | "io" |
| 9 | + | "net/http" |
| 10 | + | |
| 11 | + | "github.com/bearer/bearer/cmd/bearer/build" |
| 12 | + | ) |
| 13 | + | |
| 14 | + | type API struct { |
| 15 | + | client *http.Client |
| 16 | + | Host string |
| 17 | + | Token string |
| 18 | + | } |
| 19 | + | |
| 20 | + | type MessageType string |
| 21 | + | |
| 22 | + | const MessageTypeSuccess MessageType = "success" |
| 23 | + | const MessageTypeError MessageType = "error" |
| 24 | + | |
| 25 | + | type Message struct { |
| 26 | + | Type MessageType `json:"type"` |
| 27 | + | Data interface{} `json:"data"` |
| 28 | + | } |
| 29 | + | |
| 30 | + | type ErrorData struct { |
| 31 | + | Message string `json:"message"` |
| 32 | + | } |
| 33 | + | |
| 34 | + | func New(config API) *API { |
| 35 | + | return &API{ |
| 36 | + | client: &http.Client{}, |
| 37 | + | Token: config.Token, |
| 38 | + | Host: config.Host, |
| 39 | + | } |
| 40 | + | } |
| 41 | + | |
| 42 | + | var ErrTokenInvalid = errors.New("bearer token is invalid") |
| 43 | + | |
| 44 | + | func (api *API) makeRequest(route string, httpMethod string, data interface{}) ([]byte, error) { |
| 45 | + | fullURL := fmt.Sprintf("https://%s%s", api.Host, route) |
| 46 | + | |
| 47 | + | var req *http.Request |
| 48 | + | |
| 49 | + | if data != nil { |
| 50 | + | sendingData, err := json.Marshal(data) |
| 51 | + | if err != nil { |
| 52 | + | return nil, fmt.Errorf("fail marshaling data %e", err) |
| 53 | + | } |
| 54 | + | |
| 55 | + | req, err = http.NewRequest(httpMethod, fullURL, bytes.NewBuffer(sendingData)) |
| 56 | + | if err != nil { |
| 57 | + | return nil, fmt.Errorf("fail creating request %e %s", err, fullURL) |
| 58 | + | } |
| 59 | + | req.Header.Set("Content-Type", "application/json") |
| 60 | + | defer req.Body.Close() |
| 61 | + | } else { |
| 62 | + | var err error |
| 63 | + | req, err = http.NewRequest(httpMethod, fullURL, nil) |
| 64 | + | if err != nil { |
| 65 | + | return nil, fmt.Errorf("fail creating request %e %s", err, fullURL) |
| 66 | + | } |
| 67 | + | } |
| 68 | + | |
| 69 | + | req.Header.Set("Authorization", api.Token) |
| 70 | + | req.Header.Set("X-Bearer-SHA", build.CommitSHA) |
| 71 | + | req.Header.Set("X-Bearer-Version", build.Version) |
| 72 | + | |
| 73 | + | resp, err := api.client.Do(req) |
| 74 | + | if err != nil { |
| 75 | + | return nil, fmt.Errorf("fail getting response %e %s", err, fullURL) |
| 76 | + | } |
| 77 | + | defer resp.Body.Close() |
| 78 | + | |
| 79 | + | body, err := io.ReadAll(resp.Body) |
| 80 | + | if err != nil { |
| 81 | + | return nil, fmt.Errorf("fail reading response body %e %s", err, fullURL) |
| 82 | + | } |
| 83 | + | |
| 84 | + | if resp.StatusCode == http.StatusUnauthorized { |
| 85 | + | var BearerTokenInvalidMsgErr = "invalid token" |
| 86 | + | type BearerTokenInvalidMsg struct { |
| 87 | + | Error string `json:"error"` |
| 88 | + | } |
| 89 | + | |
| 90 | + | var unathorizedErr BearerTokenInvalidMsg |
| 91 | + | err := json.Unmarshal(body, &unathorizedErr) |
| 92 | + | if err != nil { |
| 93 | + | return nil, err |
| 94 | + | } |
| 95 | + | |
| 96 | + | if unathorizedErr.Error == BearerTokenInvalidMsgErr { |
| 97 | + | return nil, ErrTokenInvalid |
| 98 | + | } |
| 99 | + | } |
| 100 | + | |
| 101 | + | if resp.StatusCode != http.StatusOK && resp.StatusCode != 204 { |
| 102 | + | return nil, fmt.Errorf("didn't get response status 200/204 got %d %s", resp.StatusCode, string(body)) |
| 103 | + | } |
| 104 | + | |
| 105 | + | return body, nil |
| 106 | + | } |
| 107 | + | |