Skip to content

Instantly share code, notes, and snippets.

@egonelbre
Created February 3, 2015 09:52
Show Gist options
  • Save egonelbre/cb3dcdecc941d7fa7b0d to your computer and use it in GitHub Desktop.
Save egonelbre/cb3dcdecc941d7fa7b0d to your computer and use it in GitHub Desktop.

Revisions

  1. egonelbre created this gist Feb 3, 2015.
    172 changes: 172 additions & 0 deletions books.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,172 @@
    package main

    import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "time"

    "github.com/gorilla/mux"
    "github.com/jmoiron/sqlx"
    )

    const StatusHandled = 0

    type Book struct {
    BookID string
    CategoryID string
    Title string
    Slug string
    Description string
    LikeCount int
    Creation time.Time
    LastUpdate time.Time
    Deleted bool
    }

    type NewBook struct {
    Title string
    Description string
    }

    type Context struct {
    DB *sqlx.DB
    }

    func New(db *sql.DB) *Context {
    return &Context{DB}
    }

    func readjson(r io.ReadCloser, v interface{}) error {
    data, err := ioutil.ReadAll(r)
    if err != nil {
    return err
    }

    return json.Unmarshal(data, v)
    }

    func (c *Context) json(rw http.ResponseWriter, r *http.Request, v interface{}) (err string, code int) {
    data, err := json.Marshal(v)
    if err != nil {
    return err.Error(), http.StatusInternalServerError
    }

    rw.Header().Set("Content-Type", "application/json")
    rw.WriteHeader(http.StatusOK)
    rw.Write(data)
    return "", StatusHandled
    }

    func (c *Context) Books(rw http.ResponseWriter, r *http.Request) (err string, code int) {
    vars, db := mux.Vars(r), c.DB

    switch r.Method {
    case "GET":
    books := &Books{}
    category := vars["category"]
    if category == "" {
    http.Error(rw, fmt.Sprintf("Category cannot be empty."), http.StatusBadRequest)
    return
    }

    err := db.Select(books, "SELECT * FROM book WHERE categoryid=?", category)
    if err != nil {
    return fmt.Sprintf(`Error searching for books: %v`, err), http.StatusInternalServerError
    }

    return c.json(rw, r, books)
    default:
    return fmt.Sprintf("Invalid method: %s", r.Method), http.StatusForbidden
    }
    }

    func (c *Context) ByCategory(rw http.ResponseWriter, r *http.Request) (err string, code int) {
    vars, db := mux.Vars(r), c.DB

    slug := vars["slug"]
    category := vars["category"]

    switch r.Method {

    case "GET":
    book := &Book{}
    if slug == "" || cat == "" {
    return fmt.Sprintf("Slug or category cannot be empty."), http.StatusBadRequest
    }

    err = db.Get(book, "SELECT * FROM book WHERE slug=? AND categoryid=?", slug, cat)
    if err != nil {
    return fmt.Sprintf(`Error searching for books: %v`, err), http.StatusInternalServerError
    }
    return c.json(rw, r, book)

    case "POST":
    book := &NewBook{}
    err := readjson(r.Body, book)
    if err != nil {
    return fmt.Sprintf("Invalid book."), http.StatusBadRequest
    }

    _, err = db.Exec(`
    INSERT INTO book
    (categoryid, title, slug, description, likecount, creation, lastupdate, deleted)
    VALUES
    (?, ?, ?, ?, 0, NOW(), NOW(), false)`, category, book.Title, slug(book.Title), book.Description)
    if err != nil {
    return fmt.Sprintf("Error inserting the book: %s", err.Error()), http.StatusInternalServerError
    }

    return "", http.StatusOK
    default:
    return fmt.Sprintf("Invalid method: %s", r.Method), http.StatusForbidden
    }
    }

    func (c *Context) BookLike(rw http.ResponseWriter, r *http.Request) (err string, code int) {
    vars, db := mux.Vars(r), c.DB

    switch r.Method {
    case "POST":
    id := vars["bookid"]
    if id == "" {
    return fmt.Sprintf("Book id cannot be empty."), http.StatusBadRequest
    }

    _, err = db.Exec(`UPDATE book SET likecount=likecount+1 WHERE bookid=?`, id)
    if err != nil {
    return fmt.Sprintf("Error inserting the book: %s", err.Error()), http.StatusInternalServerError
    }

    return "", http.StatusOK
    default:
    return fmt.Sprintf("Invalid method: %s", r.Method), http.StatusForbidden
    }
    }

    var Router = mux.NewRouter()

    type Handler func(rw http.ResponseWriter, r *http.Request) (err string, code int)

    func route(path string, fn Handler) {
    Router.HandleFunc(path, func(rw http.ResponseWriter, r *http.Request) {
    err, code := fn(rw, r)
    if code != StatusHandled {
    http.Error(rw, err, code)
    }
    })
    }

    func main() {
    db := sqlx.Open("sqlite3", "memory")
    c := &Context{db}

    route("categories/{category}/books/{slug}", c.ByCategory)
    route("categories/{category}/books/{bookid}/like", c.BookLike)

    http.Handle("/api/", Router)
    http.ListenAndServe(":3000", nil)
    }