Enhance user filtering
New response structure Use query-builder Refactor user service & repo
This commit is contained in:
parent
4433ef2c37
commit
70cac41579
@ -701,6 +701,42 @@
|
||||
"name": "email",
|
||||
"required": false,
|
||||
"title": "Search email to match against users"
|
||||
},
|
||||
{
|
||||
"name": "kind",
|
||||
"type": "types.UserKind",
|
||||
"required": false,
|
||||
"title": "Kind (normal, bot)"
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "incDeleted",
|
||||
"required": false,
|
||||
"title": "Include deleted users (requires 'access' permission)"
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "incSuspended",
|
||||
"required": false,
|
||||
"title": "Include suspended users (requires 'access' permission)"
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"title": "Sort by (createdAt, updatedAt, deletedAt, suspendedAt, email, username, userID)"
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"type": "uint",
|
||||
"required": false,
|
||||
"title": "Page number (0 based)"
|
||||
},
|
||||
{
|
||||
"name": "perPage",
|
||||
"type": "uint",
|
||||
"required": false,
|
||||
"title": "Returned items per page"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -40,6 +40,42 @@
|
||||
"required": false,
|
||||
"title": "Search email to match against users",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "kind",
|
||||
"required": false,
|
||||
"title": "Kind (normal, bot)",
|
||||
"type": "types.UserKind"
|
||||
},
|
||||
{
|
||||
"name": "incDeleted",
|
||||
"required": false,
|
||||
"title": "Include deleted users (requires 'access' permission)",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "incSuspended",
|
||||
"required": false,
|
||||
"title": "Include suspended users (requires 'access' permission)",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"required": false,
|
||||
"title": "Sort by (createdAt, updatedAt, deletedAt, suspendedAt, email, username, userID)",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"title": "Page number (0 based)",
|
||||
"type": "uint"
|
||||
},
|
||||
{
|
||||
"name": "perPage",
|
||||
"required": false,
|
||||
"title": "Returned items per page",
|
||||
"type": "uint"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -750,6 +750,12 @@ An organisation may have many roles. Roles may have many channels available. Acc
|
||||
| query | string | GET | Search query to match against users | N/A | NO |
|
||||
| username | string | GET | Search username to match against users | N/A | NO |
|
||||
| email | string | GET | Search email to match against users | N/A | NO |
|
||||
| kind | types.UserKind | GET | Kind (normal, bot) | N/A | NO |
|
||||
| incDeleted | bool | GET | Include deleted users (requires 'access' permission) | N/A | NO |
|
||||
| incSuspended | bool | GET | Include suspended users (requires 'access' permission) | N/A | NO |
|
||||
| sort | string | GET | Sort by (createdAt, updatedAt, deletedAt, suspendedAt, email, username, userID) | N/A | NO |
|
||||
| page | uint | GET | Page number (0 based) | N/A | NO |
|
||||
| perPage | uint | GET | Returned items per page | N/A | NO |
|
||||
|
||||
## Create user
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ func parseUInt64(s string) uint64 {
|
||||
return i
|
||||
}
|
||||
|
||||
// parseUInt64 parses a string to uint64
|
||||
// parseUInt parses a string to uint64
|
||||
func parseUint(s string) uint {
|
||||
if s == "" {
|
||||
return 0
|
||||
|
||||
@ -32,11 +32,11 @@ func Users(ctx context.Context) *cobra.Command {
|
||||
)
|
||||
|
||||
userRepo := repository.User(ctx, db)
|
||||
uf := &types.UserFilter{
|
||||
OrderBy: "updated_at",
|
||||
uf := types.UserFilter{
|
||||
Sort: "updatedAt",
|
||||
}
|
||||
|
||||
users, err := userRepo.Find(uf)
|
||||
users, _, err := userRepo.Find(uf)
|
||||
if err != nil {
|
||||
cli.HandleError(err)
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ const (
|
||||
ErrApplicationNotFound = repositoryError("ApplicationNotFound")
|
||||
)
|
||||
|
||||
// @todo migrate to same pattern as we have for users
|
||||
func Application(ctx context.Context, db *factory.DB) ApplicationRepository {
|
||||
return (&application{}).With(ctx, db)
|
||||
}
|
||||
|
||||
@ -106,11 +106,12 @@ func (mr *MockUserRepositoryMockRecorder) FindByIDs(id ...interface{}) *gomock.C
|
||||
}
|
||||
|
||||
// Find mocks base method
|
||||
func (m *MockUserRepository) Find(filter *types.UserFilter) ([]*types.User, error) {
|
||||
func (m *MockUserRepository) Find(filter types.UserFilter) (types.UserSet, types.UserFilter, error) {
|
||||
ret := m.ctrl.Call(m, "Find", filter)
|
||||
ret0, _ := ret[0].([]*types.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
ret0, _ := ret[0].(types.UserSet)
|
||||
ret1, _ := ret[1].(types.UserFilter)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// Find indicates an expected call of Find
|
||||
|
||||
@ -36,6 +36,7 @@ const (
|
||||
ErrOrganisationNotFound = repositoryError("OrganisationNotFound")
|
||||
)
|
||||
|
||||
// @todo migrate to same pattern as we have for users
|
||||
func Organisation(ctx context.Context, db *factory.DB) OrganisationRepository {
|
||||
return (&organisation{}).With(ctx, db)
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/titpetric/factory"
|
||||
squirrel "gopkg.in/Masterminds/squirrel.v1"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/auth"
|
||||
)
|
||||
@ -45,3 +46,73 @@ func (r *repository) db() *factory.DB {
|
||||
}
|
||||
return DB(r.ctx)
|
||||
}
|
||||
|
||||
func (r repository) fetchOne(one interface{}, q squirrel.SelectBuilder) (err error) {
|
||||
var (
|
||||
sql string
|
||||
args []interface{}
|
||||
)
|
||||
|
||||
if sql, args, err = q.ToSql(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.db().Get(one, sql, args...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Fetches single row from table
|
||||
func (r repository) fetchSet(set interface{}, q squirrel.SelectBuilder) (err error) {
|
||||
var (
|
||||
sql string
|
||||
args []interface{}
|
||||
)
|
||||
|
||||
if sql, args, err = q.ToSql(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.db().Select(set, sql, args...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Fetches paged rows
|
||||
func (r repository) fetchPaged(set interface{}, q squirrel.SelectBuilder, page, perPage uint) error {
|
||||
if perPage > 0 {
|
||||
q = q.Limit(uint64(perPage))
|
||||
}
|
||||
|
||||
if page > 0 {
|
||||
q = q.Offset(uint64(page * perPage))
|
||||
}
|
||||
|
||||
if sqlSelect, argsSelect, err := q.ToSql(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return r.db().Select(set, sqlSelect, argsSelect...)
|
||||
}
|
||||
}
|
||||
|
||||
// Counts all rows that match conditions from given query builder
|
||||
func (r repository) count(q squirrel.SelectBuilder) (uint, error) {
|
||||
var (
|
||||
count uint
|
||||
cq = q.Column("COUNT(*)")
|
||||
)
|
||||
|
||||
if sqlSelect, argsSelect, err := cq.ToSql(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
if err := r.db().Get(&count, sqlSelect, argsSelect...); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@ const (
|
||||
ErrRoleNotFound = repositoryError("RoleNotFound")
|
||||
)
|
||||
|
||||
// @todo migrate to same pattern as we have for uselang/en.jsonrs
|
||||
func Role(ctx context.Context, db *factory.DB) RoleRepository {
|
||||
return (&role{}).With(ctx, db)
|
||||
}
|
||||
|
||||
@ -2,12 +2,11 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/titpetric/factory"
|
||||
"gopkg.in/Masterminds/squirrel.v1"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
@ -20,7 +19,7 @@ type (
|
||||
FindByUsername(username string) (*types.User, error)
|
||||
FindByID(id uint64) (*types.User, error)
|
||||
FindByIDs(id ...uint64) (types.UserSet, error)
|
||||
Find(filter *types.UserFilter) ([]*types.User, error)
|
||||
Find(filter types.UserFilter) (set types.UserSet, f types.UserFilter, err error)
|
||||
Total() uint
|
||||
|
||||
Create(mod *types.User) (*types.User, error)
|
||||
@ -35,19 +34,10 @@ type (
|
||||
|
||||
user struct {
|
||||
*repository
|
||||
|
||||
// sql table reference
|
||||
users string
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
sqlUserColumns = "id, email, username, name, handle, " +
|
||||
"meta, rel_organisation, email_confirmed, " +
|
||||
"created_at, updated_at, suspended_at, deleted_at"
|
||||
sqlUserScope = "suspended_at IS NULL AND deleted_at IS NULL"
|
||||
sqlUserSelect = "SELECT " + sqlUserColumns + " FROM %s WHERE " + sqlUserScope
|
||||
|
||||
ErrUserNotFound = repositoryError("UserNotFound")
|
||||
)
|
||||
|
||||
@ -55,109 +45,149 @@ func User(ctx context.Context, db *factory.DB) UserRepository {
|
||||
return (&user{}).With(ctx, db)
|
||||
}
|
||||
|
||||
func (r user) table() string {
|
||||
return "sys_user"
|
||||
}
|
||||
|
||||
func (r user) columns() []string {
|
||||
return []string{
|
||||
"u.id",
|
||||
"u.email",
|
||||
"u.username",
|
||||
"u.name",
|
||||
"u.handle",
|
||||
"u.meta",
|
||||
"u.kind",
|
||||
"u.rel_organisation",
|
||||
"u.email_confirmed",
|
||||
"u.created_at",
|
||||
"u.updated_at",
|
||||
"u.suspended_at",
|
||||
"u.deleted_at",
|
||||
}
|
||||
}
|
||||
|
||||
func (r user) query() squirrel.SelectBuilder {
|
||||
return r.queryNoFilter().Where("u.deleted_at IS NULL AND u.suspended_at IS NULL")
|
||||
}
|
||||
|
||||
func (r user) queryNoFilter() squirrel.SelectBuilder {
|
||||
return squirrel.
|
||||
Select().
|
||||
From(r.table() + " AS u").
|
||||
Columns(r.columns()...)
|
||||
}
|
||||
|
||||
func (r *user) With(ctx context.Context, db *factory.DB) UserRepository {
|
||||
return &user{
|
||||
repository: r.repository.With(ctx, db),
|
||||
users: "sys_user",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *user) FindByUsername(username string) (*types.User, error) {
|
||||
sql := fmt.Sprintf(sqlUserSelect, r.users) + " AND username = ?"
|
||||
mod := &types.User{}
|
||||
func (r user) findBy(field string, value interface{}) (*types.User, error) {
|
||||
var (
|
||||
query = r.query().Where("u."+field+" = ?", value)
|
||||
u = &types.User{}
|
||||
)
|
||||
|
||||
return mod, isFound(r.db().Get(mod, sql, username), mod.ID > 0, ErrUserNotFound)
|
||||
return u, isFound(r.fetchOne(u, query), u.ID > 0, ErrUserNotFound)
|
||||
}
|
||||
|
||||
func (r *user) FindByEmail(email string) (*types.User, error) {
|
||||
sql := fmt.Sprintf(sqlUserSelect, r.users) + " AND email = ?"
|
||||
mod := &types.User{}
|
||||
|
||||
return mod, isFound(r.db().Get(mod, sql, email), mod.ID > 0, ErrUserNotFound)
|
||||
func (r user) FindByUsername(username string) (*types.User, error) {
|
||||
return r.findBy("username", username)
|
||||
}
|
||||
|
||||
func (r *user) FindByID(id uint64) (*types.User, error) {
|
||||
sql := fmt.Sprintf(sqlUserSelect, r.users) + " AND id = ?"
|
||||
mod := &types.User{}
|
||||
if err := isFound(r.db().Get(mod, sql, id), mod.ID > 0, ErrUserNotFound); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mod, nil
|
||||
func (r user) FindByEmail(email string) (*types.User, error) {
|
||||
return r.findBy("email", email)
|
||||
}
|
||||
|
||||
func (r *user) FindByIDs(IDs ...uint64) (uu types.UserSet, err error) {
|
||||
func (r user) FindByID(id uint64) (*types.User, error) {
|
||||
return r.findBy("id", id)
|
||||
}
|
||||
|
||||
func (r user) FindByIDs(IDs ...uint64) (types.UserSet, error) {
|
||||
if len(IDs) == 0 {
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf(sqlUserSelect, r.users) + " AND id IN (?)"
|
||||
|
||||
if sql, args, err := sqlx.In(sql, IDs); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return uu, r.db().Select(&uu, sql, args...)
|
||||
}
|
||||
var (
|
||||
query = r.query().Where("u.id IN (?)", IDs)
|
||||
uu = types.UserSet{}
|
||||
)
|
||||
|
||||
return uu, r.fetchSet(&uu, query)
|
||||
}
|
||||
|
||||
func (r *user) Find(filter *types.UserFilter) ([]*types.User, error) {
|
||||
if filter == nil {
|
||||
filter = &types.UserFilter{}
|
||||
func (r user) Find(filter types.UserFilter) (set types.UserSet, f types.UserFilter, err error) {
|
||||
f = filter
|
||||
q := r.queryNoFilter()
|
||||
|
||||
if !f.IncDeleted {
|
||||
q = q.Where("u.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
rval := make([]*types.User, 0)
|
||||
params := make([]interface{}, 0)
|
||||
sql := fmt.Sprintf(sqlUserSelect, r.users)
|
||||
|
||||
if filter.Query != "" {
|
||||
sql += " AND (username LIKE ?"
|
||||
params = append(params, filter.Query+"%")
|
||||
sql += " OR email LIKE ?"
|
||||
params = append(params, filter.Query+"%")
|
||||
sql += " OR name LIKE ?)"
|
||||
params = append(params, filter.Query+"%")
|
||||
if !f.IncSuspended {
|
||||
q = q.Where("u.suspended_at IS NULL")
|
||||
}
|
||||
|
||||
if filter.Email != "" {
|
||||
sql += " AND (email = ?)"
|
||||
params = append(params, filter.Email)
|
||||
if f.Query != "" {
|
||||
qs := f.Query + "%"
|
||||
q = q.Where("u.username LIKE ? OR u.email LIKE ? OR u.name LIKE ?", qs, qs, qs)
|
||||
}
|
||||
|
||||
if filter.Username != "" {
|
||||
sql += " AND (username = ?)"
|
||||
params = append(params, filter.Username)
|
||||
if f.Email != "" {
|
||||
q = q.Where("u.email = ?", f.Email)
|
||||
}
|
||||
|
||||
switch filter.OrderBy {
|
||||
case "updated_at", "createdAt":
|
||||
sql += " ORDER BY updated_at DESC"
|
||||
if f.Username != "" {
|
||||
q = q.Where("u.username = ?", f.Username)
|
||||
}
|
||||
|
||||
if f.Kind != "" {
|
||||
q = q.Where("u.kind = ?", f.Kind)
|
||||
}
|
||||
|
||||
if f.Email != "" {
|
||||
q = q.Where("u.email = ?", f.Email)
|
||||
}
|
||||
|
||||
// @todo add support for more sophisticated sorting through ql
|
||||
// refactor github.com/cortezaproject/corteza-server/compose/internal/repository/ql
|
||||
// for common use (out of compose pkg)
|
||||
switch f.Sort {
|
||||
case "createdAt":
|
||||
q = q.OrderBy("created_at")
|
||||
case "updatedAt":
|
||||
q = q.OrderBy("updated_at")
|
||||
case "deletedAt":
|
||||
q = q.OrderBy("deleted_at")
|
||||
case "suspendedAt":
|
||||
q = q.OrderBy("suspended_at")
|
||||
case "email", "username":
|
||||
q = q.OrderBy(f.Sort)
|
||||
case "userID":
|
||||
q = q.OrderBy("id")
|
||||
default:
|
||||
sql += " ORDER BY username ASC"
|
||||
q = q.OrderBy("id")
|
||||
}
|
||||
|
||||
if err := r.db().Select(&rval, sql, params...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rval, nil
|
||||
return set, f, r.fetchPaged(&set, q, f.Page, f.PerPage)
|
||||
}
|
||||
|
||||
func (r user) Total() (count uint) {
|
||||
query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s", r.users, sqlUserScope)
|
||||
_ = r.db().Get(&count, query)
|
||||
count, _ = r.count(r.query())
|
||||
return
|
||||
}
|
||||
|
||||
func (r *user) Create(mod *types.User) (*types.User, error) {
|
||||
mod.ID = factory.Sonyflake.NextID()
|
||||
mod.CreatedAt = time.Now()
|
||||
return mod, r.db().Insert(r.users, mod)
|
||||
return mod, r.db().Insert(r.table(), mod)
|
||||
}
|
||||
|
||||
func (r *user) Update(mod *types.User) (*types.User, error) {
|
||||
mod.UpdatedAt = timeNowPtr()
|
||||
return mod, r.db().Replace(r.users, mod)
|
||||
return mod, r.db().Replace(r.table(), mod)
|
||||
}
|
||||
|
||||
func (r *user) BindAvatar(user *types.User, avatar io.Reader) (*types.User, error) {
|
||||
@ -170,13 +200,13 @@ func (r *user) BindAvatar(user *types.User, avatar io.Reader) (*types.User, erro
|
||||
}
|
||||
|
||||
func (r *user) SuspendByID(id uint64) error {
|
||||
return r.updateColumnByID(r.users, "suspend_at", time.Now(), id)
|
||||
return r.updateColumnByID(r.table(), "suspend_at", time.Now(), id)
|
||||
}
|
||||
|
||||
func (r *user) UnsuspendByID(id uint64) error {
|
||||
return r.updateColumnByID(r.users, "suspend_at", nil, id)
|
||||
return r.updateColumnByID(r.table(), "suspend_at", nil, id)
|
||||
}
|
||||
|
||||
func (r *user) DeleteByID(id uint64) error {
|
||||
return r.updateColumnByID(r.users, "deleted_at", time.Now(), id)
|
||||
return r.updateColumnByID(r.table(), "deleted_at", time.Now(), id)
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ type (
|
||||
}
|
||||
|
||||
userAccessController interface {
|
||||
CanAccess(context.Context) bool
|
||||
CanCreateUser(context.Context) bool
|
||||
CanUpdateUser(context.Context, *types.User) bool
|
||||
CanDeleteUser(context.Context, *types.User) bool
|
||||
@ -42,7 +43,7 @@ type (
|
||||
FindByEmail(email string) (*types.User, error)
|
||||
FindByID(id uint64) (*types.User, error)
|
||||
FindByIDs(id ...uint64) (types.UserSet, error)
|
||||
Find(filter *types.UserFilter) (types.UserSet, error)
|
||||
Find(types.UserFilter) (types.UserSet, types.UserFilter, error)
|
||||
|
||||
Create(input *types.User) (*types.User, error)
|
||||
Update(mod *types.User) (*types.User, error)
|
||||
@ -103,8 +104,14 @@ func (svc user) FindByUsername(username string) (*types.User, error) {
|
||||
return svc.user.FindByUsername(username)
|
||||
}
|
||||
|
||||
func (svc user) Find(filter *types.UserFilter) (types.UserSet, error) {
|
||||
return svc.user.Find(filter)
|
||||
func (svc user) Find(f types.UserFilter) (types.UserSet, types.UserFilter, error) {
|
||||
if f.IncDeleted || f.IncSuspended {
|
||||
if !svc.ac.CanAccess(svc.ctx) {
|
||||
return nil, f, ErrNoPermissions.withStack()
|
||||
}
|
||||
}
|
||||
|
||||
return svc.user.Find(f)
|
||||
}
|
||||
|
||||
func (svc user) Create(input *types.User) (out *types.User, err error) {
|
||||
|
||||
@ -34,9 +34,15 @@ var _ = multipart.FileHeader{}
|
||||
|
||||
// User list request parameters
|
||||
type UserList struct {
|
||||
Query string
|
||||
Username string
|
||||
Email string
|
||||
Query string
|
||||
Username string
|
||||
Email string
|
||||
Kind types.UserKind
|
||||
IncDeleted bool
|
||||
IncSuspended bool
|
||||
Sort string
|
||||
Page uint
|
||||
PerPage uint
|
||||
}
|
||||
|
||||
func NewUserList() *UserList {
|
||||
@ -49,6 +55,12 @@ func (r UserList) Auditable() map[string]interface{} {
|
||||
out["query"] = r.Query
|
||||
out["username"] = r.Username
|
||||
out["email"] = r.Email
|
||||
out["kind"] = r.Kind
|
||||
out["incDeleted"] = r.IncDeleted
|
||||
out["incSuspended"] = r.IncSuspended
|
||||
out["sort"] = r.Sort
|
||||
out["page"] = r.Page
|
||||
out["perPage"] = r.PerPage
|
||||
|
||||
return out
|
||||
}
|
||||
@ -89,6 +101,24 @@ func (r *UserList) Fill(req *http.Request) (err error) {
|
||||
if val, ok := get["email"]; ok {
|
||||
r.Email = val
|
||||
}
|
||||
if val, ok := get["kind"]; ok {
|
||||
r.Kind = types.UserKind(val)
|
||||
}
|
||||
if val, ok := get["incDeleted"]; ok {
|
||||
r.IncDeleted = parseBool(val)
|
||||
}
|
||||
if val, ok := get["incSuspended"]; ok {
|
||||
r.IncSuspended = parseBool(val)
|
||||
}
|
||||
if val, ok := get["sort"]; ok {
|
||||
r.Sort = val
|
||||
}
|
||||
if val, ok := get["page"]; ok {
|
||||
r.Page = parseUint(val)
|
||||
}
|
||||
if val, ok := get["perPage"]; ok {
|
||||
r.PerPage = parseUint(val)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -28,6 +28,15 @@ func parseInt(s string) int {
|
||||
return i
|
||||
}
|
||||
|
||||
// parseUInt parses a string to uint64
|
||||
func parseUint(s string) uint {
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
i, _ := strconv.ParseUint(s, 10, 32)
|
||||
return uint(i)
|
||||
}
|
||||
|
||||
// parseInt64 parses a string to int64
|
||||
func parseInt64(s string) int64 {
|
||||
if s == "" {
|
||||
|
||||
@ -17,6 +17,11 @@ type (
|
||||
User struct {
|
||||
user service.UserService
|
||||
}
|
||||
|
||||
userSetPayload struct {
|
||||
Filter types.UserFilter `json:"filter"`
|
||||
Set types.UserSet `json:"set"`
|
||||
}
|
||||
)
|
||||
|
||||
func (User) New() *User {
|
||||
@ -25,50 +30,65 @@ func (User) New() *User {
|
||||
return ctrl
|
||||
}
|
||||
|
||||
// Searches the users table in the database to find users by matching (by-prefix) their.Username
|
||||
func (ctrl *User) List(ctx context.Context, r *request.UserList) (interface{}, error) {
|
||||
return ctrl.user.With(ctx).Find(&types.UserFilter{
|
||||
Query: r.Query,
|
||||
Email: r.Email,
|
||||
Username: r.Username,
|
||||
})
|
||||
func (ctrl User) List(ctx context.Context, r *request.UserList) (interface{}, error) {
|
||||
f := types.UserFilter{
|
||||
Query: r.Query,
|
||||
Email: r.Email,
|
||||
Username: r.Username,
|
||||
Kind: r.Kind,
|
||||
IncSuspended: r.IncSuspended,
|
||||
IncDeleted: r.IncDeleted,
|
||||
Page: r.Page,
|
||||
PerPage: r.PerPage,
|
||||
}
|
||||
|
||||
set, filter, err := ctrl.user.With(ctx).Find(f)
|
||||
return ctrl.makeFilterPayload(ctx, set, filter, err)
|
||||
}
|
||||
|
||||
func (ctrl *User) Create(ctx context.Context, r *request.UserCreate) (interface{}, error) {
|
||||
func (ctrl User) Create(ctx context.Context, r *request.UserCreate) (interface{}, error) {
|
||||
user := &types.User{
|
||||
Email: r.Email,
|
||||
Name: r.Name,
|
||||
Handle: r.Handle,
|
||||
Kind: types.UserKind(r.Kind),
|
||||
Kind: r.Kind,
|
||||
}
|
||||
|
||||
return ctrl.user.With(ctx).Create(user)
|
||||
}
|
||||
|
||||
func (ctrl *User) Update(ctx context.Context, r *request.UserUpdate) (interface{}, error) {
|
||||
func (ctrl User) Update(ctx context.Context, r *request.UserUpdate) (interface{}, error) {
|
||||
user := &types.User{
|
||||
ID: r.UserID,
|
||||
Email: r.Email,
|
||||
Name: r.Name,
|
||||
Handle: r.Handle,
|
||||
Kind: types.UserKind(r.Kind),
|
||||
Kind: r.Kind,
|
||||
}
|
||||
|
||||
return ctrl.user.With(ctx).Update(user)
|
||||
}
|
||||
|
||||
func (ctrl *User) Read(ctx context.Context, r *request.UserRead) (interface{}, error) {
|
||||
func (ctrl User) Read(ctx context.Context, r *request.UserRead) (interface{}, error) {
|
||||
return ctrl.user.With(ctx).FindByID(r.UserID)
|
||||
}
|
||||
|
||||
func (ctrl *User) Delete(ctx context.Context, r *request.UserDelete) (interface{}, error) {
|
||||
func (ctrl User) Delete(ctx context.Context, r *request.UserDelete) (interface{}, error) {
|
||||
return resputil.OK(), ctrl.user.With(ctx).Delete(r.UserID)
|
||||
}
|
||||
|
||||
func (ctrl *User) Suspend(ctx context.Context, r *request.UserSuspend) (interface{}, error) {
|
||||
func (ctrl User) Suspend(ctx context.Context, r *request.UserSuspend) (interface{}, error) {
|
||||
return resputil.OK(), ctrl.user.With(ctx).Suspend(r.UserID)
|
||||
}
|
||||
|
||||
func (ctrl *User) Unsuspend(ctx context.Context, r *request.UserUnsuspend) (interface{}, error) {
|
||||
func (ctrl User) Unsuspend(ctx context.Context, r *request.UserUnsuspend) (interface{}, error) {
|
||||
return resputil.OK(), ctrl.user.With(ctx).Unsuspend(r.UserID)
|
||||
}
|
||||
|
||||
func (ctrl User) makeFilterPayload(ctx context.Context, uu types.UserSet, f types.UserFilter, err error) (*userSetPayload, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &userSetPayload{Filter: f, Set: uu}, nil
|
||||
}
|
||||
|
||||
@ -43,10 +43,17 @@ type (
|
||||
}
|
||||
|
||||
UserFilter struct {
|
||||
Query string
|
||||
Email string
|
||||
Username string
|
||||
OrderBy string
|
||||
Query string `json:"query"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
Kind UserKind `json:"kind"`
|
||||
IncDeleted bool `json:"incDeleted"`
|
||||
IncSuspended bool `json:"incSuspended"`
|
||||
|
||||
Page uint `json:"page"`
|
||||
PerPage uint `json:"perPage"`
|
||||
Sort string `json:"sort"`
|
||||
Count uint `json:"count"`
|
||||
}
|
||||
|
||||
UserKind string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user