Skip to content

Commit 6ccafcb

Browse files
dataBaseErrorwwwdata
authored andcommitted
Added copy context from gin to api2go (#318)
Added copy context from gin to api2go Copy the gin's key value store to api2go's key value store. This allows for middleware context values to be set in gin middleware and be used in api2go resources.
1 parent 9fd8e93 commit 6ccafcb

File tree

7 files changed

+135
-21
lines changed

7 files changed

+135
-21
lines changed

api.go

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -250,19 +250,29 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
250250
baseURL = "/" + prefix + baseURL
251251
}
252252

253-
api.router.Handle("OPTIONS", baseURL, func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
253+
api.router.Handle("OPTIONS", baseURL, func(w http.ResponseWriter, r *http.Request, _ map[string]string, context map[string]interface{}) {
254254
c := api.contextPool.Get().(APIContexter)
255255
c.Reset()
256+
257+
for key, val := range context {
258+
c.Set(key, val)
259+
}
260+
256261
api.middlewareChain(c, w, r)
257262
w.Header().Set("Allow", strings.Join(getAllowedMethods(source, true), ","))
258263
w.WriteHeader(http.StatusNoContent)
259264
api.contextPool.Put(c)
260265
})
261266

262-
api.router.Handle("GET", baseURL, func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
267+
api.router.Handle("GET", baseURL, func(w http.ResponseWriter, r *http.Request, _ map[string]string, context map[string]interface{}) {
263268
info := requestInfo(r, api)
264269
c := api.contextPool.Get().(APIContexter)
265270
c.Reset()
271+
272+
for key, val := range context {
273+
c.Set(key, val)
274+
}
275+
266276
api.middlewareChain(c, w, r)
267277

268278
err := res.handleIndex(c, w, r, *info)
@@ -273,19 +283,29 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
273283
})
274284

275285
if _, ok := source.(ResourceGetter); ok {
276-
api.router.Handle("OPTIONS", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
286+
api.router.Handle("OPTIONS", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, _ map[string]string, context map[string]interface{}) {
277287
c := api.contextPool.Get().(APIContexter)
278288
c.Reset()
289+
290+
for key, val := range context {
291+
c.Set(key, val)
292+
}
293+
279294
api.middlewareChain(c, w, r)
280295
w.Header().Set("Allow", strings.Join(getAllowedMethods(source, false), ","))
281296
w.WriteHeader(http.StatusNoContent)
282297
api.contextPool.Put(c)
283298
})
284299

285-
api.router.Handle("GET", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) {
300+
api.router.Handle("GET", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
286301
info := requestInfo(r, api)
287302
c := api.contextPool.Get().(APIContexter)
288303
c.Reset()
304+
305+
for key, val := range context {
306+
c.Set(key, val)
307+
}
308+
289309
api.middlewareChain(c, w, r)
290310
err := res.handleRead(c, w, r, params, *info)
291311
api.contextPool.Put(c)
@@ -301,10 +321,15 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
301321
relations := casted.GetReferences()
302322
for _, relation := range relations {
303323
api.router.Handle("GET", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc {
304-
return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
324+
return func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
305325
info := requestInfo(r, api)
306326
c := api.contextPool.Get().(APIContexter)
307327
c.Reset()
328+
329+
for key, val := range context {
330+
c.Set(key, val)
331+
}
332+
308333
api.middlewareChain(c, w, r)
309334
err := res.handleReadRelation(c, w, r, params, *info, relation)
310335
api.contextPool.Put(c)
@@ -315,10 +340,15 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
315340
}(relation))
316341

317342
api.router.Handle("GET", baseURL+"/:id/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc {
318-
return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
343+
return func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
319344
info := requestInfo(r, api)
320345
c := api.contextPool.Get().(APIContexter)
321346
c.Reset()
347+
348+
for key, val := range context {
349+
c.Set(key, val)
350+
}
351+
322352
api.middlewareChain(c, w, r)
323353
err := res.handleLinked(c, api, w, r, params, relation, *info)
324354
api.contextPool.Put(c)
@@ -329,9 +359,14 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
329359
}(relation))
330360

331361
api.router.Handle("PATCH", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc {
332-
return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
362+
return func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
333363
c := api.contextPool.Get().(APIContexter)
334364
c.Reset()
365+
366+
for key, val := range context {
367+
c.Set(key, val)
368+
}
369+
335370
api.middlewareChain(c, w, r)
336371
err := res.handleReplaceRelation(c, w, r, params, relation)
337372
api.contextPool.Put(c)
@@ -344,9 +379,14 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
344379
if _, ok := ptrPrototype.(jsonapi.EditToManyRelations); ok && relation.Name == jsonapi.Pluralize(relation.Name) {
345380
// generate additional routes to manipulate to-many relationships
346381
api.router.Handle("POST", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc {
347-
return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
382+
return func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
348383
c := api.contextPool.Get().(APIContexter)
349384
c.Reset()
385+
386+
for key, val := range context {
387+
c.Set(key, val)
388+
}
389+
350390
api.middlewareChain(c, w, r)
351391
err := res.handleAddToManyRelation(c, w, r, params, relation)
352392
api.contextPool.Put(c)
@@ -357,9 +397,14 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
357397
}(relation))
358398

359399
api.router.Handle("DELETE", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc {
360-
return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
400+
return func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
361401
c := api.contextPool.Get().(APIContexter)
362402
c.Reset()
403+
404+
for key, val := range context {
405+
c.Set(key, val)
406+
}
407+
363408
api.middlewareChain(c, w, r)
364409
err := res.handleDeleteToManyRelation(c, w, r, params, relation)
365410
api.contextPool.Put(c)
@@ -373,10 +418,15 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
373418
}
374419

375420
if _, ok := source.(ResourceCreator); ok {
376-
api.router.Handle("POST", baseURL, func(w http.ResponseWriter, r *http.Request, params map[string]string) {
421+
api.router.Handle("POST", baseURL, func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
377422
info := requestInfo(r, api)
378423
c := api.contextPool.Get().(APIContexter)
379424
c.Reset()
425+
426+
for key, val := range context {
427+
c.Set(key, val)
428+
}
429+
380430
api.middlewareChain(c, w, r)
381431
err := res.handleCreate(c, w, r, info.prefix, *info)
382432
api.contextPool.Put(c)
@@ -387,9 +437,14 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
387437
}
388438

389439
if _, ok := source.(ResourceDeleter); ok {
390-
api.router.Handle("DELETE", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) {
440+
api.router.Handle("DELETE", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
391441
c := api.contextPool.Get().(APIContexter)
392442
c.Reset()
443+
444+
for key, val := range context {
445+
c.Set(key, val)
446+
}
447+
393448
api.middlewareChain(c, w, r)
394449
err := res.handleDelete(c, w, r, params)
395450
api.contextPool.Put(c)
@@ -400,10 +455,15 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac
400455
}
401456

402457
if _, ok := source.(ResourceUpdater); ok {
403-
api.router.Handle("PATCH", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) {
458+
api.router.Handle("PATCH", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{}) {
404459
info := requestInfo(r, api)
405460
c := api.contextPool.Get().(APIContexter)
406461
c.Reset()
462+
463+
for key, val := range context {
464+
c.Set(key, val)
465+
}
466+
407467
api.middlewareChain(c, w, r)
408468
err := res.handleUpdate(c, w, r, params, *info)
409469
api.contextPool.Put(c)

routing/echo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func (e echoRouter) Handle(protocol, route string, handler HandlerFunc) {
2424
params[p] = c.ParamValues()[i]
2525
}
2626

27-
handler(c.Response(), c.Request(), params)
27+
handler(c.Response(), c.Request(), params, make(map[string]interface{}))
2828

2929
return nil
3030
}

routing/gingonic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func (g ginRouter) Handle(protocol, route string, handler HandlerFunc) {
2323
params[p.Key] = p.Value
2424
}
2525

26-
handler(c.Writer, c.Request, params)
26+
handler(c.Writer, c.Request, params, c.Keys)
2727
}
2828

2929
g.router.Handle(protocol, route, wrappedCallback)

routing/gingonic_test.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"log"
88
"net/http"
99
"net/http/httptest"
10+
"reflect"
1011
"strings"
12+
"unsafe"
1113

1214
"github.com/gin-gonic/gin"
1315
"github.com/manyminds/api2go"
@@ -22,10 +24,13 @@ import (
2224

2325
var _ = Describe("api2go with gingonic router adapter", func() {
2426
var (
25-
router routing.Routeable
26-
gg *gin.Engine
27-
api *api2go.API
28-
rec *httptest.ResponseRecorder
27+
router routing.Routeable
28+
gg *gin.Engine
29+
api *api2go.API
30+
rec *httptest.ResponseRecorder
31+
contextKey = "userID"
32+
contextValue *string
33+
apiContext api2go.APIContext
2934
)
3035

3136
BeforeSuite(func() {
@@ -38,9 +43,22 @@ var _ = Describe("api2go with gingonic router adapter", func() {
3843
router,
3944
)
4045

46+
// Define the ApiContext to allow for access.
47+
apiContext = api2go.APIContext{}
48+
api.SetContextAllocator(func(*api2go.API) api2go.APIContexter {
49+
return &apiContext
50+
})
51+
4152
userStorage := storage.NewUserStorage()
4253
chocStorage := storage.NewChocolateStorage()
4354
api.AddResource(model.User{}, resource.UserResource{ChocStorage: chocStorage, UserStorage: userStorage})
55+
56+
gg.Use(func(c *gin.Context) {
57+
if contextValue != nil {
58+
c.Set(contextKey, *contextValue)
59+
}
60+
})
61+
4462
api.AddResource(model.Chocolate{}, resource.ChocolateResource{ChocStorage: chocStorage, UserStorage: userStorage})
4563
})
4664

@@ -144,4 +162,40 @@ var _ = Describe("api2go with gingonic router adapter", func() {
144162
Expect(string(rec.Body.Bytes())).To(MatchJSON(expected))
145163
})
146164
})
165+
166+
Context("Gin Context Key Copy Tests", func() {
167+
BeforeEach(func() {
168+
contextValue = nil
169+
})
170+
171+
It("context value is present for chocolate resource", func() {
172+
tempVal := "1"
173+
contextValue = &tempVal
174+
expected := `{"data":[],"meta":{"author": "The api2go examples crew", "license": "wtfpl", "license-url": "http://www.wtfpl.net"}}`
175+
req, err := http.NewRequest("GET", "/api/chocolates", strings.NewReader(""))
176+
Expect(err).To(BeNil())
177+
gg.ServeHTTP(rec, req)
178+
Expect(rec.Code).To(Equal(http.StatusOK))
179+
Expect(string(rec.Body.Bytes())).To(MatchJSON(expected))
180+
181+
rawKeys := reflect.ValueOf(&apiContext).Elem().Field(0)
182+
keys := reflect.NewAt(rawKeys.Type(), unsafe.Pointer(rawKeys.UnsafeAddr())).Elem().Interface().(map[string]interface{})
183+
184+
Expect(keys).To(Equal(map[string]interface{}{contextKey: *contextValue}))
185+
})
186+
187+
It("context value is not present for chocolate resource", func() {
188+
expected := `{"data":[],"meta":{"author": "The api2go examples crew", "license": "wtfpl", "license-url": "http://www.wtfpl.net"}}`
189+
req, err := http.NewRequest("GET", "/api/chocolates", strings.NewReader(""))
190+
Expect(err).To(BeNil())
191+
gg.ServeHTTP(rec, req)
192+
Expect(rec.Code).To(Equal(http.StatusOK))
193+
Expect(string(rec.Body.Bytes())).To(MatchJSON(expected))
194+
195+
rawKeys := reflect.ValueOf(&apiContext).Elem().Field(0)
196+
keys := reflect.NewAt(rawKeys.Type(), unsafe.Pointer(rawKeys.UnsafeAddr())).Elem().Interface().(map[string]interface{})
197+
198+
Expect(keys).To(BeNil())
199+
})
200+
})
147201
})

routing/gorillamux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func (gm gorillamuxRouter) Handler() http.Handler {
2020

2121
func (gm gorillamuxRouter) Handle(protocol, route string, handler HandlerFunc) {
2222
wrappedHandler := func(w http.ResponseWriter, r *http.Request) {
23-
handler(w, r, mux.Vars(r))
23+
handler(w, r, mux.Vars(r), make(map[string]interface{}))
2424
}
2525

2626
// The request path will have parameterized segments indicated as :name. Convert

routing/httprouter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func (h HTTPRouter) Handle(protocol, route string, handler HandlerFunc) {
1919
params[p.Key] = p.Value
2020
}
2121

22-
handler(w, r, params)
22+
handler(w, r, params, make(map[string]interface{}))
2323
}
2424

2525
h.router.Handle(protocol, route, wrappedCallback)

routing/router.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import "net/http"
44

55
// HandlerFunc must contain all params from the route
66
// in the form key,value
7-
type HandlerFunc func(w http.ResponseWriter, r *http.Request, params map[string]string)
7+
type HandlerFunc func(w http.ResponseWriter, r *http.Request, params map[string]string, context map[string]interface{})
88

99
// Routeable allows drop in replacement for api2go's router
1010
// by default, we are using julienschmidt/httprouter

0 commit comments

Comments
 (0)