restclient

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2022 License: MPL-2.0 Imports: 18 Imported by: 9

README

GoDoc Mozilla Public License Build Status Go Report Card

This is a simple http client package with validation. It makes use of github.com/go-playground/validator as well as the github.com/google/go-querystring packages.

Package restclient

This is yet another rest http client package for consuming APIs. The goal of this is to cover the most common use cases and be easy to use. This package has two main dependencies,

gopkg.in/go-playground/validator.v9

and

github.com/google/go-querystring

There are two main ways to use this, the Client methods, or BaseClient methods. BaseClient is a thin wrapper around Client, but but has a baseURL stored inside the struct so that will be used for each request. This is useful when the client will be hitting endpoints on a single base URL. The other way is with Client, which allows you to specify a baseURL with each call.

Following is an example program that demonstrates how to use this package.

package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"

	rc "github.com/myENA/restclient"
)

type UserCapsRequest struct {
	UID string `url:"uid" validate:"required"`

	// UserCaps - this deonstrates using the validate:"dive" tag to
	// visit members of the list, as well as the url "semicolon" modifier
	// which gives us how to represent members of the slice on the
	// uri string.
	UserCaps []UserCap `url:"user-caps,semicolon" validate:"required,dive"`
}

type UserCap struct {
	// This demonstrates the validate alternation syntax for "enum" type
	// values
	Type string `json:"type" validate:"required,eq=users|eq=buckets|eq=metadata|eq=usage|eq=zone"`

	// This demonstrates how to represent an embedded comma character in
	// alternation syntax.
	Permission string `json:"perm" validate:"required,eq=*|eq=read|eq=write|eq=read0x2Cwrite"`
}

// String - Implement Stringer, informs go query string how to represent
// this struct.
func (uc UserCap) String() string {
	return uc.Type + "=" + uc.Permission
}

func main() {
	u, _ := url.Parse("http://localhost:8200/admin")
	bc := &rc.BaseClient{
		Client: &rc.Client{
			Client: &http.Client{},
			FixupCallback: func(req *http.Request) error {
				req.Header.Set("User-Agent", "TotallyChromeAndStuff")
				return nil
			},
		},
		BaseURL: u,
	}
	ucr := &UserCapsRequest{
		UID: "59",
		UserCaps: []UserCap{
			UserCap{
				Type:       "users",
				Permission: "*",
			},
			UserCap{
				Type:       "buckets",
				Permission: "*",
			},
			UserCap{
				Type:       "metadata",
				Permission: "*",
			},
			UserCap{
				Type:       "usage",
				Permission: "*",
			},
			UserCap{
				Type:       "zone",
				Permission: "*",
			},
		},
	}
	resp := []UserCap{}
	err := bc.Put(context.Background(), "/user?caps", ucr, nil, &resp)
	if err != nil {
		if _, ok := err.(rc.ValidationErrors); ok {
			// We had an error on validation from the query string.
			// Note that there is no pointer on the type assertion.  This
			// follows the playground validator package ValidationErrors.

			fmt.Println("Got a validation error: ", err)
		} else if e, ok := err.(*rc.ResponseError); ok {
			// We had a response code >= 400 from the server.  Note
			// the pointer on the type assertion above.  This
			// is to avoid breaking backwards compatibility.

			fmt.Printf("Got a response error code: %d\n", e.StatusCode)
		} else {
			// Something else went wrong.
			fmt.Println("Got unknown error: ", err)
		}
	}
}

Documentation

Overview

Package restclient - This is yet another rest http client package for consuming APIs. The goal of this is to cover the most common use cases and be easy to use. This package has two main dependencies,

gopkg.in/go-playground/validator.v9

and

github.com/google/go-querystring

There are two main ways to use this, the Client methods, or BaseClient methods. BaseClient is a thin wrapper around Client, but but has a baseURL stored inside the struct so that will be used for each request. This is useful when the client will be hitting endpoints on a single base URL. The other way is with Client, which allows you to specify a baseURL with each call.

