func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source CRUD, marshalers map[string]ContentMarshaler) *resource { resourceType := reflect.TypeOf(prototype) if resourceType.Kind() != reflect.Struct && resourceType.Kind() != reflect.Ptr { panic("pass an empty resource struct or a struct pointer to AddResource!") } var ptrPrototype interface{} var name string if resourceType.Kind() == reflect.Struct { ptrPrototype = reflect.New(resourceType).Interface() name = resourceType.Name() } else { ptrPrototype = reflect.ValueOf(prototype).Interface() name = resourceType.Elem().Name() } // check if EntityNamer interface is implemented and use that as name entityName, ok := prototype.(jsonapi.EntityNamer) if ok { name = entityName.GetName() } else { name = jsonapi.Jsonify(jsonapi.Pluralize(name)) } res := resource{ resourceType: resourceType, name: name, source: source, marshalers: marshalers, } api.router.Handle("OPTIONS", api.prefix+name, func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { w.Header().Set("Allow", "GET,POST,PATCH,OPTIONS") w.WriteHeader(http.StatusNoContent) }) api.router.Handle("OPTIONS", api.prefix+name+"/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { w.Header().Set("Allow", "GET,PATCH,DELETE,OPTIONS") w.WriteHeader(http.StatusNoContent) }) api.router.GET(api.prefix+name, func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { err := res.handleIndex(w, r, api.info) if err != nil { handleError(err, w, r, marshalers) } }) api.router.GET(api.prefix+name+"/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleRead(w, r, ps, api.info) if err != nil { handleError(err, w, r, marshalers) } }) // generate all routes for linked relations if there are relations casted, ok := prototype.(jsonapi.MarshalReferences) if ok { relations := casted.GetReferences() for _, relation := range relations { api.router.GET(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleReadRelation(w, r, ps, api.info, relation) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) api.router.GET(api.prefix+name+"/:id/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleLinked(api, w, r, ps, relation, api.info) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) api.router.PATCH(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleReplaceRelation(w, r, ps, relation) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) if _, ok := ptrPrototype.(jsonapi.EditToManyRelations); ok && relation.Name == jsonapi.Pluralize(relation.Name) { // generate additional routes to manipulate to-many relationships api.router.POST(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleAddToManyRelation(w, r, ps, relation) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) api.router.DELETE(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleDeleteToManyRelation(w, r, ps, relation) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) } } } api.router.POST(api.prefix+name, func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleCreate(w, r, api.prefix, api.info) if err != nil { handleError(err, w, r, marshalers) } }) api.router.DELETE(api.prefix+name+"/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleDelete(w, r, ps) if err != nil { handleError(err, w, r, marshalers) } }) api.router.PATCH(api.prefix+name+"/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { err := res.handleUpdate(w, r, ps) if err != nil { handleError(err, w, r, marshalers) } }) api.resources = append(api.resources, res) return &res }
func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source CRUD, marshalers map[string]ContentMarshaler) *resource { resourceType := reflect.TypeOf(prototype) if resourceType.Kind() != reflect.Struct && resourceType.Kind() != reflect.Ptr { panic("pass an empty resource struct or a struct pointer to AddResource!") } var ptrPrototype interface{} var name string if resourceType.Kind() == reflect.Struct { ptrPrototype = reflect.New(resourceType).Interface() name = resourceType.Name() } else { ptrPrototype = reflect.ValueOf(prototype).Interface() name = resourceType.Elem().Name() } // check if EntityNamer interface is implemented and use that as name entityName, ok := prototype.(jsonapi.EntityNamer) if ok { name = entityName.GetName() } else { name = jsonapi.Jsonify(jsonapi.Pluralize(name)) } res := resource{ resourceType: resourceType, name: name, source: source, marshalers: marshalers, } requestInfo := func(r *http.Request, api *API) *information { var info *information if resolver, ok := api.info.resolver.(RequestAwareURLResolver); ok { resolver.SetRequest(*r) info = &information{prefix: api.info.prefix, resolver: resolver} } else { info = &api.info } return info } prefix := strings.Trim(api.info.prefix, "/") baseURL := "/" + name if prefix != "" { baseURL = "/" + prefix + baseURL } api.router.Handle("OPTIONS", baseURL, func(w http.ResponseWriter, r *http.Request, _ map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) w.Header().Set("Allow", "GET,POST,PATCH,OPTIONS") w.WriteHeader(http.StatusNoContent) api.contextPool.Put(c) }) api.router.Handle("OPTIONS", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, _ map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) w.Header().Set("Allow", "GET,PATCH,DELETE,OPTIONS") w.WriteHeader(http.StatusNoContent) api.contextPool.Put(c) }) api.router.Handle("GET", baseURL, func(w http.ResponseWriter, r *http.Request, _ map[string]string) { info := requestInfo(r, api) c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleIndex(c, w, r, *info) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } }) api.router.Handle("GET", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) { info := requestInfo(r, api) c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleRead(c, w, r, params, *info) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } }) // generate all routes for linked relations if there are relations casted, ok := prototype.(jsonapi.MarshalReferences) if ok { relations := casted.GetReferences() for _, relation := range relations { api.router.Handle("GET", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc { return func(w http.ResponseWriter, r *http.Request, params map[string]string) { info := requestInfo(r, api) c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleReadRelation(c, w, r, params, *info, relation) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) api.router.Handle("GET", baseURL+"/:id/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc { return func(w http.ResponseWriter, r *http.Request, params map[string]string) { info := requestInfo(r, api) c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleLinked(c, api, w, r, params, relation, *info) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) api.router.Handle("PATCH", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc { return func(w http.ResponseWriter, r *http.Request, params map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleReplaceRelation(c, w, r, params, relation) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) if _, ok := ptrPrototype.(jsonapi.EditToManyRelations); ok && relation.Name == jsonapi.Pluralize(relation.Name) { // generate additional routes to manipulate to-many relationships api.router.Handle("POST", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc { return func(w http.ResponseWriter, r *http.Request, params map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleAddToManyRelation(c, w, r, params, relation) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) api.router.Handle("DELETE", baseURL+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) routing.HandlerFunc { return func(w http.ResponseWriter, r *http.Request, params map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleDeleteToManyRelation(c, w, r, params, relation) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } } }(relation)) } } } api.router.Handle("POST", baseURL, func(w http.ResponseWriter, r *http.Request, params map[string]string) { info := requestInfo(r, api) c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleCreate(c, w, r, info.prefix, *info) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } }) api.router.Handle("DELETE", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleDelete(c, w, r, params) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } }) api.router.Handle("PATCH", baseURL+"/:id", func(w http.ResponseWriter, r *http.Request, params map[string]string) { c := api.contextPool.Get().(APIContexter) c.Reset() api.middlewareChain(c, w, r) err := res.handleUpdate(c, w, r, params) api.contextPool.Put(c) if err != nil { handleError(err, w, r, marshalers) } }) api.resources = append(api.resources, res) return &res }
func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source CRUD, marshalers map[string]ContentMarshaler) *resource { resourceType := reflect.TypeOf(prototype) if resourceType.Kind() != reflect.Struct && resourceType.Kind() != reflect.Ptr { panic("pass an empty resource struct or a struct pointer to AddResource!") } var ptrPrototype interface{} var name string if resourceType.Kind() == reflect.Struct { ptrPrototype = reflect.New(resourceType).Interface() name = resourceType.Name() } else { ptrPrototype = reflect.ValueOf(prototype).Interface() name = resourceType.Elem().Name() } // check if EntityNamer interface is implemented and use that as name entityName, ok := prototype.(jsonapi.EntityNamer) if ok { name = entityName.GetName() } else { name = jsonapi.Jsonify(jsonapi.Pluralize(name)) } // Check if CRUDHooks is implemented. hooks, _ := source.(CRUDHooks) // Nil when not implemented. res := resource{ resourceType: resourceType, name: name, source: source, hooks: hooks, marshalers: marshalers, api: api, } api.router.Handle("OPTIONS", api.prefix+name, res.getMiddleware(func(r Request) response { return response{ Status: http.StatusNoContent, Header: http.Header{"Allow": []string{"GET,POST,PATCH,OPTIONS"}}, } })) api.router.Handle("OPTIONS", api.prefix+name+"/:id", res.getMiddleware(func(r Request) response { return response{ Status: http.StatusNoContent, Header: http.Header{"Allow": []string{"GET,PATCH,DELETE,OPTIONS"}}, } })) api.router.GET(api.prefix+name, res.getMiddleware(func(r Request) response { return res.handleIndex(r) })) api.router.GET(api.prefix+name+"/:id", res.getMiddleware(func(r Request) response { return res.handleRead(r) })) // generate all routes for linked relations if there are relations casted, ok := prototype.(jsonapi.MarshalReferences) if ok { relations := casted.GetReferences() for _, relation := range relations { api.router.GET(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return res.getMiddleware(func(r Request) response { return res.handleReadRelation(r, relation) }) }(relation)) api.router.GET(api.prefix+name+"/:id/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return res.getMiddleware(func(r Request) response { return res.handleLinked(r, relation) }) }(relation)) api.router.PATCH(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return res.getMiddleware(func(r Request) response { return res.handleReplaceRelation(r, relation) }) }(relation)) if _, ok := ptrPrototype.(jsonapi.EditToManyRelations); ok && relation.Name == jsonapi.Pluralize(relation.Name) { // generate additional routes to manipulate to-many relationships api.router.POST(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return res.getMiddleware(func(r Request) response { return res.handleAddToManyRelation(r, relation) }) }(relation)) api.router.DELETE(api.prefix+name+"/:id/relationships/"+relation.Name, func(relation jsonapi.Reference) httprouter.Handle { return res.getMiddleware(func(r Request) response { return res.handleDeleteToManyRelation(r, relation) }) }(relation)) } } } api.router.POST(api.prefix+name, res.getMiddleware(func(r Request) response { return res.handleCreate(r) })) api.router.DELETE(api.prefix+name+"/:id", res.getMiddleware(func(r Request) response { return res.handleDelete(r) })) api.router.PATCH(api.prefix+name+"/:id", res.getMiddleware(func(r Request) response { return res.handleUpdate(r) })) api.resources = append(api.resources, res) return &res }