Browse Source

GetQueryTemplates and GetQueryRegexp extraction (#304)

Developers can now extract the query templates and regexps
from a router as lists of combined query pairs.
pull/207/merge
Paul B. Beskow 8 years ago committed by Kamil Kisiel
parent
commit
10490f55fa
  1. 28
      README.md
  2. 72
      mux_test.go
  3. 38
      route.go

28
README.md

@ -201,22 +201,34 @@ func main() { @@ -201,22 +201,34 @@ func main() {
r.HandleFunc("/products", handler).Methods("POST")
r.HandleFunc("/articles", handler).Methods("GET")
r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
t, err := route.GetPathTemplate()
if err != nil {
return err
}
qt, err := route.GetQueriesTemplates()
if err != nil {
return err
}
// p will contain regular expression is compatible with regular expression in Perl, Python, and other languages.
// for instance the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$'
p, err := route.GetPathRegexp()
if err != nil {
return err
}
// qr will contain a list of regular expressions with the same semantics as GetPathRegexp,
// just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return
// {"^surname=(?P<v0>.*)$}. Where each combined query pair will have an entry in the list.
qr, err := route.GetQueriesRegexp()
if err != nil {
return err
}
m, err := route.GetMethods()
if err != nil {
return err
}
fmt.Println(strings.Join(m, ","), t, p)
fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p)
return nil
})
http.Handle("/", r)
@ -339,22 +351,34 @@ r.HandleFunc("/", handler) @@ -339,22 +351,34 @@ r.HandleFunc("/", handler)
r.HandleFunc("/products", handler).Methods("POST")
r.HandleFunc("/articles", handler).Methods("GET")
r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
t, err := route.GetPathTemplate()
if err != nil {
return err
}
qt, err := route.GetQueriesTemplates()
if err != nil {
return err
}
// p will contain a regular expression that is compatible with regular expressions in Perl, Python, and other languages.
// For example, the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$'.
p, err := route.GetPathRegexp()
if err != nil {
return err
}
// qr will contain a list of regular expressions with the same semantics as GetPathRegexp,
// just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return
// {"^surname=(?P<v0>.*)$}. Where each combined query pair will have an entry in the list.
qr, err := route.GetQueriesRegexp()
if err != nil {
return err
}
m, err := route.GetMethods()
if err != nil {
return err
}
fmt.Println(strings.Join(m, ","), t, p)
fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p)
return nil
})
```

72
mux_test.go

@ -39,8 +39,10 @@ type routeTest struct { @@ -39,8 +39,10 @@ type routeTest struct {
query string // the expected query string of the built URL
pathTemplate string // the expected path template of the route
hostTemplate string // the expected host template of the route
queriesTemplate string // the expected query template of the route
methods []string // the expected route methods
pathRegexp string // the expected path regexp
queriesRegexp string // the expected query regexp
shouldMatch bool // whether the request is expected to match the route at all
shouldRedirect bool // whether the request should result in a redirect
}
@ -746,6 +748,8 @@ func TestQueries(t *testing.T) { @@ -746,6 +748,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=bar&baz=ding",
queriesTemplate: "foo=bar,baz=ding",
queriesRegexp: "^foo=bar$,^baz=ding$",
shouldMatch: true,
},
{
@ -758,6 +762,8 @@ func TestQueries(t *testing.T) { @@ -758,6 +762,8 @@ func TestQueries(t *testing.T) {
query: "foo=bar&baz=ding",
pathTemplate: `/api`,
hostTemplate: `www.example.com`,
queriesTemplate: "foo=bar,baz=ding",
queriesRegexp: "^foo=bar$,^baz=ding$",
shouldMatch: true,
},
{
@ -770,6 +776,8 @@ func TestQueries(t *testing.T) { @@ -770,6 +776,8 @@ func TestQueries(t *testing.T) {
query: "foo=bar&baz=ding",
pathTemplate: `/api`,
hostTemplate: `www.example.com`,
queriesTemplate: "foo=bar,baz=ding",
queriesRegexp: "^foo=bar$,^baz=ding$",
shouldMatch: true,
},
{
@ -779,6 +787,8 @@ func TestQueries(t *testing.T) { @@ -779,6 +787,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo=bar,baz=ding",
queriesRegexp: "^foo=bar$,^baz=ding$",
shouldMatch: false,
},
{
@ -789,6 +799,8 @@ func TestQueries(t *testing.T) { @@ -789,6 +799,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=bar",
queriesTemplate: "foo={v1}",
queriesRegexp: "^foo=(?P<v0>.*)$",
shouldMatch: true,
},
{
@ -799,6 +811,8 @@ func TestQueries(t *testing.T) { @@ -799,6 +811,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=bar&baz=ding",
queriesTemplate: "foo={v1},baz={v2}",
queriesRegexp: "^foo=(?P<v0>.*)$,^baz=(?P<v0>.*)$",
shouldMatch: true,
},
{
@ -809,6 +823,8 @@ func TestQueries(t *testing.T) { @@ -809,6 +823,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=10",
queriesTemplate: "foo={v1:[0-9]+}",
queriesRegexp: "^foo=(?P<v0>[0-9]+)$",
shouldMatch: true,
},
{
@ -818,6 +834,8 @@ func TestQueries(t *testing.T) { @@ -818,6 +834,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo={v1:[0-9]+}",
queriesRegexp: "^foo=(?P<v0>[0-9]+)$",
shouldMatch: false,
},
{
@ -828,6 +846,8 @@ func TestQueries(t *testing.T) { @@ -828,6 +846,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=1",
queriesTemplate: "foo={v1:[0-9]{1}}",
queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
shouldMatch: true,
},
{
@ -838,6 +858,8 @@ func TestQueries(t *testing.T) { @@ -838,6 +858,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=1",
queriesTemplate: "foo={v1:[0-9]{1}}",
queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
shouldMatch: true,
},
{
@ -847,6 +869,8 @@ func TestQueries(t *testing.T) { @@ -847,6 +869,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo={v1:[0-9]{1}}",
queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
shouldMatch: false,
},
{
@ -857,6 +881,8 @@ func TestQueries(t *testing.T) { @@ -857,6 +881,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=1a",
queriesTemplate: "foo={v1:[0-9]{1}(?:a|b)}",
queriesRegexp: "^foo=(?P<v0>[0-9]{1}(?:a|b))$",
shouldMatch: true,
},
{
@ -866,6 +892,8 @@ func TestQueries(t *testing.T) { @@ -866,6 +892,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo={v1:[0-9]{1}}",
queriesRegexp: "^foo=(?P<v0>[0-9]{1})$",
shouldMatch: false,
},
{
@ -876,6 +904,8 @@ func TestQueries(t *testing.T) { @@ -876,6 +904,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=bar",
queriesTemplate: "foo={v-1}",
queriesRegexp: "^foo=(?P<v0>.*)$",
shouldMatch: true,
},
{
@ -886,6 +916,8 @@ func TestQueries(t *testing.T) { @@ -886,6 +916,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=bar&baz=ding",
queriesTemplate: "foo={v-1},baz={v-2}",
queriesRegexp: "^foo=(?P<v0>.*)$,^baz=(?P<v0>.*)$",
shouldMatch: true,
},
{
@ -896,6 +928,8 @@ func TestQueries(t *testing.T) { @@ -896,6 +928,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=10",
queriesTemplate: "foo={v-1:[0-9]+}",
queriesRegexp: "^foo=(?P<v0>[0-9]+)$",
shouldMatch: true,
},
{
@ -906,6 +940,8 @@ func TestQueries(t *testing.T) { @@ -906,6 +940,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=1a",
queriesTemplate: "foo={v-1:[0-9]{1}(?:a|b)}",
queriesRegexp: "^foo=(?P<v0>[0-9]{1}(?:a|b))$",
shouldMatch: true,
},
{
@ -916,6 +952,8 @@ func TestQueries(t *testing.T) { @@ -916,6 +952,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=",
queriesTemplate: "foo=",
queriesRegexp: "^foo=.*$",
shouldMatch: true,
},
{
@ -925,6 +963,8 @@ func TestQueries(t *testing.T) { @@ -925,6 +963,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo=",
queriesRegexp: "^foo=.*$",
shouldMatch: false,
},
{
@ -935,6 +975,8 @@ func TestQueries(t *testing.T) { @@ -935,6 +975,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=",
queriesTemplate: "foo=",
queriesRegexp: "^foo=.*$",
shouldMatch: true,
},
{
@ -944,6 +986,8 @@ func TestQueries(t *testing.T) { @@ -944,6 +986,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo=bar",
queriesRegexp: "^foo=bar$",
shouldMatch: false,
},
{
@ -953,6 +997,8 @@ func TestQueries(t *testing.T) { @@ -953,6 +997,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo={bar}",
queriesRegexp: "^foo=(?P<v0>.*)$",
shouldMatch: false,
},
{
@ -963,6 +1009,8 @@ func TestQueries(t *testing.T) { @@ -963,6 +1009,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=",
queriesTemplate: "foo={bar}",
queriesRegexp: "^foo=(?P<v0>.*)$",
shouldMatch: true,
},
{
@ -972,6 +1020,8 @@ func TestQueries(t *testing.T) { @@ -972,6 +1020,8 @@ func TestQueries(t *testing.T) {
vars: map[string]string{},
host: "",
path: "",
queriesTemplate: "foo=bar,baz=ding",
queriesRegexp: "^foo=bar$,^baz=ding$",
shouldMatch: false,
},
{
@ -982,6 +1032,8 @@ func TestQueries(t *testing.T) { @@ -982,6 +1032,8 @@ func TestQueries(t *testing.T) {
host: "",
path: "",
query: "foo=%25bar%26+%2F%3D%3F",
queriesTemplate: "foo={v1}",
queriesRegexp: "^foo=(?P<v0>.*)$",
shouldMatch: true,
},
}
@ -989,7 +1041,9 @@ func TestQueries(t *testing.T) { @@ -989,7 +1041,9 @@ func TestQueries(t *testing.T) {
for _, test := range tests {
testRoute(t, test)
testTemplate(t, test)
testQueriesTemplates(t, test)
testUseEscapedRoute(t, test)
testQueriesRegexp(t, test)
}
}
@ -1717,6 +1771,24 @@ func testRegexp(t *testing.T, test routeTest) { @@ -1717,6 +1771,24 @@ func testRegexp(t *testing.T, test routeTest) {
}
}
func testQueriesRegexp(t *testing.T, test routeTest) {
route := test.route
queries, queriesErr := route.GetQueriesRegexp()
gotQueries := strings.Join(queries, ",")
if test.queriesRegexp != "" && queriesErr == nil && gotQueries != test.queriesRegexp {
t.Errorf("(%v) GetQueriesRegexp not equal: expected %v, got %v", test.title, test.queriesRegexp, gotQueries)
}
}
func testQueriesTemplates(t *testing.T, test routeTest) {
route := test.route
queries, queriesErr := route.GetQueriesTemplates()
gotQueries := strings.Join(queries, ",")
if test.queriesTemplate != "" && queriesErr == nil && gotQueries != test.queriesTemplate {
t.Errorf("(%v) GetQueriesTemplates not equal: expected %v, got %v", test.title, test.queriesTemplate, gotQueries)
}
}
type TestA301ResponseWriter struct {
hh http.Header
status int

38
route.go

@ -608,6 +608,44 @@ func (r *Route) GetPathRegexp() (string, error) { @@ -608,6 +608,44 @@ func (r *Route) GetPathRegexp() (string, error) {
return r.regexp.path.regexp.String(), nil
}
// GetQueriesRegexp returns the expanded regular expressions used to match the
// route queries.
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.
// An empty list will be returned if the route does not have queries.
func (r *Route) GetQueriesRegexp() ([]string, error) {
if r.err != nil {
return nil, r.err
}
if r.regexp == nil || r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries")
}
var queries []string
for _, query := range r.regexp.queries {
queries = append(queries, query.regexp.String())
}
return queries, nil
}
// GetQueriesTemplates returns the templates used to build the
// query matching.
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.
// An empty list will be returned if the route does not define queries.
func (r *Route) GetQueriesTemplates() ([]string, error) {
if r.err != nil {
return nil, r.err
}
if r.regexp == nil || r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries")
}
var queries []string
for _, query := range r.regexp.queries {
queries = append(queries, query.template)
}
return queries, nil
}
// GetMethods returns the methods the route matches against
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.

Loading…
Cancel
Save