Following is an example program that demonstrates how to use this package.

   package main

   import (
   	"context"
   	"fmt"
   	"net/http"
   	"net/url"

   	rc "github.com/myENA/restclient"
   )

   type UserCapsRequest struct {
   	UID string `url:"uid" validate:"required"`

   	// UserCaps - this deonstrates using the validate:"dive" tag to
   	// visit members of the list, as well as the url "semicolon" modifier
   	// which gives us how to represent members of the slice on the
   	// uri string.
   	UserCaps []UserCap `url:"user-caps,semicolon" validate:"required,dive"`
   }

   type UserCap struct {
   	// This demonstrates the validate alternation syntax for "enum" type
   	// values
   	Type string `json:"type" validate:"required,eq=users|eq=buckets|eq=metadata|eq=usage|eq=zone"`

   	// This demonstrates how to represent an embedded comma character in
   	// alternation syntax.
   	Permission string `json:"perm" validate:"required,eq=*|eq=read|eq=write|eq=read0x2Cwrite"`
   }

   // String - Implement Stringer, informs go query string how to represent
   // this struct.
   func (uc UserCap) String() string {
   	return uc.Type + "=" + uc.Permission
   }

   func main() {
   	u, _ := url.Parse("http://localhost:8200/admin")
     bc := &rc.BaseClient{
     Client: &rc.Client{
         Client: &http.Client{},
         FixupCallback: func(req *http.Request) error {
             req.Header.Set("User-Agent", "TotallyChromeAndStuff")
             return nil
         },
     },
		BaseURL: u,
	}
   	ucr := &UserCapsRequest{
   		UID: "59",
   		UserCaps: []UserCap{
   			UserCap{
   				Type:       "users",
   				Permission: "*",
   			},
   			UserCap{
   				Type:       "buckets",
   				Permission: "*",
   			},
   			UserCap{
   				Type:       "metadata",
   				Permission: "*",
   			},
   			UserCap{
   				Type:       "usage",
   				Permission: "*",
   			},
   			UserCap{
   				Type:       "zone",
   				Permission: "*",
   			},
   		},
   	}
   	resp := []UserCap{}
   	err := bc.Put(context.Background(), "/user?caps", ucr, nil, &resp)
   	if err != nil {
   		if _, ok := err.(rc.ValidationErrors); ok {
   			// We had an error on validation from the query string.
   			// Note that there is no pointer on the type assertion.  This
   			// follows the playground validator package ValidationErrors.

   			fmt.Println("Got a validation error: ", err)
   		} else if e, ok := err.(*rc.ResponseError); ok {
   			// We had a response code >= 400 from the server.  Note
   			// the pointer on the type assertion above.  This
   			// is to avoid breaking backwards compatibility.

   			fmt.Printf("Got a response error code: %d\n", e.StatusCode)
   		} else {
   			// Something else went wrong.
   			fmt.Println("Got unknown error: ", err)
   		}
   	}
   }

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BaseClient

type BaseClient struct {
	Client  *Client
	BaseURL *url.URL
}

BaseClient - convenience wrapper for requests that all go to the same BaseURL.

func NewBaseClient added in v1.0.1

func NewBaseClient(baseURL string, cfg *ClientConfig, transport http.RoundTripper) (*BaseClient, error)

NewBaseClient - create a new BaseClient instance based off of the baseURL string.

func (*BaseClient) Delete

func (bc *BaseClient) Delete(ctx context.Context, path string, queryStruct interface{}, responseBody interface{}) error

Delete - like Client.Delete, except uses BaseClient.BaseURL instead of needing to be passed in.

func (*BaseClient) Get

func (bc *BaseClient) Get(ctx context.Context, path string, queryStruct interface{}, responseBody interface{}) error

Get - like Client.Get, except uses the BaseClient.BaseURL instead of needing to be passed in.

