pastis

package module
v0.0.0-...-b9557e0 Latest Latest
Warning

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

Go to latest
Published: Apr 20, 2017 License: MIT Imports: 11 Imported by: 0

README

pastis GoDoc

Go framework for developing ops-friendly RESTful web services

Getting Started

Pastis is a framework for creating RESTful web services with minimal effort:

Quick Example

//main.go
package main

import "net/url"
import "github.com/guregodevo/pastis"

func main() {
	api := pastis.NewAPI()
	api.Get("/foo",  func() (int, interface{}) {
		return 200, "Hello"
	})
	api.Start(4567)
}

And run with:

go run main.go

View at: http://localhost:4567/foo

Installing

Using go get
$ go get github.com/guregodevo/pastis

After this command pastis is ready to use. Its source will be in:

$GOROOT/src/pkg/github.com/guregodevo/pastis

You can use go get -u -a to update all installed packages.

Routes

In Pastis, a route is an HTTP method paired with a URL-matching pattern. Each route is associated with a callback function:


	api.Get("/foo", func(params url.Values) (int, interface{}) {
		...show something
	})

	api.Post("/foo", func(params url.Values) (int, interface{}) {
		...create something
	})

	api.Put("/foo", func(params url.Values) (int, interface{}) {
		...modify something
	})

	api.Patch("/foo", func(params url.Values) (int, interface{}) {
		...modify something
	})

	api.Delete("/foo", func(params url.Values) (int, interface{}) {
		...delete something
	})

	api.Link("/foo", func(params url.Values) (int, interface{}) {
		...affiliate something
	})

	api.Unlink("/foo", func(params url.Values) (int, interface{}) {
		...separate something
	})

Routes are matched in the order they are defined. The first route that matches the request is invoked.

In Pastis, query or path parameters are both accessible via the optional callback parameter of type url.Values.

Route patterns may include **named parameters:

	api.Get("/posts/:title", func(params url.Values) (int, interface{}) {
		title := params.Get("title")
                ...show something with this named parameter
	})

Routes may also utilize **query parameters:

	api.Get("/posts", func(params url.Values) (int, interface{}) {
		title := params.Get("title")
		author := params.Get("author")
		greeding := fmt.SPrintf("Hello %s", name)	
		// uses title and author variables; query is optional to the /posts route
	})

Routes may require the request body. In Pastis, the request body is decoded to the type of the callback parameter that you declared as input parameter in the callback. Any parameter that has a type different from url.Values will match the request body content provided that it can be represented as valid JSON.

Possible request body parameter can be any of the following types:

  • map[string]interface{} or struct (those that begin with uppercase letter) for JSON Objects
  • []interface{} for JSON arrays
  • Any Go primitive type that matches the body content that is more convenient that the type above (int, string etc..)

Return Values

Every callback execution should end up returning a tuple (int, interface{}). The tuple element of type int represents the HTTP status code. The other one of type interface{} represents the response content. The return handler will take care of marshalling this content into JSON.

Examples:

	return http.StatusOK, [] Chart{Chart{"name", 1},Chart{"name", 1}}
	return http.StatusCreated, Identifier{params.Get("id"), 2}
	return http.StatusCreated, map[string]interface{} {"id":1, "size":3, "type":"line"}
	return http.StatusOK, "Hello"

Resources

In Pastis, a resource is any Go struct that implements HTTP methods (GET, PUT etc..).

type DashboardResource struct {
}

type ChartResource struct {
}

type Chart struct {
	Name  string
	Order int
}

func (api DashboardResource) Get(params url.Values) (int, interface{}) {
	...do something with params params.Get("dashboardid")	
	return http.StatusOK, [] Chart{Chart{"name", 1},Chart{"name", 1}}
}

func (api ChartResource) Get(params url.Values) (int, interface{}) {
	return http.StatusOK, Chart{params.Get("chartid"), 2}
}

func (api ChartResource) Put(params url.Values) (int, interface{}) {
	...do something with params params.Get("chartid")
}

A resource has a unique URL-matching pattern. Therefore, each resource route method is associated with the resource method function whose name matches.

