withttp

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2022 License: MIT Imports: 12 Imported by: 1

README

withttp

Build http requests and parse their responses with fluent syntax and wit. This package aims to quickly configure http roundtrips by covering common scenarios, while leaving all details of http requests and responses open for developers to allow maximum flexibility.

Supported underlying http implementations are:

Query Restful endpoints
type GithubRepoInfo struct {
  ID  int    `json:"id"`
  URL string `json:"html_url"`
}

func GetRepoInfo(user, repo string) (GithubRepoInfo, error) {

  call := withttp.NewCall[GithubRepoInfo](withttp.NewDefaultFastHttpHttpClientAdapter()).
    WithURL(fmt.Sprintf("https://api.github.com/repos/%s/%s", user, repo)).
    WithMethod(http.MethodGet).
    WithHeader("User-Agent", "withttp/0.1.0 See https://github.com/sonirico/withttp", false).
    WithParseJSON().
    WithExpectedStatusCodes(http.StatusOK)

  err := call.Call(context.Background())

  return call.BodyParsed, err
}

func main() {
  info, _ := GetRepoInfo("sonirico", "withttp")
  log.Println(info)
}

In case of a wide range catalog of endpoints, predefined parameters and behaviours can be defined by employing an endpoint definition.

var (
  githubApi = withttp.NewEndpoint("GithubAPI").
    Request(withttp.WithBaseURL("https://api.github.com/"))
)

type GithubRepoInfo struct {
  ID  int    `json:"id"`
  URL string `json:"html_url"`
}

func GetRepoInfo(user, repo string) (GithubRepoInfo, error) {
  call := withttp.NewCall[GithubRepoInfo](withttp.NewDefaultFastHttpHttpClientAdapter()).
    WithURI(fmt.Sprintf("repos/%s/%s", user, repo)).
    WithMethod(http.MethodGet).
    WithHeader("User-Agent", "withttp/0.1.0 See https://github.com/sonirico/withttp", false).
    WithHeaderFunc(func() (key, value string, override bool) {
      key = "X-Date"
      value = time.Now().String()
      override = true
      return
    }).
    WithParseJSON().
    WithExpectedStatusCodes(http.StatusOK)

  err := call.CallEndpoint(context.Background(), githubApi)

  return call.BodyParsed, err
}

type GithubCreateIssueResponse struct {
  ID  int    `json:"id"`
  URL string `json:"url"`
}

func CreateRepoIssue(user, repo, title, body, assignee string) (GithubCreateIssueResponse, error) {
  type payload struct {
    Title    string `json:"title"`
    Body     string `json:"body"`
    Assignee string `json:"assignee"`
  }

  p := payload{
    Title:    title,
    Body:     body,
    Assignee: assignee,
  }

  call := withttp.NewCall[GithubCreateIssueResponse](
    withttp.NewDefaultFastHttpHttpClientAdapter(),
  ).
    WithURI(fmt.Sprintf("repos/%s/%s/issues", user, repo)).
    WithMethod(http.MethodPost).
    WithContentType("application/vnd+github+json").
    WithBody(p).
    WithHeaderFunc(func() (key, value string, override bool) {
      key = "Authorization"
      value = fmt.Sprintf("Bearer %s", "S3cret")
      override = true
      return
    }).
    WithExpectedStatusCodes(http.StatusCreated)

  err := call.CallEndpoint(context.Background(), githubApi)

  log.Println("req body", string(call.Req.Body()))

  return call.BodyParsed, err
}

func main() {
  // Fetch repo info
  info, _ := GetRepoInfo("sonirico", "withttp")
  log.Println(info)

  // Create an issue
  res, err := CreateRepoIssue("sonirico", "withttp", "test",
    "This is a test", "sonirico")
  log.Println(res, err)
}
Test your calls again a mock endpoint

Quickly test your calls by creating a mock endpoint

var (
  exchangeListOrders = withttp.NewEndpoint("ListOrders").
        Request(withttp.WithURL("http://example.com")).
        Response(
      withttp.WithResMock(func(res withttp.Response) {
        res.SetBody(io.NopCloser(bytes.NewReader(mockResponse)))
        res.SetStatus(http.StatusOK)
      }),
    )
  mockResponse = []byte(strings.TrimSpace(`
    {"amount": 234, "pair": "BTC/USDT"}
    {"amount": 123, "pair": "ETH/USDT"}`))
)

