SAM types, handlers, services, repositories implemented
This commit is contained in:
@@ -411,6 +411,8 @@
|
||||
"fields": [
|
||||
{ "type": "uint64", "name": "ID" },
|
||||
{ "type": "string", "name": "Username" },
|
||||
{ "type": "interface{}", "name": "Meta", "tag": "json:\"-\"" },
|
||||
{ "type": "uint64", "name": "OrganisationID", "dbname": "rel_organisation" },
|
||||
{ "type": "[]byte", "name": "Password", "tag": "json:\"-\"", "complex": true },
|
||||
{ "type": "*time.Time", "name": "SuspendedAt", "tag": "json:\",omitempty\"", "complex": true },
|
||||
{ "type": "*time.Time", "name": "DeletedAt", "tag": "json:\",omitempty\"", "complex": true }
|
||||
|
||||
@@ -13,6 +13,16 @@
|
||||
"name": "Username",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "Meta",
|
||||
"tag": "json:\"-\"",
|
||||
"type": "interface{}"
|
||||
},
|
||||
{
|
||||
"dbname": "rel_organisation",
|
||||
"name": "OrganisationID",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"complex": true,
|
||||
"name": "Password",
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/titpetric/factory"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sqlChannelScope = "deleted_at IS NULL AND archived_at IS NULL"
|
||||
|
||||
ErrChannelNotFound = repositoryError("ChannelNotFound")
|
||||
)
|
||||
|
||||
type (
|
||||
channel struct{}
|
||||
)
|
||||
|
||||
func Channel() channel {
|
||||
return channel{}
|
||||
}
|
||||
|
||||
func (r channel) FindById(ctx context.Context, id uint64) (*types.Channel, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod := &types.Channel{}
|
||||
if err := db.Get(mod, "SELECT * FROM channels WHERE id = ? AND "+sqlChannelScope, id); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else if mod.ID == 0 {
|
||||
return nil, ErrChannelNotFound
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r channel) Find(ctx context.Context, filter *types.ChannelFilter) ([]*types.Channel, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
var params = make([]interface{}, 0)
|
||||
sql := "SELECT * FROM channels WHERE " + sqlChannelScope
|
||||
|
||||
if filter != nil {
|
||||
if filter.Query != "" {
|
||||
sql += "AND name LIKE ?"
|
||||
params = append(params, filter.Query+"%")
|
||||
}
|
||||
}
|
||||
|
||||
sql += " ORDER BY name ASC"
|
||||
|
||||
rval := make([]*types.Channel, 0)
|
||||
if err := db.Select(&rval, sql, params...); err != nil {
|
||||
panic(err)
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return rval, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r channel) Create(ctx context.Context, mod *types.Channel) (*types.Channel, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod.SetID(factory.Sonyflake.NextID())
|
||||
if err := db.Insert("channels", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r channel) Update(ctx context.Context, mod *types.Channel) (*types.Channel, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
if err := db.Replace("channels", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r channel) Archive(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "channels", "archived_at", time.Now(), id)
|
||||
}
|
||||
|
||||
func (r channel) Unarchive(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "channels", "archived_at", nil, id)
|
||||
}
|
||||
|
||||
func (r channel) Delete(ctx context.Context, id uint64) error {
|
||||
return simpleDelete(ctx, "channels", id)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package repository
|
||||
|
||||
type (
|
||||
repositoryError string
|
||||
)
|
||||
|
||||
const (
|
||||
ErrDatabaseError = repositoryError("DatabaseError")
|
||||
ErrNotImplemented = repositoryError("NotImplemented")
|
||||
)
|
||||
|
||||
func (e repositoryError) Error() string {
|
||||
return "crust.sam.repository." + string(e)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/titpetric/factory"
|
||||
)
|
||||
|
||||
func simpleUpdate(ctx context.Context, tableName, columnName string, value interface{}, id uint64) error {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return ErrDatabaseError
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf("UPDATE %s SET %s = ? WHERE id = ?", tableName, columnName)
|
||||
|
||||
if _, err := db.Exec(sql, value, id); err != nil {
|
||||
return ErrDatabaseError
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func simpleDelete(ctx context.Context, tableName string, id uint64) error {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return ErrDatabaseError
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf("DELETE %s WHERE id = ?", tableName)
|
||||
|
||||
if _, err := db.Exec(sql, id); err != nil {
|
||||
return ErrDatabaseError
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/titpetric/factory"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sqlOrganisationScope = "deleted_at IS NULL AND archived_at IS NULL"
|
||||
|
||||
ErrOrganisationNotFound = repositoryError("OrganisationNotFound")
|
||||
)
|
||||
|
||||
type (
|
||||
organisation struct{}
|
||||
)
|
||||
|
||||
func Organisation() organisation {
|
||||
return organisation{}
|
||||
}
|
||||
|
||||
func (r organisation) FindById(ctx context.Context, id uint64) (*types.Organisation, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod := &types.Organisation{}
|
||||
if err := db.Get(mod, "SELECT * FROM organisations WHERE id = ? AND "+sqlOrganisationScope, id); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else if mod.ID == 0 {
|
||||
return nil, ErrOrganisationNotFound
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r organisation) Find(ctx context.Context, filter *types.OrganisationFilter) ([]*types.Organisation, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
var params = make([]interface{}, 0)
|
||||
sql := "SELECT * FROM organisations WHERE " + sqlOrganisationScope
|
||||
|
||||
if filter != nil {
|
||||
if filter.Query != "" {
|
||||
sql += "AND ame LIKE ?"
|
||||
params = append(params, filter.Query+"%")
|
||||
}
|
||||
}
|
||||
|
||||
sql += " ORDER BY name ASC"
|
||||
|
||||
rval := make([]*types.Organisation, 0)
|
||||
if err := db.Select(&rval, sql, params...); err != nil {
|
||||
panic(err)
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return rval, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r organisation) Create(ctx context.Context, mod *types.Organisation) (*types.Organisation, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod.SetID(factory.Sonyflake.NextID())
|
||||
if err := db.Insert("organisations", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r organisation) Update(ctx context.Context, mod *types.Organisation) (*types.Organisation, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
if err := db.Replace("organisations", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r organisation) Archive(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "organisations", "archived_at", time.Now(), id)
|
||||
}
|
||||
|
||||
func (r organisation) Unarchive(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "organisations", "archived_at", nil, id)
|
||||
}
|
||||
|
||||
func (r organisation) Delete(ctx context.Context, id uint64) error {
|
||||
return simpleDelete(ctx, "organisations", id)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/titpetric/factory"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sqlTeamScope = "deleted_at IS NULL AND archived_at IS NULL"
|
||||
|
||||
ErrTeamNotFound = repositoryError("TeamNotFound")
|
||||
)
|
||||
|
||||
type (
|
||||
team struct{}
|
||||
)
|
||||
|
||||
func Team() team {
|
||||
return team{}
|
||||
}
|
||||
|
||||
func (r team) FindById(ctx context.Context, id uint64) (*types.Team, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod := &types.Team{}
|
||||
if err := db.Get(mod, "SELECT * FROM teams WHERE id = ? AND "+sqlTeamScope, id); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else if mod.ID == 0 {
|
||||
return nil, ErrTeamNotFound
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r team) Find(ctx context.Context, filter *types.TeamFilter) ([]*types.Team, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
var params = make([]interface{}, 0)
|
||||
sql := "SELECT * FROM teams WHERE " + sqlTeamScope
|
||||
|
||||
if filter != nil {
|
||||
if filter.Query != "" {
|
||||
sql += "AND name LIKE ?"
|
||||
params = append(params, filter.Query+"%")
|
||||
}
|
||||
}
|
||||
|
||||
sql += " ORDER BY name ASC"
|
||||
|
||||
rval := make([]*types.Team, 0)
|
||||
if err := db.Select(&rval, sql, params...); err != nil {
|
||||
panic(err)
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return rval, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r team) Create(ctx context.Context, mod *types.Team) (*types.Team, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod.SetID(factory.Sonyflake.NextID())
|
||||
if err := db.Insert("teams", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r team) Update(ctx context.Context, mod *types.Team) (*types.Team, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
if err := db.Replace("teams", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r team) Archive(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "teams", "archived_at", time.Now(), id)
|
||||
}
|
||||
|
||||
func (r team) Unarchive(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "teams", "archived_at", nil, id)
|
||||
}
|
||||
|
||||
func (r team) Delete(ctx context.Context, id uint64) error {
|
||||
return simpleDelete(ctx, "teams", id)
|
||||
}
|
||||
|
||||
func (r team) Merge(ctx context.Context, id, targetTeamId uint64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (r team) Move(ctx context.Context, id, targetOrganisationId uint64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/titpetric/factory"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sqlUserScope = "suspended_at IS NULL AND deleted_at IS NULL"
|
||||
sqlUserSelect = "SELECT * FROM users WHERE " + sqlUserScope
|
||||
|
||||
ErrUserNotFound = repositoryError("UserNotFound")
|
||||
)
|
||||
|
||||
type (
|
||||
user struct{}
|
||||
)
|
||||
|
||||
func User() user {
|
||||
return user{}
|
||||
}
|
||||
|
||||
func (r user) FindByUsername(ctx context.Context, username string) (*types.User, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod := &types.User{}
|
||||
if err := db.Get(mod, "SELECT * FROM users WHERE username = ? AND "+sqlUserScope, username); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else if mod.ID == 0 {
|
||||
return nil, ErrUserNotFound
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r user) FindById(ctx context.Context, id uint64) (*types.User, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod := &types.User{}
|
||||
if err := db.Get(mod, "SELECT * FROM users WHERE id = ? AND "+sqlUserScope, id); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else if mod.ID == 0 {
|
||||
return nil, ErrUserNotFound
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r user) Find(ctx context.Context, filter *types.UserFilter) ([]*types.User, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
var params = make([]interface{}, 0)
|
||||
sql := "SELECT * FROM users WHERE " + sqlUserScope
|
||||
|
||||
if filter != nil {
|
||||
if filter.Query != "" {
|
||||
sql += "AND username LIKE ?"
|
||||
params = append(params, filter.Query+"%")
|
||||
}
|
||||
}
|
||||
|
||||
sql += " ORDER BY username ASC"
|
||||
|
||||
rval := make([]*types.User, 0)
|
||||
if err := db.Select(&rval, sql, params...); err != nil {
|
||||
panic(err)
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return rval, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r user) Create(ctx context.Context, mod *types.User) (*types.User, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
mod.SetID(factory.Sonyflake.NextID())
|
||||
if err := db.Insert("users", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r user) Update(ctx context.Context, mod *types.User) (*types.User, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
}
|
||||
|
||||
if err := db.Replace("users", mod); err != nil {
|
||||
return nil, ErrDatabaseError
|
||||
} else {
|
||||
return mod, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r user) Suspend(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "users", "suspend_at", time.Now(), id)
|
||||
}
|
||||
|
||||
func (r user) Unsuspend(ctx context.Context, id uint64) error {
|
||||
return simpleUpdate(ctx, "users", "suspend_at", nil, id)
|
||||
}
|
||||
|
||||
func (r user) Delete(ctx context.Context, id uint64) error {
|
||||
return simpleDelete(ctx, "users", id)
|
||||
}
|
||||
+31
-98
@@ -3,133 +3,66 @@ package rest
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/sam/rest/server"
|
||||
"github.com/crusttech/crust/sam/service"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
const (
|
||||
sqlChannelScope = "deleted_at IS NULL AND archived_at IS NULL"
|
||||
sqlChannelSelect = "SELECT * FROM channels WHERE " + sqlChannelScope
|
||||
)
|
||||
|
||||
type Channel struct{}
|
||||
|
||||
func (Channel) New() *Channel {
|
||||
return &Channel{}
|
||||
}
|
||||
|
||||
func (*Channel) Create(ctx context.Context, r *server.ChannelCreateRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
type (
|
||||
Channel struct {
|
||||
service channelService
|
||||
}
|
||||
|
||||
// @todo: topic message/log entry
|
||||
// @todo: channel name cmessage/log entry
|
||||
// @todo: permission check if user can add channel
|
||||
channelService interface {
|
||||
FindById(context.Context, uint64) (*types.Channel, error)
|
||||
Find(context.Context, *types.ChannelFilter) ([]*types.Channel, error)
|
||||
|
||||
c := types.Channel{}.
|
||||
Create(context.Context, *types.Channel) (*types.Channel, error)
|
||||
Update(context.Context, *types.Channel) (*types.Channel, error)
|
||||
|
||||
deleter
|
||||
archiver
|
||||
}
|
||||
)
|
||||
|
||||
func (Channel) New() *Channel {
|
||||
return &Channel{service: service.Channel()}
|
||||
}
|
||||
|
||||
func (ctrl *Channel) Create(ctx context.Context, r *server.ChannelCreateRequest) (interface{}, error) {
|
||||
channel := types.Channel{}.
|
||||
New().
|
||||
SetName(r.Name).
|
||||
SetTopic(r.Topic).
|
||||
SetMeta([]byte("{}")).
|
||||
SetID(factory.Sonyflake.NextID())
|
||||
|
||||
return c, db.Insert("channels", c)
|
||||
return ctrl.service.Create(ctx, channel)
|
||||
}
|
||||
|
||||
func (*Channel) Edit(ctx context.Context, r *server.ChannelEditRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*var c *types.Channel
|
||||
if c, err = c.load(r.ID); err != nil {
|
||||
return nil, err
|
||||
}*/
|
||||
|
||||
// @todo: topic change message/log entry
|
||||
// @todo: channel name change message/log entry
|
||||
// @todo: permission check if user can edit channel
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
// @todo: handle channel moving
|
||||
// @todo: handle channel archiving
|
||||
|
||||
c := types.Channel{}.
|
||||
func (ctrl *Channel) Edit(ctx context.Context, r *server.ChannelEditRequest) (interface{}, error) {
|
||||
channel := types.Channel{}.
|
||||
New().
|
||||
SetName(r.Name).
|
||||
SetTopic(r.Topic)
|
||||
|
||||
return c, db.Replace("channels", c)
|
||||
return ctrl.service.Update(ctx, channel)
|
||||
|
||||
}
|
||||
|
||||
func (*Channel) Delete(ctx context.Context, r *server.ChannelDeleteRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*var c *types.Channel
|
||||
if c, err = c.load(r.ID); err != nil {
|
||||
return nil, err
|
||||
}*/
|
||||
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that channel has been removed (remove from web UI)
|
||||
// @todo: permissions check if user cah remove channel
|
||||
|
||||
stmt := "UPDATE channels SET deleted_at = NOW() WHERE id = ? AND deleted_at IS NULL"
|
||||
spew.Dump(r.ID)
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
func (ctrl *Channel) Delete(ctx context.Context, r *server.ChannelDeleteRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Delete(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (s *Channel) Read(ctx context.Context, r *server.ChannelReadRequest) (interface{}, error) {
|
||||
return s.load(r.ID)
|
||||
func (ctrl *Channel) Read(ctx context.Context, r *server.ChannelReadRequest) (interface{}, error) {
|
||||
return ctrl.service.FindById(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (*Channel) List(ctx context.Context, r *server.ChannelListRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permission check to return only channels that user has access to
|
||||
// @todo: actual searching not just a full select
|
||||
|
||||
res := make([]types.Channel, 0)
|
||||
err = db.Select(&res, sqlChannelSelect+" ORDER BY name ASC")
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (*Channel) load(id uint64) (*types.Channel, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := types.Channel{}.New()
|
||||
|
||||
if id == 0 {
|
||||
return nil, errors.New("Provide channel ID")
|
||||
} else if err := db.Get(c, sqlChannelSelect+" AND id = ?", id); err != nil {
|
||||
return nil, err
|
||||
} else if c.ID != id {
|
||||
spew.Dump(c)
|
||||
return nil, errors.New("Unexisting channel")
|
||||
}
|
||||
|
||||
// @todo: permission check if user can read channel
|
||||
|
||||
return c, nil
|
||||
func (ctrl *Channel) List(ctx context.Context, r *server.ChannelListRequest) (interface{}, error) {
|
||||
return ctrl.service.Find(ctx, &types.ChannelFilter{Query: r.Query})
|
||||
}
|
||||
|
||||
+35
-83
@@ -2,111 +2,63 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/sam/rest/server"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
const (
|
||||
sqlOrganisationScope = "deleted_at IS NULL AND archived_at IS NULL"
|
||||
sqlOrganisationSelect = "SELECT * FROM organisations WHERE " + sqlOrganisationScope
|
||||
)
|
||||
type (
|
||||
Organisation struct {
|
||||
service organisationService
|
||||
}
|
||||
|
||||
type Organisation struct{}
|
||||
organisationService interface {
|
||||
FindById(context.Context, uint64) (*types.Organisation, error)
|
||||
Find(context.Context, *types.OrganisationFilter) ([]*types.Organisation, error)
|
||||
|
||||
Create(context.Context, *types.Organisation) (*types.Organisation, error)
|
||||
Update(context.Context, *types.Organisation) (*types.Organisation, error)
|
||||
|
||||
deleter
|
||||
archiver
|
||||
}
|
||||
)
|
||||
|
||||
func (Organisation) New() *Organisation {
|
||||
return &Organisation{}
|
||||
}
|
||||
|
||||
func (*Organisation) Create(ctx context.Context, r *server.OrganisationCreateRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permission check if user can add/edit organisation
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
o := types.Organisation{}.New().SetName(r.Name).SetID(factory.Sonyflake.NextID())
|
||||
return o, db.Insert("organisation", o)
|
||||
func (ctrl *Organisation) Read(ctx context.Context, r *server.OrganisationReadRequest) (interface{}, error) {
|
||||
return ctrl.service.FindById(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (*Organisation) Edit(ctx context.Context, r *server.OrganisationEditRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permission check if user can add/edit organisation
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
o := types.Organisation{}.New().SetID(r.ID).SetName(r.Name)
|
||||
return o, db.Replace("organisation", o)
|
||||
func (ctrl *Organisation) List(ctx context.Context, r *server.OrganisationListRequest) (interface{}, error) {
|
||||
return ctrl.service.Find(ctx, &types.OrganisationFilter{Query: r.Query})
|
||||
}
|
||||
|
||||
func (*Organisation) Remove(ctx context.Context, r *server.OrganisationRemoveRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (ctrl *Organisation) Create(ctx context.Context, r *server.OrganisationCreateRequest) (interface{}, error) {
|
||||
org := types.Organisation{}.
|
||||
New().
|
||||
SetName(r.Name)
|
||||
|
||||
// @todo: permission check
|
||||
// @todo: don't actually delete organisation?!
|
||||
|
||||
stmt := "UPDATE organisationss SET deleted_at = NOW() WHERE deleted_at IS NULL AND id = ?"
|
||||
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
return ctrl.service.Create(ctx, org)
|
||||
}
|
||||
|
||||
func (*Organisation) Read(ctx context.Context, r *server.OrganisationReadRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (ctrl *Organisation) Edit(ctx context.Context, r *server.OrganisationEditRequest) (interface{}, error) {
|
||||
org := types.Organisation{}.
|
||||
New().
|
||||
SetID(r.ID).
|
||||
SetName(r.Name)
|
||||
|
||||
// @todo: permissions check
|
||||
|
||||
o := types.Organisation{}.New()
|
||||
return o, db.Get(o, sqlOrganisationSelect+" AND id = ?", r.ID)
|
||||
return ctrl.service.Update(ctx, org)
|
||||
}
|
||||
|
||||
func (*Organisation) List(ctx context.Context, r *server.OrganisationListRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permissions check
|
||||
// @todo: actual search for org
|
||||
|
||||
res := make([]Organisation, 0)
|
||||
err = db.Select(&res, sqlOrganisationSelect+" WHERE label LIKE = ? ORDER BY label ASC", r.Query+"%")
|
||||
return res, err
|
||||
func (ctrl *Organisation) Remove(ctx context.Context, r *server.OrganisationRemoveRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Delete(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (*Organisation) Archive(ctx context.Context, r *server.OrganisationArchiveRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permission check
|
||||
|
||||
stmt := fmt.Sprintf(
|
||||
"UPDATE organisation SET archived_at = NOW() WHERE %s AND id = ?",
|
||||
sqlChannelScope)
|
||||
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
func (ctrl *Organisation) Archive(ctx context.Context, r *server.OrganisationArchiveRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Archive(ctx, r.ID)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
suspender interface {
|
||||
Suspend(context.Context, uint64) error
|
||||
Unsuspend(context.Context, uint64) error
|
||||
}
|
||||
archiver interface {
|
||||
Archive(context.Context, uint64) error
|
||||
Unarchive(context.Context, uint64) error
|
||||
}
|
||||
|
||||
deleter interface {
|
||||
Delete(context.Context, uint64) error
|
||||
}
|
||||
)
|
||||
+41
-79
@@ -2,111 +2,73 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/sam/rest/server"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
const (
|
||||
sqlTeamScope = "deleted_at IS NULL AND archived_at IS NULL"
|
||||
sqlTeamSelect = "SELECT * FROM teams WHERE " + sqlTeamScope
|
||||
)
|
||||
type (
|
||||
Team struct {
|
||||
service teamService
|
||||
}
|
||||
|
||||
type Team struct{}
|
||||
teamService interface {
|
||||
FindById(context.Context, uint64) (*types.Team, error)
|
||||
Find(context.Context, *types.TeamFilter) ([]*types.Team, error)
|
||||
|
||||
Create(context.Context, *types.Team) (*types.Team, error)
|
||||
Update(context.Context, *types.Team) (*types.Team, error)
|
||||
Merge(context.Context, *types.Team) error
|
||||
Move(context.Context, *types.Team) error
|
||||
|
||||
deleter
|
||||
archiver
|
||||
}
|
||||
)
|
||||
|
||||
func (Team) New() *Team {
|
||||
return &Team{}
|
||||
}
|
||||
|
||||
func (*Team) Create(ctx context.Context, r *server.TeamCreateRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permission check if user can add/edit the team
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
t := types.Team{}.New()
|
||||
t.SetName(r.Name).SetMemberIDs(r.Members).SetID(factory.Sonyflake.NextID())
|
||||
return t, db.Insert("team", t)
|
||||
func (ctrl *Team) Read(ctx context.Context, r *server.TeamReadRequest) (interface{}, error) {
|
||||
return ctrl.service.FindById(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (*Team) Edit(ctx context.Context, r *server.TeamEditRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @todo: permission check if user can add/edit the team
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
t := types.Team{}.New()
|
||||
t.SetID(r.ID).SetName(r.Name).SetMemberIDs(r.Members)
|
||||
return t, db.Replace("team", t)
|
||||
func (ctrl *Team) List(ctx context.Context, r *server.TeamListRequest) (interface{}, error) {
|
||||
return ctrl.service.Find(ctx, &types.TeamFilter{Query: r.Query})
|
||||
}
|
||||
|
||||
func (*Team) Remove(ctx context.Context, r *server.TeamRemoveRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (ctrl *Team) Create(ctx context.Context, r *server.TeamCreateRequest) (interface{}, error) {
|
||||
org := types.Team{}.
|
||||
New().
|
||||
SetName(r.Name)
|
||||
|
||||
stmt := "UPDATE teams SET deleted_at = NOW() WHERE deleted_at IS NULL AND id = ?"
|
||||
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
return ctrl.service.Create(ctx, org)
|
||||
}
|
||||
|
||||
func (*Team) Read(ctx context.Context, r *server.TeamReadRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (ctrl *Team) Edit(ctx context.Context, r *server.TeamEditRequest) (interface{}, error) {
|
||||
org := types.Team{}.
|
||||
New().
|
||||
SetID(r.ID).
|
||||
SetName(r.Name)
|
||||
|
||||
t := types.Team{}.New()
|
||||
return t, db.Get(t, sqlTeamSelect+" AND id = ?", r.ID)
|
||||
return ctrl.service.Update(ctx, org)
|
||||
}
|
||||
|
||||
func (*Team) List(ctx context.Context, r *server.TeamListRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]Team, 0)
|
||||
err = db.Select(&res, sqlTeamSelect+" ORDER BY name ASC")
|
||||
return res, err
|
||||
func (ctrl *Team) Remove(ctx context.Context, r *server.TeamRemoveRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Delete(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (*Team) Archive(ctx context.Context, r *server.TeamArchiveRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt := fmt.Sprintf(
|
||||
"UPDATE teams SET archived_at = NOW() WHERE %s AND id = ?",
|
||||
sqlTeamScope)
|
||||
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
func (ctrl *Team) Archive(ctx context.Context, r *server.TeamArchiveRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Archive(ctx, r.ID)
|
||||
}
|
||||
|
||||
func (*Team) Move(ctx context.Context, r *server.TeamMoveRequest) (interface{}, error) {
|
||||
return nil, errors.New("Not implemented: Team.move")
|
||||
func (ctrl *Team) Merge(ctx context.Context, r *server.TeamMergeRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Merge(ctx, &types.Team{ID: r.ID})
|
||||
}
|
||||
|
||||
func (*Team) Merge(ctx context.Context, r *server.TeamMergeRequest) (interface{}, error) {
|
||||
return nil, errors.New("Not implemented: Team.merge")
|
||||
func (ctrl *Team) Move(ctx context.Context, r *server.TeamMoveRequest) (interface{}, error) {
|
||||
return nil, ctrl.service.Move(ctx, &types.Team{ID: r.ID})
|
||||
}
|
||||
|
||||
+17
-92
@@ -3,118 +3,43 @@ package rest
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/sam/rest/server"
|
||||
"github.com/crusttech/crust/sam/service"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
const (
|
||||
sqlUserScope = "suspended_at IS NULL AND archived_at IS NULL"
|
||||
sqlUserSelect = "SELECT * FROM users WHERE " + sqlUserScope
|
||||
)
|
||||
|
||||
type (
|
||||
User struct {
|
||||
service UserInterface
|
||||
service userService
|
||||
}
|
||||
|
||||
UserInterface interface {
|
||||
Set(*types.User)
|
||||
CanLogin() bool
|
||||
GeneratePassword(value string) ([]byte, error)
|
||||
ValidatePassword(value string) bool
|
||||
ValidateUser() bool
|
||||
userService interface {
|
||||
ValidateCredentials(context.Context, string, string) (*types.User, error)
|
||||
|
||||
FindById(context.Context, uint64) (*types.User, error)
|
||||
Find(context.Context, *types.UserFilter) ([]*types.User, error)
|
||||
|
||||
Create(context.Context, *types.User) (*types.User, error)
|
||||
Update(context.Context, *types.User) (*types.User, error)
|
||||
|
||||
deleter
|
||||
suspender
|
||||
}
|
||||
)
|
||||
|
||||
func (User) New() *User {
|
||||
return &User{service.User{}.New()}
|
||||
return &User{service: service.User()}
|
||||
}
|
||||
|
||||
/*
|
||||
func (self *User) Read(ctx context.Context, r *teamReadRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := types.User{}.New()
|
||||
return t, db.Get(t, sqlUserSelect+" AND id = ?", r.ID)
|
||||
}
|
||||
*/
|
||||
|
||||
// User lookup & login
|
||||
func (self *User) Login(ctx context.Context, r *server.UserLoginRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := types.User{}.New()
|
||||
if err = db.Get(u, sqlUserSelect+" AND username = ?", r.Username); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.service.Set(u)
|
||||
|
||||
if self.service.ValidateUser() || !self.service.ValidatePassword(r.Password) {
|
||||
return nil, errors.New("Invalid username and password combination")
|
||||
}
|
||||
|
||||
if !self.service.CanLogin() {
|
||||
return nil, errors.New("User is not allowed to login")
|
||||
}
|
||||
|
||||
return u, nil
|
||||
return self.service.ValidateCredentials(ctx, r.Username, r.Password)
|
||||
}
|
||||
|
||||
// Searches the users table in the database to find users by matching (by-prefix) their.Username
|
||||
func (*User) Search(ctx context.Context, r *server.UserSearchRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uu := []types.User{}
|
||||
if err != db.Get(uu, sqlUserSelect+" AND username LIKE ?", r.Query+"%") {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uu, nil
|
||||
func (self *User) Search(ctx context.Context, r *server.UserSearchRequest) (interface{}, error) {
|
||||
return self.service.Find(ctx, &types.UserFilter{Query: r.Query})
|
||||
}
|
||||
|
||||
/*
|
||||
func (self *User) Remove(ctx context.Context, r *teamRemoveRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt := "UPDATE users SET deleted_at = NOW() WHERE deleted_at IS NULL AND id = ?"
|
||||
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
}
|
||||
|
||||
func (self *User) Archive(ctx context.Context, r *teamArchiveRequest) (interface{}, error) {
|
||||
db, err := factory.Database.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt := fmt.Sprintf(
|
||||
"UPDATE users SET archived_at = NOW() WHERE %s AND id = ?",
|
||||
sqlUserScope)
|
||||
|
||||
return nil, func() error {
|
||||
_, err := db.Exec(stmt, r.ID)
|
||||
return err
|
||||
}()
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/repository"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
)
|
||||
|
||||
type (
|
||||
channel struct {
|
||||
repository channelRepository
|
||||
}
|
||||
|
||||
channelRepository interface {
|
||||
FindById(context.Context, uint64) (*types.Channel, error)
|
||||
Find(context.Context, *types.ChannelFilter) ([]*types.Channel, error)
|
||||
|
||||
Create(context.Context, *types.Channel) (*types.Channel, error)
|
||||
Update(context.Context, *types.Channel) (*types.Channel, error)
|
||||
|
||||
deleter
|
||||
archiver
|
||||
}
|
||||
)
|
||||
|
||||
func Channel() *channel {
|
||||
return &channel{repository: repository.Channel()}
|
||||
}
|
||||
|
||||
func (svc channel) FindById(ctx context.Context, id uint64) (*types.Channel, error) {
|
||||
// @todo: permission check if current user can read channel
|
||||
return svc.repository.FindById(ctx, id)
|
||||
}
|
||||
|
||||
func (svc channel) Find(ctx context.Context, filter *types.ChannelFilter) ([]*types.Channel, error) {
|
||||
// @todo: permission check to return only channels that channel has access to
|
||||
// @todo: actual searching not just a full select
|
||||
return svc.repository.Find(ctx, filter)
|
||||
}
|
||||
|
||||
func (svc channel) Create(ctx context.Context, mod *types.Channel) (*types.Channel, error) {
|
||||
// @todo: topic message/log entry
|
||||
// @todo: channel name cmessage/log entry
|
||||
// @todo: permission check if channel can add channel
|
||||
|
||||
return svc.repository.Create(ctx, mod)
|
||||
}
|
||||
|
||||
func (svc channel) Update(ctx context.Context, mod *types.Channel) (*types.Channel, error) {
|
||||
// @todo: topic change message/log entry
|
||||
// @todo: channel name change message/log entry
|
||||
// @todo: permission check if current user can edit channel
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
// @todo: handle channel movinga
|
||||
// @todo: handle channel archiving
|
||||
|
||||
return svc.repository.Update(ctx, mod)
|
||||
}
|
||||
|
||||
func (svc channel) Delete(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that channel has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can remove channel
|
||||
return svc.repository.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (svc channel) Archive(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that channel has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can remove channel
|
||||
return svc.repository.Archive(ctx, id)
|
||||
}
|
||||
|
||||
func (svc channel) Unarchive(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that channel has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can remove channel
|
||||
return svc.repository.Unarchive(ctx, id)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package service
|
||||
|
||||
type (
|
||||
serviceError string
|
||||
)
|
||||
|
||||
func (e serviceError) Error() string {
|
||||
return "crust.sam.service." + string(e)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/repository"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
)
|
||||
|
||||
type (
|
||||
organisation struct {
|
||||
repository organisationRepository
|
||||
}
|
||||
|
||||
organisationRepository interface {
|
||||
FindById(context.Context, uint64) (*types.Organisation, error)
|
||||
Find(context.Context, *types.OrganisationFilter) ([]*types.Organisation, error)
|
||||
|
||||
Create(context.Context, *types.Organisation) (*types.Organisation, error)
|
||||
Update(context.Context, *types.Organisation) (*types.Organisation, error)
|
||||
|
||||
deleter
|
||||
archiver
|
||||
}
|
||||
)
|
||||
|
||||
func Organisation() *organisation {
|
||||
return &organisation{repository: repository.Organisation()}
|
||||
}
|
||||
|
||||
func (svc organisation) FindById(ctx context.Context, id uint64) (*types.Organisation, error) {
|
||||
// @todo: permission check if current user can read organisation
|
||||
return svc.repository.FindById(ctx, id)
|
||||
}
|
||||
|
||||
func (svc organisation) Find(ctx context.Context, filter *types.OrganisationFilter) ([]*types.Organisation, error) {
|
||||
// @todo: permission check to return only organisations that organisation has access to
|
||||
// @todo: actual searching not just a full select
|
||||
return svc.repository.Find(ctx, filter)
|
||||
}
|
||||
|
||||
func (svc organisation) Create(ctx context.Context, mod *types.Organisation) (*types.Organisation, error) {
|
||||
// @todo: permission check if current user can add/edit organisation
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
return svc.repository.Create(ctx, mod)
|
||||
}
|
||||
|
||||
func (svc organisation) Update(ctx context.Context, mod *types.Organisation) (*types.Organisation, error) {
|
||||
// @todo: permission check if current user can add/edit organisation
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
return svc.repository.Update(ctx, mod)
|
||||
}
|
||||
|
||||
func (svc organisation) Delete(ctx context.Context, id uint64) error {
|
||||
// @todo: permissions check if current user can remove organisation
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that organisation has been removed (remove from web UI)
|
||||
return svc.repository.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (svc organisation) Archive(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that organisation has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can archive organisation
|
||||
return svc.repository.Archive(ctx, id)
|
||||
}
|
||||
|
||||
func (svc organisation) Unarchive(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that organisation has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can unarchive organisation
|
||||
return svc.repository.Unarchive(ctx, id)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
suspender interface {
|
||||
Suspend(context.Context, uint64) error
|
||||
Unsuspend(context.Context, uint64) error
|
||||
}
|
||||
archiver interface {
|
||||
Archive(context.Context, uint64) error
|
||||
Unarchive(context.Context, uint64) error
|
||||
}
|
||||
|
||||
deleter interface {
|
||||
Delete(context.Context, uint64) error
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,85 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/repository"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
)
|
||||
|
||||
type (
|
||||
team struct {
|
||||
repository teamRepository
|
||||
}
|
||||
|
||||
teamRepository interface {
|
||||
FindById(context.Context, uint64) (*types.Team, error)
|
||||
Find(context.Context, *types.TeamFilter) ([]*types.Team, error)
|
||||
|
||||
Create(context.Context, *types.Team) (*types.Team, error)
|
||||
Update(context.Context, *types.Team) (*types.Team, error)
|
||||
|
||||
Merge(context.Context, uint64, uint64) error
|
||||
Move(context.Context, uint64, uint64) error
|
||||
|
||||
deleter
|
||||
archiver
|
||||
}
|
||||
)
|
||||
|
||||
func Team() *team {
|
||||
return &team{repository: repository.Team()}
|
||||
}
|
||||
|
||||
func (svc team) FindById(ctx context.Context, id uint64) (*types.Team, error) {
|
||||
// @todo: permission check if current user has access to this team
|
||||
return svc.repository.FindById(ctx, id)
|
||||
}
|
||||
|
||||
func (svc team) Find(ctx context.Context, filter *types.TeamFilter) ([]*types.Team, error) {
|
||||
// @todo: permission check to return only teams that current user has access to
|
||||
return svc.repository.Find(ctx, filter)
|
||||
}
|
||||
|
||||
func (svc team) Create(ctx context.Context, mod *types.Team) (*types.Team, error) {
|
||||
// @todo: permission check if current user can add/edit team
|
||||
|
||||
return svc.repository.Create(ctx, mod)
|
||||
}
|
||||
|
||||
func (svc team) Update(ctx context.Context, mod *types.Team) (*types.Team, error) {
|
||||
// @todo: permission check if current user can add/edit team
|
||||
// @todo: make sure archived & deleted entries can not be edited
|
||||
|
||||
return svc.repository.Update(ctx, mod)
|
||||
}
|
||||
|
||||
func (svc team) Delete(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that team has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can remove team
|
||||
return svc.repository.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (svc team) Archive(ctx context.Context, id uint64) error {
|
||||
// @todo: make history unavailable
|
||||
// @todo: notify users that team has been removed (remove from web UI)
|
||||
// @todo: permissions check if current user can remove team
|
||||
return svc.repository.Archive(ctx, id)
|
||||
}
|
||||
|
||||
func (svc team) Unarchive(ctx context.Context, id uint64) error {
|
||||
// @todo: permissions check if current user can unarchive team
|
||||
// @todo: make history accessible
|
||||
// @todo: notify users that team has been unarchived
|
||||
return svc.repository.Unarchive(ctx, id)
|
||||
}
|
||||
|
||||
func (svc team) Merge(ctx context.Context, id uint64, targetTeamId uint64) error {
|
||||
// @todo: permission check if current user can merge team
|
||||
return svc.repository.Merge(ctx, id, targetTeamId)
|
||||
}
|
||||
|
||||
func (svc team) Move(ctx context.Context, id uint64, targetOrganisationId uint64) error {
|
||||
// @todo: permission check if current user can move team to another organisation
|
||||
return svc.repository.Move(ctx, id, targetOrganisationId)
|
||||
}
|
||||
+86
-16
@@ -1,35 +1,105 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"context"
|
||||
"github.com/crusttech/crust/sam/repository"
|
||||
"github.com/crusttech/crust/sam/types"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
*types.User
|
||||
const (
|
||||
ErrUserInvalidCredentials = serviceError("UserInvalidCredentials")
|
||||
ErrUserLocked = serviceError("UserLocked")
|
||||
)
|
||||
|
||||
type (
|
||||
user struct {
|
||||
repository userRepository
|
||||
}
|
||||
|
||||
userRepository interface {
|
||||
FindByUsername(context.Context, string) (*types.User, error)
|
||||
FindById(context.Context, uint64) (*types.User, error)
|
||||
Find(context.Context, *types.UserFilter) ([]*types.User, error)
|
||||
|
||||
Create(context.Context, *types.User) (*types.User, error)
|
||||
Update(context.Context, *types.User) (*types.User, error)
|
||||
|
||||
deleter
|
||||
suspender
|
||||
}
|
||||
)
|
||||
|
||||
func User() *user {
|
||||
return &user{repository: repository.User()}
|
||||
}
|
||||
|
||||
func (User) New() *User {
|
||||
return &User{types.User{}.New()}
|
||||
func (svc user) ValidateCredentials(ctx context.Context, username, password string) (*types.User, error) {
|
||||
user, err := svc.repository.FindByUsername(ctx, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !svc.validatePassword(user, password) {
|
||||
return nil, ErrUserInvalidCredentials
|
||||
}
|
||||
|
||||
if !svc.canLogin(user) {
|
||||
return nil, ErrUserLocked
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (u *User) Set(user *types.User) {
|
||||
u.User = user
|
||||
func (svc user) FindById(ctx context.Context, id uint64) (*types.User, error) {
|
||||
return svc.repository.FindById(ctx, id)
|
||||
}
|
||||
|
||||
func (u *User) CanLogin() bool {
|
||||
return u.User != nil && u.User.ID > 0
|
||||
func (svc user) Find(ctx context.Context, filter *types.UserFilter) ([]*types.User, error) {
|
||||
return svc.repository.Find(ctx, filter)
|
||||
}
|
||||
|
||||
func (u *User) GeneratePassword(value string) ([]byte, error) {
|
||||
return bcrypt.GenerateFromPassword([]byte(value), bcrypt.DefaultCost)
|
||||
func (svc user) Create(ctx context.Context, mod *types.User) (*types.User, error) {
|
||||
return svc.repository.Create(ctx, mod)
|
||||
}
|
||||
|
||||
func (u *User) ValidatePassword(value string) bool {
|
||||
return u.User != nil && bcrypt.CompareHashAndPassword(u.User.Password, []byte(value)) == nil
|
||||
func (svc user) Update(ctx context.Context, mod *types.User) (*types.User, error) {
|
||||
return svc.repository.Update(ctx, mod)
|
||||
}
|
||||
|
||||
func (u *User) ValidateUser() bool {
|
||||
return u.User != nil && u.User.ID > 0
|
||||
func (svc user) validatePassword(user *types.User, password string) bool {
|
||||
return user != nil &&
|
||||
bcrypt.CompareHashAndPassword(user.Password, []byte(password)) == nil
|
||||
}
|
||||
|
||||
func (svc user) generatePassword(user *types.User, password string) error {
|
||||
pwd, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.SetPassword(pwd)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc user) canLogin(u *types.User) bool {
|
||||
return u != nil && u.ID > 0 && u.SuspendedAt == nil && u.DeletedAt == nil
|
||||
}
|
||||
|
||||
func (svc user) Delete(ctx context.Context, id uint64) error {
|
||||
// @todo: permissions check if current user can delete this user
|
||||
// @todo: notify users that user has been removed (remove from web UI)
|
||||
return svc.repository.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (svc user) Suspend(ctx context.Context, id uint64) error {
|
||||
// @todo: permissions check if current user can suspend this user
|
||||
// @todo: notify users that user has been supsended (remove from web UI)
|
||||
return svc.repository.Suspend(ctx, id)
|
||||
}
|
||||
|
||||
func (svc user) Unsuspend(ctx context.Context, id uint64) error {
|
||||
// @todo: permissions check if current user can unsuspend this user
|
||||
// @todo: notify users that user has been unsuspended
|
||||
return svc.repository.Unsuspend(ctx, id)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
ChannelFilter struct {
|
||||
Query string
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
OrganisationFilter struct {
|
||||
Query string
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
TeamFilter struct {
|
||||
Query string
|
||||
}
|
||||
)
|
||||
+29
-5
@@ -22,11 +22,13 @@ import (
|
||||
type (
|
||||
// Users
|
||||
User struct {
|
||||
ID uint64 `db:"id"`
|
||||
Username string `db:"username"`
|
||||
Password []byte `json:"-" db:"password"`
|
||||
SuspendedAt *time.Time `json:",omitempty" db:"suspended_at"`
|
||||
DeletedAt *time.Time `json:",omitempty" db:"deleted_at"`
|
||||
ID uint64 `db:"id"`
|
||||
Username string `db:"username"`
|
||||
Meta interface{} `json:"-" db:"meta"`
|
||||
OrganisationID uint64 `db:"rel_organisation"`
|
||||
Password []byte `json:"-" db:"password"`
|
||||
SuspendedAt *time.Time `json:",omitempty" db:"suspended_at"`
|
||||
DeletedAt *time.Time `json:",omitempty" db:"deleted_at"`
|
||||
|
||||
changed []string
|
||||
}
|
||||
@@ -60,6 +62,28 @@ func (u *User) SetUsername(value string) *User {
|
||||
}
|
||||
return u
|
||||
}
|
||||
func (u *User) GetMeta() interface{} {
|
||||
return u.Meta
|
||||
}
|
||||
|
||||
func (u *User) SetMeta(value interface{}) *User {
|
||||
if u.Meta != value {
|
||||
u.changed = append(u.changed, "Meta")
|
||||
u.Meta = value
|
||||
}
|
||||
return u
|
||||
}
|
||||
func (u *User) GetOrganisationID() uint64 {
|
||||
return u.OrganisationID
|
||||
}
|
||||
|
||||
func (u *User) SetOrganisationID(value uint64) *User {
|
||||
if u.OrganisationID != value {
|
||||
u.changed = append(u.changed, "OrganisationID")
|
||||
u.OrganisationID = value
|
||||
}
|
||||
return u
|
||||
}
|
||||
func (u *User) GetPassword() []byte {
|
||||
return u.Password
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
type (
|
||||
UserFilter struct {
|
||||
Query string
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user