dashboardResource := new(DashboardResource)
chartResource := new(ChartResource)
api := NewAPI()
api.AddResource("/dashboards/:dashboardid", dashboardResource)
api.AddResource("/dashboards/:dashboardid/charts/:chartid", chartResource )
api.Start(44444)

In the above example, the chart resource Put method matches the HTTP method "PUT" and the resource URL "/dashboards/:dashboardid/charts/:chartid".

Resource method functions behave exactly like callback method except that they match the resource route.

Filters

Filters are evaluated before and/or after request within the same context as the routes will be and can modify the request and response.

A filter is any function that sastifies this interface :

type Filter func(http.ResponseWriter, *http.Request, *FilterChain)


// Filter (post-process) Filter (as a struct that defines a FilterFunction)
func LoggingFilter(w http.ResponseWriter, request *http.Request, chain *FilterChain) {
	now := time.Now()
	chain.NextFilter(w, request)
	log.Printf("[HTTP] %s %s [%v]\n", request.Method, request.URL, time.Now().Sub(now))
}

Any filter can be added to apis

	var api = pastis.NewAPI()
	api.AddFilter(pastis.LoggingFilter)

CORS Support

Pastis provides CORS filter. If you need it, just add the CORS filter to your api.

	var api = pastis.NewAPI()
	api.AddFilter(pastis.CORSFilter)

Testing

Pastis tests can be written using any testing library or framework. The native Go package httptest is recommended:

import (
	"net/http/httptest"
	"reflect"
	"testing"
)

/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
	....
}


func assert_HTTP_Response(t *testing.T, res *http.Response, expectedStatusCode int, expectedResponsebody interface{}) {
	....
}

func Test_Callback_With_Params(t *testing.T) {
	p := NewAPI()
	p.Get( "/hello/:name", func(params url.Values) (int, interface{}) {
		fmt.Printf("Name : %v",params.Get("name"))
		return http.StatusOK, Foo { params.Get("name"), 1 }
	})
	p.HandleFunc()

	ts := httptest.NewServer(p)
	defer ts.Close()

	url := ts.URL + "/hello/guregodevo"
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	assert_HTTP_Response(t, res, http.StatusOK, Foo{"guregodevo", 1})
}

Logging

Pastis includes its own logging API. It allows the developer to control which log statements are output with arbitrary granularity. It is fully configurable at runtime.

Pastis Logger may be assigned a minimum level. The set of possible levels, in ascending order is:

  • DEBUG,
  • INFO,
  • WARN,
  • ERROR and
  • FATAL
  • OFF

A logger minimum level enables any log message whose level order is equals or lower.

By default, this API Logger level is "DEBUG" and the log output stream is StdOut.

//main.go
package main

import "net/url"
import "github.com/guregodevo/pastis"
import "os"
func main() {
	api := pastis.NewAPI()
	api.SetLevel("INFO")
	api.SetOutput("ERROR", os.Stderr, log.Ltime)
	//ERROR logs will now be printed out to the standard error stream
	//Prefixed by ERROR and the time flag "ERROR 01:23:23" 
}

JSON

Pastis speaks JSON. In terms of data formats, JSON has become mainstream, and the package encoding/json is fairly robust in the Go programming language. In addition to being lightning fast, it has a sophisticated mashaller, allowing you to use type safe parameter when recieving request content.

In the example below, the body content of the request is fully decoded using the Go JSON decoder. Pastis just takes care of detecting the parameter type and unmarshalling it.

//main.go
package main

import "net/url"
import "github.com/guregodevo/pastis"
import "fmt"

type Foo struct {
	Name string
	Order int	
}

func main() {
	api := pastis.NewAPI()
	api.Post( "/foo", func(vals url.Values, body Foo) (int, interface{}) {
		fmpt.Printf("Here is the request body %v ", foo)
		return http.StatusOK, body
	})
}

Documentation

Overview

Package pastis implements a simple library for building RESTful APIs. This package provides you with everything you will need for most of your applications. Pastis has 3 building blocks : 1) An API associated to a set of Resource and callback functions. It is paired with an arbitrary port. 2) Resource paired with an URL-pattern. Its represents a REST resource. 3) A callback paired with a URL-pattern and a request method. Note that a pastis server can support more than one API. Pastis rich features are : Nice URL-pattern matching, Path parameter parsing, Configurable loggers, CORS support, Speaks JSON and Type-safe request

