flamel

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: May 14, 2020 License: MIT Imports: 19 Imported by: 3

README

Flamel: a framework for Google App Engine

Flamel is a session-less, simple web framework built to structure and ease development of web applications running on Google App Engine using the Go API.

It exposes a minimalistic lifecycle and implements its own performant and low-allocation routing system.

Except for the Google Client Libraries and the Appengine Package (for obvious reasons), Flamel comes with zero dependencies.

Flamel is 100% compatible with GAE go1.11 API

  • Fast and reliable
  • Not invasive: its own packages are all optional, including the router, which means you can build your own system of top of flamel lifecyclele. Flamel is built on top of few interfaces: you can use the stock implementations or you can roll your own renderer, use your own router, build your own controller.
  • Squared and structured: the lifecycle and the basic interfaces the framework is composed of allow client code to be simple and well organized. The architecture of Flamel incentives the use of stateless logic and helps in implementing proper http responses by not hiding the protocol logic.

Installation

go get -u decodica.com/flamel

Usage

  • [Quickstart]

To start using some alchemy goodies, set up the app.yaml file as you would for any GAE go application:

runtime: go111

handlers:

- url: /.*
  script: auto

Next we need to define our Flamel Application and a controller, by implementing the following interfaces:

The first one is the Application interface:

type Application interface {
	OnStart(ctx context.Context) context.Context
	AfterResponse(ctx context.Context)
}

OnStart() will be called for each request that Flamel will handle, as soon as an appengine context is available and before any routing happens. AfterResponse() is instead called after the response has been delivered.

As a minimum, we can implement the following Application:

type HelloWorld struct {}

func (app HelloWorld) OnStart(ctx context.Context) context.Context {
    return ctx
}

func (app HelloWorld) AfterResponse(ctx context.Context) {} 

The second interface is the Controller interface:

type Controller interface {
	Process(ctx context.Context, out *ResponseOutput) HttpResponse
	OnDestroy(ctx context.Context)
}

OnDestroy() will be invoked after the response output has been sent to the client, but before the application AfterResponse() method is invoked. Process() is the heart of the controller. It is here that the response logic must be implemented, for any request that our application wants to handle.

The following controller responds with the usual "Hello World!" and returns a 405 status code if the request method is not "GET":

type HelloWorldController struct {}

func (controller *HelloWorldController) Process(ctx context.Context, out *flamel.ResponseOutput) flamel.HttpResponse {

	ins := flamel.InputsFromContext(ctx)
	method := ins[flamel.KeyRequestMethod].Value()
	switch method {
	case http.MethodGet:
		renderer := flamel.TextRenderer{}
		renderer.Data = "Hello Flamel!"
		out.Renderer = &renderer
		return flamel.HttpResponse{Status:http.StatusOK}
	}
	
	return flamel.HttpResponse{Status:http.StatusMethodNotAllowed}
}

func (controller *HelloWorldController) OnDestroy(ctx context.Context) {}

Putting all the pieces together allows us to have a GAE application up and running (and well organized):

package main

import "context"
import "decodica.com/flamel"
import "net/http"

// Define the application struct

type HelloWorld struct {}

func (app HelloWorld) OnStart(ctx context.Context) context.Context {
    return ctx
}

func (app HelloWorld) AfterResponse(ctx context.Context) {}

// Define our one and only controller

type HelloWorldController struct {}

func (controller *HelloWorldController) Process(ctx context.Context, out *flamel.ResponseOutput) flamel.HttpResponse {

	ins := flamel.InputsFromContext(ctx)
	method := ins[flamel.KeyRequestMethod].Value()
	switch method {
	case http.MethodGet:
		renderer := flamel.TextRenderer{}
		renderer.Data = "Hello Flamel!"
		out.Renderer = &renderer
		return flamel.HttpResponse{Status:http.StatusOK}
	}
	
	return flamel.HttpResponse{Status:http.StatusMethodNotAllowed}
}

func (controller *HelloWorldController) OnDestroy(ctx context.Context) {}

func main() {
    instance := flamel.Instance()
    instance.SetRoute("/", func(ctx context.Context) flamel.Controller {
            c := HelloWorldController{}
            return &c
        }, nil)
    
    app := HelloWorld{}
    instance.Run(app)
}

Now running

dev_appserver.py app.yaml

and sending a GET request to localhost:8080 will make your app output Hello Flamel!

  • [Handling routes] // Todo

  • [Managing authentication] // Todo

Roadmap

  • Support for GAE go112 API by replacing Memcache and Search with Redis and Elastic on the GCP (or any other official solution Google will provide us with - Memorystore API perhaps?).
  • Static content caching, provided GCS and the above-mentioned KVP caching system

Any help would be greatly appreciated. :)

License

Flamel uses the MIT license.

Documentation

Index

Constants

View Source
const (
	//request related special vars
	KeyRequestInputs     = "__flamel_request_inputs__"
	KeyRequestMethod     = "__flamel__method__"
	KeyRequestIPV4       = "__flamel_remote_address__"
	KeyRequestJSON       = "__flamel_json__"
	KeyRequestURL        = "__flamel_URL__"
	KeyRequestScheme     = "__flamel_scheme__"
	KeyRequestQuery      = "__flamel_query__"
	KeyRequestHost       = "__flamel_host__"
	KeyNegotiatedContent = "__flamel_negotiated_content__"
)

Variables

View Source
var ErrNoInputs = errors.New("request has no inputs")

Functions

func Instance

func Instance() *flamel

singleton instance

func ParseAccept added in v0.6.0

func ParseAccept(accept []string) []contentSpecification

parses the "Accept" header value. e.g: "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8"

func ParseJSONInputs

func ParseJSONInputs(ctx context.Context) (map[string]interface{}, error)

Convenience method to recover all json inputs Returns user json inputs as a map string -> interface{}

Types

type Application

type Application interface {
	// called as soon as the request is received and the context is created
	OnStart(ctx context.Context) context.Context
	// called after each response has been finalized
	AfterResponse(ctx context.Context)
}

type Authenticator

type Authenticator interface {
	Authenticate(ctx context.Context) context.Context
}

type BlobRenderer

type BlobRenderer struct {
	Data appengine.BlobKey
}

Renders a file as returned from the BlobStore

func (*BlobRenderer) Render

func (renderer *BlobRenderer) Render(w http.ResponseWriter) error

type Config

type Config struct {
	//true if the server suport Cross Origin Request
	CORS                    *cors.Cors
	EnforceHostnameRedirect string
	MaxFileUploadSize       int64
	ContentOfferer          ContentOfferer
	Router
}

func DefaultConfig

func DefaultConfig() Config

type ContentOfferer added in v0.6.0

type ContentOfferer interface {
	DefaultOffer() string
	Offers() []string
}

type Controller

type Controller interface {
	//the page logic is executed here
	//Process method consumes the context -> context variations, i.e. appengine.Namespace
	//can be used INSIDE the Process function
	Process(ctx context.Context, out *ResponseOutput) HttpResponse
	//called to release resources
	OnDestroy(ctx context.Context)
}

type DefaultRouter

type DefaultRouter struct {
	router.Router
}

func NewDefaultRouter

func NewDefaultRouter() *DefaultRouter

func (*DefaultRouter) RouteForPath

func (router *DefaultRouter) RouteForPath(ctx context.Context, path string) (context.Context, error, Controller)

func (*DefaultRouter) SetRoute

func (router *DefaultRouter) SetRoute(url string, handler func(ctx context.Context) Controller, authenticator Authenticator)

func (*DefaultRouter) SetRoutes

func (router *DefaultRouter) SetRoutes(urls []string, handler func(ctx context.Context) Controller, authenticator Authenticator)

type DownloadRenderer added in v0.6.0

type DownloadRenderer struct {
	Mime     string
	Encoding string
	FileName string
	Data     []byte
}

func (*DownloadRenderer) Render added in v0.6.0

func (renderer *DownloadRenderer) Render(w http.ResponseWriter) error

type ErrorRenderer

type ErrorRenderer struct {
	Data error
}

func (*ErrorRenderer) Render

