taxi

package
v0.0.0-...-eb599cc Latest Latest
Warning

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

Go to latest
Published: Jan 5, 2021 License: MIT Imports: 15 Imported by: 0

README

Taxi 🚕

Taxi is a lightweight, remote procedure call (RPC) framework for services written in Go. Requests are sent as JSON over HTTP, making it easy to interact with Taxi-based services. Taxi does not impose any particular URL scheme or request structure, and responses are fully-customisable, giving you full control over the design of your API.

Installation

go get github.com/shadracnicholas/taxi

Usage

Taxi is made up of two components: a client for dispatching RPCs to a remote service, and a router for handling incoming RPCs.

Client
Creating a new client
// Create a new client
client := taxi.NewClient()

// Taxi uses an http.Client{} to send requests.
// You can provide your own if you wish using
// NewClientUsing(doer Doer).

type Doer interface {
	Do(r *http.Request) (*http.Response, error)
}

custom := &http.Client{Timeout: 30 * time.Second}
client := taxi.NewClientUsing(custom)
Mock client

It's useful in tests to use a mock client. The mock client has an http.Handler that it uses to serve requests. Set this to an instance of TestFixture to create a handler that expects and responds to particular requests.

Router

Use the router to build a server that responds to RPCs.

// Create a new router (optionally set a logger)
router := taxi.NewRouter().WithLogger(log.Printf)

// Set global middleware
router.UseMiddleware(...)

// Register a handler
router.RegisterHandlerFund("GET", "/foo", fooHandler)

func fooHandler(ctx context.Context, decode taxi.Decoder) (interface{}, error) {
	body := &sruct{
    	Bar string `json:"bar"`
    }{}
    if err := decode(bar); err != nil {
    	return err
    }

	...
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeRequest

func DecodeRequest(r *http.Request, v interface{}) error

DecodeRequest unmarshals URL parameters and the JSON body of the given request into the value pointed to by v. It is exported because it might be useful, e.g. in middleware.

func WriteError

func WriteError(w http.ResponseWriter, err error) error

WriteError writes the error to the ResponseWriter. If the error is an oops.Error, the status code is taken from that, otherwise a status code of 500 is set.

func WriteSuccess

func WriteSuccess(w http.ResponseWriter, v interface{}) error

WriteSuccess writes the data to the ResponseWriter. A status code of 200 is set.

Types

type Client

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

Client dispatches RPC requests

func NewClient

func NewClient() *Client

NewClient returns an initialised Client

func NewClientUsing

func NewClientUsing(doer Doer) *Client

NewClientUsing returns an initialised client, using the given Doer to make the HTTP requests. The standard http.Client implements the Doer interface.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, url string, body interface{}, v interface{}) error

Delete dispatches a DELETE RPC

func (*Client) Dispatch

func (c *Client) Dispatch(ctx context.Context, rpc *RPC) *Future

Dispatch makes a request and returns a Future that represents the in-flight request

func (*Client) Get

func (c *Client) Get(ctx context.Context, url string, body interface{}, v interface{}) error

Get dispatches a GET RPC

func (*Client) Patch

func (c *Client) Patch(ctx context.Context, url string, body interface{}, v interface{}) error

Patch dispatches a PATCH RPC

func (*Client) Post

func (c *Client) Post(ctx context.Context, url string, body interface{}, v interface{}) error

Post dispatches a POST RPC

func (*Client) Put

func (c *Client) Put(ctx context.Context, url string, body interface{}, v interface{}) error

Put dispatches a PUT RPC

type Decoder

type Decoder func(v interface{}) error

Decoder is a function that decodes a request body into the given interface

type Dispatcher

type Dispatcher interface {
	Dispatch(ctx context.Context, rpc *RPC) *Future
	Get(ctx context.Context, url string, body interface{}, v interface{}) error
	Post(ctx context.Context, url string, body interface{}, v interface{}) error
	Put(ctx context.Context, url string, body interface{}, v interface{}) error
	Patch(ctx context.Context, url string, body interface{}, v interface{}) error
	Delete(ctx context.Context, url string, body interface{}, v interface{}) error
}

Dispatcher is the interface for making remote procedure calls

type Doer

type Doer interface {
	Do(r *http.Request) (*http.Response, error)
}

Doer is an interface that http.Client implements

type Future

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

Future represents an in-flight remote procedure call

func (*Future) DecodeResponse

func (f *Future) DecodeResponse(v interface{}) error

DecodeResponse will block until the response has been received, and then decode the JSON body into the given argument.

func (*Future) Wait

func (f *Future) Wait() error

Wait will block until the response has been received

type Handler

type Handler interface {
	ServeRPC(ctx context.Context, decode Decoder) (interface{}, error)
}

Handler is an interface that wraps the ServeRPC method

type HandlerFunc

type HandlerFunc func(ctx context.Context, decode Decoder) (interface{}, error)

HandlerFunc is a type that allows normal functions to be used as Handlers

func (HandlerFunc) ServeRPC

func (f HandlerFunc) ServeRPC(ctx context.Context, decode Decoder) (interface{}, error)

ServeRPC calls f(ctx, decode)

type MockClient

type MockClient struct {
	Handler http.Handler
}

MockClient dispatches RPCs by passing the request directly to an internal http.Handler. It does not make network requests. This is useful in unit tests as it allows endpoints to be mocked.

func (*MockClient) Delete

func (c *MockClient) Delete(ctx context.Context, url string, body interface{}, v interface{}) error

Delete dispatches a DELETE RPC

func (*MockClient) Dispatch

func (c *MockClient) Dispatch(ctx context.Context, rpc *RPC) *Future

Dispatch converts the RPC into an http request and gives it to the client's handler to handle. It returns a Future which will resolve to the response given by the handler.