Index

Constants

View Source
const (
	HEADER_Allow_Methods                 = "GET,POST,DELETE,PUT,PATCH,HEAD"
	HEADER_Access_Control_Allow_Headers  = "Origin,Accept,Produce,Content-Type,X-Requested-With,Authorization,Token"
	HEADER_Origin                        = "Origin"
	HEADER_Allow_Credentials             = "Access-Control-Allow-Credentials"
	HEADER_Allow_Origin                  = "Access-Control-Allow-Origin"
	HEADER_AccessControlRequestHeaders   = "Access-Control-Request-Headers"
	HEADER_Access_Control_Request_Method = "Access-Control-Request-Method"
)
View Source
const (
	DEBUG = 1
	INFO  = 2
	WARN  = 3
	ERROR = 4
	FATAL = 5
	OFF   = 6
)

Variables

This section is empty.

Functions

func CORSFilter

func CORSFilter(rw http.ResponseWriter, request *http.Request, chain *FilterChain)

A Cross Origin Filter

func ErrorResponse

func ErrorResponse(err error) interface{}

A Pretty Error response

func HandlerPath

func HandlerPath(pattern string) string

HandlerPath returns the Server path

func LevelInt

func LevelInt(level string) int

Level converts a strong value of a level into into its integer value

func LoggingFilter

func LoggingFilter(w http.ResponseWriter, request *http.Request, chain *FilterChain)

Filter (post-process) Filter (as a struct that defines a FilterFunction)

func Match

func Match(r *regexp.Regexp, path string) (bool, map[string]string)

Match checks whether the given pat matches the given regular expresion.

func Regexp

func Regexp(pattern string) *regexp.Regexp

Regex simply builds a more reliable regex based on the initial pattern

Types

type API

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

An API manages a group of resources by routing to requests to the correct method and URL.

You can instantiate multiple APIs on separate ports. Each API will manage its own set of resources.

func NewAPI

func NewAPI() *API

NewAPI allocates and returns a new API.

func (*API) AddFilter

func (api *API) AddFilter(filter Filter)

AddFilter adds a new filter to an API. The API will execute the filter before calling the target function.

func (*API) AddResource

func (api *API) AddResource(pattern string, resource interface{})

AddResource adds a new resource to an API. The API will route requests that match the given path to its HTTP method on the resource.

func (*API) Delete

func (api *API) Delete(fn interface{}, pattern string)

Function callback paired with DELETE Method and URL-matching pattern.

func (*API) Do

func (api *API) Do(requestMethod string, pattern string, fn interface{})

Function callback paired with a request Method and URL-matching pattern.

func (*API) Get

func (api *API) Get(pattern string, fn interface{})

Function callback paired with GET Method and URL-matching pattern.

func (*API) HandleFunc

func (api *API) HandleFunc()

func (*API) Head

func (api *API) Head(pattern string, fn interface{})

Function callback paired with HEAD Method and URL-matching pattern.

func (api *API) Link(pattern string, fn interface{})

Function callback paired with LINK Method and URL-matching pattern.

func (*API) Options

func (api *API) Options(pattern string, fn interface{})

Function callback paired with OPTIONS Method and URL-matching pattern.

func (*API) Patch

func (api *API) Patch(pattern string, fn interface{})

Function callback paired with PATH Method and URL-matching pattern.

func (*API) Post

func (api *API) Post(pattern string, fn interface{})

Function callback paired with POST Method and URL-matching pattern.

func (*API) Put

func (api *API) Put(pattern string, fn interface{})

Function callback paired with PUT Method and URL-matching pattern.

func (*API) ServeHTTP

func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request)

Implements HandlerFunc

func (*API) SetLevel

func (api *API) SetLevel(level string)

func (*API) SetOutput

func (api *API) SetOutput(level string, w io.Writer, flag int) *Logger

SetOutput sets the output destination for the standard logger of the given level. Example: api.SetOutput("ERROR", os.StdErr, log.Lmicroseconds)

func (*API) Start

func (api *API) Start(port int) error

Start causes the API to begin serving requests on the given port.

func (api *API) Unlink(pattern string, fn interface{})

Function callback paired with UNLINK Method and URL-matching pattern.

