mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-01-08 04:03:58 +02:00
Add request builder to simplify request handling
This commit is contained in:
parent
9d39816709
commit
0bc0feb4bb
173
pkg/requests/builder.go
Normal file
173
pkg/requests/builder.go
Normal file
@ -0,0 +1,173 @@
|
||||
package requests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
// Builder allows users to construct a request and then either get the requests
|
||||
// response via Do(), parse the response into a simplejson.Json via JSON(),
|
||||
// or to parse the json response into an object via UnmarshalInto().
|
||||
type Builder interface {
|
||||
WithContext(context.Context) Builder
|
||||
WithBody(io.Reader) Builder
|
||||
WithMethod(string) Builder
|
||||
WithHeaders(http.Header) Builder
|
||||
SetHeader(key, value string) Builder
|
||||
Do() (*http.Response, error)
|
||||
UnmarshalInto(interface{}) error
|
||||
UnmarshalJSON() (*simplejson.Json, error)
|
||||
}
|
||||
|
||||
type builder struct {
|
||||
context context.Context
|
||||
method string
|
||||
endpoint string
|
||||
body io.Reader
|
||||
header http.Header
|
||||
response *http.Response
|
||||
}
|
||||
|
||||
// New provides a new Builder for the given endpoint.
|
||||
func New(endpoint string) Builder {
|
||||
return &builder{
|
||||
endpoint: endpoint,
|
||||
method: "GET",
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext adds a context to the request.
|
||||
// If no context is provided, context.Background() is used instead.
|
||||
func (r *builder) WithContext(ctx context.Context) Builder {
|
||||
r.context = ctx
|
||||
return r
|
||||
}
|
||||
|
||||
// WithBody adds a body to the request.
|
||||
func (r *builder) WithBody(body io.Reader) Builder {
|
||||
r.body = body
|
||||
return r
|
||||
}
|
||||
|
||||
// WithMethod sets the request method. Defaults to "GET".
|
||||
func (r *builder) WithMethod(method string) Builder {
|
||||
r.method = method
|
||||
return r
|
||||
}
|
||||
|
||||
// WithHeaders replaces the request header map with the given header map.
|
||||
func (r *builder) WithHeaders(header http.Header) Builder {
|
||||
r.header = header
|
||||
return r
|
||||
}
|
||||
|
||||
// SetHeader sets a single header to the given value.
|
||||
// May be used to add multiple headers.
|
||||
func (r *builder) SetHeader(key, value string) Builder {
|
||||
if r.header == nil {
|
||||
r.header = make(http.Header)
|
||||
}
|
||||
r.header.Set(key, value)
|
||||
return r
|
||||
}
|
||||
|
||||
// Do performs the request and returns the response in its raw form.
|
||||
// If the request has already been performed, returns the previous result.
|
||||
// This will not allow you to repeat a request.
|
||||
func (r *builder) Do() (*http.Response, error) {
|
||||
if r.response != nil {
|
||||
// Request has already been done
|
||||
return r.response, nil
|
||||
}
|
||||
|
||||
// Must provide a non-nil context to NewRequestWithContext
|
||||
if r.context == nil {
|
||||
r.context = context.Background()
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(r.context, r.method, r.endpoint, r.body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating request: %v", err)
|
||||
}
|
||||
req.Header = r.header
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing request: %v", err)
|
||||
}
|
||||
|
||||
r.response = resp
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UnmarshalInto performs the request and attempts to unmarshal the response into the
|
||||
// the given interface. The response body is assumed to be JSON.
|
||||
// The response must have a 200 status otherwise an error will be returned.
|
||||
func (r *builder) UnmarshalInto(into interface{}) error {
|
||||
resp, err := r.Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return UnmarshalInto(resp, into)
|
||||
}
|
||||
|
||||
// UnmarshalJSON performs the request and attempts to unmarshal the response into a
|
||||
// simplejson.Json. The response body is assume to be JSON.
|
||||
// The response must have a 200 status otherwise an error will be returned.
|
||||
func (r *builder) UnmarshalJSON() (*simplejson.Json, error) {
|
||||
resp, err := r.Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := getResponseBody(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := simplejson.NewJson(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading json: %v", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UnmarshalInto attempts to unmarshal the response into the the given interface.
|
||||
// The response body is assumed to be JSON.
|
||||
// The response must have a 200 status otherwise an error will be returned.
|
||||
func UnmarshalInto(resp *http.Response, into interface{}) error {
|
||||
body, err := getResponseBody(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, into); err != nil {
|
||||
return fmt.Errorf("error unmarshalling body: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getResponseBody extracts the response body, but will only return the body
|
||||
// if the response was successful.
|
||||
func getResponseBody(resp *http.Response) ([]byte, error) {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response body: %v", err)
|
||||
}
|
||||
|
||||
// Only unmarshal body if the response was successful
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status \"%d\": %s", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user