API endpoints.

This commit is contained in:
Matthew Dillon 2014-09-30 16:03:16 -08:00
parent 1e283e7cd6
commit e1685bd32b
8 changed files with 129 additions and 11 deletions

View file

@ -1,7 +0,0 @@
package api
import "github.com/gorilla/mux"
func Handler() *mux.Router {
return mux.NewRouter()
}

35
api/handler.go Normal file
View file

@ -0,0 +1,35 @@
package api
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/thermokarst/bactdb/datastore"
"github.com/thermokarst/bactdb/router"
)
var (
store = datastore.NewDatastore(nil)
schemaDecoder = schema.NewDecoder()
)
func Handler() *mux.Router {
m := router.API()
m.Get(router.User).Handler(handler(serveUser))
m.Get(router.Users).Handler(handler(serveUsers))
return m
}
type handler func(http.ResponseWriter, *http.Request) error
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := h(w, r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "error: %s", err)
log.Println(err)
}
}

19
api/helpers.go Normal file
View file

@ -0,0 +1,19 @@
package api
import (
"encoding/json"
"net/http"
)
// writeJSON writes a JSON Content-Type header and a JSON-encoded object to
// the http.ResponseWriter.
func writeJSON(w http.ResponseWriter, v interface{}) error {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
return err
}
w.Header().Set("content-type", "application/json; charset=utf-8")
_, err = w.Write(data)
return err
}

42
api/users.go Normal file
View file

@ -0,0 +1,42 @@
package api
import (
"strconv"
"github.com/gorilla/mux"
"net/http"
"github.com/thermokarst/bactdb/models"
)
func serveUser(w http.ResponseWriter, r *http.Request) error {
id, err := strconv.ParseInt(mux.Vars(r)["ID"], 10, 0)
if err != nil {
return err
}
user, err := store.Users.Get(id)
if err != nil {
return err
}
return writeJSON(w, user)
}
func serveUsers(w http.ResponseWriter, r *http.Request) error {
var opt models.UserListOptions
if err := schemaDecoder.Decode(&opt, r.URL.Query()); err != nil {
return err
}
users, err := store.Users.List(&opt)
if err != nil {
return err
}
if users == nil {
users = []*models.User{}
}
return writeJSON(w, users)
}

View file

@ -8,6 +8,7 @@ import (
"os"
"github.com/thermokarst/bactdb/api"
"github.com/thermokarst/bactdb/datastore"
)
func init() {
@ -62,6 +63,7 @@ type subcmd struct {
var subcmds = []subcmd{
{"serve", "start web server", serveCmd},
{"create-db", "create the database schema", createDBCmd},
}
func serveCmd(args []string) {
@ -83,8 +85,10 @@ The options are:
fs.Usage()
}
datastore.Connect()
m := http.NewServeMux()
m.Handle("/api", api.Handler())
m.Handle("/api/", http.StripPrefix("/api", api.Handler()))
log.Print("Listening on ", *httpAddr)
err := http.ListenAndServe(*httpAddr, m)
@ -92,3 +96,25 @@ The options are:
log.Fatal("ListenAndServe:", err)
}
}
func createDBCmd(args []string) {
fs := flag.NewFlagSet("create-db", flag.ExitOnError)
fs.Usage = func() {
fmt.Fprintln(os.Stderr, `usage: bactdb create-db [options]
Creates the necessary DB schema.
The options are:
`)
fs.PrintDefaults()
os.Exit(1)
}
fs.Parse(args)
if fs.NArg() != 0 {
fs.Usage()
}
datastore.Connect()
datastore.Create()
}

View file

@ -24,7 +24,7 @@ var connectOnce sync.Once
func Connect() {
connectOnce.Do(func() {
var err error
DB.Dbx, err = sqlx.Open("postgres", "")
DB.Dbx, err = sqlx.Open("postgres", "sslmode=disable")
if err != nil {
log.Fatal("Error connecting to PostgreSQL database (using PG* environment variables): ", err)
}

View file

@ -3,7 +3,7 @@ package datastore
import "github.com/thermokarst/bactdb/models"
func init() {
DB.AddTableWithName(models.User{}, "users").SetKeys(false, "Id")
DB.AddTableWithName(models.User{}, "users").SetKeys(true, "Id")
createSQL = append(createSQL,
`CREATE UNIQUE INDEX username_idx ON users (username);`,
)
@ -25,6 +25,9 @@ func (s *usersStore) Get(id int64) (*models.User, error) {
}
func (s *usersStore) List(opt *models.UserListOptions) ([]*models.User, error) {
if opt == nil {
opt = &models.UserListOptions{}
}
var users []*models.User
err := s.dbh.Select(&users, `SELECT * FROM users LIMIT $1 OFFSET $2;`, opt.PerPageOrDefault(), opt.Offset())
if err != nil {

View file

@ -10,7 +10,7 @@ import (
// A User is a person that has administrative access to bactdb.
type User struct {
Id int64 `json:"id"`
Id int64 `json:"id,omitempty"`
UserName string `sql:"size:100" json:"user_name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`