Skip to content

Commit ab6ccb7

Browse files
Merge pull request #169 from monzo/add-router-endpoint-pattern
Add router endpoint pattern method to Typhon Request
2 parents ea17ae8 + fc0f840 commit ab6ccb7

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

request.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (r *Request) BodyBytes(consume bool) ([]byte, error) {
183183
// Send round-trips the request via the default Client. It does not block, instead returning a ResponseFuture
184184
// representing the asynchronous operation to produce the response. It is equivalent to:
185185
//
186-
// r.SendVia(Client)
186+
// r.SendVia(Client)
187187
func (r Request) Send() *ResponseFuture {
188188
return Send(r)
189189
}
@@ -213,6 +213,16 @@ func (r Request) ResponseWithCode(body interface{}, statusCode int) Response {
213213
return rsp
214214
}
215215

216+
// RequestPathPattern finds the router entry pattern that matches the request
217+
func (r Request) RequestPathPattern() string {
218+
return routerPathPatternForRequest(r)
219+
}
220+
221+
// RequestMethod returns the HTTP method of the request
222+
func (r Request) RequestMethod() string {
223+
return r.Method
224+
}
225+
216226
func (r Request) String() string {
217227
if r.URL == nil {
218228
return "Request(Unknown)"

request_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/json"
88
"io/ioutil"
99
"math"
10+
"net/http"
1011
"strings"
1112
"testing"
1213

@@ -223,6 +224,27 @@ func TestRequestSetMetadata(t *testing.T) {
223224
assert.Equal(t, []string{"data"}, req.Request.Header["meta"])
224225
}
225226

227+
func TestRouterEndpointPattern(t *testing.T) {
228+
req := NewRequest(context.Background(), http.MethodGet, "/foo/some-url-identifier", nil)
229+
assert.Equal(t, "", req.RequestPathPattern()) // should be empty if request has not been served by a router
230+
231+
router := Router{}
232+
routerEndpointPattern := "/foo/:id"
233+
router.GET(routerEndpointPattern, func(req Request) Response {
234+
// as we are currently serving the request, we should be able to get the router endpoint pattern
235+
assert.Equal(t, routerEndpointPattern, req.RequestPathPattern())
236+
return req.Response(nil)
237+
})
238+
239+
rsp := req.SendVia(router.Serve()).Response()
240+
require.NoError(t, rsp.Error) // check we didn't get a "route not found" error
241+
}
242+
243+
func TestRequestMethod(t *testing.T) {
244+
req := NewRequest(context.Background(), http.MethodGet, "", nil)
245+
assert.Equal(t, http.MethodGet, req.RequestMethod())
246+
}
247+
226248
func jsonStreamMarshal(v interface{}) ([]byte, error) {
227249
var buffer bytes.Buffer
228250
writer := bufio.NewWriter(&buffer)

router.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import (
1313
// directly means we'd get a collision with any other package that does the same.
1414
// https://play.golang.org/p/MxhRiL37R-9
1515
type routerContextKeyType struct{}
16+
type routerRequestPatternContextKeyType struct{}
1617

1718
var (
18-
routerContextKey = routerContextKeyType{}
19-
routerComponentsRe = regexp.MustCompile(`(?:^|/)(\*\w*|:\w+)`)
19+
routerContextKey = routerContextKeyType{}
20+
routerRequestPatternContextKey = routerRequestPatternContextKeyType{}
21+
routerComponentsRe = regexp.MustCompile(`(?:^|/)(\*\w*|:\w+)`)
2022
)
2123

2224
type routerEntry struct {
@@ -44,6 +46,13 @@ func RouterForRequest(r Request) *Router {
4446
return nil
4547
}
4648

49+
func routerPathPatternForRequest(r Request) string {
50+
if v := r.Context.Value(routerRequestPatternContextKey); v != nil {
51+
return v.(string)
52+
}
53+
return ""
54+
}
55+
4756
func (r *Router) compile(pattern string) *regexp.Regexp {
4857
re, pos := ``, 0
4958
for _, m := range routerComponentsRe.FindAllStringSubmatchIndex(pattern, -1) {
@@ -116,14 +125,15 @@ func (r Router) Lookup(method, path string) (Service, string, map[string]string,
116125
// Serve returns a Service which will route inbound requests to the enclosed routes.
117126
func (r Router) Serve() Service {
118127
return func(req Request) Response {
119-
svc, _, ok := r.lookup(req.Method, req.URL.Path, nil)
128+
svc, pathPattern, ok := r.lookup(req.Method, req.URL.Path, nil)
120129
if !ok {
121130
txt := fmt.Sprintf("No handler for %s %s", req.Method, req.URL.Path)
122131
rsp := NewResponse(req)
123132
rsp.Error = terrors.NotFound("no_handler", txt, nil)
124133
return rsp
125134
}
126135
req.Context = context.WithValue(req.Context, routerContextKey, &r)
136+
req.Context = context.WithValue(req.Context, routerRequestPatternContextKey, pathPattern)
127137
rsp := svc(req)
128138
if rsp.Request == nil {
129139
rsp.Request = &req

0 commit comments

Comments
 (0)