Project Amber CLI client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
4.6 KiB

package main
import (
"encoding/json"
"strconv"
"github.com/pkg/errors"
"git.tdem.in/tdemin/amber_cli/internal/tasks"
"github.com/ddliu/go-httpclient"
)
type httpClient struct {
Token string
BaseURI string
// Be aware that this needs wrappers for attaching headers when using its
// methods.
Client *httpclient.HttpClient
}
func newHTTPClient(baseURI string) *httpClient {
return &httpClient{Client: httpclient.NewHttpClient(), BaseURI: baseURI}
}
func (h *httpClient) Headers() map[string]string {
m := map[string]string{"Content-Type": "application/json"}
if h.Token != "" {
m["Authorization"] = "Bearer " + h.Token
}
return m
}
func (h *httpClient) Get(url string, params ...interface{}) (*httpclient.Response, error) {
return h.Client.WithHeaders(h.Headers()).Get(url, params...)
}
func (h *httpClient) Delete(url string, params ...interface{}) (*httpclient.Response, error) {
return h.Client.WithHeaders(h.Headers()).Delete(url, params...)
}
func (h *httpClient) PostJSON(url string, data interface{}) (*httpclient.Response, error) {
return h.Client.WithHeaders(h.Headers()).PostJson(url, data)
}
func (h *httpClient) PatchJSON(url string, data interface{}) (*httpclient.Response, error) {
return h.Client.WithHeaders(h.Headers()).PatchJson(url, data)
}
var (
errLoginFailed = errors.New("login failed")
errInvalidToken = errors.New("invalid token")
errBadRequest = errors.New("bad request, likely server does not support API v0")
errNotFound = errors.New("task does not exist or server does not support API v0")
errUnknownError = errors.New("unknown error")
)
// Login performs the complete login routine with a given username and password,
// and sets the token internally.
func (h *httpClient) Login(login, password string) error {
payload, _ := json.Marshal(map[string]string{
"username": login,
"password": password,
})
r, httperr := h.PostJSON(h.BaseURI+"/login", string(payload))
if httperr != nil {
return httperr
}
defer r.Body.Close()
switch r.StatusCode {
case 200:
response := make(map[string]string)
json.NewDecoder(r.Body).Decode(&response)
if token, ok := response["token"]; ok {
h.Token = token
return nil
}
case 401:
return errLoginFailed
case 400:
return errBadRequest
}
return errors.Wrapf(errUnknownError, "HTTP code was %d", r.StatusCode)
}
// PostTask sends a task creation request to the server. Returns the ID received
// from the server on success.
func (h *httpClient) PostTask(task *tasks.Task) (int, error) {
query, _ := json.Marshal(*task)
r, err := h.PostJSON(h.BaseURI+"/task", string(query))
if err != nil {
return 0, err
}
defer r.Body.Close()
switch r.StatusCode {
case 200:
resp := make(map[string]int)
json.NewDecoder(r.Body).Decode(&resp)
if id, ok := resp["id"]; ok {
return id, nil
}
case 401:
return 0, errInvalidToken
case 400:
return 0, errBadRequest
}
return 0, errors.Wrapf(errUnknownError, "HTTP code was %d", r.StatusCode)
}
// DeleteTask sends a DELETE request to the sync server containing the
// task ID. Returns the list of task IDs removed.
func (h *httpClient) DeleteTask(task *tasks.Task) (ids []int, err error) {
r, err := h.Delete(h.BaseURI + "/task/" + strconv.Itoa(task.ID))
if err != nil {
return nil, err
}
defer r.Body.Close()
switch r.StatusCode {
case 200:
var rm []int
json.NewDecoder(r.Body).Decode(&rm)
return rm, nil
case 401:
return nil, errInvalidToken
case 400:
return nil, errBadRequest
case 404:
return nil, errNotFound
}
return nil, errors.Wrapf(errUnknownError, "HTTP code was %d", r.StatusCode)
}
// UpdateTask sends a PATCH request to the sync server containing the
// new task details.
func (h *httpClient) UpdateTask(task *tasks.Task) error {
body, _ := json.Marshal(*task)
r, err := h.PatchJSON(h.BaseURI+"/task/"+strconv.Itoa(task.ID), body)
if err != nil {
return err
}
defer r.Body.Close()
switch r.StatusCode {
case 200:
return nil
case 401:
return errInvalidToken
case 400:
return errBadRequest
case 404:
return errNotFound
}
return errors.Wrapf(errUnknownError, "HTTP code was %d", r.StatusCode)
}
func (h *httpClient) GetTasks() ([]tasks.Task, error) {
r, err := h.Get(h.BaseURI + "/task")
if err != nil {
return nil, err
}
defer r.Body.Close()
switch r.StatusCode {
case 200:
data := make([]tasks.Task, 0)
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
return nil, err
}
return data, nil
case 401:
return nil, errInvalidToken
case 404:
return nil, errNotFound
case 400:
return nil, errBadRequest
}
return nil, errors.Wrapf(errUnknownError, "HTTP code was %d", r.StatusCode)
}
// TODO: http.go: write a session getter