func (*MockClient) Get

func (c *MockClient) Get(ctx context.Context, url string, body interface{}, v interface{}) error

Get dispatches a GET RPC

func (*MockClient) Patch

func (c *MockClient) Patch(ctx context.Context, url string, body interface{}, v interface{}) error

Patch dispatches a PATCH RPC

func (*MockClient) Post

func (c *MockClient) Post(ctx context.Context, url string, body interface{}, v interface{}) error

Post dispatches a POST RPC

func (*MockClient) Put

func (c *MockClient) Put(ctx context.Context, url string, body interface{}, v interface{}) error

Put dispatches a PUT RPC

type RPC

type RPC struct {
	Method string
	URL    string
	Body   interface{}
}

RPC represents a remote procedure call

func (*RPC) RPC

func (r *RPC) RPC() *RPC

RPC returns itself so an RPC implements the rpcProvider interface used by the TestFixture

func (*RPC) ToRequest

func (r *RPC) ToRequest(ctx context.Context) (*http.Request, error)

ToRequest converts the RPC into an http.Request

type Router

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

Router registers routes and handlers to handle RPCs over HTTP.

func NewRouter

func NewRouter() *Router

NewRouter returns an initialised Router

func (*Router) RegisterHandler

func (r *Router) RegisterHandler(method, path string, handler Handler)

RegisterHandler registers a new route

func (*Router) RegisterHandlerFunc

func (r *Router) RegisterHandlerFunc(method, path string, handler func(context.Context, Decoder) (interface{}, error))

RegisterHandlerFunc registers a new route

func (*Router) RegisterRawHandler

func (r *Router) RegisterRawHandler(method, path string, handler http.Handler)

RegisterRawHandler registers a new route with a standard http.Handler

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP dispatches requests to the appropriate handler

func (*Router) UseMiddleware

func (r *Router) UseMiddleware(mw ...func(http.Handler) http.Handler)

UseMiddleware adds a stack of middleware to the router

func (*Router) WithLogger

func (r *Router) WithLogger(f func(format string, v ...interface{})) *Router

WithLogger sets a log function for the router to use when something goes. wrong. If not set, no logs will be output.

type Stub

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

Stub is an http.Handler to use with a TestFixture. It can be configured to match requests based on arbitrary rules, and make assertions based on the requests that were received.

func NewStub

func NewStub(t *testing.T) *Stub

NewStub returns an initialised Stub

func (*Stub) Expect

func (s *Stub) Expect(n int) *Stub

Expect adds an assertion that the stub receives n requests

func (*Stub) Match

func (s *Stub) Match(r *http.Request) bool

Match returns whether this stub should handle the given request

func (*Stub) MatchBody

func (s *Stub) MatchBody(body interface{}) *Stub

MatchBody adds a matcher that matches on the request body

func (*Stub) MatchMethod

func (s *Stub) MatchMethod(method string) *Stub

MatchMethod adds a matcher that matches on the request method

func (*Stub) MatchPartialBody

func (s *Stub) MatchPartialBody(body interface{}, fields []string) *Stub

MatchPartialBody adds a matcher that matches on the request body but only compares fields that are set in the fields slice.

func (*Stub) MatchPath

func (s *Stub) MatchPath(path string) *Stub

MatchPath adds a matcher that matches on the request path

func (*Stub) RespondWith

func (s *Stub) RespondWith(v interface{}) *Stub

RespondWith sets the data that should be returned by the stub when handling requests. The interface will be marshaled to JSON and wrapped in a data field.

func (*Stub) RespondWithError

func (s *Stub) RespondWithError(err error) *Stub

RespondWithError sets the error that should be returned by the stub when handling requests. The error will be converted to a string and sent in the error field of the JSON payload.

func (*Stub) RunAssertions

func (s *Stub) RunAssertions()

RunAssertions runs the stub's assertions

func (*Stub) ServeHTTP

func (s *Stub) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP tracks that the request was received by the stub, and calls the stub's serve function if not nil.

func (*Stub) WithAssertion

func (s *Stub) WithAssertion(f func(t *testing.T, requests []*http.Request)) *Stub

WithAssertion adds a custom assertion

func (*Stub) WithMatcher

func (s *Stub) WithMatcher(f func(r *http.Request) bool) *Stub

WithMatcher adds a custom matcher

type TestFixture

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

TestFixture is an http.Handler that can be used in testing. It dispatches requests to one of a set of Stubs that match based on arbitrary details about the request. Assertions can be run to assert that expected requests were received.

func NewTestFixture

func NewTestFixture(t *testing.T) *TestFixture

NewTestFixture returns an initialised TestFixture

func (*TestFixture) Allow

func (f *TestFixture) Allow(rpc rpcProvider, fields ...string) *Stub

Allow returns a new stub that matches on the method and path but does not care how many times it is called.

func (*TestFixture) Expect

func (f *TestFixture) Expect(n int, rpc rpcProvider, fields ...string) *Stub

Expect returns a new stub that matches on the RPC, and asserts that it is called n times.

func (*TestFixture) ExpectOne

func (f *TestFixture) ExpectOne(rpc rpcProvider, fields ...string) *Stub

ExpectOne returns a new stub that matches on the RPC, and asserts that it is called exactly once.

func (*TestFixture) NewStub

func (f *TestFixture) NewStub() *Stub

NewStub returns a new empty stub

func (*TestFixture) RunAssertions

func (f *TestFixture) RunAssertions()

RunAssertions runs all of the stubs' assertions

func (*TestFixture) ServeHTTP

func (f *TestFixture) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP dispatches requests to the first stub that matches

Jump to

Keyboard shortcuts

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