Skip to content

Commit b819c22

Browse files
item_get: bring back support for using Find
If there are query predicates apart from `id`, use `resource.Find` instead of `resource.Get`. This puts back ability add additional predicates apart from `id`. Who needs that, anyway?
1 parent 4989304 commit b819c22

File tree

3 files changed

+132
-9
lines changed

3 files changed

+132
-9
lines changed

rest/method_item_get.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"context"
55
"net/http"
66
"time"
7+
8+
"github.com/rs/rest-layer/resource"
9+
"github.com/rs/rest-layer/schema/query"
710
)
811

912
// itemGet handles GET and HEAD resquests on an item URL.
@@ -13,12 +16,29 @@ func itemGet(ctx context.Context, r *http.Request, route *RouteMatch) (status in
1316
return e.Code, nil, e
1417
}
1518
rsrc := route.Resource()
16-
item, err := rsrc.Get(ctx, route.ResourceID())
17-
if err != nil {
18-
e, code := NewError(err)
19-
return code, nil, e
20-
} else if item == nil {
21-
return ErrNotFound.Code, nil, ErrNotFound
19+
var item *resource.Item
20+
21+
// See if there are additional predicates.
22+
// There is already one predicate: id
23+
if len(q.Predicate) > 1 {
24+
q.Window = &query.Window{Limit: 1}
25+
list, err := rsrc.Find(ctx, q)
26+
if err != nil {
27+
e, code := NewError(err)
28+
return code, nil, e
29+
} else if len(list.Items) == 0 {
30+
return ErrNotFound.Code, nil, ErrNotFound
31+
}
32+
item = list.Items[0]
33+
} else {
34+
var err error
35+
item, err = rsrc.Get(ctx, route.ResourceID())
36+
if err != nil {
37+
e, code := NewError(err)
38+
return code, nil, e
39+
} else if item == nil {
40+
return ErrNotFound.Code, nil, ErrNotFound
41+
}
2242
}
2343
// Handle conditional request: If-None-Match.
2444
if compareEtag(r.Header.Get("If-None-Match"), item.ETag) {
@@ -34,6 +54,7 @@ func itemGet(ctx context.Context, r *http.Request, route *RouteMatch) (status in
3454
return 304, nil, nil
3555
}
3656
}
57+
var err error
3758
item.Payload, err = q.Projection.Eval(ctx, item.Payload, restResource{rsrc})
3859
if err != nil {
3960
e, code := NewError(err)

rest/method_item_get_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/rs/rest-layer/resource"
1111
"github.com/rs/rest-layer/resource/testing/mem"
1212
"github.com/rs/rest-layer/schema"
13+
"github.com/rs/rest-layer/schema/query"
1314
)
1415

1516
func TestGetItem(t *testing.T) {
@@ -300,3 +301,104 @@ func TestHandlerGetItemNoStorage(t *testing.T) {
300301
}
301302
tc.Test(t)
302303
}
304+
305+
type Hook struct {
306+
Called map[string]bool
307+
}
308+
309+
func (h *Hook) OnGet(ctx context.Context, id interface{}) error {
310+
h.Called["OnGet"] = true
311+
return nil
312+
}
313+
func (h *Hook) OnFind(ctx context.Context, q *query.Query) error {
314+
h.Called["OnFind"] = true
315+
return nil
316+
}
317+
318+
func TestHandlerGetHook(t *testing.T) {
319+
tests := map[string]requestTest{
320+
"hooks:trigger - OnGet": {
321+
Init: func() *requestTestVars {
322+
s := mem.NewHandler()
323+
s.Insert(context.TODO(), []*resource.Item{
324+
{ID: "1", Payload: map[string]interface{}{"id": "1", "foo": "bar"}},
325+
})
326+
327+
idx := resource.NewIndex()
328+
foo := idx.Bind("foo", schema.Schema{
329+
Fields: schema.Fields{
330+
"id": {Filterable: true},
331+
"foo": {Filterable: true},
332+
},
333+
}, s, resource.DefaultConf)
334+
335+
hook := &Hook{
336+
Called: make(map[string]bool),
337+
}
338+
foo.Use(hook)
339+
340+
return &requestTestVars{
341+
Index: idx,
342+
CalledHooks: &hook.Called,
343+
}
344+
},
345+
NewRequest: func() (*http.Request, error) {
346+
return http.NewRequest("GET", "/foo/1", nil)
347+
},
348+
ResponseCode: http.StatusOK,
349+
ResponseBody: `{"id": "1", "foo": "bar"}`,
350+
ExtraTest: func(t *testing.T, v *requestTestVars) {
351+
if !(*v.CalledHooks)["OnGet"] {
352+
t.Errorf("Expected OnGet to be called")
353+
}
354+
if (*v.CalledHooks)["OnFind"] {
355+
t.Errorf("Expected OnFind not to be called")
356+
}
357+
},
358+
},
359+
"hooks:trigger - OnFind": {
360+
Init: func() *requestTestVars {
361+
s := mem.NewHandler()
362+
s.Insert(context.TODO(), []*resource.Item{
363+
{ID: "1", Payload: map[string]interface{}{"id": "1", "foo": "bar"}},
364+
})
365+
366+
idx := resource.NewIndex()
367+
foo := idx.Bind("foo", schema.Schema{
368+
Fields: schema.Fields{
369+
"id": {Filterable: true},
370+
"foo": {Filterable: true},
371+
},
372+
}, s, resource.DefaultConf)
373+
374+
hook := &Hook{
375+
Called: make(map[string]bool),
376+
}
377+
foo.Use(hook)
378+
379+
return &requestTestVars{
380+
Index: idx,
381+
CalledHooks: &hook.Called,
382+
}
383+
},
384+
NewRequest: func() (*http.Request, error) {
385+
return http.NewRequest("GET", `/foo/1?filter={foo:"bar"}`, nil)
386+
},
387+
ResponseCode: http.StatusOK,
388+
ResponseBody: `{"id": "1", "foo": "bar"}`,
389+
ExtraTest: func(t *testing.T, v *requestTestVars) {
390+
if (*v.CalledHooks)["OnGet"] {
391+
t.Errorf("Expected OnGet not to be called")
392+
}
393+
if !(*v.CalledHooks)["OnFind"] {
394+
t.Errorf("Expected OnFind to be called")
395+
}
396+
},
397+
},
398+
}
399+
400+
for n, tc := range tests {
401+
tc := tc // capture range variable
402+
t.Run(n, tc.Test)
403+
}
404+
}

rest/method_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
)
1313

1414
// requestTest is a reusable type for testing POST, PUT, PATCH or GET requests. Best used in a map, E.g.:
15-
// go
1615
type requestTest struct {
1716
Init func() *requestTestVars
1817
NewRequest func() (*http.Request, error)
@@ -26,8 +25,9 @@ type requestCheckerFunc func(*testing.T, *requestTestVars)
2625

2726
// requestTestVars provodes test runtime variables.
2827
type requestTestVars struct {
29-
Index resource.Index // required
30-
Storers map[string]resource.Storer // optional: may be used by ExtraTest function
28+
Index resource.Index // required
29+
Storers map[string]resource.Storer // optional: may be used by ExtraTest function
30+
CalledHooks *map[string]bool
3131
}
3232

3333
// Test runs tt in parallel mode. It can be passed as a second parameter to

0 commit comments

Comments
 (0)