klient

package module
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2023 License: MIT Imports: 14 Imported by: 16

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

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 {
	klient *klient.Client
}

type RandomRequest struct{}

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

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

func (r RandomRequest) Request(ctx context.Context) (*http.Request, error) {
	req, err := http.NewRequestWithContext(ctx, r.Method(), r.Path(), nil)
	if err != nil {
		return nil, err
	}

	return req, nil
}

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 (
	"bytes"
	"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) 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
}

func (r CreateXRequest) Request(ctx context.Context) (*http.Request, error) {
	if err := r.Validate(); err != nil {
		return nil, err
	}

	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, r.Method(), r.Path(), body)
	if err != nil {
		return nil, err
	}

	req.Header = r.Header()

	return req, 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()

	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 (
	ErrCreateRequest   = fmt.Errorf("failed to create request")
	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 TransportHeaderKey ctxKlient = "HTTP_HEADER"

TransportHeaderKey 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, request Requester, 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.

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

func (OptionClientHolder) WithBackoff(backoff retryablehttp.Backoff) OptionClientFn

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) WithDisableTransportHeader added in v0.4.1

func (OptionClientHolder) WithDisableTransportHeader() OptionClientFn

WithDisableTransportHeader to disable TransportHeader in default.

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) WithHeader added in v0.4.1

func (OptionClientHolder) WithHeader(header http.Header) OptionClientFn

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

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

type Requester interface {
	// Request returns the http.Request.
	Request(context.Context) (*http.Request, 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 TransportHeader added in v0.4.1

type TransportHeader 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
}

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

func (*TransportHeader) RoundTrip added in v0.4.1

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

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

func (*TransportHeader) SetHeader added in v0.4.1

func (t *TransportHeader) 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