Skip to content

Commit

Permalink
feat(mux): support query string in path
Browse files Browse the repository at this point in the history
  • Loading branch information
x1unix committed Dec 5, 2024
1 parent 7ce29ff commit 9122063
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 20 deletions.
8 changes: 8 additions & 0 deletions examples/gno.land/p/demo/mux/request.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import "strings"

// Request represents an incoming request.
type Request struct {
// Path is request path name.
//
// Note: use RawPath to obtain a raw path with query string.
Path string

// RawPath contains a whole request path, including query string.
RawPath string

// HandlerPath is handler rule that matches a request.
HandlerPath string
}

Expand Down
15 changes: 13 additions & 2 deletions examples/gno.land/p/demo/mux/router.gno
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ func NewRouter() *Router {

// Render renders the output for the given path using the registered route handler.
func (r *Router) Render(reqPath string) string {
reqParts := strings.Split(reqPath, "/")
clearPath := stripQueryString(reqPath)
reqParts := strings.Split(clearPath, "/")

for _, route := range r.routes {
patParts := strings.Split(route.Pattern, "/")
Expand All @@ -45,7 +46,8 @@ func (r *Router) Render(reqPath string) string {
}
if match {
req := &Request{
Path: reqPath,
Path: clearPath,
RawPath: reqPath,
HandlerPath: route.Pattern,
}
res := &ResponseWriter{}
Expand All @@ -66,3 +68,12 @@ func (r *Router) HandleFunc(pattern string, fn HandlerFunc) {
route := Handler{Pattern: pattern, Fn: fn}
r.routes = append(r.routes, route)
}

func stripQueryString(reqPath string) string {
i := strings.Index(reqPath, "?")
if i == -1 {
return reqPath
}

return reqPath[:i]
}
84 changes: 66 additions & 18 deletions examples/gno.land/p/demo/mux/router_test.gno
Original file line number Diff line number Diff line change
@@ -1,34 +1,82 @@
package mux

import "gno.land/p/demo/uassert"

import "testing"

func TestRouter_Render(t *testing.T) {
// Define handlers and route configuration
router := NewRouter()
router.HandleFunc("hello/{name}", func(res *ResponseWriter, req *Request) {
name := req.GetVar("name")
if name != "" {
res.Write("Hello, " + name + "!")
} else {
res.Write("Hello, world!")
}
})
router.HandleFunc("hi", func(res *ResponseWriter, req *Request) {
res.Write("Hi, earth!")
})

cases := []struct {
label string
path string
expectedOutput string
setupHandler func(t *testing.T, r *Router)
}{
{"hello/Alice", "Hello, Alice!"},
{"hi", "Hi, earth!"},
{"hello/Bob", "Hello, Bob!"},
{
label: "route with named parameter",
path: "hello/Alice",
expectedOutput: "Hello, Alice!",
setupHandler: func(t *testing.T, r *Router) {
r.HandleFunc("hello/{name}", func(rw *ResponseWriter, req *Request) {
name := req.GetVar("name")
uassert.Equal(t, "Alice", name)
rw.Write("Hello, " + name + "!")
})
},
},
{
label: "static route",
path: "hi",
expectedOutput: "Hi, earth!",
setupHandler: func(t *testing.T, r *Router) {
r.HandleFunc("hi", func(rw *ResponseWriter, req *Request) {
uassert.Equal(t, req.Path, "hi")
rw.Write("Hi, earth!")
})
},
},
{
label: "route with named parameter and query string",
path: "hello/foo/bar?foo=bar&baz",
expectedOutput: "foo bar",
setupHandler: func(t *testing.T, r *Router) {
r.HandleFunc("hello/{key}/{val}", func(rw *ResponseWriter, req *Request) {
key := req.GetVar("key")
val := req.GetVar("val")
uassert.Equal(t, "foo", key)
uassert.Equal(t, "bar", val)
uassert.Equal(t, "hello/foo/bar?foo=bar&baz", req.RawPath)
rw.Write(key + " " + val)
})
},
},
{
// TODO: finalize how router should behave with double slash in path.
label: "double slash in nested route",
path: "a/foo//",
expectedOutput: "test foo",
setupHandler: func(t *testing.T, r *Router) {
r.HandleFunc("a/{key}", func(rw *ResponseWriter, req *Request) {
// Assert not called
uassert.False(t, true, "unexpected handler called")
})

r.HandleFunc("a/{key}/{val}/", func(rw *ResponseWriter, req *Request) {
key := req.GetVar("key")
val := req.GetVar("val")
uassert.Equal(t, key, "foo")
uassert.Empty(t, val)
rw.Write("test " + key)
})
},
},

// TODO: {"hello", "Hello, world!"},
// TODO: hello/, /hello, hello//Alice, hello/Alice/, hello/Alice/Bob, etc
}
for _, tt := range cases {
t.Run(tt.path, func(t *testing.T) {
t.Run(tt.label, func(t *testing.T) {
router := NewRouter()
tt.setupHandler(t, router)
output := router.Render(tt.path)
if output != tt.expectedOutput {
t.Errorf("Expected output %q, but got %q", tt.expectedOutput, output)
Expand Down

0 comments on commit 9122063

Please sign in to comment.