Skip to content

Commit

Permalink
refactor(entities): refactor and improve Rule & Plugin interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
h2non committed May 27, 2016
1 parent 5b4497a commit 9619496
Show file tree
Hide file tree
Showing 19 changed files with 243 additions and 94 deletions.
15 changes: 12 additions & 3 deletions _examples/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ func main() {

// Instance level scope
scope := instance.NewScope("custom", "Custom scope")
scope.UseRule(rule.Init("path", config.Config{"path": "/image/(.*)"}))
r, err := rule.Init("path", config.Config{"path": "/image/(.*)"})
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
scope.UseRule(r)

plu, err := plugin.Init("forward", config.Config{"url": "http://httpbin.org"})
if err != nil {
Expand All @@ -45,8 +50,12 @@ func main() {

// Register scopes
scope = mgr.NewScope("default", "Default scope")
scope.UseRule(rule.Init("path", config.Config{"path": "/vinxi/(.*)"}))
scope.UseRule(rule.Init("vhost", config.Config{"host": "localhost"}))

// Register scope-specific rules
r, _ = rule.Init("path", config.Config{"path": "/vinxi/(.*)"})
scope.UseRule(r)
r, _ = rule.Init("vhost", config.Config{"host": "localhost"})
scope.UseRule(r)

plu, err = plugin.Init("static", config.Config{"path": "/Users/h2non/Projects/vinxi"})
if err != nil {
Expand Down
10 changes: 6 additions & 4 deletions manager/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ var instances InstancesController
// routes stores the registered routes.
var routes = []*Route{}

// route registers a new HTTP route based on the
// given verb and path expression.
func route(method, path string, fn RouteHandler) {
routes = append(routes, &Route{
Path: path,
Expand Down Expand Up @@ -37,8 +39,8 @@ func init() {

// Scope-specific plugins routes
route("GET", "/scopes/:scope/plugins", plugins.List)
route("POST", "/scopes/:scope/plugins", plugins.Create)
route("GET", "/scopes/:scope/plugins/:plugin", plugins.Get)
route("POST", "/scopes/:scope/plugins/:plugin", plugins.Create)
route("DELETE", "/scopes/:scope/plugins/:plugin", plugins.Delete)

// Scope-specific rules routes
Expand All @@ -54,19 +56,19 @@ func init() {

// Instance-specific scopes
route("GET", "/instances/:instance/scopes", scopes.List)
route("POST", "/instances/:instance/scopes", scopes.Create)
route("GET", "/instances/:instance/scopes/:scope", scopes.Get)
route("POST", "/instances/:instance/scopes/:scope", scopes.Create)
route("DELETE", "/instances/:instance/scopes/:scope", scopes.Delete)

// Instance-specific, scope-specific plugins
route("GET", "/instances/:instance/scopes/:scope/plugins", plugins.List)
route("POST", "/instances/:instance/scopes/:scope/plugins", plugins.Create)
route("GET", "/instances/:instance/scopes/:scope/plugins/:plugin", plugins.Get)
route("POST", "/instances/:instance/scopes/:scope/plugins/:plugin", plugins.Create)
route("DELETE", "/instances/:instance/scopes/:scope/plugins/:plugin", plugins.Delete)

// Instance-specific, scope-specific rules
route("GET", "/instances/:instance/scopes/:scope/rules", rules.List)
route("POST", "/instances/:instance/scopes/:scope/rules", rules.Create)
route("GET", "/instances/:instance/scopes/:scope/rules/:rule", rules.Get)
route("POST", "/instances/:instance/scopes/:scope/rules/:rule", rules.Create)
route("DELETE", "/instances/:instance/scopes/:scope/rules/:rule", rules.Delete)
}
2 changes: 1 addition & 1 deletion manager/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (c *Context) SendNoContent() {
c.Response.WriteHeader(204)
}

// SendNoContent replies with 204 status code.
// SendNotFound replies with 404 status code and custom message.
func (c *Context) SendNotFound(message string) {
c.SendError(404, message)
}
19 changes: 9 additions & 10 deletions manager/controller_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@ package manager

import (
"gopkg.in/vinxi/vinxi.v0"
"gopkg.in/vinxi/vinxi.v0/config"
)

// JSONInstance represents the Instance entity for JSON serialization.
type JSONInstance struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Metadata []config.Config `json:"metadata,omitempty"`
Scopes []JSONScope `json:"scopes"`
Info *vinxi.Metadata `json:"info"`
Scopes []JSONScope `json:"scopes"`
}

func createInstance(instance *Instance) *vinxi.Metadata {
return instance.Metadata()
func createInstance(instance *Instance) JSONInstance {
return JSONInstance{
Info: instance.Metadata(),
Scopes: createScopes(instance.Scopes()),
}
}

func createInstances(instances []*Instance) []*vinxi.Metadata {
list := []*vinxi.Metadata{}
func createInstances(instances []*Instance) []JSONInstance {
list := []JSONInstance{}
for _, instance := range instances {
list = append(list, createInstance(instance))
}
Expand Down
12 changes: 10 additions & 2 deletions manager/controller_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (PluginsController) Delete(ctx *Context) {
}
}

func (PluginsController) Create(ctx *Context) {
func (p PluginsController) Create(ctx *Context) {
type data struct {
Name string `json:"name"`
Params config.Config `json:"config"`
Expand Down Expand Up @@ -87,6 +87,14 @@ func (PluginsController) Create(ctx *Context) {
return
}

ctx.Manager.UsePlugin(instance)
p.registerPlugin(ctx, instance)
ctx.Send(createPlugin(instance))
}

func (PluginsController) registerPlugin(ctx *Context, instance plugin.Plugin) {
if ctx.Scope != nil {
ctx.Scope.UsePlugin(instance)
} else {
ctx.Manager.UsePlugin(instance)
}
}
37 changes: 33 additions & 4 deletions manager/controller_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,43 @@ func (RulesController) Get(ctx *Context) {
ctx.Send(createRule(ctx.Rule))
}

func (RulesController) Create(ctx *Context) {
ctx.Send(createRule(ctx.Rule))
}

func (RulesController) Delete(ctx *Context) {
if ctx.Scope.RemoveRule(ctx.Rule.ID()) {
ctx.SendNoContent()
} else {
ctx.SendError(500, "Cannot remove rule")
}
}

func (RulesController) Create(ctx *Context) {
type data struct {
Name string `json:"name"`
Params config.Config `json:"config"`
}

var input data
err := ctx.ParseBody(&input)
if err != nil {
return
}

if input.Name == "" {
ctx.SendError(400, "Missing required param: name")
return
}

factory := rule.Get(input.Name)
if factory == nil {
ctx.SendNotFound("Rule not found")
return
}

instance, err := factory(input.Params)
if err != nil {
ctx.SendError(400, "Cannot create rule: "+err.Error())
return
}

ctx.Scope.UseRule(instance)
ctx.Send(createRule(instance))
}
34 changes: 11 additions & 23 deletions manager/controller_scopes.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package manager

import (
"gopkg.in/vinxi/vinxi.v0/config"
"gopkg.in/vinxi/vinxi.v0/plugin"
)

// JSONScope represents the scope entity for JSON serialization.
type JSONScope struct {
ID string `json:"id"`
Expand Down Expand Up @@ -57,33 +52,26 @@ func (ScopesController) Delete(ctx *Context) {

func (ScopesController) Create(ctx *Context) {
type data struct {
Name string `json:"name"`
Params config.Config `json:"config"`
Name string `json:"name"`
Description string `json:"description"`
}

var plu data
err := ctx.ParseBody(&plu)
var scope data
err := ctx.ParseBody(&scope)
if err != nil {
return
}

if plu.Name == "" {
if scope.Name == "" {
ctx.SendError(400, "Missing required param: name")
return
}

factory := plugin.Get(plu.Name)
if factory == nil {
ctx.SendNotFound("Plugin not found")
return
}

instance, err := factory(plu.Params)
if err != nil {
ctx.SendError(400, "Cannot create plugin: "+err.Error())
return
instance := NewScope(scope.Name, scope.Description)
if ctx.Instance != nil {
ctx.Instance.UseScope(instance)
} else {
ctx.Manager.UseScope(instance)
}

ctx.Manager.UsePlugin(instance)
ctx.Send(createPlugin(instance))
ctx.Send(createScope(instance))
}
7 changes: 7 additions & 0 deletions manager/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ func (i *Instance) Metadata() *vinxi.Metadata {
return i.instance.Metadata
}

// UseScope registers one or multiple scopes at instance level.
func (i *Instance) UseScope(scopes ...*Scope) {
i.sm.Lock()
i.scopes = append(i.scopes, scopes...)
i.sm.Unlock()
}

// NewScope creates a new scope based on the given name
// and optional description.
func (i *Instance) NewScope(name, description string) *Scope {
Expand Down
7 changes: 7 additions & 0 deletions manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ func (m *Manager) ServeDefault() (*http.Server, error) {
return m.ListenAndServe(ServerOptions{})
}

// UseScope registers one or multiple scopes at global manager level.
func (m *Manager) UseScope(scopes ...*Scope) {
m.sm.Lock()
m.scopes = append(m.scopes, scopes...)
m.sm.Unlock()
}

// NewScope creates a new scope based on the given name
// and optional description.
func (m *Manager) NewScope(name, description string) *Scope {
Expand Down
2 changes: 1 addition & 1 deletion mux/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// Matcher represents the function interface implemented by matchers
type Matcher func(req *http.Request) bool
type Matcher func(*http.Request) bool

// MatchMethod matches the HTTP method name againts the request.
func MatchMethod(methods ...string) Matcher {
Expand Down
3 changes: 2 additions & 1 deletion plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Plugin interface {
HandleHTTP(http.Handler) http.Handler
}

// plugin implements the Plugin interface.
type plugin struct {
id string
name string
Expand All @@ -39,7 +40,7 @@ type plugin struct {

// New creates a new Plugin capable interface based on the
// given HTTP handler logic encapsulated as plugin.
func New(info Info) FactoryFunc {
func New(info Info) NewFunc {
return func(opts config.Config) (Plugin, error) {
return NewWithConfig(info, opts)
}
Expand Down
13 changes: 9 additions & 4 deletions plugin/plugins.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package plugin

import (
"errors"

"gopkg.in/vinxi/vinxi.v0/config"
)

// ErrPluginNotFound is used when a plugin does not exists.
var ErrPluginNotFound = errors.New("vinxi: plugin does not exists")

// Plugins is used to store the available plugins globally.
var Plugins = make(map[string]Info)

// Factory represents the plugin factory function interface.
type Factory func(config.Config) Handler

// FactoryFunc represents the plugin factory configuration function interface.
type FactoryFunc func(config.Config) (Plugin, error)
// NewFunc represents the Plugin constructor factory function interface.
type NewFunc func(config.Config) (Plugin, error)

// Validator represents the plugin config field validator function interface.
type Validator func(interface{}, config.Config) error
Expand Down Expand Up @@ -49,13 +54,13 @@ func Register(plugin Info) {
// based on the given config options.
func Init(name string, opts config.Config) (Plugin, error) {
if !Exists(name) {
panic("vinxi: plugin '" + name + "' does not exists.")
return nil, ErrPluginNotFound
}
return NewWithConfig(Plugins[name], opts)
}

// Get is used to find and retrieve a plugin.
func Get(name string) FactoryFunc {
func Get(name string) NewFunc {
plugin, ok := Plugins[name]
if ok {
return New(plugin)
Expand Down
3 changes: 2 additions & 1 deletion plugin/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"gopkg.in/vinxi/vinxi.v0/config"
)

// getField is used to find and retrieve the param Field configuration.
func getField(name string, params Params) (Field, bool) {
for _, field := range params {
if field.Name == name {
Expand All @@ -15,7 +16,7 @@ func getField(name string, params Params) (Field, bool) {
return Field{}, false
}

// Validate is used to validate plugin params
// Validate is used to validates plugin params
// and report the proper param specific error.
func Validate(params Params, opts config.Config) error {
// Set defaults params
Expand Down
Loading

0 comments on commit 9619496

Please sign in to comment.