Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Generic handler for mapping database-stored resources to a REST endpoint.
- package main
- import (
- "encoding/json"
- "github.com/boltdb/bolt"
- "github.com/djherbis/stow"
- "io/ioutil"
- "net/http"
- "regexp"
- "strings"
- )
- // Storable entity in an interface.
- type Storable interface {
- // Get the key from a storable resource
- key() []byte
- // Get an instance of an empty resource for population by a resource handler
- empty() Storable
- }
- // All the information needed to manage retrieval and storage of a RESTful resource.
- type Resource struct {
- // URL path to the resource
- ResourcePath string
- // Database that stores the resource
- Db *bolt.DB
- // Name of the bucket used for storage
- BucketName []byte
- // Exemplar instance of the resource that should be storable
- Storable Storable
- // Function to call when resource is successfully stored
- OnStore func()
- }
- // Initialize a resource handler. This sets up the http handlers necessary to manage the resource.
- func (r *Resource) Init() {
- http.HandleFunc(r.ResourcePath, r.resourceHandler)
- http.HandleFunc(r.ResourcePath + "/", r.resourceHandler)
- }
- func (resource *Resource) resourceHandler(w http.ResponseWriter, r *http.Request) {
- switch r.Method {
- case "GET":
- resourceName := resource.getNameFromPath(r)
- if resourceName == "" {
- resources, err := resource.GetAll()
- if err != nil {
- WriteError(w, err)
- return
- }
- WriteAsJson(w, resources)
- } else {
- resource, err := resource.Get(resourceName)
- if err != nil {
- WriteError(w, err)
- return
- }
- if resource == nil {
- http.Error(w, "", 404)
- return
- }
- WriteAsJson(w, resource)
- }
- case "PUT":
- r, err := resource.FetchFromBody(r)
- if err != nil {
- WriteError(w, err)
- return
- }
- err = resource.AddResource(r)
- if err != nil {
- WriteError(w, err)
- return
- }
- w.Write([]byte(""))
- if resource.OnStore != nil {
- resource.OnStore()
- }
- case "POST":
- resourceName := resource.getNameFromPath(r)
- entity, err := resource.FetchFromBody(r)
- if err != nil {
- WriteError(w, err)
- return
- }
- err = resource.Update(resourceName, entity)
- if err != nil {
- WriteError(w, err)
- return
- }
- w.Write([]byte(""))
- if resource.OnStore != nil {
- resource.OnStore()
- }
- case "DELETE":
- resourceName := resource.getNameFromPath(r)
- err := resource.Delete(resourceName)
- if err != nil {
- WriteError(w, err)
- return
- }
- w.Write([]byte(""))
- }
- }
- const pathRegexpString = ".*/([^/]*)$"
- func (resource *Resource) FetchFromBody(r *http.Request) (s Storable, err error) {
- s = resource.Storable.empty()
- data, err := ioutil.ReadAll(r.Body)
- if err != nil {
- return
- }
- err = json.Unmarshal(data, s)
- return
- }
- func (resource *Resource) getNameFromPath(r *http.Request) string {
- requestPath := r.URL.Path
- if !strings.HasPrefix(requestPath, resource.ResourcePath + "/") {
- return ""
- }
- matcher := regexp.MustCompile(pathRegexpString)
- result := matcher.FindStringSubmatch(r.URL.Path)
- if len(result) < 2 {
- return ""
- }
- return result[1]
- }
- func (resource *Resource) GetAll() (s []Storable, err error) {
- s = make([]Storable, 0)
- err = resource.Db.View(func(tx *bolt.Tx) error {
- b := tx.Bucket(resource.BucketName)
- if b == nil {
- return nil
- }
- return b.ForEach(func(k, v []byte) error {
- elem := resource.Storable.empty()
- err = json.Unmarshal(v, elem)
- if err != nil {
- return err
- }
- s = append(s, elem)
- return nil
- })
- })
- return
- }
- // Fetch the specified resource. Returns the resource and an error code; if
- // resource is nil, it didn't exist.
- func (resource *Resource) Get(resourceName string) (s Storable, err error) {
- err = resource.Db.View(func(tx *bolt.Tx) error {
- b := tx.Bucket(resource.BucketName)
- if b == nil {
- return nil
- }
- v := b.Get([]byte(resourceName))
- if v == nil {
- s = nil
- return nil
- }
- s = resource.Storable.empty()
- err = json.Unmarshal(v, s)
- if err != nil {
- return err
- }
- return nil
- })
- return
- }
- func (resource *Resource) AddResource(r Storable) error {
- store := stow.NewJSONStore(resource.Db, resource.BucketName)
- return store.Put(r.key(), r)
- }
- func (resource *Resource) Update(resourceName string, r Storable) error {
- // TODO(mtomczak): Should confirm old name existed.
- if resourceName != string(r.key()) {
- err := resource.Delete(resourceName)
- if err != nil {
- return err
- }
- }
- return resource.AddResource(r)
- }
- func (resource *Resource) Delete(resourceName string) error {
- return resource.Db.Update(func(tx *bolt.Tx) error {
- bucket := tx.Bucket(resource.BucketName)
- if bucket == nil {
- return nil
- }
- return bucket.Delete([]byte(resourceName))
- })
- }
Advertisement
Add Comment
Please, Sign In to add comment