func main() {
  type Order struct {
    Amount float64 `json:"amount"`
    Pair   string  `json:"pair"`
  }

  res := make(chan Order)

  call := withttp.NewCall[Order](withttp.NewDefaultFastHttpHttpClientAdapter()).
    WithURL("https://github.com/").
    WithMethod(http.MethodGet).
    WithHeader("User-Agent", "withttp/0.1.0 See https://github.com/sonirico/withttp", false).
    WithJSONEachRowChan(res).
    WithExpectedStatusCodes(http.StatusOK)

  go func() {
    for order := range res {
      log.Println(order)
    }
  }()

  err := call.Call(context.Background(), exchangeListOrders)

  if err != nil {
    panic(err)
  }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrAssertion            = errors.New("assertion was unmet")
	ErrUnexpectedStatusCode = errors.Wrap(ErrAssertion, "unexpected status code")
)
View Source
var (
	ContentTypeJSON = "application/json"
)
View Source
var (
	ErrUnknownContentType = errors.New("unknown content type")
)

Functions

func BtsIsset added in v0.2.0

func BtsIsset(bts []byte) bool

func ConfigureHeader

func ConfigureHeader(req Request, key, value string, override bool) error

func EncodeBody added in v0.2.0

func EncodeBody(payload any, contentType ContentType) (bts []byte, err error)

func ReadJSON

func ReadJSON[T any](rc io.ReadCloser) (res T, err error)

func ReadStream

func ReadStream[T any](rc io.ReadCloser, factory StreamFactory[T], fn func(T) bool) (err error)

func ReadStreamChan

func ReadStreamChan[T any](rc io.ReadCloser, factory StreamFactory[T], out chan<- T) (err error)

func StrIsset added in v0.2.0

func StrIsset(s string) bool

Types

type CalReqOption added in v0.2.0

type CalReqOption[T any] interface {
	Configure(c *Call[T], r Request) error
}

type Call

type Call[T any] struct {
	Req Request
	Res Response

	BodyRaw    []byte
	BodyParsed T

	ReqContentType ContentType
	ReqBodyRaw     []byte
	// contains filtered or unexported fields
}

func NewCall

func NewCall[T any](client client) *Call[T]

func (*Call[T]) Call

func (c *Call[T]) Call(ctx context.Context) (err error)

func (*Call[T]) CallEndpoint added in v0.2.0

func (c *Call[T]) CallEndpoint(ctx context.Context, e *Endpoint) (err error)

func (*Call[T]) Request

func (c *Call[T]) Request(opts ...ReqOption) *Call[T]

func (*Call[T]) Response

func (c *Call[T]) Response(opts ...ResOption) *Call[T]

func (*Call[T]) WithAssert

func (c *Call[T]) WithAssert(fn func(req Response) error) *Call[T]

func (*Call[T]) WithBody added in v0.2.0

func (c *Call[T]) WithBody(payload any) *Call[T]

func (*Call[T]) WithBodyStream added in v0.2.0

func (c *Call[T]) WithBodyStream(rc io.ReadCloser, bodySize int) *Call[T]

WithBodyStream receives a stream of data to set on the request. Second parameter `bodySize` indicates the estimated content-length of this stream. Required when employing fasthttp http client.

func (*Call[T]) WithContentLength added in v0.2.0

func (c *Call[T]) WithContentLength(length int) *Call[T]

func (*Call[T]) WithContentType added in v0.2.0

func (c *Call[T]) WithContentType(ct ContentType) *Call[T]

func (*Call[T]) WithExpectedStatusCodes

func (c *Call[T]) WithExpectedStatusCodes(states ...int) *Call[T]

func (*Call[T]) WithHeader

func (c *Call[T]) WithHeader(key, value string, override bool) *Call[T]

func (*Call[T]) WithHeaderFunc

func (c *Call[T]) WithHeaderFunc(fn func() (key, value string, override bool)) *Call[T]

func (*Call[T]) WithIgnoreBody

func (c *Call[T]) WithIgnoreBody() *Call[T]

func (*Call[T]) WithJSONEachRow