type AdminResource

type AdminResource struct {
}

A simple admin REST resource

func (AdminResource) Get

func (api AdminResource) Get() (int, interface{})

Simply GETs request and returns OK response

type Filter

type Filter func(http.ResponseWriter, *http.Request, *FilterChain)

Filter function definition. Must be called on the FilterChain to pass on the control and eventually call the Target function

type FilterChain

type FilterChain struct {
	Filters []Filter         // ordered list of Filter function
	Index   int              // index into filters that is currently in progress
	Target  http.HandlerFunc // function to call after passing all filters
}

FilterChain is a request scoped object to process one or more filters before calling the target function.

func (*FilterChain) Copy

func (f *FilterChain) Copy() FilterChain

func (*FilterChain) NextFilter

func (f *FilterChain) NextFilter(rw http.ResponseWriter, request *http.Request)

NextFilter passes the request,response pair through the next of Filters. Each filter can decide to proceed to the next Filter or handle the Response itself.

type Logger

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

An API Logger whose level is configurable

func GetLogger

func GetLogger(level string) *Logger

GetLogger retrieves a logger having the given level. The level defines the minimum set of levels recognized by the system, that is OFF, FATAL, ERROR, WARN, INFO, DEBUG and ALL.

func (*Logger) Debug

func (logger *Logger) Debug(s string)

Log a message object with the DEBUG level. Arguments are handled in the manner of fmt.Print.

func (*Logger) Debugf

func (logger *Logger) Debugf(format string, v ...interface{})

Log a message object with the DEBUG level. Arguments are handled in the manner of fmt.Printf.

func (*Logger) Error

func (logger *Logger) Error(s string)

Log a message object with the ERROR level. Arguments are handled in the manner of fmt.Print.

func (*Logger) Errorf

func (logger *Logger) Errorf(format string, v ...interface{})

Log a message object with the ERROR level. Arguments are handled in the manner of fmt.Printf.

func (*Logger) Fatal

func (logger *Logger) Fatal(s string)

Log a message object with the FATAL level. Arguments are handled in the manner of fmt.Print.

func (*Logger) Fatalf

func (logger *Logger) Fatalf(format string, v ...interface{})

Log a message object with the FATAL level. Arguments are handled in the manner of fmt.Printf.

func (*Logger) Info

func (logger *Logger) Info(s string)

Log a message object with the INFO level. Arguments are handled in the manner of fmt.Print.

func (*Logger) Infof

func (logger *Logger) Infof(format string, v ...interface{})

Log a message object with the INFO level. Arguments are handled in the manner of fmt.Printf.

func (*Logger) SetLevel

func (logger *Logger) SetLevel(level string)

Set the minimum set of levels recognized by the system, that is OFF, FATAL, ERROR, WARN, INFO, DEBUG and ALL.

func (*Logger) SetOutput

func (logger *Logger) SetOutput(level string, w io.Writer, flag int)

SetOutput sets the output destination for the logger having the given level.

func (*Logger) SetOutputs

func (logger *Logger) SetOutputs(w io.Writer, flag int, levels ...string)

SetOutput sets the output destination for the logger having the given level.

func (*Logger) Warn

func (logger *Logger) Warn(s string)

Log a message object with the WARN level. Arguments are handled in the manner of fmt.Print.

func (*Logger) Warnf

func (logger *Logger) Warnf(format string, v ...interface{})

Log a message object with the WARN level. Arguments are handled in the manner of fmt.Printf.

type Router

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

Router is a struct consisting of a set of method paired with URL-matchin pattern where each pair is mapped to an handler function.

func NewRouter

func NewRouter() *Router

NewRouter allocates and returns a new Router.

func (*Router) Add

func (router *Router) Add(pattern string, method string, handler http.HandlerFunc)

Add adds a route wichi consist of an URL-pattern matching, a method and an handler of type http.HandlerFunc

func (*Router) Handler

func (router *Router) Handler(logger *Logger) http.HandlerFunc

Handler returns an handler function of the API. This handler is built from the set of routes that have been defined previously.

func (*Router) OpsFriendlyLog

func (router *Router) OpsFriendlyLog(logger *Logger)

Prints out the routes in a friendly manner

Jump to

Keyboard shortcuts

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