klient

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2023 License: MIT Imports: 14 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.

client, err := klient.New(klient.OptionClient.WithBaseURL("https://api.punkapi.com/v2/"))
if err != nil {
    // handle 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"
}

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

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

	if err := c.klient.DoWithFunc(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.DoWithFunc(ctx, r, func(r *http.Response) error {
		if r.StatusCode != http.StatusOK {
			return klient.UnexpectedResponseError(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 = optionClient{}
View Source
var OptionRetry = optionRetry{}
View Source
var ResponseErrLimit int64 = 1 << 20 // 1MB

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 Request

func Request(ctx context.Context, baseURL, method, path string, body io.Reader, header http.Header, fn func(*http.Response) error) error

Request sends an HTTP request and calls the response function with the global client.

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.

func UnexpectedResponseError

func UnexpectedResponseError(resp *http.Response) error

UnexpectedResponseError returns an error with the response body.

Types

type Client

type Client struct {
	HTTPClient *http.Client

	BaseURL *url.URL
	// contains filtered or unexported fields
}

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(ctx context.Context, req InfRequest, resp interface{}) error

Do sends an HTTP request and json unmarshals the response body to data.

Do work same as DoWithFunc with defaultResponseFunc.

func (*Client) DoWithFunc

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

DoWithFunc sends an HTTP request and calls the response function.

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

func (*Client) Request

func (c *Client) Request(ctx context.Context, method, path string, body io.Reader, header http.Header, fn func(*http.Response) error) error

Request sends an HTTP request and calls the response function.

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 OptionRetryValue

type OptionRetryValue = optionRetryValue

Directories

Path Synopsis
example
api command

Jump to

Keyboard shortcuts

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