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.
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)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment