roku

package module
v1.5.3 Latest Latest
Warning

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

Go to latest
Published: May 24, 2024 License: MIT Imports: 12 Imported by: 0

README

Roku

Overview

The Roku REST client provides a straightforward and simple interface for interacting with REST APIs. With Roku you can:

  • Easily marshal and unmarshal requests and responses without the need for boilerplate or repetitive code.
  • Utilize the built-in exponential backoff algorithm.
  • Enhance your request functions by wrapping them with RxGo 2 Observable streams, enabling the creation of custom pipelines, including parallel requests.

Table of Contents

Usage.
  • Before starting to make requests, we need to create a *http.Client in Golang. For this, we can use Roku's constructor method:

    import (
      ...
      "github.com/v8tix/roku"
      "net/http"
      ...
    )
    
    func NewHTTPClient(
      timeout time.Duration,
      redirectPolicy func(req *http.Request, via []*http.Request) error,
      transport http.RoundTripper,
    ) *http.Client {
    ...
    }
    
    • Where:

      timeout: Its purpose is to control how long the client waits to receive a response from the server before considering the request as failed due to a timeout.

      redirectPolicy: Its purpose is to allow you to control how the HTTP client handles 3xx redirection responses.

      transport: Its purpose is to allow you to have more granular control over how HTTP requests are made and how network connections are managed.

    • Example:

    httpClient := roku.NewHTTPClient(
    	5*time.Second,
    	policy.OneRedirect,
    	transport.IdleConnectionTimeout(15*time.Second),
    )
    


    The OneRedirect and IdleConnectionTimeout functions are examples of how the Golang REST client can be configured and are part of Roku.

*Once our client is configured, we can start configuring the functions provided by Roku: Fetch and FetchRx. The difference between them is that Fetch does not provide a backoff mechanism and does not return the response as an observable. For production environments, we recommend using Rx.

  • FetchRx signature:
func FetchRx[T ReqI, U ResI](
  ctx context.Context,
  client *http.Client,
  method HTTPMethod,
  endpoint string,
  request *T,
  headers map[string]string,
  deadline time.Duration,
  backoffInterval time.Duration,
  backoffRetries uint64,
  statusCodeValidator ...func(res *http.Response) bool,
) rxgo.Observable {
  ...
}
  • FetchRx example:
  ch := roku.FetchRx[CreateUserV1Req, GetUserEnvV1Res](
          context.Background(),
          httpClient,
          roku.Put,
          url,
          createUserV1Req,
          nil,
          time.Second,
          150 * time.Millisecond,
          3,
  ).Observe()

  res, err := roku.To[roku.Envelope[GetUserEnvV1Res]](<-ch)
  switch err {
    case nil:
      if res.Body != nil {
        ...  
        return res.Body.User
      }
    default:
        var errHTTP roku.ErrInvalidHTTPStatus

        if errors.As(err, &errHTTP) {
            errDesc := roku.GetErrorDesc(errHTTP)
            return fmt.Errorf("http error: %v", errDesc)      
        }
  }
  • Notes about the example:
    • Due to generics in Golang, CreateUserV1Req and GetUserEnvV1Res must implement the roku.ReqI and roku.ResI interfaces, respectively.
  type CreateUserV1Req struct {
		Name  string `json:"name,omitempty"`
		Email string `json:"email,omitempty"`
  }
  
  func (CreateUserV1Req) Req() {}
  
  type GetUserEnvV1Res struct {
    User GetUserV1Res `json:"user,omitempty"`
  }
  
  func (GetUserEnvV1Res) Res() {}
  • In cases where the request does not have a request body, you should use the type roku.NoReq and pass nil as a parameter to FetchRx.
  • In cases where the request does not return a response, you should use the type roku.NoRes for FetchRx.
  ch:= roku.FetchRx[roku.NoReq, roku.NoRes](
    ctx,
    httpClient,
    roku.Delete,
    url,
    nil,
    nil,
    roku.DeadLine,
    roku.RetryInterval,
    3,
  ).Observe()

  _, err := roku.To[roku.Envelope[roku.NoRes]](<-ch)
  switch err {
    case nil:
      return nil
    default:
        var errHTTP roku.ErrInvalidHTTPStatus

        if errors.As(err, &errHTTP) {
            errDesc := roku.GetErrorDesc(errHTTP)
            return fmt.Errorf("http error: %v", errDesc)      
        }
  }
  • deadline: It enables FetchRx to block requests to the service, preventing both overloading and cascading failures.

  • backoffInterval: It represents a waiting period in which FetchRx delays before attempting to retry a failed request.

  • backoffRetries: It represents the maximum number of retry attempts that FetchRx will make for a request.

Contributing.
  1. Fork the repository
  2. Clone the forked repository
  3. Create a new branch for your feature or fix
  4. Make your changes
  5. Submit a pull request
License.

This project is licensed under the MIT License. You can refer to the LICENSE file for more information.

Documentation

Index

Constants

View Source
const (
	Get        = HTTPMethod("GET")
	Post       = HTTPMethod("POST")
	Put        = HTTPMethod("PUT")
	Patch      = HTTPMethod("PATCH")
	Delete     = HTTPMethod("DELETE")
	ConTimeOut = 15 * time.Second
)

Variables

View Source
var (
	ErrTimeOut                  = rokuErr("timeout occurred")
	ErrMarshallValue            = rokuErr("failed to marshal the value to JSON")
	ErrBadRequest               = rokuErr("bad request")
	ErrNonPointerOrWrongCasting = rokuErr("value is not a pointer or casting type is incorrect")
	ErrEmptyItem                = rokuErr("item has no value and no error")
	ErrNilValue                 = rokuErr("nil value provided")
	ErrNilRequest               = rokuErr("nil request provided")
	ErrBadlyJSON                = rokuErr("badly-formed JSON in the body")
	ErrBadJSONType              = rokuErr("incorrect JSON type in the body")
	ErrEmptyBody                = rokuErr("body must not be empty")
	ErrBodyUnknownKey           = rokuErr("unknown key in the body")
	ErrBodySizeLimit            = rokuErr("body size limit exceeded")
	ErrBodyValue                = rokuErr("body must contain a single JSON value")
)

Functions

func FetchRx

func FetchRx[T ReqI, U ResI](
	ctx context.Context,
	client *http.Client,
	method HTTPMethod,
	endpoint string,
	request *T,
	headers map[string]string,
	deadline time.Duration,
	backoffInterval time.Duration,
	backoffRetries uint64,
	statusCodeValidator ...func(res *http.Response) bool,
) rxgo.Observable

func NewHTTPClient

func NewHTTPClient(
	timeout time.Duration,
	redirectPolicy func(req *http.Request, via []*http.Request) error,
	transport http.RoundTripper,
) *http.Client

func ReadJSON

func ReadJSON(body io.Reader, dst any) error

func To

func To[T any](item rxgo.Item) (*T, error)

Types

type Envelope

type Envelope[T ResI] struct {
	Body *T
	*http.Response
}

func Fetch

func Fetch[T ReqI, U ResI](
	ctx context.Context,
	client *http.Client,
	method HTTPMethod,
	endpoint string,
	request *T,
	headers map[string]string,
	deadline time.Duration,
	statusCodeValidator ...func(res *http.Response) bool,
) (*Envelope[U], error)

type ErrDesc

type ErrDesc struct {
	StatusCode int    `json:"status_code,omitempty"`
	Status     string `json:"status,omitempty"`
	ErrMessage string `json:"error_message,omitempty"`
}

func GetErrorDesc

func GetErrorDesc(errHTTP ErrInvalidHTTPStatus) ErrDesc

type ErrInvalidHTTPStatus

type ErrInvalidHTTPStatus struct {
	Res *http.Response
}

func (ErrInvalidHTTPStatus) Error

func (e ErrInvalidHTTPStatus) Error() string

func (ErrInvalidHTTPStatus) UnmarshalError

func (e ErrInvalidHTTPStatus) UnmarshalError() (ErrDesc, error)

type HTTPMethod

type HTTPMethod string

type NoReq

type NoReq string

func (NoReq) Req

func (nrq NoReq) Req()

type NoRes

type NoRes string

func (NoRes) Res

func (nrp NoRes) Res()

type ReqI

type ReqI interface {
	Req()
}

type ReqURLI added in v1.5.1

type ReqURLI interface {
	GetURLValues() url.Values
}

type ResI

type ResI interface {
	Res()
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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