func (*BaseClient) Post

func (bc *BaseClient) Post(ctx context.Context, path string, queryStruct, requestBody interface{}, responseBody interface{}) error

Post - like Client.Post, except uses BaseClient.BaseURL instead of needing to be passed in.

func (*BaseClient) Put

func (bc *BaseClient) Put(ctx context.Context, path string, queryStruct, requestBody interface{}, responseBody interface{}) error

Put - like Client.Put, except uses BaseClient.BaseURL instead of needing to be passed in.

func (*BaseClient) Req

func (bc *BaseClient) Req(ctx context.Context, method, path string, queryStruct,
	requestBody interface{}, responseBody interface{}) (*http.Response, error)

Req - like Client.Req, except uses BaseClient.BaseURL instead of needing to be passed in.

func (*BaseClient) ReqWithHeaders added in v1.0.5

func (bc *BaseClient) ReqWithHeaders(ctx context.Context, method, path string, queryStruct,
	requestBody interface{}, responseBody interface{}, headers http.Header) (*http.Response, error)

ReqWithHeaders - like client.ReqWithHeaders, except uses BaseClient.BaseURL instead of needing to be passed in.

type Client

type Client struct {
	Client *http.Client

	// Specifying this allows you to modify headers, add
	// auth tokens or signatures etc before the request is sent.
	FixupCallback FixupCallback

	// ErrorResponseCallback - allows you to specify custom behavior
	// on responses that are >= 400 status code.
	ErrorResponseCallback ErrorResponseCallback

	// StripBOM - setting this to true gives you the option to strip
	// byte order markings from certain responses.
	StripBOM bool

	// FormEncodedBody - setting this to true uses x-www-form-urlencoded.
	// false (default) will do json encoding.
	FormEncodedBody bool

	// SkipValidate - setting this to true bypasses validator run.
	SkipValidate bool
	// contains filtered or unexported fields
}

Client - admin api struct

func NewClient

func NewClient(cfg *ClientConfig, transport http.RoundTripper) (*Client, error)

NewClient - Client factory method. - if transport is nil, build one using config data in cfg. This is optional, you can also initialize the following way:

cl := &restclient.Client{Client: &http.Client{}}

func (*Client) Delete

func (cl *Client) Delete(ctx context.Context, baseURL *url.URL, path string, queryStruct interface{}, responseBody interface{}) error

Delete - makes an http DELETE request to baseURL with path appended, and queryStruct optionally parsed by go-querystring and validated with go-playground/validator.v9. Upon successful request, response is unmarshaled as json into responseBody, unless responseBody implements CustomDecoder, in which case Decode() is called.

func (*Client) Get

func (cl *Client) Get(ctx context.Context, baseURL *url.URL, path string, queryStruct interface{}, responseBody interface{}) error

Get - makes an http GET request to baseURL with path appended, and queryStruct optionally parsed by go-querystring and validated with go-playground/validator.v9. Upon successful request, response is unmarshaled as json into responseBody, unless responseBody implements CustomDecoder, in which case Decode() is called.

func (*Client) Post

func (cl *Client) Post(ctx context.Context, baseURL *url.URL, path string, queryStruct, requestBody interface{}, responseBody interface{}) error

Post - makes an http POST request to baseURL with path appended, and queryStruct optionally parsed by go-querystring and validated with go-playground/validator.v9. requestBody is passed to go-playground/validator.v9 and is sent json-encoded as the body. Upon successful request, response is unmarshaled as json into responseBody, unless responseBody implements CustomDecoder, in which case Decode() is called.

func (*Client) Put

func (cl *Client) Put(ctx context.Context, baseURL *url.URL, path string, queryStruct, requestBody interface{}, responseBody interface{}) error

