Beispiel #1
0
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
}
Beispiel #2
0
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
}
Beispiel #3
0
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
}