klient

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2023 License: MIT Imports: 15 Imported by: 29

README

klient

License Coverage GitHub Workflow Status Go Report Card Go PKG

Retryable http client with some helper functions.

go get github.com/worldline-go/klient

Usage

Create a new client with a base url. Base url is mandatory in default also it can set with KLIENT_BASE_URL environment variable.

client, err := klient.New(klient.OptionClient.WithBaseURL("https://api.punkapi.com/v2/"))
if err != nil {
    // handle error
}

Client has Do and DoWithInf methods to send request.
Methods will automatically drain and close the response body and it resolves reference of URL.

Request with http.Request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "beers/random", nil)
if err != nil {
	// handle error
}

var response interface{}

if err := client.Do(req, func(r *http.Response) error {
	if r.StatusCode != http.StatusOK {
		return klient.ResponseError(r)
	}

	if err := json.NewDecoder(r.Body).Decode(&response); err != nil {
		return fmt.Errorf("failed to decode response: %w, body: %v", err, klient.LimitedResponse(r))
	}

	return nil
}); err != nil {
	// handle error
}

log.Info().Interface("beers", response).Msg("got beers")
Request with interface

Set an API's struct with has client.

type BeerAPI struct {
	klient *klient.Client
}

type RandomRequest struct{}

func (RandomRequest) Method() string {
	return http.MethodGet
}

func (RandomRequest) Path() string {
	return "beers/random"
}

type RandomRequestResponse struct {
	Name string `json:"name"`
}

func (c BeerAPI) GetRandomBeer(ctx context.Context) ([]RandomRequestResponse, error) {
	var v []RandomRequestResponse

	if err := c.klient.DoWithInf(ctx, RandomRequest{}, func(r *http.Response) error {
		if r.StatusCode != http.StatusOK {
			return klient.UnexpectedResponseError(r)
		}

		return json.NewDecoder(r.Body).Decode(&v)
	}); err != nil {
		return nil, err
	}

	return v, nil
}

Now you need to create a new instance of your API and use it.

api := BeerAPI{
    klient: client,
}

respond, err := api.GetRandomBeer(ctx)
if err != nil {
    // handle error
}

Documentation

Overview

Example
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/worldline-go/klient"
)

type Client struct {
	klient *klient.Client
}

func (c *Client) CreateX(ctx context.Context, r CreateXRequest) (*CreateXResponse, error) {
	var v CreateXResponse
	if err := c.klient.DoWithInf(ctx, r, func(r *http.Response) error {
		if r.StatusCode != http.StatusOK {
			return klient.ResponseError(r)
		}

		if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
			return err
		}

		return nil
	}); err != nil {
		return nil, err
	}

	return &v, nil
}

// ----

type CreateXRequest struct {
	ID string `json:"id"`
}

func (CreateXRequest) Method() string {
	return http.MethodPost
}

func (CreateXRequest) Path() string {
	return "/api/v1/x"
}

func (r CreateXRequest) BodyJSON() interface{} {
	return r
}

func (r CreateXRequest) Validate() error {
	if r.ID == "" {
		return fmt.Errorf("id is required")
	}

	return nil
}

func (r CreateXRequest) Header() http.Header {
	v := http.Header{}
	v.Set("X-Info", "example")

	return v
}

type CreateXResponse struct {
	RequestID string `json:"request_id"`
}

// ---

