requester

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2023 License: MIT Imports: 14 Imported by: 0

README

Pipeline coverage GitHub Go Report Card GoDoc

Requester

Requester is a Go package that provides a wrapper around the standard HTTP package, utilizing the function options pattern for handling requests and responses. It offers additional features for handling HTTP requests, including retries, fallback policies, and more.

In the realm of HTTP client libraries for Go, developers often find themselves working with the standard HTTP client, request, and response pattern. While effective, this pattern can become verbose, especially when dealing with error handling and mutating requests.

Many projects and libraries in the Go ecosystem follow the builder pattern, which can provide a cleaner and more fluent API for constructing requests. However, the builder pattern can sometimes be challenging to extend with additional functionality.

In contrast, the functional pattern offers a flexible and composable approach to working with HTTP requests. This pattern aligns well with Go's idiomatic style, allowing developers to easily apply modifications and customizations to requests through functional options.

Components

This package extends the functionality of the standard http package by incorporating and enhancing its core components: the Client, Request, and Response objects. It introduces additional fields, methods, and object compositions of the standard http package, augmenting the capabilities provided by the standard HTTP package with some extra stuff.

Errors are propagated through the client initialization, request construction, sending, and handling. If any errors occur along this process, subsequent steps will not execute the provided callbacks. Therefore you omit checking the errors for each stage. See the usage section below.

Installation

go get github.com/andreasisnes/requester

Usage

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/andreasisnes/requester"
)

type Response struct {
	Args    map[string]string `json:"args"`
	Data    map[string]any    `json:"data"`
	Headers map[string]string `json:"headers"`
	URL     string            `json:"url"`
}

type EchoServer struct {
	client *requester.Client
}

func main() {
	sdk := &EchoServer{
		client: requester.New(
			requester.WithBaseURL("https://postman-echo.com"),
		),
	}

	ctx := context.Background()

	response, _ := sdk.GET(ctx, map[string][]any{
		"timstamp": {time.Now()},
	})
	PrintJSON(response)

	response, _ = sdk.POST(ctx, map[string]any{
		"test": 123,
	})
	PrintJSON(response)
}

func PrintJSON(response Response) {
	jsonBytes, _ := json.MarshalIndent(response, "", "  ")
	fmt.Println(string(jsonBytes))
}

func (sdk *EchoServer) POST(ctx context.Context, payload map[string]any) (out Response, err error) {
	return out, sdk.client.POST(ctx, "post").
		Do(
			requester.WithRequestJSON(payload),
			requester.WithRequestHeader("X-Custom", 123),
			requester.WithRequestAuthorizationBasic("admin", "password"),
		).
		Handle(
			requester.WithResponseJSON(&out),
		)
}

func (sdk *EchoServer) GET(ctx context.Context, query map[string][]any) (out Response, err error) {
	return out, sdk.client.GET(ctx, "get").
		Do(
			requester.WithRequestURLQuery(query),
		).
		Handle(
			requester.WithResponseJSON(&out),
		)
}

The first request GET will write following to stdout.

{
  "args": {
    "timstamp": "2023-10-19 13:05:34.994444724 +0200 CEST m=+0.000163639"
  },
  "data": null,
  "headers": {
    "accept-encoding": "gzip",
    "host": "postman-echo.com",
    "user-agent": "Go-http-client/2.0",
    "x-amzn-trace-id": "Root=1-65310d53-0db6144a5175aea0772afad7",
    "x-forwarded-port": "443",
    "x-forwarded-proto": "https"
  },
  "url": "https://postman-echo.com/get?timstamp=2023-10-19+13%3A05%3A34.994444724+%2B0200+CEST+m%3D%2B0.000163639"
}

The second request POST will write the following to stdout.