Put - makes an http PUT request to baseURL with path appended, and queryStruct optionally parsed by go-querystring and validated with go-playground/validator.v9. requestBody is passed to go-playground/validator.v9 and is sent json-encoded as the body. Upon successful request, response is unmarshaled as json into responseBody, unless responseBody implements CustomDecoder, in which case Decode() is called.

func (*Client) Req

func (cl *Client) Req(ctx context.Context, baseURL *url.URL, method, path string,
	queryStruct, requestBody, responseBody interface{}) (*http.Response, error)

Req - like the method-specific versions above, this is the general purpose. the *http.Response return value will either be nil or return with the Body closed and fully read. This is mainly useful for inspecting headers, status code etc.

func (*Client) ReqWithHeaders added in v1.0.5

func (cl *Client) ReqWithHeaders(ctx context.Context, baseURL *url.URL, method, path string,
	queryStruct, requestBody, responseBody interface{}, headers http.Header) (*http.Response, error)

ReqWithHeaders - this is Req allowing you to specify custom headers.

type ClientConfig

type ClientConfig struct {
	ClientTimeout      Duration
	CACertBundlePath   string
	CACertBundle       []byte
	InsecureSkipVerify bool

	// RawValidateErrors - If true, then no attempt to interpret validator errors will be made.
	RawValidatorErrors bool

	// SkipValidate - if true, this bypasses all input validation for request bodies.
	SkipValidate bool

	// StripBOM - if true, this strips the byte order markings which will otherwise bork the json decoder.
	StripBOM bool

	// FixupCallback - this is a method that will get called before every request
	// so that you can, for instance, manipulate headers for auth purposes, for
	// instance.
	FixupCallback FixupCallback
}

ClientConfig - this configures an Client. This is meant to be easily serializable unserializable so that it can be used with yaml / toml etc for the purposes of config files.

Specify CACertBundlePath to load a bundle from disk to override the default. Specify CACertBundle if you want embed the cacert bundle in PEM format. Specify one or the other if you want to override, or neither to use the default. If both are specified, CACertBundle is honored.

type CustomDecoder

type CustomDecoder interface {
	Decode(data io.Reader) error
}

CustomDecoder - If a response struct implements this interface, calls the Decode() method instead of json.Unmarshal.

type Duration

type Duration time.Duration

Duration - this allows us to use a text representation of a duration and have it parse correctly. The go standard library time.Duration does not implement the TextUnmarshaller interface, so we have to do this workaround in order for json.Unmarshal or external parsers like toml.Decode to work with human friendly input.

func (Duration) MarshalText

func (d Duration) MarshalText() ([]byte, error)

MarshalText - this implements TextMarshaler

func (*Duration) UnmarshalText

func (d *Duration) UnmarshalText(text []byte) error

UnmarshalText - this implements the TextUnmarshaler interface

type ErrorResponseCallback

type ErrorResponseCallback func(resp *http.Response) error

ErrorResponseCallback - this allows you to hook into the error response, for response codes that are >= 400. If error returned here is nil, processing continues. Otherwise, the error returned is bubbled to caller.

type FixupCallback

type FixupCallback func(req *http.Request) error

FixupCallback - this is a method that will get called before every request so that you can, for instance, manipulate headers for auth purposes, for instance.

type ResponseError added in v0.1.4

type ResponseError struct {
	Status       string
	StatusCode   int
	ResponseBody []byte
	Header       http.Header
}

ResponseError - this is an http response error type. returned on >=400 status code.

func (*ResponseError) Error added in v0.1.4

func (rs *ResponseError) Error() string

type ValidationErrors added in v0.1.5

type ValidationErrors struct {
	// The original unmolested ValidationErrors from the validator package
	OrigVE validator.ValidationErrors
	// contains filtered or unexported fields
}

ValidationErrors - this is a thin wrapper around the validator ValidationErrors type. This makes a friendlier error message that attempts to interpret why validation failed and give a user friendly message.

func (ValidationErrors) Error added in v0.1.5

func (ve ValidationErrors) Error() string

Error - implement the Error interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL