2024-05-07 21:49:09 +03:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2024-05-10 11:46:43 +03:00
|
|
|
"errors"
|
|
|
|
"go.uber.org/zap"
|
2024-05-07 21:49:09 +03:00
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
URL string
|
|
|
|
Key string
|
|
|
|
client *http.Client
|
2024-05-10 11:46:43 +03:00
|
|
|
log *zap.SugaredLogger
|
2024-05-07 21:49:09 +03:00
|
|
|
}
|
|
|
|
|
2024-05-10 11:46:43 +03:00
|
|
|
func New(url, key string, log *zap.SugaredLogger) *Client {
|
2024-05-07 21:49:09 +03:00
|
|
|
return &Client{
|
|
|
|
URL: strings.TrimSuffix(url, "/"),
|
|
|
|
Key: key,
|
|
|
|
client: &http.Client{Timeout: time.Second * 2},
|
2024-05-10 11:46:43 +03:00
|
|
|
log: log,
|
2024-05-07 21:49:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-10 11:46:43 +03:00
|
|
|
func (c *Client) Issue(id int) (*Issue, error) {
|
2024-05-07 21:49:09 +03:00
|
|
|
var resp IssueResponse
|
2024-05-10 11:46:43 +03:00
|
|
|
_, err := c.sendJSONRequest(http.MethodGet, "/issues/"+strconv.Itoa(id), nil, &resp)
|
2024-05-07 21:49:09 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if resp.IsError() {
|
|
|
|
return nil, resp
|
|
|
|
}
|
|
|
|
return resp.Issue, nil
|
|
|
|
}
|
|
|
|
|
2024-05-10 11:46:43 +03:00
|
|
|
func (c *Client) UpdateIssue(id int, issue *Issue) error {
|
|
|
|
st, err := c.sendJSONRequest(http.MethodPut, "/issues/"+strconv.Itoa(id), IssueResponse{Issue: issue}, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if st != http.StatusOK {
|
|
|
|
return errors.New("unexpected status code: " + strconv.Itoa(st))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) sendRequest(method, path string, headers map[string]string, body io.Reader) (*http.Response, error) {
|
2024-05-07 21:49:09 +03:00
|
|
|
req, err := http.NewRequest(method, c.URL+path, body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req.Header.Set("X-Redmine-API-Key", c.Key)
|
2024-05-10 11:46:43 +03:00
|
|
|
for name, value := range headers {
|
|
|
|
req.Header.Set(name, value)
|
|
|
|
}
|
2024-05-07 21:49:09 +03:00
|
|
|
|
|
|
|
return c.client.Do(req)
|
|
|
|
}
|
|
|
|
|
2024-05-10 11:46:43 +03:00
|
|
|
func (c *Client) sendJSONRequest(method, path string, body, out interface{}) (int, error) {
|
2024-05-07 21:49:09 +03:00
|
|
|
var buf io.Reader
|
|
|
|
if body != nil {
|
|
|
|
data, err := json.Marshal(body)
|
|
|
|
if err != nil {
|
2024-05-10 11:46:43 +03:00
|
|
|
return 0, err
|
2024-05-07 21:49:09 +03:00
|
|
|
}
|
|
|
|
buf = bytes.NewBuffer(data)
|
|
|
|
}
|
|
|
|
|
2024-05-10 11:46:43 +03:00
|
|
|
path = path + ".json"
|
|
|
|
c.log.Debugf("[Redmine Request] %s %s; body: %s", method, path, func() string {
|
|
|
|
if buf != nil {
|
|
|
|
return buf.(*bytes.Buffer).String()
|
|
|
|
}
|
|
|
|
return "<nil>"
|
|
|
|
}())
|
|
|
|
|
|
|
|
resp, err := c.sendRequest(method, path, map[string]string{
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
}, buf)
|
2024-05-07 21:49:09 +03:00
|
|
|
if err != nil {
|
2024-05-10 11:46:43 +03:00
|
|
|
return 0, err
|
2024-05-07 21:49:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
defer func() { _ = resp.Body.Close() }()
|
2024-05-10 11:46:43 +03:00
|
|
|
if out != nil {
|
|
|
|
return resp.StatusCode, json.NewDecoder(resp.Body).Decode(out)
|
|
|
|
}
|
|
|
|
c.log.Debugf("[Redmine Response] code: %d; body: %#v", resp.StatusCode, out)
|
|
|
|
return resp.StatusCode, nil
|
2024-05-07 21:49:09 +03:00
|
|
|
}
|