117 lines
2.8 KiB
Go
117 lines
2.8 KiB
Go
package slapshotapi
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type endpoint interface {
|
|
path() string
|
|
method() string
|
|
}
|
|
|
|
// bodyEndpoint is an optional interface for endpoints that send a JSON body
|
|
type bodyEndpoint interface {
|
|
endpoint
|
|
body() ([]byte, error)
|
|
}
|
|
|
|
func (c *SlapAPI) request(
|
|
ctx context.Context,
|
|
ep endpoint,
|
|
) ([]byte, error) {
|
|
baseurl := fmt.Sprintf("https://%s.slapshot.gg%s", c.env, ep.path())
|
|
|
|
var bodyReader io.Reader
|
|
if bep, ok := ep.(bodyEndpoint); ok {
|
|
data, err := bep.body()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "endpoint.body")
|
|
}
|
|
bodyReader = bytes.NewReader(data)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, ep.method(), baseurl, bodyReader)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "http.NewRequestWithContext")
|
|
}
|
|
req.Header.Add("Accept", "application/json")
|
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.key))
|
|
if bodyReader != nil {
|
|
req.Header.Add("Content-Type", "application/json")
|
|
}
|
|
|
|
res, err := c.do(ctx, req)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "c.do")
|
|
}
|
|
defer func() { _ = res.Body.Close() }()
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
return nil, errors.New(fmt.Sprintf("API request failed with status %d", res.StatusCode))
|
|
}
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "io.ReadAll")
|
|
}
|
|
return body, nil
|
|
}
|
|
|
|
// requestRaw performs an API request and returns the raw status code and body.
|
|
// This is used for endpoints where non-200 status codes carry meaningful data
|
|
// (e.g. DELETE returning a plain text response).
|
|
func (c *SlapAPI) requestRaw(
|
|
ctx context.Context,
|
|
ep endpoint,
|
|
) (int, []byte, error) {
|
|
baseurl := fmt.Sprintf("https://%s.slapshot.gg%s", c.env, ep.path())
|
|
|
|
var bodyReader io.Reader
|
|
if bep, ok := ep.(bodyEndpoint); ok {
|
|
data, err := bep.body()
|
|
if err != nil {
|
|
return 0, nil, errors.Wrap(err, "endpoint.body")
|
|
}
|
|
bodyReader = bytes.NewReader(data)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, ep.method(), baseurl, bodyReader)
|
|
if err != nil {
|
|
return 0, nil, errors.Wrap(err, "http.NewRequestWithContext")
|
|
}
|
|
req.Header.Add("Accept", "application/json")
|
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.key))
|
|
if bodyReader != nil {
|
|
req.Header.Add("Content-Type", "application/json")
|
|
}
|
|
|
|
res, err := c.do(ctx, req)
|
|
if err != nil {
|
|
return 0, nil, errors.Wrap(err, "c.do")
|
|
}
|
|
defer func() { _ = res.Body.Close() }()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return 0, nil, errors.Wrap(err, "io.ReadAll")
|
|
}
|
|
return res.StatusCode, body, nil
|
|
}
|
|
|
|
// unmarshal is a helper that unmarshals JSON response data into a target struct
|
|
func unmarshal[T any](data []byte) (*T, error) {
|
|
var result T
|
|
err := json.Unmarshal(data, &result)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "json.Unmarshal")
|
|
}
|
|
return &result, nil
|
|
}
|