{
  "args": {},
  "data": {
    "test": 123
  },
  "headers": {
    "accept-encoding": "gzip",
    "authorization": "Basic YWRtaW46cGFzc3dvcmQ=",
    "content-length": "12",
    "content-type": "application/json",
    "host": "postman-echo.com",
    "user-agent": "Go-http-client/2.0",
    "x-amzn-trace-id": "Root=1-65310d53-14c9787b400c18fc69ec0f40",
    "x-custom": "123",
    "x-forwarded-port": "443",
    "x-forwarded-proto": "https"
  },
  "url": "https://postman-echo.com/post"
}

Contributing

If you want to contribute, you are welcome to open an issue or submit a pull request.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Elapsed

func Elapsed(fn func()) time.Duration

Types

type Client

type Client struct {
	*http.Client
	// contains filtered or unexported fields
}

ClientOptions is a callback signature for modifying client options.

func New

func New(opts ...ClientOptions) *Client

New initializes a default client. Provide ClientOptions to modify default behavior.

func (*Client) DELETE

func (c *Client) DELETE(ctx context.Context, route ...string) *Request

DELETE creates a HTTP DELETE request with the given route.

func (*Client) GET

func (c *Client) GET(ctx context.Context, route ...string) *Request

GET creates a HTTP GET request with the given route.

func (*Client) PATCH

func (c *Client) PATCH(ctx context.Context, route ...string) *Request

PATCH creates a HTTP PATCH request with the given route.

func (*Client) POST

func (c *Client) POST(ctx context.Context, route ...string) *Request

POST creates a HTTP POST request with the given route.

func (*Client) PUT

func (c *Client) PUT(ctx context.Context, route ...string) *Request

PUT creates a HTTP PUT request with the given route.

func (*Client) Request

func (c *Client) Request(ctx context.Context, method string, routes ...string) *Request

Request creates a HTTP request with the given HTTP method and route. If a base URL is specified in the client, the given route should just contain the path; otherwise, provide the whole URL. The route segments will be joined with "/" as separator.

type ClientOptions

type ClientOptions func(client *Client)

ClientOptions is a callback signature for modifying client options.

func WithBaseURL

func WithBaseURL(url string) ClientOptions

WithBaseURL sets a base URL which will be the prefix for all outbound HTTP requests.

func WithClient

func WithClient(httpClient *http.Client) ClientOptions

WithClient sets the client to the given HTTP client instance.

type FallbackPolicy

type FallbackPolicy int

FallbackPolicy specifies the cooldown strategy for failed request issuing an identical request of a failing request

const (
	// FallbackPolicyLinear waits for issuing a new request by
	// given duration multiplied by the attempt.
	FallbackPolicyLinear FallbackPolicy = iota
	// FallbackPolicyExponential waits for issuing a new request by
	// given attempt multiplied with itself and attempt.
	FallbackPolicyExponential
)

type Request

type Request struct {
	// Request is the underlying standard HTTP request being made.
	*http.Request

	// Client is the HTTP client used to perform the request.
	*http.Client

	// Error stores any errors generated when creating the request.
	Error error

	// Retries specifies the number of times the request will be retried in case of failure.
	Retries int

	// FallbackDuration is the duration to wait before attempting the request again.
	FallbackDuration time.Duration

	// FallbackPolicy represents the policy used for fallback requests.
	FallbackPolicy FallbackPolicy

	// FallbackStatusCodes contains a list of HTTP status codes that will
	// trigger a new request.
	FallbackStatusCodes []int
}

Request is a wrapper around the standard http.Request providing additional features.

func (*Request) Do

func (r *Request) Do(opts ...RequestOption) *Response

Do executes the request.

func (*Request) Dry

func (r *Request) Dry(opts ...RequestOption) (err error)

Dry performs a dry run of the request without actually executing it.

type RequestOption

type RequestOption func(request *Request) (err error)

RequestOption callback signature for modifying request

func WithRequestAuthorizationBasic

func WithRequestAuthorizationBasic(username, password string) RequestOption

WithRequestAuthorizationBasic encodes the credentials with basic HTTP authentication. It sets the valkue in the Authorization HTTP header.

