1
0
Fork 0

Adding upstream version 0.8.9.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-22 10:16:14 +02:00
parent 3b2c48b5e4
commit c0c4addb85
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
285 changed files with 25880 additions and 0 deletions

View file

@ -0,0 +1,165 @@
package jsonclient
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
// ContentType defines the default MIME type for JSON requests.
const ContentType = "application/json"
// HTTPClientErrorThreshold specifies the status code threshold for client errors (400+).
const HTTPClientErrorThreshold = 400
// ErrUnexpectedStatus indicates an unexpected HTTP response status.
var (
ErrUnexpectedStatus = errors.New("got unexpected HTTP status")
)
// DefaultClient provides a singleton JSON client using http.DefaultClient.
var DefaultClient = NewClient()
// Client wraps http.Client for JSON operations.
type client struct {
httpClient *http.Client
headers http.Header
indent string
}
// Get fetches a URL using GET and unmarshals the response into the provided object using DefaultClient.
func Get(url string, response any) error {
if err := DefaultClient.Get(url, response); err != nil {
return fmt.Errorf("getting JSON from %q: %w", url, err)
}
return nil
}
// Post sends a request as JSON and unmarshals the response into the provided object using DefaultClient.
func Post(url string, request any, response any) error {
if err := DefaultClient.Post(url, request, response); err != nil {
return fmt.Errorf("posting JSON to %q: %w", url, err)
}
return nil
}
// NewClient creates a new JSON client using the default http.Client.
func NewClient() Client {
return NewWithHTTPClient(http.DefaultClient)
}
// NewWithHTTPClient creates a new JSON client using the specified http.Client.
func NewWithHTTPClient(httpClient *http.Client) Client {
return &client{
httpClient: httpClient,
headers: http.Header{
"Content-Type": []string{ContentType},
},
}
}
// Headers returns the default headers for requests.
func (c *client) Headers() http.Header {
return c.headers
}
// Get fetches a URL using GET and unmarshals the response into the provided object.
func (c *client) Get(url string, response any) error {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating GET request for %q: %w", url, err)
}
for key, val := range c.headers {
req.Header.Set(key, val[0])
}
res, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("executing GET request to %q: %w", url, err)
}
return parseResponse(res, response)
}
// Post sends a request as JSON and unmarshals the response into the provided object.
func (c *client) Post(url string, request any, response any) error {
var err error
var body []byte
if strReq, ok := request.(string); ok {
// If the request is a string, pass it through without serializing
body = []byte(strReq)
} else {
body, err = json.MarshalIndent(request, "", c.indent)
if err != nil {
return fmt.Errorf("marshaling request to JSON: %w", err)
}
}
req, err := http.NewRequestWithContext(
context.Background(),
http.MethodPost,
url,
bytes.NewReader(body),
)
if err != nil {
return fmt.Errorf("creating POST request for %q: %w", url, err)
}
for key, val := range c.headers {
req.Header.Set(key, val[0])
}
res, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("sending POST request to %q: %w", url, err)
}
return parseResponse(res, response)
}
// ErrorResponse checks if an error is a JSON error and unmarshals its body into the response.
func (c *client) ErrorResponse(err error, response any) bool {
var errMsg Error
if errors.As(err, &errMsg) {
return json.Unmarshal([]byte(errMsg.Body), response) == nil
}
return false
}
// parseResponse parses the HTTP response and unmarshals it into the provided object.
func parseResponse(res *http.Response, response any) error {
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if res.StatusCode >= HTTPClientErrorThreshold {
err = fmt.Errorf("%w: %v", ErrUnexpectedStatus, res.Status)
}
if err == nil {
err = json.Unmarshal(body, response)
}
if err != nil {
if body == nil {
body = []byte{}
}
return Error{
StatusCode: res.StatusCode,
Body: string(body),
err: err,
}
}
return nil
}