2) get services JSON errors
This commit is contained in:
parent
cf6dcb6a7b
commit
4a1d968539
9 changed files with 131 additions and 67 deletions
|
@ -1,14 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCharacteristicTypeNotFound = errors.New("Characteristic Type not found")
|
||||
ErrCharacteristicTypeNotFoundJSON = newJSONError(ErrCharacteristicTypeNotFound, http.StatusNotFound)
|
||||
)
|
||||
|
||||
func init() {
|
||||
DB.AddTableWithName(CharacteristicTypeBase{}, "characteristic_types").SetKeys(true, "Id")
|
||||
}
|
||||
|
@ -89,15 +96,18 @@ func (c CharacteristicTypeService) list(val *url.Values) (entity, error) {
|
|||
return &characteristic_types, nil
|
||||
}
|
||||
|
||||
func (c CharacteristicTypeService) get(id int64, dummy string) (entity, error) {
|
||||
func (c CharacteristicTypeService) get(id int64, dummy string) (entity, *appError) {
|
||||
var characteristic_type CharacteristicType
|
||||
sql := `SELECT ct.*, array_agg(c.id) AS characteristics, 0 AS sort_order
|
||||
q := `SELECT ct.*, array_agg(c.id) AS characteristics, 0 AS sort_order
|
||||
FROM characteristic_types ct
|
||||
INNER JOIN characteristics c ON c.characteristic_type_id=ct.id
|
||||
WHERE ct.id=$1
|
||||
GROUP BY ct.id;`
|
||||
if err := DBH.SelectOne(&characteristic_type, sql, id); err != nil {
|
||||
return nil, err
|
||||
if err := DBH.SelectOne(&characteristic_type, q, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrCharacteristicTypeNotFoundJSON
|
||||
}
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return &characteristic_type, nil
|
||||
}
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCharacteristicNotFound = errors.New("Characteristic not found")
|
||||
ErrCharacteristicNotFoundJSON = newJSONError(ErrCharacteristicNotFound, http.StatusNotFound)
|
||||
)
|
||||
|
||||
func init() {
|
||||
DB.AddTableWithName(CharacteristicBase{}, "characteristics").SetKeys(true, "Id")
|
||||
}
|
||||
|
@ -91,16 +98,19 @@ func (c CharacteristicService) list(val *url.Values) (entity, error) {
|
|||
return &characteristics, nil
|
||||
}
|
||||
|
||||
func (c CharacteristicService) get(id int64, dummy string) (entity, error) {
|
||||
func (c CharacteristicService) get(id int64, dummy string) (entity, *appError) {
|
||||
var characteristic Characteristic
|
||||
sql := `SELECT c.*, array_agg(m.id) AS measurements, array_agg(st.id) AS strains
|
||||
q := `SELECT c.*, array_agg(m.id) AS measurements, array_agg(st.id) AS strains
|
||||
FROM characteristics c
|
||||
LEFT OUTER JOIN measurements m ON m.characteristic_id=c.id
|
||||
LEFT OUTER JOIN strains st ON st.id=m.strain_id
|
||||
WHERE c.id=$1
|
||||
GROUP BY c.id;`
|
||||
if err := DBH.SelectOne(&characteristic, sql, id); err != nil {
|
||||
return nil, err
|
||||
if err := DBH.SelectOne(&characteristic, q, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrCharacteristicNotFoundJSON
|
||||
}
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return &characteristic, nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ type entity interface {
|
|||
}
|
||||
|
||||
type getter interface {
|
||||
get(int64, string) (entity, error)
|
||||
get(int64, string) (entity, *appError)
|
||||
}
|
||||
|
||||
type lister interface {
|
||||
|
|
80
handlers.go
80
handlers.go
|
@ -68,74 +68,73 @@ func Handler() http.Handler {
|
|||
}
|
||||
|
||||
m := mux.NewRouter()
|
||||
userService := UserService{}
|
||||
strainService := StrainService{}
|
||||
speciesService := SpeciesService{}
|
||||
characteristicService := CharacteristicService{}
|
||||
characteristicTypeService := CharacteristicTypeService{}
|
||||
measurementService := MeasurementService{}
|
||||
|
||||
// Non-auth routes
|
||||
m.Handle("/authenticate", tokenHandler(j.GenerateToken())).Methods("POST")
|
||||
|
||||
// Auth routes
|
||||
m.Handle("/users", j.Secure(http.HandlerFunc(handleLister(UserService{})), verifyClaims)).Methods("GET")
|
||||
m.Handle("/users", j.Secure(http.HandlerFunc(handleCreater(UserService{})), verifyClaims)).Methods("POST")
|
||||
m.Handle("/users/{Id:.+}", j.Secure(http.HandlerFunc(handleGetter(UserService{})), verifyClaims)).Methods("GET")
|
||||
m.Handle("/users/{Id:.+}", j.Secure(http.HandlerFunc(handleUpdater(UserService{})), verifyClaims)).Methods("PUT")
|
||||
m.Handle("/users", j.Secure(http.HandlerFunc(handleLister(userService)), verifyClaims)).Methods("GET")
|
||||
m.Handle("/users", j.Secure(http.HandlerFunc(handleCreater(userService)), verifyClaims)).Methods("POST")
|
||||
m.Handle("/users/{Id:.+}", j.Secure(errorHandler(handleGetter(userService)), verifyClaims)).Methods("GET")
|
||||
m.Handle("/users/{Id:.+}", j.Secure(http.HandlerFunc(handleUpdater(userService)), verifyClaims)).Methods("PUT")
|
||||
|
||||
// Path-based pattern matching subrouter
|
||||
s := m.PathPrefix("/{genus}").Subrouter()
|
||||
|
||||
type r struct {
|
||||
f http.HandlerFunc
|
||||
f errorHandler
|
||||
m string
|
||||
p string
|
||||
}
|
||||
|
||||
routes := []r{
|
||||
r{handleLister(StrainService{}), "GET", "/strains"},
|
||||
r{handleCreater(StrainService{}), "POST", "/strains"},
|
||||
r{handleGetter(StrainService{}), "GET", "/strains/{Id:.+}"},
|
||||
r{handleUpdater(StrainService{}), "PUT", "/strains/{Id:.+}"},
|
||||
r{handleLister(MeasurementService{}), "GET", "/measurements"},
|
||||
r{handleGetter(MeasurementService{}), "GET", "/measurements/{Id:.+}"},
|
||||
r{handleLister(CharacteristicService{}), "GET", "/characteristics"},
|
||||
r{handleGetter(CharacteristicService{}), "GET", "/characteristics/{Id:.+}"},
|
||||
r{handleLister(SpeciesService{}), "GET", "/species"},
|
||||
r{handleCreater(SpeciesService{}), "POST", "/species"},
|
||||
r{handleGetter(SpeciesService{}), "GET", "/species/{Id:.+}"},
|
||||
r{handleUpdater(SpeciesService{}), "PUT", "/species/{Id:.+}"},
|
||||
r{handleLister(CharacteristicTypeService{}), "GET", "/characteristicTypes"},
|
||||
r{handleGetter(CharacteristicTypeService{}), "GET", "/characteristicTypes/{Id:.+}"},
|
||||
// r{handleLister(speciesService), "GET", "/species"},
|
||||
// r{handleCreater(speciesService), "POST", "/species"},
|
||||
r{handleGetter(speciesService), "GET", "/species/{Id:.+}"},
|
||||
// r{handleUpdater(speciesService), "PUT", "/species/{Id:.+}"},
|
||||
// r{handleLister(strainService), "GET", "/strains"},
|
||||
// r{handleCreater(strainService), "POST", "/strains"},
|
||||
r{handleGetter(strainService), "GET", "/strains/{Id:.+}"},
|
||||
// r{handleUpdater(strainService), "PUT", "/strains/{Id:.+}"},
|
||||
// r{handleLister(characteristicService), "GET", "/characteristics"},
|
||||
r{handleGetter(characteristicService), "GET", "/characteristics/{Id:.+}"},
|
||||
// r{handleLister(characteristicTypeService), "GET", "/characteristicTypes"},
|
||||
r{handleGetter(characteristicTypeService), "GET", "/characteristicTypes/{Id:.+}"},
|
||||
// r{handleLister(measurementService), "GET", "/measurements"},
|
||||
r{handleGetter(measurementService), "GET", "/measurements/{Id:.+}"},
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
s.Handle(route.p, j.Secure(http.HandlerFunc(route.f), verifyClaims)).Methods(route.m)
|
||||
s.Handle(route.p, j.Secure(errorHandler(route.f), verifyClaims)).Methods(route.m)
|
||||
}
|
||||
|
||||
return jsonHandler(corsHandler(m))
|
||||
}
|
||||
|
||||
func Error(w http.ResponseWriter, err string, code int) {
|
||||
w.WriteHeader(code)
|
||||
fmt.Fprintln(w, err)
|
||||
}
|
||||
|
||||
func handleGetter(g getter) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func handleGetter(g getter) errorHandler {
|
||||
return func(w http.ResponseWriter, r *http.Request) *appError {
|
||||
id, err := strconv.ParseInt(mux.Vars(r)["Id"], 10, 0)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
return newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
e, err := g.get(id, mux.Vars(r)["genus"])
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
e, appErr := g.get(id, mux.Vars(r)["genus"])
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
data, err := e.marshal()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
return newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
w.Write(data)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,7 +213,7 @@ func handleCreater(c creater) http.HandlerFunc {
|
|||
|
||||
err = c.create(&e, claims)
|
||||
if err != nil {
|
||||
Error(w, err.Error(), http.StatusInternalServerError)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -264,3 +263,12 @@ func jsonHandler(h http.Handler) http.Handler {
|
|||
}
|
||||
return http.HandlerFunc(j)
|
||||
}
|
||||
|
||||
type errorHandler func(http.ResponseWriter, *http.Request) *appError
|
||||
|
||||
func (fn errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := fn(w, r); err != nil {
|
||||
w.WriteHeader(err.Status)
|
||||
fmt.Fprintln(w, err.Error.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrMeasurementNotFound = errors.New("measurement not found")
|
||||
var (
|
||||
ErrMeasurementNotFound = errors.New("Measurement not found")
|
||||
ErrMeasurementNotFoundJSON = newJSONError(ErrMeasurementNotFound, http.StatusNotFound)
|
||||
)
|
||||
|
||||
func init() {
|
||||
DB.AddTableWithName(MeasurementBase{}, "measurements").SetKeys(true, "Id")
|
||||
|
@ -134,9 +139,9 @@ func (m MeasurementService) list(val *url.Values) (entity, error) {
|
|||
return &measurements, nil
|
||||
}
|
||||
|
||||
func (m MeasurementService) get(id int64, genus string) (entity, error) {
|
||||
func (m MeasurementService) get(id int64, genus string) (entity, *appError) {
|
||||
var measurement Measurement
|
||||
sql := `SELECT m.*, t.text_measurement_name AS text_measurement_type_name,
|
||||
q := `SELECT m.*, t.text_measurement_name AS text_measurement_type_name,
|
||||
u.symbol AS unit_type_name, te.name AS test_method_name
|
||||
FROM measurements m
|
||||
INNER JOIN strains st ON st.id=m.strain_id
|
||||
|
@ -147,11 +152,11 @@ func (m MeasurementService) get(id int64, genus string) (entity, error) {
|
|||
LEFT OUTER JOIN unit_types u ON u.id=m.unit_type_id
|
||||
LEFT OUTER JOIN test_methods te ON te.id=m.test_method_id
|
||||
WHERE m.id=$2;`
|
||||
if err := DBH.SelectOne(&measurement, sql, genus, id); err != nil {
|
||||
return nil, err
|
||||
if err := DBH.SelectOne(&measurement, q, genus, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrMeasurementNotFoundJSON
|
||||
}
|
||||
if &measurement == nil {
|
||||
return nil, ErrMeasurementNotFound
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return &measurement, nil
|
||||
}
|
||||
|
|
12
species.go
12
species.go
|
@ -5,14 +5,16 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSpeciesNotFound = errors.New("species not found")
|
||||
ErrSpeciesNotUpdated = errors.New("species not updated")
|
||||
ErrSpeciesNotFound = errors.New("Species not found")
|
||||
ErrSpeciesNotFoundJSON = newJSONError(ErrSpeciesNotFound, http.StatusNotFound)
|
||||
ErrSpeciesNotUpdated = errors.New("Species not updated")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -110,7 +112,7 @@ func (s SpeciesService) list(val *url.Values) (entity, error) {
|
|||
return &species, nil
|
||||
}
|
||||
|
||||
func (s SpeciesService) get(id int64, genus string) (entity, error) {
|
||||
func (s SpeciesService) get(id int64, genus string) (entity, *appError) {
|
||||
var species Species
|
||||
q := `SELECT sp.*, g.genus_name, array_agg(st.id) AS strains,
|
||||
COUNT(st) AS total_strains, 0 AS sort_order
|
||||
|
@ -121,9 +123,9 @@ func (s SpeciesService) get(id int64, genus string) (entity, error) {
|
|||
GROUP BY sp.id, g.genus_name;`
|
||||
if err := DBH.SelectOne(&species, q, genus, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrStrainNotFound
|
||||
return nil, ErrSpeciesNotFoundJSON
|
||||
}
|
||||
return nil, err
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return &species, nil
|
||||
}
|
||||
|
|
12
strains.go
12
strains.go
|
@ -5,14 +5,16 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStrainNotFound = errors.New("strain not found")
|
||||
ErrStrainNotUpdated = errors.New("strain not updated")
|
||||
ErrStrainNotFound = errors.New("Strain not found")
|
||||
ErrStrainNotFoundJSON = newJSONError(ErrStrainNotFound, http.StatusNotFound)
|
||||
ErrStrainNotUpdated = errors.New("Strain not updated")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -112,7 +114,7 @@ func (s StrainService) list(val *url.Values) (entity, error) {
|
|||
return &strains, nil
|
||||
}
|
||||
|
||||
func (s StrainService) get(id int64, genus string) (entity, error) {
|
||||
func (s StrainService) get(id int64, genus string) (entity, *appError) {
|
||||
var strain Strain
|
||||
q := `SELECT st.*, array_agg(m.id) AS measurements, COUNT(m) AS total_measurements,
|
||||
0 AS sort_order
|
||||
|
@ -124,9 +126,9 @@ func (s StrainService) get(id int64, genus string) (entity, error) {
|
|||
GROUP BY st.id, st.species_id;`
|
||||
if err := DBH.SelectOne(&strain, q, genus, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrStrainNotFound
|
||||
return nil, ErrStrainNotFoundJSON
|
||||
}
|
||||
return nil, err
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return &strain, nil
|
||||
}
|
||||
|
|
25
types.go
25
types.go
|
@ -186,3 +186,28 @@ func strToIntSlice(s string) []int64 {
|
|||
}
|
||||
return a
|
||||
}
|
||||
|
||||
type ErrorJSON struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (ej ErrorJSON) Error() string {
|
||||
e, _ := json.Marshal(struct {
|
||||
Err string `json:"error"`
|
||||
}{
|
||||
Err: ej.Err.Error(),
|
||||
})
|
||||
return string(e)
|
||||
}
|
||||
|
||||
type appError struct {
|
||||
Error error
|
||||
Status int
|
||||
}
|
||||
|
||||
func newJSONError(err error, status int) *appError {
|
||||
return &appError{
|
||||
Error: ErrorJSON{Err: err},
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
|
|
10
users.go
10
users.go
|
@ -4,6 +4,7 @@ import (
|
|||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
|
@ -11,7 +12,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrUserNotFound = errors.New("User not found")
|
||||
ErrUserNotFoundJSON = newJSONError(ErrUserNotFound, http.StatusNotFound)
|
||||
ErrInvalidEmailOrPassword = errors.New("Invalid email or password")
|
||||
)
|
||||
|
||||
|
@ -104,14 +106,14 @@ func (u UserService) list(val *url.Values) (entity, error) {
|
|||
return &users, nil
|
||||
}
|
||||
|
||||
func (u UserService) get(id int64, genus string) (entity, error) {
|
||||
func (u UserService) get(id int64, genus string) (entity, *appError) {
|
||||
var user User
|
||||
q := `SELECT * FROM users WHERE id=$1;`
|
||||
if err := DBH.SelectOne(&user, q, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrUserNotFound
|
||||
return nil, ErrUserNotFoundJSON
|
||||
}
|
||||
return nil, err
|
||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
|
Reference in a new issue