func main() {
	httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// check request method
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(`{"error": "invalid request method"}`))
			return
		}

		// check request path
		if r.URL.Path != "/api/v1/x" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(`{"error": "invalid request path"}`))
			return
		}

		// check request header
		if r.Header.Get("X-Info") != "example" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(`{"error": "invalid request header"}`))
			return
		}

		// get request body
		var m map[string]interface{}
		if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(`{"error": "invalid request body"}`))
			return
		}

		// check request body
		if m["id"] != "123" {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(`{"error": "invalid id"}`))
			return
		}

		// write response
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"request_id": "123+"}`))
	}))

	defer httpServer.Close()

	httpxClient, err := klient.New(
		klient.OptionClient.WithBaseURL(httpServer.URL),
	)
	if err != nil {
		fmt.Println(err)
		return
	}

	c := &Client{
		klient: httpxClient,
	}

	v, err := c.CreateX(context.Background(), CreateXRequest{
		ID: "123",
	})
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(v.RequestID)
}
Output:

123+

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrValidating      = fmt.Errorf("failed to validate request")
	ErrMarshal         = fmt.Errorf("failed to marshal request body")
	ErrRequest         = fmt.Errorf("failed to do request")
	ErrResponseFuncNil = fmt.Errorf("response function is nil")
)
View Source
var OptionClient = OptionClientHolder{}
View Source
var OptionRetry = OptionRetryHolder{}
View Source
var ResponseErrLimit int64 = 1 << 20 // 1MB
View Source
var TransportCtxKey ctxKlient = "HTTP_HEADER"

TransportCtxKey is the context key to use with context.WithValue to specify http.Header for a request.

Functions

func LimitedResponse

func LimitedResponse(resp *http.Response) []byte

LimitedResponse not close body, retry library draining it.

func NewRetryPolicy

func NewRetryPolicy(opts ...OptionRetryFn) retryablehttp.CheckRetry

func ResponseError added in v0.3.0

func ResponseError(resp *http.Response) error

ResponseError returns an error with the limited response body.

func ResponseFuncJSON added in v0.3.0

func ResponseFuncJSON(data interface{}) func(*http.Response) error

ResponseFuncJSON returns a response function that decodes the response into data with json decoder. It will return an error if the response status code is not 2xx.

If data is nil, it will not decode the response body.

func RetryPolicy

func RetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error)

RetryPolicy provides a default callback for Client.CheckRetry, which will retry on connection errors and server errors.

func UnexpectedResponse

func UnexpectedResponse(resp *http.Response) error

UnexpectedResponse returns an error if the response status code is not 2xx.

Types

type Client

type Client struct {
	HTTP    *http.Client
	BaseURL *url.URL
}

func New

func New(opts ...OptionClientFn) (*Client, error)

New creates a new http client with the provided options.

Default BaseURL is required, it can be disabled by setting DisableBaseURLCheck to true.

func (*Client) Do

func (c *Client) Do(req *http.Request, fn func(*http.Response) error) error

Do sends an HTTP request and calls the response function with resolving reference URL.

It is automatically drain and close the response body.

func (*Client) DoWithInf added in v0.3.0

func (c *Client) DoWithInf(ctx context.Context, req InfRequest, fn func(*http.Response) error) error

DoWithInf sends an HTTP request and calls the response function with resolving reference URL.

It is automatically drain and close the response body.

Request additional implements InfRequestValidator, InfQueryStringGenerator, InfHeader, InfBody, InfBodyJSON.

type InfBody

type InfBody interface {
	// Body returns the body to be sent.
	Body() io.Reader
}

type InfBodyJSON

type InfBodyJSON interface {
	// BodyJSON can return any type that can be marshaled to JSON.
	// Automatically sets Content-Type to application/json.
	BodyJSON() interface{}
}

type InfHeader

type InfHeader interface {
	// Header returns the header to be sent.
	Header() http.Header
}

type InfQueryStringGenerator

type InfQueryStringGenerator interface {
	// ToQuery returns the query string to be sent.
	ToQuery() url.Values
}

type InfRequest

type InfRequest interface {
	// Method returns the HTTP method.
	Method() string
	// Path returns the path to be sent.
	Path() string
}

InfRequest is the base interface for all requests.

type InfRequestValidator

type InfRequestValidator interface {
	// Validate returns error if request is invalid.
	Validate() error
}

type Null

type Null[T any] struct {
	Value T
	Valid bool
}

type OptionClientFn added in v0.1.2

type OptionClientFn func(*optionClientValue)

OptionClientFn is a function that configures the client.

type OptionClientHolder added in v0.1.1

type OptionClientHolder struct{}

func (OptionClientHolder) WithBackoff added in v0.1.1

WithBackoff configures the client to use the provided backoff.

func (OptionClientHolder) WithBaseURL added in v0.1.1

func (OptionClientHolder) WithBaseURL(baseURL string) OptionClientFn

WithBaseURL configures the client to use the provided base URL.

func (OptionClientHolder) WithCtx added in v0.1.1

WithCtx for TransportWrapper call.

func (OptionClientHolder) WithDisableBaseURLCheck added in v0.1.1

func (OptionClientHolder) WithDisableBaseURLCheck(baseURLCheck bool) OptionClientFn

WithDisableBaseURLCheck configures the client to disable base URL check.

func (OptionClientHolder) WithDisableRetry added in v0.1.1

func (OptionClientHolder) WithDisableRetry(disableRetry bool) OptionClientFn

WithDisableRetry configures the client to disable retry.

func (OptionClientHolder) WithHTTPClient added in v0.1.1

func (OptionClientHolder) WithHTTPClient(httpClient *http.Client) OptionClientFn

WithHTTPClient configures the client to use the provided http client.

func (OptionClientHolder) WithInsecureSkipVerify added in v0.1.1

func (OptionClientHolder) WithInsecureSkipVerify(insecureSkipVerify bool) OptionClientFn

WithInsecureSkipVerify configures the client to skip TLS verification.

func (OptionClientHolder) WithLogger added in v0.1.1

func (OptionClientHolder) WithLogger(logger zerolog.Logger) OptionClientFn

WithLogger configures the client to use the provided logger.

func (OptionClientHolder) WithMaxConnections added in v0.1.1

func (OptionClientHolder) WithMaxConnections(maxConnections int) OptionClientFn

WithMaxConnections configures the client to use the provided maximum number of idle connections.

func (OptionClientHolder) WithPooledClient added in v0.1.1

func (OptionClientHolder) WithPooledClient(pooledClient bool) OptionClientFn

func (OptionClientHolder) WithRetryLog added in v0.1.1

func (OptionClientHolder) WithRetryLog(retryLog bool) OptionClientFn

WithRetryLog configures the client to use the provided retry log flag, default is true.

This option is only used with default retry policy.

func (OptionClientHolder) WithRetryMax added in v0.1.1

func (OptionClientHolder) WithRetryMax(retryMax int) OptionClientFn

WithRetryMax configures the client to use the provided maximum number of retry.

func (OptionClientHolder) WithRetryOptions added in v0.1.1

func (OptionClientHolder) WithRetryOptions(opts ...OptionRetryFn) OptionClientFn

func (OptionClientHolder) WithRetryPolicy added in v0.1.1

func (OptionClientHolder) WithRetryPolicy(retryPolicy retryablehttp.CheckRetry) OptionClientFn

WithRetryPolicy configures the client to use the provided retry policy.

func (OptionClientHolder) WithRetryWaitMax added in v0.1.1

func (OptionClientHolder) WithRetryWaitMax(retryWaitMax time.Duration) OptionClientFn

WithRetryWaitMax configures the client to use the provided maximum wait time.

func (OptionClientHolder) WithRetryWaitMin added in v0.1.1

func (OptionClientHolder) WithRetryWaitMin(retryWaitMin time.Duration) OptionClientFn

WithRetryWaitMin configures the client to use the provided minimum wait time.

func (OptionClientHolder) WithRoundTripper added in v0.3.0

WithRoundTripper configures the client to wrap the default transport.

func (OptionClientHolder) WithTimeout added in v0.2.2

func (OptionClientHolder) WithTimeout(timeout time.Duration) OptionClientFn

WithTimeout configures the client to use the provided timeout. Default is no timeout.

Warning: This timeout is for the whole request, including retries.

type OptionRetryFn added in v0.1.2

type OptionRetryFn func(*optionRetryValue)

type OptionRetryHolder added in v0.1.1

type OptionRetryHolder struct{}

func (OptionRetryHolder) WithOptionRetry added in v0.1.1

func (OptionRetryHolder) WithOptionRetry(oRetry *OptionRetryValue) OptionRetryFn

WithOptionRetry configures the retry policy directly.

This option overrides all other retry options when previously set.

func (OptionRetryHolder) WithRetryDisable added in v0.1.1

func (OptionRetryHolder) WithRetryDisable() OptionRetryFn

func (OptionRetryHolder) WithRetryDisabledStatusCodes added in v0.1.1

func (OptionRetryHolder) WithRetryDisabledStatusCodes(codes ...int) OptionRetryFn

func (OptionRetryHolder) WithRetryEnabledStatusCodes added in v0.1.1

func (OptionRetryHolder) WithRetryEnabledStatusCodes(codes ...int) OptionRetryFn

func (OptionRetryHolder) WithRetryLog added in v0.1.1

func (OptionRetryHolder) WithRetryLog(log logz.Adapter) OptionRetryFn

type OptionRetryValue

type OptionRetryValue = optionRetryValue

type TransportCtx added in v0.2.1

type TransportCtx struct {
	// Base is the base RoundTripper used to make HTTP requests.
	// If nil, http.DefaultTransport is used.
	Base http.RoundTripper
}

Transport is an http.RoundTripper that wrapping a base RoundTripper and adding headers from context.

func (*TransportCtx) RoundTrip added in v0.2.1

func (t *TransportCtx) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip authorizes and authenticates the request with an access token from Transport's Source.

func (*TransportCtx) SetHeader added in v0.2.1

func (t *TransportCtx) SetHeader(req *http.Request)

RoundTrip authorizes and authenticates the request with an access token from Transport's Source.

Directories

Path Synopsis
example
api command

Jump to

Keyboard shortcuts

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