func (c *Call[T]) WithJSONEachRow(fn func(T) bool) *Call[T]

func (*Call[T]) WithJSONEachRowChan

func (c *Call[T]) WithJSONEachRowChan(out chan<- T) *Call[T]

func (*Call[T]) WithMethod

func (c *Call[T]) WithMethod(method string) *Call[T]

func (*Call[T]) WithParseJSON added in v0.2.0

func (c *Call[T]) WithParseJSON() *Call[T]

func (*Call[T]) WithRawBody

func (c *Call[T]) WithRawBody(payload []byte) *Call[T]

func (*Call[T]) WithReadBody

func (c *Call[T]) WithReadBody() *Call[T]

func (*Call[T]) WithStream

func (c *Call[T]) WithStream(factory StreamFactory[T], fn func(T) bool) *Call[T]

func (*Call[T]) WithStreamChan

func (c *Call[T]) WithStreamChan(factory StreamFactory[T], ch chan<- T) *Call[T]

func (*Call[T]) WithURI

func (c *Call[T]) WithURI(raw string) *Call[T]

func (*Call[T]) WithURL

func (c *Call[T]) WithURL(raw string) *Call[T]

type CallReqOptionFunc added in v0.2.0

type CallReqOptionFunc[T any] func(c *Call[T], res Request) error

func WithBody added in v0.2.0

func WithBody[T any](payload any) CallReqOptionFunc[T]

func WithBodyStream added in v0.2.0

func WithBodyStream[T any](rc io.ReadCloser, bodySize int) CallReqOptionFunc[T]

func WithContentType added in v0.2.0

func WithContentType[T any](ct ContentType) CallReqOptionFunc[T]

func WithHeader added in v0.2.0

func WithHeader[T any](k, v string, override bool) CallReqOptionFunc[T]

func WithHeaderFunc added in v0.2.0

func WithHeaderFunc[T any](fn func() (string, string, bool)) CallReqOptionFunc[T]

func WithMethod added in v0.2.0

func WithMethod[T any](method string) CallReqOptionFunc[T]

func WithRawBody

func WithRawBody[T any](payload []byte) CallReqOptionFunc[T]

func WithURI

func WithURI[T any](raw string) CallReqOptionFunc[T]

func WithURL

func WithURL[T any](raw string) CallReqOptionFunc[T]

func (CallReqOptionFunc[T]) Configure added in v0.2.0

func (f CallReqOptionFunc[T]) Configure(c *Call[T], req Request) error

type CallResOption

type CallResOption[T any] interface {
	Parse(c *Call[T], r Response) error
}

type CallResOptionFunc

type CallResOptionFunc[T any] func(c *Call[T], res Response) error

func WithAssertion

func WithAssertion[T any](fn func(res Response) error) CallResOptionFunc[T]

func WithCloseBody

func WithCloseBody[T any]() CallResOptionFunc[T]

func WithExpectedStatusCodes

func WithExpectedStatusCodes[T any](states ...int) CallResOptionFunc[T]

func WithIgnoredBody

func WithIgnoredBody[T any]() CallResOptionFunc[T]

func WithJSON

func WithJSON[T any]() CallResOptionFunc[T]

func WithParseBodyRaw added in v0.2.0

func WithParseBodyRaw[T any]() CallResOptionFunc[T]

func WithStream

func WithStream[T any](factory StreamFactory[T], fn func(T) bool) CallResOptionFunc[T]

func WithStreamChan

func WithStreamChan[T any](factory StreamFactory[T], out chan<- T) CallResOptionFunc[T]

func (CallResOptionFunc[T]) Parse

func (f CallResOptionFunc[T]) Parse(c *Call[T], res Response) error

type ContentType added in v0.2.0

type ContentType string

func (ContentType) Codec added in v0.2.0

func (c ContentType) Codec() (codec.Codec, error)

func (ContentType) IsJSON added in v0.2.0

func (c ContentType) IsJSON() bool

func (ContentType) String added in v0.2.0

func (c ContentType) String() string

type Endpoint

type Endpoint struct {
	// contains filtered or unexported fields
}

func NewEndpoint

func NewEndpoint(name string) *Endpoint

func (*Endpoint) Request

func (e *Endpoint) Request(opts ...ReqOption) *Endpoint

func (*Endpoint) Response

