parent
a678eb4017
commit
ae17363f8b
12 changed files with 162 additions and 52 deletions
|
@ -137,6 +137,9 @@ func (c CharacteristicService) Update(id int64, e *types.Entity, genus string, c
|
||||||
if err == errors.ErrCharacteristicNotUpdated {
|
if err == errors.ErrCharacteristicNotUpdated {
|
||||||
return newJSONError(err, http.StatusBadRequest)
|
return newJSONError(err, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +179,9 @@ func (c CharacteristicService) Create(e *types.Entity, genus string, claims *typ
|
||||||
payload.Characteristic.CharacteristicTypeID = id
|
payload.Characteristic.CharacteristicTypeID = id
|
||||||
|
|
||||||
if err := models.Create(payload.Characteristic.CharacteristicBase); err != nil {
|
if err := models.Create(payload.Characteristic.CharacteristicBase); err != nil {
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,9 @@ func (m MeasurementService) Update(id int64, e *types.Entity, genus string, clai
|
||||||
if err == errors.ErrMeasurementNotUpdated {
|
if err == errors.ErrMeasurementNotUpdated {
|
||||||
return newJSONError(err, http.StatusBadRequest)
|
return newJSONError(err, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +135,9 @@ func (m MeasurementService) Create(e *types.Entity, genus string, claims *types.
|
||||||
payload.Measurement.UpdatedBy = claims.Sub
|
payload.Measurement.UpdatedBy = claims.Sub
|
||||||
|
|
||||||
if err := models.Create(payload.Measurement.MeasurementBase); err != nil {
|
if err := models.Create(payload.Measurement.MeasurementBase); err != nil {
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,9 @@ func (s SpeciesService) Update(id int64, e *types.Entity, genus string, claims *
|
||||||
if err == errors.ErrSpeciesNotUpdated {
|
if err == errors.ErrSpeciesNotUpdated {
|
||||||
return newJSONError(err, http.StatusBadRequest)
|
return newJSONError(err, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +136,9 @@ func (s SpeciesService) Create(e *types.Entity, genus string, claims *types.Clai
|
||||||
payload.Species.SpeciesBase.GenusID = genusID
|
payload.Species.SpeciesBase.GenusID = genusID
|
||||||
|
|
||||||
if err := models.Create(payload.Species.SpeciesBase); err != nil {
|
if err := models.Create(payload.Species.SpeciesBase); err != nil {
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,9 @@ func (s StrainService) Update(id int64, e *types.Entity, genus string, claims *t
|
||||||
if err == errors.ErrStrainNotUpdated {
|
if err == errors.ErrStrainNotUpdated {
|
||||||
return newJSONError(err, http.StatusBadRequest)
|
return newJSONError(err, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +193,9 @@ func (s StrainService) Create(e *types.Entity, genus string, claims *types.Claim
|
||||||
payload.Strain.UpdatedBy = claims.Sub
|
payload.Strain.UpdatedBy = claims.Sub
|
||||||
|
|
||||||
if err := models.Create(payload.Strain.StrainBase); err != nil {
|
if err := models.Create(payload.Strain.StrainBase); err != nil {
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
api/users.go
14
api/users.go
|
@ -105,14 +105,13 @@ func (u UserService) Update(id int64, e *types.Entity, dummy string, claims *typ
|
||||||
user.Verified = originalUser.Verified
|
user.Verified = originalUser.Verified
|
||||||
user.UpdatedAt = helpers.CurrentTime()
|
user.UpdatedAt = helpers.CurrentTime()
|
||||||
|
|
||||||
if err := user.Validate(); err != nil {
|
|
||||||
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := models.Update(user.UserBase); err != nil {
|
if err := models.Update(user.UserBase); err != nil {
|
||||||
if err == errors.ErrUserNotUpdated {
|
if err == errors.ErrUserNotUpdated {
|
||||||
return newJSONError(err, http.StatusBadRequest)
|
return newJSONError(err, http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,9 +123,7 @@ func (u UserService) Update(id int64, e *types.Entity, dummy string, claims *typ
|
||||||
// Create initializes a new user.
|
// Create initializes a new user.
|
||||||
func (u UserService) Create(e *types.Entity, dummy string, claims *types.Claims) *types.AppError {
|
func (u UserService) Create(e *types.Entity, dummy string, claims *types.Claims) *types.AppError {
|
||||||
user := (*e).(*payloads.User).User
|
user := (*e).(*payloads.User).User
|
||||||
if err := user.Validate(); err != nil {
|
|
||||||
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
|
||||||
}
|
|
||||||
ct := helpers.CurrentTime()
|
ct := helpers.CurrentTime()
|
||||||
user.CreatedAt = ct
|
user.CreatedAt = ct
|
||||||
user.UpdatedAt = ct
|
user.UpdatedAt = ct
|
||||||
|
@ -144,6 +141,9 @@ func (u UserService) Create(e *types.Entity, dummy string, claims *types.Claims)
|
||||||
return newJSONError(errors.ErrEmailAddressTaken, http.StatusInternalServerError)
|
return newJSONError(errors.ErrEmailAddressTaken, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(types.ValidationError); ok {
|
||||||
|
return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
|
||||||
|
}
|
||||||
return newJSONError(err, http.StatusInternalServerError)
|
return newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,24 @@ func (c *CharacteristicBase) DeleteError() error {
|
||||||
return errors.ErrCharacteristicNotDeleted
|
return errors.ErrCharacteristicNotDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CharacteristicBase) validate() types.ValidationError {
|
||||||
|
cv := make(types.ValidationError, 0)
|
||||||
|
|
||||||
|
if c.CharacteristicName == "" {
|
||||||
|
cv["Name"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.CharacteristicTypeID == 0 {
|
||||||
|
cv["Characteristic Type"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cv) > 0 {
|
||||||
|
return cv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CharacteristicBase is what the DB expects for write operations
|
// CharacteristicBase is what the DB expects for write operations
|
||||||
type CharacteristicBase struct {
|
type CharacteristicBase struct {
|
||||||
ID int64 `json:"id,omitempty"`
|
ID int64 `json:"id,omitempty"`
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/thermokarst/bactdb/Godeps/_workspace/src/github.com/jmoiron/modl"
|
import (
|
||||||
|
"github.com/thermokarst/bactdb/Godeps/_workspace/src/github.com/jmoiron/modl"
|
||||||
|
"github.com/thermokarst/bactdb/types"
|
||||||
|
)
|
||||||
|
|
||||||
type base interface {
|
type base interface {
|
||||||
PreInsert(modl.SqlExecutor) error
|
PreInsert(modl.SqlExecutor) error
|
||||||
PreUpdate(modl.SqlExecutor) error
|
PreUpdate(modl.SqlExecutor) error
|
||||||
UpdateError() error
|
UpdateError() error
|
||||||
DeleteError() error
|
DeleteError() error
|
||||||
|
validate() types.ValidationError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create will create a new DB record of a model.
|
// Create will create a new DB record of a model.
|
||||||
func Create(b base) error {
|
func Create(b base) error {
|
||||||
|
if err := b.validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := DBH.Insert(b); err != nil {
|
if err := DBH.Insert(b); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,6 +27,10 @@ func Create(b base) error {
|
||||||
|
|
||||||
// Update runs a DB update on a model.
|
// Update runs a DB update on a model.
|
||||||
func Update(b base) error {
|
func Update(b base) error {
|
||||||
|
if err := b.validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
count, err := DBH.Update(b)
|
count, err := DBH.Update(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -26,6 +38,7 @@ func Update(b base) error {
|
||||||
if count != 1 {
|
if count != 1 {
|
||||||
return b.UpdateError()
|
return b.UpdateError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,5 +51,6 @@ func Delete(b base) error {
|
||||||
if count != 1 {
|
if count != 1 {
|
||||||
return b.DeleteError()
|
return b.DeleteError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,24 @@ func (m *MeasurementBase) DeleteError() error {
|
||||||
return errors.ErrMeasurementNotDeleted
|
return errors.ErrMeasurementNotDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MeasurementBase) validate() types.ValidationError {
|
||||||
|
mv := make(types.ValidationError, 0)
|
||||||
|
|
||||||
|
if m.StrainID == 0 {
|
||||||
|
mv["Strain"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.CharacteristicID == 0 {
|
||||||
|
mv["Characteristic"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mv) > 0 {
|
||||||
|
return mv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MeasurementBase is what the DB expects for write operations
|
// MeasurementBase is what the DB expects for write operations
|
||||||
// There are three types of supported measurements: fixed-text, free-text,
|
// There are three types of supported measurements: fixed-text, free-text,
|
||||||
// & numerical. The table has a constraint that will allow at most one
|
// & numerical. The table has a constraint that will allow at most one
|
||||||
|
|
|
@ -39,6 +39,24 @@ func (s *SpeciesBase) DeleteError() error {
|
||||||
return errors.ErrSpeciesNotDeleted
|
return errors.ErrSpeciesNotDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SpeciesBase) validate() types.ValidationError {
|
||||||
|
sv := make(types.ValidationError, 0)
|
||||||
|
|
||||||
|
if s.GenusID == 0 {
|
||||||
|
sv["Genus"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.SpeciesName == "" {
|
||||||
|
sv["Species"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sv) > 0 {
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SpeciesBase is what the DB expects for write operations.
|
// SpeciesBase is what the DB expects for write operations.
|
||||||
type SpeciesBase struct {
|
type SpeciesBase struct {
|
||||||
ID int64 `db:"id" json:"id"`
|
ID int64 `db:"id" json:"id"`
|
||||||
|
|
|
@ -39,6 +39,24 @@ func (s *StrainBase) DeleteError() error {
|
||||||
return errors.ErrStrainNotDeleted
|
return errors.ErrStrainNotDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StrainBase) validate() types.ValidationError {
|
||||||
|
sv := make(types.ValidationError, 0)
|
||||||
|
|
||||||
|
if s.SpeciesID == 0 {
|
||||||
|
sv["Species"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.StrainName == "" {
|
||||||
|
sv["Name"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sv) > 0 {
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// StrainBase is what the DB expects for write operations.
|
// StrainBase is what the DB expects for write operations.
|
||||||
type StrainBase struct {
|
type StrainBase struct {
|
||||||
ID int64 `db:"id" json:"id"`
|
ID int64 `db:"id" json:"id"`
|
||||||
|
|
|
@ -2,7 +2,6 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/thermokarst/bactdb/Godeps/_workspace/src/github.com/jmoiron/modl"
|
"github.com/thermokarst/bactdb/Godeps/_workspace/src/github.com/jmoiron/modl"
|
||||||
|
@ -40,6 +39,33 @@ func (u *UserBase) DeleteError() error {
|
||||||
return errors.ErrUserNotDeleted
|
return errors.ErrUserNotDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserBase) validate() types.ValidationError {
|
||||||
|
uv := make(types.ValidationError, 0)
|
||||||
|
|
||||||
|
if u.Name == "" {
|
||||||
|
uv["Name"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Email == "" {
|
||||||
|
uv["Email"] = []string{helpers.MustProvideAValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
regex, _ := regexp.Compile(`(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`)
|
||||||
|
if u.Email != "" && !regex.MatchString(u.Email) {
|
||||||
|
uv["Email"] = []string{"Must provide a valid email address"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(u.Password) < 8 {
|
||||||
|
uv["Password"] = []string{"Password must be at least 8 characters"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(uv) > 0 {
|
||||||
|
return uv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UserBase is what the DB expects to see for write operations.
|
// UserBase is what the DB expects to see for write operations.
|
||||||
type UserBase struct {
|
type UserBase struct {
|
||||||
ID int64 `json:"id,omitempty"`
|
ID int64 `json:"id,omitempty"`
|
||||||
|
@ -68,17 +94,6 @@ type UserValidation struct {
|
||||||
Role []string `json:"role,omitempty"`
|
Role []string `json:"role,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the JSON-encoded error response for any validation errors.
|
|
||||||
func (uv UserValidation) Error() string {
|
|
||||||
errs, err := json.Marshal(struct {
|
|
||||||
UserValidation `json:"errors"`
|
|
||||||
}{uv})
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return string(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Users are multiple user entities.
|
// Users are multiple user entities.
|
||||||
type Users []*User
|
type Users []*User
|
||||||
|
|
||||||
|
@ -87,38 +102,6 @@ type UserMeta struct {
|
||||||
CanAdd bool `json:"canAdd"`
|
CanAdd bool `json:"canAdd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a user record.
|
|
||||||
func (u *User) Validate() error {
|
|
||||||
var uv UserValidation
|
|
||||||
validationError := false
|
|
||||||
|
|
||||||
if u.Name == "" {
|
|
||||||
uv.Name = append(uv.Name, helpers.MustProvideAValue)
|
|
||||||
validationError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Email == "" {
|
|
||||||
uv.Email = append(uv.Email, helpers.MustProvideAValue)
|
|
||||||
validationError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
regex, _ := regexp.Compile(`(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`)
|
|
||||||
if u.Email != "" && !regex.MatchString(u.Email) {
|
|
||||||
uv.Email = append(uv.Email, "Must provide a valid email address")
|
|
||||||
validationError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(u.Password) < 8 {
|
|
||||||
uv.Password = append(uv.Password, "Password must be at least 8 characters")
|
|
||||||
validationError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if validationError {
|
|
||||||
return uv
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DbAuthenticate authenticates a user.
|
// DbAuthenticate authenticates a user.
|
||||||
// For thermokarst/jwt: authentication callback
|
// For thermokarst/jwt: authentication callback
|
||||||
func DbAuthenticate(email string, password string) error {
|
func DbAuthenticate(email string, password string) error {
|
||||||
|
|
17
types/validation-error.go
Normal file
17
types/validation-error.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type ValidationError map[string][]string
|
||||||
|
|
||||||
|
func (v ValidationError) Error() string {
|
||||||
|
errs, err := json.Marshal(struct {
|
||||||
|
ValidationError `json:"errors"`
|
||||||
|
}{v})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(errs)
|
||||||
|
}
|
Reference in a new issue