package client import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "strings" "git.ctrlz.es/mgdelacroix/birthdaybot/model" ) var ( ErrEmptyURL = errors.New("URL cannot be empty") ) type Client struct { url string httpClient *http.Client headers map[string]string } func New(opts ...Option) (*Client, error) { client := &Client{ httpClient: &http.Client{}, } for _, opt := range opts { client = opt(client) } if client.url == "" { return nil, ErrEmptyURL } return client, nil } func (c *Client) Do(ctx context.Context, method, path, data string, headers map[string]string) (*http.Response, error) { url, err := url.JoinPath(c.url, path) if err != nil { return nil, fmt.Errorf("cannot build request url: %w", err) } req, err := http.NewRequestWithContext(ctx, method, url, strings.NewReader(data)) if err != nil { return nil, fmt.Errorf("cannot create request: %w", err) } for k, v := range c.headers { req.Header.Set(k, v) } for k, v := range headers { req.Header.Set(k, v) } resp, err := c.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("cannot do request: %w", err) } return resp, nil } func (c *Client) Health(ctx context.Context) (bool, error) { resp, err := c.Do(ctx, http.MethodGet, "/health", "", nil) if err != nil { return false, err } defer resp.Body.Close() return resp.StatusCode == 200, nil } func (c *Client) NextBirthdays(ctx context.Context) ([]*model.Birthday, error) { resp, err := c.Do(ctx, http.MethodGet, "/next_birthdays", "", nil) if err != nil { return nil, err } defer resp.Body.Close() var birthdays []*model.Birthday if err := json.NewDecoder(resp.Body).Decode(&birthdays); err != nil { return nil, fmt.Errorf("cannot decode birthdays: %w", err) } return birthdays, nil }