presto

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2019 License: Apache-2.0 Imports: 4 Imported by: 0

README

Presto 🎩

Magical REST interfaces for Go

Presto is a thin wrapper library that helps structure and simplify the REST interfaces you create in Go. Its purpose is to encapsulate all of the boilerplate code that is commonly required to publish a server-side service via a REST interface. Using Presto, your route configuration code looks like this:

// Define a new service to expose online as a REST collection.
presto.NewCollection(echo.Echo, NoteFactory, "/notes").
    List().
    Post(role.InRoom).
    Get(role.InRoom).
    Put(role.InRoom, role.Owner).
    Patch(role.InRoom, role.Owner).
    Delete(role.InRoom, role.Owner).
    Method("action-name", customHandler, role.InRoom, role.CustomValue)

Design Philosophy

Clean Architecture

Presto lays the groundword to implement a REST API according to the CLEAN architecture, first published by Uncle Bob. This means decoupling business logic and databases, by injecting dependencies down through your application. To do this in a type-safe manner, Presto requires that your services and objects fit into its interfaces, which describe minimal behavior that each must support in order to be used by Presto. Once Presto is able to work with your business logic in an abstract way, the rest of the common code is repeated for each API endpoint you need to create.

REST API Design Rulebook

Presto works hard to implement REST APIs according to the patterns laid out in the "REST API Design Rulebook", by Mark Massé. This means:

  • Clear route names
  • Using HTTP methods (GET, PUT, POST, PATCH, DELETE) to determine the action being taken
  • Using POST and URL route parameters for other API endpoints that don't fit neatly into the standard HTTP method definitions.
Minimal Dependencies

Presto's only dependency is on the Echo router, which is a very fast, open-source router for creating HTTP servers in Go. Our ultimate goal with this package is to remove this as a hard dependency eventually, and refactor this code to work with multiple routers in the Go ecosystem.

Services

