klient

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Mar 17, 2024 License: MIT Imports: 15 Imported by: 7

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 API_GATEWAY_ADDRESS environment variable.

client, err := klient.New(klient.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.ErrResponse(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

Our interface just one function to create a request.

type Requester interface {
	Request(context.Context) (*http.Request, error)
}

Set an API's struct with has client.

type BeerAPI struct {
	client *klient.Client
}

type RandomGet struct{}

func (r RandomGet) Request(ctx context.Context) (*http.Request, error) {
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, "beers/random", nil)
	if err != nil {
		return nil, err
	}

	return req, nil
}

func (r RandomGet) Response(resp *http.Response) ([]RandomRequestResponse, error) {
	var v []RandomRequestResponse
	if err := klient.ResponseFuncJSON(&v)(resp); err != nil {
		return nil, err
	}

	return v, nil
}


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

func (c BeerAPI) GetRandomBeer(ctx context.Context) ([]RandomRequestResponse, error) {
	v, err := klient.DoWithInf(ctx, c.client.HTTP, RandomGet{})
	if 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
}

Env values

Name Description
API_GATEWAY_ADDRESS Base url of client if not set with WithBaseURL.
KLIENT_BASE_URL Base url of client same with API_GATEWAY_ADDRESS but more priority.
KLIENT_INSECURE_SKIP_VERIFY Skip tls verify. Ex KLIENT_INSECURE_SKIP_VERIFY=true
KLIENT_TIMEOUT Timeout for http client. Ex: KLIENT_TIMEOUT=30s
KLIENT_RETRY_DISABLE Disable retry. Ex: KLIENT_RETRY_DISABLE=true

Documentation

Overview

Example
package main

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

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

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

func (r CreateXRequest) Request(ctx context.Context) (*http.Request, error) {
	if r.ID == "" {
		return nil, fmt.Errorf("id is required")
	}

	bodyData, err := json.Marshal(r)
	if err != nil {
		return nil, fmt.Errorf("unable to marshal request body: %w", err)
	}

	body := bytes.NewReader(bodyData)

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/api/v1/x", body)
	if err != nil {
		return nil, err
	}

	req.Header.Add("X-Info", "example")

	return req, nil
}

func (CreateXRequest) Response(resp *http.Response) (CreateXResponse, error) {
	var v CreateXResponse

	if err := klient.UnexpectedResponse(resp); err != nil {
		return v, err
	}

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

	return v, nil
}

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()

	// os.Setenv("API_GATEWAY_ADDRESS", httpServer.URL)
	klient.DefaultBaseURL = httpServer.URL

	client, err := klient.New()
	if err != nil {
		fmt.Println(err)
		return
	}

	v, err := klient.DoWithInf(context.Background(), client.HTTP, 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 (

	// DefaultBaseURL when empty "API_GATEWAY_ADDRESS" or "KLIENT_BASE_URL" env value will be used.
	DefaultBaseURL = ""

	// DisableEnvValues when true will disable all env values check.
	DisableEnvValues = false

	EnvKlientBaseURL            = "KLIENT_BASE_URL"
	EnvKlientBaseURLGlobal      = "API_GATEWAY_ADDRESS"
	EnvKlientInsecureSkipVerify = "KLIENT_INSECURE_SKIP_VERIFY"
	EnvKlientTimeout            = "KLIENT_TIMEOUT"
	EnvKlientRetryDisable       = "KLIENT_RETRY_DISABLE"
)
View Source
var (
	ErrCreateRequest   = fmt.Errorf("failed to create request")
	ErrRequest         = fmt.Errorf("failed to do request")
	ErrResponseFuncNil = fmt.Errorf("response function is nil")
	ErrRequesterNil    = fmt.Errorf("requester is nil")
)
View Source
var OptionRetry = OptionRetryHolder{}
View Source
var ResponseErrLimit int64 = 1 << 20 // 1MB
View Source
var TransportHeaderKey ctxKlient = "HTTP_HEADER"

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

Functions

func Do added in v0.6.0

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

Do sends an HTTP request and calls the response function with using http.Client.

func DoWithInf added in v0.6.0

func DoWithInf[T any](ctx context.Context, client *http.Client, r Requester[T]) (T, error)

DoWithInf sends an HTTP request and calls the response function with using http.Client and Requester.

func DrainBody added in v0.5.0

func DrainBody(body io.ReadCloser)

DrainBody reads the entire content of r and then closes the underlying io.ReadCloser.

func ErrResponse added in v0.6.0

func ErrResponse(resp *http.Response) error

ErrResponse returns an error with the limited response body.

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 PassthroughErrorHandler added in v0.7.7

func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error)

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 with calling ResponseError.

Types

type Client

type Client struct {
	HTTP *http.Client
}

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 NewPlain added in v0.8.0

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

NewPlain creates a new http client with the some default disabled automatic features.

  • klient.WithDisableBaseURLCheck(true)
  • klient.WithDisableRetry(true)
  • klient.WithDisableEnvValues(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.

type Config added in v0.8.0

type Config struct {
	BaseURL string `cfg:"base_url"`

	Timeout             time.Duration `cfg:"timeout"`
	DisableBaseURLCheck *bool         `cfg:"disable_base_url_check"`
	DisableEnvValues    *bool         `cfg:"disable_env_values"`
	InsecureSkipVerify  *bool         `cfg:"insecure_skip_verify"`

	DisableRetry *bool         `cfg:"disable_retry"`
	RetryMax     int           `cfg:"retry_max"`
	RetryWaitMin time.Duration `cfg:"retry_wait_min"`
	RetryWaitMax time.Duration `cfg:"retry_wait_max"`
}

func (Config) New added in v0.8.0

func (c Config) New(options ...OptionClientFn) (*Client, 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.

func WithBackoff added in v0.7.2

func WithBackoff(backoff retryablehttp.Backoff) OptionClientFn

WithBackoff configures the client to use the provided backoff.

func WithBaseURL added in v0.7.2

func WithBaseURL(baseURL string) OptionClientFn

WithBaseURL configures the client to use the provided base URL.

func WithCtx added in v0.7.2

func WithCtx(ctx context.Context) OptionClientFn

WithCtx for TransportWrapper call.

func WithDisableBaseURLCheck added in v0.7.2

func WithDisableBaseURLCheck(baseURLCheck bool) OptionClientFn

WithDisableBaseURLCheck configures the client to disable base URL check.

func WithDisableEnvValues added in v0.7.6

func WithDisableEnvValues(disableEnvValues bool) OptionClientFn

WithDisableEnvValues configures the client to disable all env values check.

  • API_GATEWAY_ADDRESS and KLIENT_* env values will be disabled.

func WithDisableRetry added in v0.7.2

func WithDisableRetry(disableRetry bool) OptionClientFn

WithDisableRetry configures the client to disable retry.

func WithHTTPClient added in v0.7.2

func WithHTTPClient(httpClient *http.Client) OptionClientFn

WithHTTPClient configures the client to use the provided http client.

func WithHeader added in v0.7.2

func WithHeader(header http.Header) OptionClientFn

WithHeader configures the client to use this default header if not exist.

func WithInsecureSkipVerify added in v0.7.2

func WithInsecureSkipVerify(insecureSkipVerify bool) OptionClientFn

WithInsecureSkipVerify configures the client to skip TLS verification.

func WithLogger added in v0.7.2

func WithLogger(logger logz.Adapter) OptionClientFn

WithLogger configures the client to use the provided logger.

For zerolog logz.AdapterKV{Log: logger} can usable.

func WithMaxConnections added in v0.7.2

func WithMaxConnections(maxConnections int) OptionClientFn

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

func WithPooledClient added in v0.7.2

func WithPooledClient(pooledClient bool) OptionClientFn

func WithRetryLog added in v0.7.2

func 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 WithRetryMax added in v0.7.2

func WithRetryMax(retryMax int) OptionClientFn

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

func WithRetryOptions added in v0.7.2

func WithRetryOptions(opts ...OptionRetryFn) OptionClientFn

func WithRetryPolicy added in v0.7.2

func WithRetryPolicy(retryPolicy retryablehttp.CheckRetry) OptionClientFn

WithRetryPolicy configures the client to use the provided retry policy.

func WithRetryWaitMax added in v0.7.2

func WithRetryWaitMax(retryWaitMax time.Duration) OptionClientFn

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

func WithRetryWaitMin added in v0.7.2

func WithRetryWaitMin(retryWaitMin time.Duration) OptionClientFn

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

func WithRoundTripper added in v0.7.2

func WithRoundTripper(f ...func(context.Context, http.RoundTripper) (http.RoundTripper, error)) OptionClientFn

WithRoundTripper configures the client to wrap the default transport.

func WithTimeout added in v0.7.2

func 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 Requester added in v0.4.0

type Requester[T any] interface {
	// Request returns the http.Request.
	Request(context.Context) (*http.Request, error)
	Response(resp *http.Response) (T, error)
}

Requester is the base interface to send an HTTP request.

type RoundTripperFunc added in v0.4.1

type RoundTripperFunc = func(context.Context, http.RoundTripper) (http.RoundTripper, error)

type TransportKlient added in v0.5.0

type TransportKlient struct {
	// Base is the base RoundTripper used to make HTTP requests.
	// If nil, http.DefaultTransport is used.
	Base http.RoundTripper
	// Header for default header to set if not exist.
	Header http.Header
	// BaseURL is the base URL for relative requests.
	BaseURL *url.URL
}

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

func (*TransportKlient) RoundTrip added in v0.5.0

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

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

func (*TransportKlient) SetHeader added in v0.5.0

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

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

Directories

Path Synopsis
example
api

Jump to

Keyboard shortcuts

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