mutableware

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Feb 3, 2024 License: MIT Imports: 5 Imported by: 0

README

mutableware

mutableware is a middleware package that allows for the modification of middleware. It uses generics to define Request and Response types. All handlers have a Handle[RequestType, ResponseType](ctx context.Context, request RequestType) (ResponseType, error) function.

Example

package mutableware_test

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"github.com/erinpentecost/mutableware"
	"github.com/stretchr/testify/require"
)

// Build a new Handler that will tell us which sounds that animals make.
type Animal string
type Sound string

type AnimalSoundHandler struct {
	animal Animal
	sound  Sound
}

func (ash *AnimalSoundHandler) Handle(ctx context.Context, request Animal, next mutableware.CurriedHandlerFunc[Animal, Sound]) (Sound, error) {
	if request == ash.animal {
		return ash.sound, nil
	}
	// We don't handle this type of animal, so just pass along execution to the next handler in the chain.
	return next(ctx, request)
}

func TestExample(t *testing.T) {

	hc := mutableware.NewHandlerContainer[Animal, Sound]()
	// The first handler to be added will act as a sentinel to catch unknown Animal requests.
	hc.AddAnonymousHandler(
		func(ctx context.Context, request Animal, next mutableware.CurriedHandlerFunc[Animal, Sound]) (Sound, error) {
			return "", fmt.Errorf("unknown animal")
		},
	)
	// Now we'll deal with ducks.
	duckHandlerID := hc.Add(&AnimalSoundHandler{
		animal: Animal("duck"),
		sound:  Sound("quack"),
	})
	// And now cows.
	hc.Add(&AnimalSoundHandler{
		animal: Animal("cow"),
		sound:  Sound("moo"),
	})
	// This handler will make the sounds really loud.
	hc.AddAnonymousHandler(
		func(ctx context.Context, request Animal, next mutableware.CurriedHandlerFunc[Animal, Sound]) (Sound, error) {
			response, err := next(ctx, request)
			if err != nil {
				return response, err
			}
			return Sound(fmt.Sprintf("%s!", strings.ToUpper(string(response)))), nil
		},
	)

	// Now lets try sending in requests to the handler container.
	duckSound, err := hc.Handle(context.Background(), "duck")
	require.NoError(t, err)
	require.Equal(t, Sound("QUACK!"), duckSound)
	cowSound, err := hc.Handle(context.Background(), "cow")
	require.NoError(t, err)
	require.Equal(t, Sound("MOO!"), cowSound)
	dogSound, err := hc.Handle(context.Background(), "dog")
	require.Error(t, err)
	require.Zero(t, dogSound)

	// Let's make ducks go "bark" instead.
	hc.Add(&AnimalSoundHandler{
		animal: Animal("duck"),
		sound:  Sound("bark"),
	},
		mutableware.AddOptionSwap(duckHandlerID), // Swap out an existing handler for this new one.
	)

	// Give it another try.
	duckSound, err = hc.Handle(context.Background(), "duck")
	require.NoError(t, err)
	require.Equal(t, Sound("BARK!"), duckSound)
}

Documentation

Overview

mutableware is a mutable middleware package. You can add, remove, and swap out middleware handlers.

Index

Constants

This section is empty.

Variables

View Source
var ErrHandle = errors.New("handleError")

ErrHandle is returned when one or more handlers return an error in their Handle(...) calls.

Functions

This section is empty.

Types

type AddOption

type AddOption func(*builtAddOptions)

AddOption is an option for the Add(...) function.

func AddOptionLast

func AddOptionLast() AddOption

AddOptionLast inserts the handler so it's executed last instead of first.

func AddOptionName

func AddOptionName(name string) AddOption

AddOptionName attaches a name to the handler to aid in debugging. This will appear in wrapped errors and is available through the passed-in context.

func AddOptionSwap

func AddOptionSwap(id HandlerID) AddOption

AddOptionSwap removes the target handler and inserts this one in its place. If the target handler doesn't exist, normal handler insertion occurs.

type CurriedHandlerFunc

type CurriedHandlerFunc[Request any, Response any] func(ctx context.Context, request Request) (Response, error)

type Handler

type Handler[Request any, Response any] interface {
	// Handle runs the handler for the request.
	Handle(ctx context.Context, request Request, next CurriedHandlerFunc[Request, Response]) (Response, error)
}

Handler processes requests.

type HandlerContainer

type HandlerContainer[Request any, Response any] struct {
	// contains filtered or unexported fields
}

HandlerContainer is an ordered collection of Handlers of the same type. When a Request is sent to a HandlerContainer, Handlers are invoked in in the reverse order that they were added (the oldest Handler is executed last).

Handlers can be removed after being added. This is a distinguishing feature of this package versus traditional middleware packages.

func NewHandlerContainer

func NewHandlerContainer[Request any, Response any]() *HandlerContainer[Request, Response]

NewHandlerContainer creates a new container for Handlers of the same type.

func (*HandlerContainer[Request, Response]) Add

func (hc *HandlerContainer[Request, Response]) Add(handler Handler[Request, Response], options ...AddOption) HandlerID

Add a new handler to the container. Newer handlers are invoked first. Retain the returned HandlerID if you need to Remove() this handler later.

func (*HandlerContainer[Request, Response]) AddAnonymousHandler added in v0.2.0

func (hc *HandlerContainer[Request, Response]) AddAnonymousHandler(handlerFn HandlerFunc[Request, Response], options ...AddOption) HandlerID

Add a new handler to the container. Newer handlers are invoked first. Retain the returned HandlerID if you need to Remove() this handler later.

func (*HandlerContainer[Request, Response]) Handle

func (hc *HandlerContainer[Request, Response]) Handle(ctx context.Context, request Request) (Response, error)

Handle runs the Handle function of the contained handlers. Handlers that were added latest are executed first.

func (*HandlerContainer[Request, Response]) Remove

func (hc *HandlerContainer[Request, Response]) Remove(id HandlerID)

Remove a handler that was previously added.

type HandlerFunc

type HandlerFunc[Request any, Response any] func(ctx context.Context, request Request, next CurriedHandlerFunc[Request, Response]) (Response, error)

func (HandlerFunc[Request, Response]) Handler added in v0.2.0

func (hf HandlerFunc[Request, Response]) Handler() Handler[Request, Response]

type HandlerID

type HandlerID uint64

HandlerID identifies a handler. Use this to remove a handler from a container.

type HandlerInfo

type HandlerInfo struct {
	ID   HandlerID
	Name string
}

HandlerInfo contains metadata for a Handler.

func GetHandlerInfoFromContext

func GetHandlerInfoFromContext(ctx context.Context) []HandlerInfo

GetHandlerInfoFromContext returns the current stack of handlers for a request. The latest handler to be invoked will be last in the slice.

func (HandlerInfo) String

func (h HandlerInfo) String() string

Jump to

Keyboard shortcuts

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