func (e *Endpoint) Response(opts ...ResOption) *Endpoint

type FastHttpHttpClientAdapter

type FastHttpHttpClientAdapter struct {
	// contains filtered or unexported fields
}

func NewDefaultFastHttpHttpClientAdapter

func NewDefaultFastHttpHttpClientAdapter() *FastHttpHttpClientAdapter

func NewFastHttpHttpClientAdapter

func NewFastHttpHttpClientAdapter(cli *fasthttp.Client) *FastHttpHttpClientAdapter

func (*FastHttpHttpClientAdapter) Do

func (*FastHttpHttpClientAdapter) Request

func (a *FastHttpHttpClientAdapter) Request() (Request, error)

type JSONEachRowStream

type JSONEachRowStream[T any] struct {
	// contains filtered or unexported fields
}

func (*JSONEachRowStream[T]) Data

func (s *JSONEachRowStream[T]) Data() T

func (*JSONEachRowStream[T]) Err

func (s *JSONEachRowStream[T]) Err() error

func (*JSONEachRowStream[T]) Next

func (s *JSONEachRowStream[T]) Next(_ context.Context) bool

type MockEndpoint

type MockEndpoint struct {
	// contains filtered or unexported fields
}

type MockHttpClientAdapter

type MockHttpClientAdapter struct{}

func NewMockHttpClientAdapter

func NewMockHttpClientAdapter() *MockHttpClientAdapter

func (*MockHttpClientAdapter) Do

func (*MockHttpClientAdapter) Request

func (a *MockHttpClientAdapter) Request() (Request, error)

type NativeHttpClientAdapter

type NativeHttpClientAdapter struct {
	// contains filtered or unexported fields
}

func NewDefaultNativeHttpClientAdapter

func NewDefaultNativeHttpClientAdapter() *NativeHttpClientAdapter

func NewNativeHttpClientAdapter

func NewNativeHttpClientAdapter(cli *http.Client) *NativeHttpClientAdapter

func (*NativeHttpClientAdapter) Do

func (*NativeHttpClientAdapter) Request

func (a *NativeHttpClientAdapter) Request() (Request, error)

type ReqOption

type ReqOption interface {
	Configure(r Request) error
}

func WithBaseURL added in v0.2.0

func WithBaseURL(raw string) ReqOption

type ReqOptionFunc

type ReqOptionFunc func(req Request) error

func (ReqOptionFunc) Configure

func (f ReqOptionFunc) Configure(req Request) error

type Request

type Request interface {
	SetMethod(string)
	SetHeader(k, v string)
	AddHeader(k, v string)
	SetURL(*url.URL)
	// SetBodyStream sets the stream of body data belonging to a request. bodySize parameter is needed
	// when using fasthttp implementation.
	SetBodyStream(rc io.ReadCloser, bodySize int)
	SetBody([]byte)

	Body() []byte
	BodyStream() io.ReadCloser

	URL() *url.URL
}

type ResOption

type ResOption interface {
	Parse(r Response) error
}

func WithMockedRes added in v0.2.0

func WithMockedRes(fn func(response Response)) ResOption

type ResOptionFunc

type ResOptionFunc func(res Response) error

func (ResOptionFunc) Parse

func (f ResOptionFunc) Parse(res Response) error

type Response

type Response interface {
	Status() int
	StatusText() string
	Body() io.ReadCloser

	SetBody(rc io.ReadCloser)
	SetStatus(status int)
}

type Stream

type Stream[T any] interface {
	Next(ctx context.Context) bool
	Data() T
	Err() error
}

func NewJSONEachRowStream

func NewJSONEachRowStream[T any](r io.Reader) Stream[T]

type StreamFactory

type StreamFactory[T any] interface {
	Get(r io.Reader) Stream[T]
}

func NewJSONEachRowStreamFactory

func NewJSONEachRowStreamFactory[T any]() StreamFactory[T]

type StreamFactoryFunc

type StreamFactoryFunc[T any] func(reader io.Reader) Stream[T]

func (StreamFactoryFunc[T]) Get

func (f StreamFactoryFunc[T]) Get(r io.Reader) Stream[T]

Directories

Path Synopsis
examples
fasthttp command
mock command
singlecall command

Jump to

Keyboard shortcuts

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