Commit dbfafa1c by bergquist

routing: allows routes to be added to existing groups

this enables services to add routing to ex /api
without causing conflicts.
parent 503c8cd8
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"path" "path"
"time" "time"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
...@@ -43,7 +44,7 @@ type HTTPServer struct { ...@@ -43,7 +44,7 @@ type HTTPServer struct {
cache *gocache.Cache cache *gocache.Cache
httpSrv *http.Server httpSrv *http.Server
RouteRegister RouteRegister `inject:""` RouteRegister routing.RouteRegister `inject:""`
Bus bus.Bus `inject:""` Bus bus.Bus `inject:""`
RenderService rendering.Service `inject:""` RenderService rendering.Service `inject:""`
Cfg *setting.Cfg `inject:""` Cfg *setting.Cfg `inject:""`
......
package api package routing
import ( import (
"net/http" "net/http"
"strings"
macaron "gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )
type Router interface { type Router interface {
...@@ -36,6 +37,9 @@ type RouteRegister interface { ...@@ -36,6 +37,9 @@ type RouteRegister interface {
// with a shared prefix route. // with a shared prefix route.
Group(string, func(RouteRegister), ...macaron.Handler) Group(string, func(RouteRegister), ...macaron.Handler)
// Insert adds more routes to an existing Group.
Insert(string, func(RouteRegister), ...macaron.Handler)
// Register iterates over all routes added to the RouteRegister // Register iterates over all routes added to the RouteRegister
// and add them to the `Router` pass as an parameter. // and add them to the `Router` pass as an parameter.
Register(Router) *macaron.Router Register(Router) *macaron.Router
...@@ -67,6 +71,24 @@ type routeRegister struct { ...@@ -67,6 +71,24 @@ type routeRegister struct {
groups []*routeRegister groups []*routeRegister
} }
func (rr *routeRegister) Insert(pattern string, fn func(RouteRegister), handlers ...macaron.Handler) {
//loop over all groups at current level
for _, g := range rr.groups {
// apply routes if the prefix matches the pattern
if g.prefix == pattern {
g.Group("", fn)
break
}
// go down one level if the prefix can be find in the pattern
if strings.HasPrefix(pattern, g.prefix) {
g.Insert(pattern, fn)
}
}
}
func (rr *routeRegister) Group(pattern string, fn func(rr RouteRegister), handlers ...macaron.Handler) { func (rr *routeRegister) Group(pattern string, fn func(rr RouteRegister), handlers ...macaron.Handler) {
group := &routeRegister{ group := &routeRegister{
prefix: rr.prefix + pattern, prefix: rr.prefix + pattern,
......
package api package routing
import ( import (
"net/http" "net/http"
"strconv" "strconv"
"testing" "testing"
macaron "gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )
type fakeRouter struct { type fakeRouter struct {
...@@ -33,7 +33,7 @@ func (fr *fakeRouter) Get(pattern string, handlers ...macaron.Handler) *macaron. ...@@ -33,7 +33,7 @@ func (fr *fakeRouter) Get(pattern string, handlers ...macaron.Handler) *macaron.
} }
func emptyHandlers(n int) []macaron.Handler { func emptyHandlers(n int) []macaron.Handler {
res := []macaron.Handler{} var res []macaron.Handler
for i := 1; n >= i; i++ { for i := 1; n >= i; i++ {
res = append(res, emptyHandler(strconv.Itoa(i))) res = append(res, emptyHandler(strconv.Itoa(i)))
} }
...@@ -138,6 +138,60 @@ func TestRouteGroupedRegister(t *testing.T) { ...@@ -138,6 +138,60 @@ func TestRouteGroupedRegister(t *testing.T) {
} }
} }
} }
func TestRouteGroupInserting(t *testing.T) {
testTable := []route{
{method: http.MethodGet, pattern: "/api/", handlers: emptyHandlers(1)},
{method: http.MethodPost, pattern: "/api/group/endpoint", handlers: emptyHandlers(1)},
{method: http.MethodGet, pattern: "/api/group/inserted", handlers: emptyHandlers(1)},
{method: http.MethodDelete, pattern: "/api/inserted-endpoint", handlers: emptyHandlers(1)},
}
// Setup
rr := NewRouteRegister()
rr.Group("/api", func(api RouteRegister) {
api.Get("/", emptyHandler("1"))
api.Group("/group", func(group RouteRegister) {
group.Post("/endpoint", emptyHandler("1"))
})
})
rr.Insert("/api", func(api RouteRegister) {
api.Delete("/inserted-endpoint", emptyHandler("1"))
})
rr.Insert("/api/group", func(group RouteRegister) {
group.Get("/inserted", emptyHandler("1"))
})
fr := &fakeRouter{}
rr.Register(fr)
// Validation
if len(fr.route) != len(testTable) {
t.Fatalf("want %v routes, got %v", len(testTable), len(fr.route))
}
for i := range testTable {
if testTable[i].method != fr.route[i].method {
t.Errorf("want %s got %v", testTable[i].method, fr.route[i].method)
}
if testTable[i].pattern != fr.route[i].pattern {
t.Errorf("want %s got %v", testTable[i].pattern, fr.route[i].pattern)
}
if len(testTable[i].handlers) != len(fr.route[i].handlers) {
t.Errorf("want %d handlers got %d handlers \ntestcase: %v\nroute: %v\n",
len(testTable[i].handlers),
len(fr.route[i].handlers),
testTable[i],
fr.route[i])
}
}
}
func TestNamedMiddlewareRouteRegister(t *testing.T) { func TestNamedMiddlewareRouteRegister(t *testing.T) {
testTable := []route{ testTable := []route{
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/facebookgo/inject" "github.com/facebookgo/inject"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
...@@ -61,7 +62,7 @@ type GrafanaServerImpl struct { ...@@ -61,7 +62,7 @@ type GrafanaServerImpl struct {
shutdownReason string shutdownReason string
shutdownInProgress bool shutdownInProgress bool
RouteRegister api.RouteRegister `inject:""` RouteRegister routing.RouteRegister `inject:""`
HttpServer *api.HTTPServer `inject:""` HttpServer *api.HTTPServer `inject:""`
} }
...@@ -75,7 +76,7 @@ func (g *GrafanaServerImpl) Run() error { ...@@ -75,7 +76,7 @@ func (g *GrafanaServerImpl) Run() error {
serviceGraph := inject.Graph{} serviceGraph := inject.Graph{}
serviceGraph.Provide(&inject.Object{Value: bus.GetBus()}) serviceGraph.Provide(&inject.Object{Value: bus.GetBus()})
serviceGraph.Provide(&inject.Object{Value: g.cfg}) serviceGraph.Provide(&inject.Object{Value: g.cfg})
serviceGraph.Provide(&inject.Object{Value: api.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)}) serviceGraph.Provide(&inject.Object{Value: routing.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)})
// self registered services // self registered services
services := registry.GetServices() services := registry.GetServices()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment