Complete User CRUD

This commit is contained in:
Deluan
2020-01-19 21:39:37 -05:00
parent 1c04a19910
commit 2ab0cecd48
11 changed files with 201 additions and 45 deletions
+128 -13
View File
@@ -1,12 +1,15 @@
package persistence
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/astaxie/beego/orm"
"github.com/cloudsonic/sonic-server/model"
"github.com/deluan/rest"
"github.com/google/uuid"
)
type resourceRepository struct {
@@ -20,19 +23,43 @@ type resourceRepository struct {
func NewResource(o orm.Ormer, model interface{}, mappedModel interface{}) model.ResourceRepository {
r := &resourceRepository{model: model, mappedModel: mappedModel, ormer: o}
r.instanceType = reflect.TypeOf(mappedModel)
// Get type of mappedModel (which is a *struct)
rv := reflect.ValueOf(mappedModel)
for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
rv = rv.Elem()
}
r.instanceType = rv.Type()
r.sliceType = reflect.SliceOf(r.instanceType)
return r
}
func (r *resourceRepository) newQuery() orm.QuerySeter {
return r.ormer.QueryTable(r.mappedModel)
func (r *resourceRepository) EntityName() string {
return r.instanceType.Name()
}
func (r *resourceRepository) newQuery(options ...rest.QueryOptions) orm.QuerySeter {
qs := r.ormer.QueryTable(r.mappedModel)
if len(options) > 0 {
qs = r.addOptions(qs, options)
qs = r.addFilters(qs, r.buildFilters(qs, options))
}
return qs
}
func (r *resourceRepository) NewInstance() interface{} {
return reflect.New(r.instanceType).Interface()
}
func (r *resourceRepository) NewSlice() interface{} {
slice := reflect.MakeSlice(r.sliceType, 0, 0)
x := reflect.New(slice.Type())
x.Elem().Set(slice)
return x.Interface()
}
func (r *resourceRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) {
qs := r.newQuery()
qs = r.addOptions(qs, options)
//qs = r.addFilters(qs, r.buildFilters(qs, options), r.getRestriction())
qs := r.newQuery(options...)
dataSet := r.NewSlice()
_, err := qs.All(dataSet)
if err == orm.ErrNoRows {
@@ -42,8 +69,7 @@ func (r *resourceRepository) ReadAll(options ...rest.QueryOptions) (interface{},
}
func (r *resourceRepository) Count(options ...rest.QueryOptions) (int64, error) {
qs := r.newQuery()
//qs = r.addFilters(qs, r.buildFilters(qs, options), r.getRestriction())
qs := r.newQuery(options...)
count, err := qs.Count()
if err == orm.ErrNoRows {
err = rest.ErrNotFound
@@ -51,11 +77,44 @@ func (r *resourceRepository) Count(options ...rest.QueryOptions) (int64, error)
return count, err
}
func (r *resourceRepository) NewSlice() interface{} {
slice := reflect.MakeSlice(r.sliceType, 0, 0)
x := reflect.New(slice.Type())
x.Elem().Set(slice)
return x.Interface()
func (r *resourceRepository) Read(id string) (interface{}, error) {
qs := r.newQuery().Filter("id", id)
data := r.NewInstance()
err := qs.One(data)
if err == orm.ErrNoRows {
return data, rest.ErrNotFound
}
return data, err
}
func setUUID(p interface{}) {
f := reflect.ValueOf(p).Elem().FieldByName("ID")
if f.Kind() == reflect.String {
id, _ := uuid.NewRandom()
f.SetString(id.String())
}
}
func (r *resourceRepository) Save(p interface{}) (string, error) {
setUUID(p)
id, err := r.ormer.Insert(p)
if err != nil {
if err.Error() != "LastInsertId is not supported by this driver" {
return "", err
}
}
return strconv.FormatInt(id, 10), nil
}
func (r *resourceRepository) Update(p interface{}, cols ...string) error {
count, err := r.ormer.Update(p, cols...)
if err != nil {
return err
}
if count == 0 {
return rest.ErrNotFound
}
return err
}
func (r *resourceRepository) addOptions(qs orm.QuerySeter, options []rest.QueryOptions) orm.QuerySeter {
@@ -87,3 +146,59 @@ func (r *resourceRepository) addOptions(qs orm.QuerySeter, options []rest.QueryO
}
return qs
}
func (r *resourceRepository) addFilters(qs orm.QuerySeter, conditions ...*orm.Condition) orm.QuerySeter {
var cond *orm.Condition
for _, c := range conditions {
if c != nil {
if cond == nil {
cond = c
} else {
cond = cond.AndCond(c)
}
}
}
if cond != nil {
return qs.SetCond(cond)
}
return qs
}
func unmarshalValue(val interface{}) string {
switch v := val.(type) {
case float64:
return strconv.FormatFloat(v, 'f', -1, 64)
case string:
return v
default:
return fmt.Sprintf("%v", val)
}
}
func (r *resourceRepository) buildFilters(qs orm.QuerySeter, options []rest.QueryOptions) *orm.Condition {
if len(options) == 0 {
return nil
}
cond := orm.NewCondition()
clauses := cond
for f, v := range options[0].Filters {
fn := strings.Replace(f, ".", "__", -1)
s := unmarshalValue(v)
if strings.HasSuffix(fn, "Id") || strings.HasSuffix(fn, "__id") {
clauses = IdFilter(clauses, fn, s)
} else {
clauses = StartsWithFilter(clauses, fn, s)
}
}
return clauses
}
func IdFilter(cond *orm.Condition, field, value string) *orm.Condition {
field = strings.TrimSuffix(field, "Id") + "__id"
return cond.And(field, value)
}
func StartsWithFilter(cond *orm.Condition, field, value string) *orm.Condition {
return cond.And(field+"__istartswith", value)
}
+8 -8
View File
@@ -7,14 +7,14 @@ import (
)
type user struct {
ID string `json:"id" orm:"pk;column(id)"`
Name string `json:"name" orm:"index"`
Password string `json:"-"`
IsAdmin bool `json:"isAdmin"`
LastLoginAt time.Time `json:"lastLoginAt"`
LastAccessAt time.Time `json:"lastAccessAt"`
CreatedAt time.Time `json:"createdAt" orm:"auto_now_add;type(datetime)"`
UpdatedAt time.Time `json:"updatedAt" orm:"auto_now;type(datetime)"`
ID string `json:"id" orm:"pk;column(id)"`
Name string `json:"name" orm:"index"`
Password string `json:"-"`
IsAdmin bool `json:"isAdmin"`
LastLoginAt *time.Time `json:"lastLoginAt" orm:"null"`
LastAccessAt *time.Time `json:"lastAccessAt" orm:"null"`
CreatedAt time.Time `json:"createdAt" orm:"auto_now_add;type(datetime)"`
UpdatedAt time.Time `json:"updatedAt" orm:"auto_now;type(datetime)"`
}
var _ = model.User(user{})