dcbot/internal/twhelp/client.go

163 lines
3.6 KiB
Go
Raw Normal View History

package twhelp
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"
)
const (
defaultUserAgent = "TWHelpDCBot/development"
defaultTimeout = 10 * time.Second
endpointListVersions = "/api/v1/versions"
endpointGetServer = "/api/v1/versions/%s/servers/%s"
endpointGetTribeByTag = "/api/v1/versions/%s/servers/%s/tribes/tag/%s"
endpointListEnnoblements = "/api/v1/versions/%s/servers/%s/ennoblements"
)
type Client struct {
userAgent string
client *http.Client
baseURL *url.URL
}
type ClientOption func(c *Client)
func WithHTTPClient(hc *http.Client) ClientOption {
return func(c *Client) {
c.client = hc
}
}
func WithUserAgent(ua string) ClientOption {
return func(c *Client) {
c.userAgent = ua
}
}
func NewClient(baseURL *url.URL, opts ...ClientOption) *Client {
c := &Client{
baseURL: baseURL,
userAgent: defaultUserAgent,
client: &http.Client{
Timeout: defaultTimeout,
},
}
for _, opt := range opts {
opt(c)
}
return c
}
func (c *Client) ListVersions(ctx context.Context) ([]Version, error) {
var resp listVersionsResp
if err := c.getJSON(ctx, endpointListVersions, &resp); err != nil {
return nil, err
}
return resp.Data, nil
}
func (c *Client) GetServer(ctx context.Context, version, server string) (Server, error) {
var resp getServerResp
if err := c.getJSON(ctx, fmt.Sprintf(endpointGetServer, version, server), &resp); err != nil {
return Server{}, err
}
return resp.Data, nil
}
func (c *Client) GetTribeByTag(ctx context.Context, version, server, tag string) (Tribe, error) {
var resp getTribeResp
if err := c.getJSON(ctx, fmt.Sprintf(endpointGetTribeByTag, version, server, tag), &resp); err != nil {
return Tribe{}, err
}
return resp.Data, nil
}
type ListEnnoblementsSort string
const (
ListEnnoblementsSortCreatedAtASC ListEnnoblementsSort = "createdAt:ASC"
)
func (s ListEnnoblementsSort) String() string {
return string(s)
}
type ListEnnoblementsQueryParams struct {
Limit int32
Offset int32
Since time.Time
Sort []ListEnnoblementsSort
}
func (c *Client) ListEnnoblements(ctx context.Context, version, server string, queryParams ListEnnoblementsQueryParams) ([]Ennoblement, error) {
q := url.Values{}
if queryParams.Limit > 0 {
q.Set("limit", strconv.Itoa(int(queryParams.Limit)))
}
if queryParams.Offset > 0 {
q.Set("offset", strconv.Itoa(int(queryParams.Offset)))
}
if !queryParams.Since.IsZero() {
q.Set("since", queryParams.Since.Format(time.RFC3339))
}
for _, s := range queryParams.Sort {
q.Add("sort", s.String())
}
var resp listEnnoblementsResp
if err := c.getJSON(ctx, fmt.Sprintf(endpointListEnnoblements, version, server)+"?"+q.Encode(), &resp); err != nil {
return nil, err
}
return resp.Data, nil
}
func (c *Client) getJSON(ctx context.Context, urlStr string, v any) error {
u, err := c.baseURL.Parse(urlStr)
if err != nil {
return fmt.Errorf("c.baseURL.Parse: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return fmt.Errorf("http.NewRequestWithContext: %w", err)
}
// headers
req.Header.Set("User-Agent", c.userAgent)
resp, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("client.Do: %w", err)
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
var errResp errorResp
if err = json.NewDecoder(resp.Body).Decode(&errResp); err != nil {
_, _ = io.Copy(io.Discard, resp.Body)
return fmt.Errorf("got non-ok HTTP status: %d", resp.StatusCode)
}
return errResp.Error
}
if err = json.NewDecoder(resp.Body).Decode(v); err != nil {
return fmt.Errorf("couldn't decode resp body: %w", err)
}
return nil
}