func WithRequestAuthorizationBearer

func WithRequestAuthorizationBearer(fn func(ctx context.Context) (string, error)) RequestOption

WithRequestAuthorizationBearer executes the callback to fetch a token, the token from the result will be set in the Authorization header

func WithRequestBody

func WithRequestBody(body io.Reader) RequestOption

WithRequestBody sets the request body.

func WithRequestFormData

func WithRequestFormData(form map[string][]byte) RequestOption

WithRequestFormData writes the content to body using the multipart writer.

func WithRequestFormDataFile

func WithRequestFormDataFile(filePath, field string, opts ...func(content []byte) []byte) RequestOption

WithRequestFormDataFile reads the given files and writes it as multipart form. the functional options allows you to mutate the file content before it's being written.

func WithRequestFormURLEncoded

func WithRequestFormURLEncoded(form map[string][]string) RequestOption

WithRequestFormURLEncoded sets the request body as form-urlencoded.

func WithRequestHeader

func WithRequestHeader(key string, value any) RequestOption

WithRequestHeader sets key value as HTTP header in the request.

func WithRequestJSON

func WithRequestJSON(object any) RequestOption

WithRequestJSON JSON serializes the object and sets the request body as JSON.

func WithRequestOptions

func WithRequestOptions(opts ...RequestOption) RequestOption

WithRequestOptions composes multiple request options.

func WithRequestRetryPolicy

func WithRequestRetryPolicy(retries int, duration time.Duration, policy FallbackPolicy, statuscodes ...int) RequestOption

WithRequestRetryPolicy sets the retry policy for the request.

func WithRequestTimeout

func WithRequestTimeout(duration time.Duration) RequestOption

WithRequestTimeout sets the timeout duration for the request.

func WithRequestURL

func WithRequestURL(rawUrl string) RequestOption

WithRequestURL sets the URL for the request.

func WithRequestURLQuery

func WithRequestURLQuery(query map[string][]any) RequestOption

WithRequestURLQuery sets the URL query parameters for the request.

func WithRequestXML

func WithRequestXML(object any) RequestOption

WithRequestXML XML serializes the object and sets the request body as XML.

type Response

type Response struct {
	*http.Response
	Err error
}

Response is a wrapper around the standard http.Response providing additional features.

func (*Response) Handle

func (r *Response) Handle(opts ...ResponseOption) error

Handle executes the response handling options. If there is an error associated with the response, it returns that error.

type ResponseOption

type ResponseOption func(request *Response) error

ResponseOption is a callback signature for modifying response options.

func WithResponseBody

func WithResponseBody[T any](object *T, unmarshaler func(data []byte, v any) error, statuscodes ...int) ResponseOption

WithUnmarshalXML unmarshals the response body to an object using the given unmarshaler. The object parameter should be a pointer to the target type. It will only attempt to deserialize the payload if the response has one of the provided status codes. If the list of status codes is empty, it will attempt to deserialize for all status codes.

func WithResponseJSON

func WithResponseJSON[T any](object *T, statuscodes ...int) ResponseOption

WithResponseJSON unmarshals the JSON response body to an object. The object parameter should be a pointer to the target type. It will only attempt to deserialize the payload if the response has one of the provided status codes. If the list of status codes is empty, it will attempt to deserialize for all status codes.

func WithResponseStatusCodeAssertion

func WithResponseStatusCodeAssertion(statusCodes ...int) ResponseOption

WithResponseStatusCodeAssertion checks if the response status code matches any of the specified codes. If it does, it returns nil. Otherwise, it provides an error message.

func WithResponseXML

func WithResponseXML[T any](object *T, statuscodes ...int) ResponseOption

WithResponseXML unmarshals the XML response body to an object. The object parameter should be a pointer to the target type. It will only attempt to deserialize the payload if the response has one of the provided status codes. If the list of status codes is empty, it will attempt to deserialize for all status codes.

Jump to

Keyboard shortcuts

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