Presto does not replace your application business logic. It only exposes your internal services via a REST API. Each endpoint must be linked to a corresponding service (that matches Presto's required interface) to handle the actual loading, saving, and deleting of objects.

Factories

The specific work of creating services and objects is pushed out to a Factory object, which provides a map of your complete domain. The factories also manage dependencies (such as a live database connection) for each service that requires it. Here's an example factory:



User Roles

It's very likely that your API requires custom authentication and authorization for each endpoint. Since this is very custom to your application logic and environment, Presto can't automate this for you. But, Presto does make it very easy to organize the permissions for each endpoint into a single, readable location. Authorization requirements for each endpoint are baked into common functions called roles, and then passed in to Presto during system configuration.


/// IN YOUR ROUTE CONFIGURATION

presto.NewCollection(echo.Echo, NoteFactory, "/notes").
    Post(role.InRoom) // The user must have permissions to post into the Room into which they're posting.

// IN YOUR ROLES PACKAGE

// InRoom determines if the requester has access to the Room in which this object resides.
// If so, then access to it is valid, so return a TRUE..  If not, then return a FALSE.
func InRoom(ctx echo.Context, object Object) bool {

    // Get the list of rooms that this user has access to..
    // For example, using JWT tokens in the context request headers.
    allowedRoomIDs := getRoomListFromContext(ctx)

    // Uses a type switch to retrieve the roomID from the Object interface.
    roomID, err := getRoomIDFromObject(object)

    if err != nil {
        return false
    }

    // Try to find the object.RoomID in the list of allowed rooms.
    for _, allowedRoomID := range allowedRoomIDs {
        if allowedRoomID == roomID {
            return true // If so, then you're in.
        }
    }

    // Otherwise, you are not permitted to access this object.
    return false;
}

Selectors

Boilerplate REST Endpoints

Custom REST Endpoints

Documentation

Overview

Package Presto gives you a strong foundation for creating REST interfaces using Go and the [Echo Router](http://echo.labstack.com)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IDScope added in v0.2.0

func IDScope(context echo.Context) (map[string]interface{}, *derp.Error)

func RequestInfo

func RequestInfo(context echo.Context) map[string]string

RequestInfo inspects a request and returns any information that might be useful for debugging problems. It is primarily used by internal methods whenever there's a problem with a request.

Types

type Cache

type Cache interface {

	// Get returns the cache value (ETag) corresponding to the argument (objectID) provided.
	// If a value is not found, then Get returns empty string ("")
	Get(objectID string) string

	// Set updates the value in the cache, returning a derp.Error in case there was a problem.
	Set(objectID string, value string) *derp.Error
}

Cache maintains fast access to key/value pairs that are used to check ETags of incoming requests. By default, Presto uses a Null cache, that simply reports cache misses for every request. However, this can be extended by the user, with any external caching system that matches this interface.

var ETagCache Cache

type Collection

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

Collection provides all of the HTTP hanlers for a specific domain object, or collection of records

func NewCollection

func NewCollection(router *echo.Echo, factory ServiceFactory, name string, prefix string) *Collection

NewCollection returns a fully populated Collection object

func (*Collection) Delete

func (collection *Collection) Delete(roles ...RoleFunc) *Collection

Delete returns an HTTP handler that knows how to delete records from the collection

func (*Collection) Get

func (collection *Collection) Get(roles ...RoleFunc) *Collection

Get returns an HTTP handler that knows how to retrieve a single record from the collection

func (*Collection) List

func (collection *Collection) List(roles ...RoleFunc) *Collection

List returns an HTTP handler that knows how to list a series of records from the collection

func (*Collection) Method

func (collection *Collection) Method()

func (*Collection) Patch

func (collection *Collection) Patch(roles ...RoleFunc) *Collection

Patch returns an HTTP handler that knows how to update in the collection

func (*Collection) Post

func (collection *Collection) Post(roles ...RoleFunc) *Collection

Post returns an HTTP handler that knows how to create new objects in the collection

func (*Collection) Put

func (collection *Collection) Put(roles ...RoleFunc) *Collection

Put returns an HTTP handler that knows how to update in the collection

func (*Collection) WithScopes added in v0.2.0

func (collection *Collection) WithScopes(scopes ...ScopeFunc) *Collection

WithScopes replaces the default scope with a new list of ScopeFuncs

type Object

type Object interface {

	// ID returns the primary key of the object
	ID() string

	// IsNew returns TRUE if the object has not yet been saved to the database
	IsNew() bool

	// SetCreated stamps the CreateDate and UpdateDate of the object, and makes a note
	SetCreated(comment string)

	// SetUpdated stamps the UpdateDate of the object, and makes a note
	SetUpdated(comment string)

	// SetDeleted marks the object virtually "deleted", and makes a note
	SetDeleted(comment string)

	// ETag returns a version-unique string that helps determine if an object has changed or not.
	ETag() string
}

Object wraps all of the methods that a Domain Object must provide to Presto

type RoleFunc

type RoleFunc func(context echo.Context, object Object) bool

RoleFunc is a function signature that validates a user's permission to access a particular object

type ScopeFunc added in v0.2.0

type ScopeFunc func(context echo.Context) (map[string]interface{}, *derp.Error)

type Service added in v0.2.0

type Service interface {

	// NewObject creates a newly initialized object that is ready to use
	NewObject() Object

	// Load retrieves a single object from the database
	LoadObject(objectID string) (Object, *derp.Error)

	// Save inserts/updates a single object in the database
	SaveObject(object Object, comment string) *derp.Error

	// Delete removes a single object from the database
	DeleteObject(object Object, comment string) *derp.Error

	// Close cleans up any connections opened by the service.
	Close()
}

Service defines all of the functions that a service must provide to work with Presto. It relies on the generic Object interface to load and save objects of any type. GenericServices will likely include additional business logic that is triggered when a domain object is created, edited, or deleted, but this is hidden from presto.

type ServiceFactory

type ServiceFactory interface {
	Service(name string) Service
}

Factory is an interface for objects that generate service sessions. Each session represents a single HTTP request, which can potentially span multiple database calls. This gives the factory an opportunity to initialize a new database session for each HTTP request.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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