func (renderer *ErrorRenderer) Render(w http.ResponseWriter) error

type HttpResponse

type HttpResponse struct {
	Status   int
	Location string
}

type JSONRenderer

type JSONRenderer struct {
	Data interface{}
}

Returns the data as JSON object(s)

func (*JSONRenderer) Render

func (renderer *JSONRenderer) Render(w http.ResponseWriter) error

type MissingInputError added in v0.6.1

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

func (MissingInputError) Error added in v0.6.1

func (e MissingInputError) Error() string

type Renderer

type Renderer interface {
	Render(w http.ResponseWriter) error
}

type RequestInputs

type RequestInputs map[string]requestInput

inputs related definitions and methods

func InputsFromContext

func InputsFromContext(ctx context.Context) RequestInputs

func RoutingParams

func RoutingParams(ctx context.Context) RequestInputs

func (RequestInputs) GetFloat added in v0.6.1

func (ins RequestInputs) GetFloat(key string) (float64, error)

func (RequestInputs) GetFloatWithFormat added in v0.6.1

func (ins RequestInputs) GetFloatWithFormat(key string, size int) (float64, error)

float related methods

func (RequestInputs) GetInt added in v0.6.1

func (ins RequestInputs) GetInt(key string) (int64, error)

func (RequestInputs) GetIntWithFormat added in v0.6.1

func (ins RequestInputs) GetIntWithFormat(key string, base int, size int) (int64, error)

func (RequestInputs) GetString added in v0.6.1

func (ins RequestInputs) GetString(key string) (string, error)

func (RequestInputs) GetUint added in v0.6.1

func (ins RequestInputs) GetUint(key string) (uint64, error)

func (RequestInputs) GetUintWithFormat added in v0.6.1

func (ins RequestInputs) GetUintWithFormat(key string, base int, size int) (uint64, error)

Uint related methods

func (RequestInputs) Has added in v0.6.1

func (ins RequestInputs) Has(key string) bool

func (RequestInputs) MustFloat added in v0.6.1

func (ins RequestInputs) MustFloat(key string) float64

func (RequestInputs) MustInt added in v0.6.1

func (ins RequestInputs) MustInt(key string) int64

func (RequestInputs) MustString added in v0.6.1

func (ins RequestInputs) MustString(key string) string

func (RequestInputs) MustUint added in v0.6.1

func (ins RequestInputs) MustUint(key string) uint64

type ResponseOutput

type ResponseOutput struct {
	Renderer Renderer
	// contains filtered or unexported fields
}

func (*ResponseOutput) AddCookie

func (out *ResponseOutput) AddCookie(cookie http.Cookie)

func (*ResponseOutput) AddHeader

func (out *ResponseOutput) AddHeader(key string, value string)

func (*ResponseOutput) RemoveCookie

func (out *ResponseOutput) RemoveCookie(name string)

func (*ResponseOutput) SetCookie added in v0.6.1

func (out *ResponseOutput) SetCookie(cookie http.Cookie)

type Router

type Router interface {
	SetRoute(url string, handler func(ctx context.Context) Controller, authenticator Authenticator)

	// Utility method. Calls @SetRoute on each element of @urls
	SetRoutes(urls []string, handler func(ctx context.Context) Controller, authenticator Authenticator)

	RouteForPath(ctx context.Context, path string) (context.Context, error, Controller)
}

type Service

type Service interface {
	Name() string
	// used to set the service up
	Initialize()
	// called everytime a request is being processed
	OnStart(ctx context.Context) context.Context
	// called once the request has been processed
	OnEnd(ctx context.Context)
	// called once the main function returns. The service should implement its destruction code here.
	Destroy()
}

type TemplateRenderer

type TemplateRenderer struct {
	Template     *template.Template
	TemplateName string
	Data         interface{}
}

Renders a GO HTML template

func (*TemplateRenderer) Render

func (renderer *TemplateRenderer) Render(w http.ResponseWriter) error

type TextRenderer

type TextRenderer struct {
	Data string
}

Renders plain text

func (*TextRenderer) Render

func (renderer *TextRenderer) Render(w http.ResponseWriter) error

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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