Add (created|updated|delete)_by fields on record
Move data manipulation from repo to service layer
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE `crm_record` CHANGE COLUMN `user_id` `owned_by` BIGINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `crm_record` ADD COLUMN `created_by` BIGINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `crm_record` ADD COLUMN `updated_by` BIGINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `crm_record` ADD COLUMN `deleted_by` BIGINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
UPDATE crm_record SET created_by = owned_by;
|
||||
UPDATE crm_record SET updated_by = owned_by WHERE updated_at IS NOT NULL;
|
||||
UPDATE crm_record SET deleted_by = owned_by WHERE deleted_at IS NOT NULL;
|
||||
+23
-12
@@ -3,7 +3,6 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lann/builder"
|
||||
@@ -27,10 +26,11 @@ type (
|
||||
|
||||
Create(record *types.Record) (*types.Record, error)
|
||||
Update(record *types.Record) (*types.Record, error)
|
||||
DeleteByID(id uint64) error
|
||||
Delete(record *types.Record) error
|
||||
|
||||
UpdateValues(recordID uint64, rvs types.RecordValueSet) (err error)
|
||||
LoadValues(IDs ...uint64) (rvs types.RecordValueSet, err error)
|
||||
DeleteValues(record *types.Record) error
|
||||
UpdateValues(recordID uint64, rvs types.RecordValueSet) (err error)
|
||||
}
|
||||
|
||||
FindResponseMeta struct {
|
||||
@@ -215,7 +215,7 @@ func (r *record) buildQuery(module *types.Module, filter string, sort string) (q
|
||||
), i.Value)
|
||||
}
|
||||
|
||||
// @todo switch value for ref when doing Record/User lookup
|
||||
// @todo switch value for ref when doing Record/Owner lookup
|
||||
i.Value = fmt.Sprintf("rv_%s.value", i.Value)
|
||||
|
||||
return i, nil
|
||||
@@ -258,7 +258,7 @@ func (r *record) buildQuery(module *types.Module, filter string, sort string) (q
|
||||
), i.Value)
|
||||
}
|
||||
|
||||
// @todo switch value for ref when doing Record/User lookup
|
||||
// @todo switch value for ref when doing Record/Owner lookup
|
||||
i.Value = fmt.Sprintf("rv_%s.value ", i.Value)
|
||||
|
||||
return i, nil
|
||||
@@ -266,6 +266,7 @@ func (r *record) buildQuery(module *types.Module, filter string, sort string) (q
|
||||
|
||||
if sc, err = sp.ParseColumns(sort); err != nil {
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
query = query.OrderBy(sc.Strings()...)
|
||||
@@ -276,8 +277,6 @@ func (r *record) buildQuery(module *types.Module, filter string, sort string) (q
|
||||
|
||||
func (r *record) Create(record *types.Record) (*types.Record, error) {
|
||||
record.ID = factory.Sonyflake.NextID()
|
||||
record.CreatedAt = time.Now()
|
||||
record.UserID = Identity(r.Context())
|
||||
|
||||
if err := r.db().Replace("crm_record", record); err != nil {
|
||||
return nil, errors.Wrap(err, "could not update record")
|
||||
@@ -287,9 +286,6 @@ func (r *record) Create(record *types.Record) (*types.Record, error) {
|
||||
}
|
||||
|
||||
func (r *record) Update(record *types.Record) (*types.Record, error) {
|
||||
now := time.Now()
|
||||
record.UpdatedAt = &now
|
||||
|
||||
if err := r.db().Replace("crm_record", record); err != nil {
|
||||
return nil, errors.Wrap(err, "could not update record")
|
||||
}
|
||||
@@ -297,8 +293,23 @@ func (r *record) Update(record *types.Record) (*types.Record, error) {
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (r *record) DeleteByID(id uint64) error {
|
||||
_, err := r.db().Exec("update crm_record set deleted_at=? where id=?", time.Now(), id)
|
||||
func (r *record) Delete(record *types.Record) error {
|
||||
_, err := r.db().Exec(
|
||||
"UPDATE crm_record SET deleted_at = ?, deleted_by = ? WHERE id = ?",
|
||||
record.DeletedAt,
|
||||
record.DeletedBy,
|
||||
record.ID,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *record) DeleteValues(record *types.Record) error {
|
||||
_, err := r.db().Exec(
|
||||
"UPDATE crm_record_value SET deleted_at = ? WHERE record_id = ?",
|
||||
record.DeletedAt,
|
||||
record.ID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ func (b *recordReportBuilder) Build(metrics, dimensions, filters string) (sql st
|
||||
), i.Value)
|
||||
}
|
||||
|
||||
// @todo switch value for ref when doing Record/User lookup
|
||||
// @todo switch value for ref when doing Record/Owner lookup
|
||||
i.Value = fmt.Sprintf("rv_%s.value", i.Value)
|
||||
|
||||
return i, nil
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -20,10 +18,6 @@ func DB(ctx context.Context) *factory.DB {
|
||||
return factory.Database.MustGet().With(ctx)
|
||||
}
|
||||
|
||||
func Identity(ctx context.Context) uint64 {
|
||||
return auth.GetIdentityFromContext(ctx).Identity()
|
||||
}
|
||||
|
||||
func (r *repository) With(ctx context.Context, db *factory.DB) *repository {
|
||||
return &repository{
|
||||
ctx: ctx,
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestUserRefExpanding(t *testing.T) {
|
||||
ntf := ¬ification{}
|
||||
|
||||
// msg := &gomail.Message{}
|
||||
usr := &types.User{ID: 72932592256548967, Email: "user@mock.ed", Name: "Mocked User"}
|
||||
usr := &types.User{ID: 72932592256548967, Email: "user@mock.ed", Name: "Mocked Owner"}
|
||||
|
||||
usrSvc := NewMocknotificationUserService(mockCtrl)
|
||||
usrSvc.EXPECT().FindByID(usr.ID).Times(1).Return(usr, nil)
|
||||
|
||||
+35
-33
@@ -3,12 +3,14 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/crm/repository"
|
||||
"github.com/crusttech/crust/crm/types"
|
||||
"github.com/crusttech/crust/internal/auth"
|
||||
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
)
|
||||
@@ -34,6 +36,7 @@ type (
|
||||
|
||||
Create(record *types.Record) (*types.Record, error)
|
||||
Update(record *types.Record) (*types.Record, error)
|
||||
|
||||
DeleteByID(recordID uint64) error
|
||||
|
||||
// Fields(module *types.Module, record *types.Record) ([]*types.RecordValue, error)
|
||||
@@ -67,10 +70,6 @@ func (svc *record) FindByID(recordID uint64) (r *types.Record, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.preloadUsers(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
@@ -108,10 +107,6 @@ func (svc *record) Find(moduleID uint64, filter, sort string, page, perPage int)
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.preloadUsers(rsp.Records...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
@@ -131,6 +126,9 @@ func (svc *record) Create(new *types.Record) (record *types.Record, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
new.OwnedBy = auth.GetIdentityFromContext(svc.ctx).Identity()
|
||||
new.CreatedBy = new.OwnedBy
|
||||
new.CreatedAt = time.Now()
|
||||
if record, err = svc.repository.Create(new); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -143,10 +141,6 @@ func (svc *record) Create(new *types.Record) (record *types.Record, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.preloadUsers(record); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
@@ -165,9 +159,6 @@ func (svc *record) Update(updated *types.Record) (record *types.Record, err erro
|
||||
return errors.Wrap(err, "nonexistent record")
|
||||
}
|
||||
|
||||
updated.CreatedAt = record.CreatedAt
|
||||
updated.UserID = record.UserID
|
||||
|
||||
if module, err = svc.moduleRepo.FindByID(updated.ModuleID); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -176,7 +167,11 @@ func (svc *record) Update(updated *types.Record) (record *types.Record, err erro
|
||||
return
|
||||
}
|
||||
|
||||
if record, err = svc.repository.Update(updated); err != nil {
|
||||
now := time.Now()
|
||||
record.UpdatedAt = &now
|
||||
record.UpdatedBy = auth.GetIdentityFromContext(svc.ctx).Identity()
|
||||
|
||||
if record, err = svc.repository.Update(record); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -184,10 +179,6 @@ func (svc *record) Update(updated *types.Record) (record *types.Record, err erro
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.preloadUsers(record); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
@@ -198,8 +189,30 @@ func (svc *record) Update(updated *types.Record) (record *types.Record, err erro
|
||||
// return s.repository.Fields(module, record)
|
||||
// }
|
||||
|
||||
func (svc *record) DeleteByID(id uint64) error {
|
||||
return svc.repository.DeleteByID(id)
|
||||
func (svc *record) DeleteByID(ID uint64) (err error) {
|
||||
err = svc.db.Transaction(func() (err error) {
|
||||
var record *types.Record
|
||||
|
||||
if record, err = svc.repository.FindByID(ID); err != nil {
|
||||
return errors.Wrap(err, "nonexistent record")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
record.DeletedAt = &now
|
||||
record.DeletedBy = auth.GetIdentityFromContext(svc.ctx).Identity()
|
||||
|
||||
if err = svc.repository.Delete(record); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = svc.repository.DeleteValues(record); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
return errors.Wrap(err, "unable to delete record")
|
||||
}
|
||||
|
||||
// Validates and filters record values
|
||||
@@ -248,14 +261,3 @@ func (svc *record) preloadValues(rr ...*types.Record) error {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *record) preloadUsers(rr ...*types.Record) error {
|
||||
if uu, err := svc.userSvc.FindByIDs(types.RecordSet(rr).UserIDs()...); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return types.RecordSet(rr).Walk(func(r *types.Record) error {
|
||||
r.User = uu.FindByID(r.UserID)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -1,14 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx/types"
|
||||
|
||||
systemTypes "github.com/crusttech/crust/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -17,16 +14,15 @@ type (
|
||||
ID uint64 `json:"recordID,string" db:"id"`
|
||||
ModuleID uint64 `json:"moduleID,string" db:"module_id"`
|
||||
|
||||
User *systemTypes.User `json:"user,omitempty" db:"-"`
|
||||
UserID uint64 `json:"userID,string" db:"user_id"`
|
||||
|
||||
Page *Page `json:"page,omitempty"`
|
||||
|
||||
Values RecordValueSet `json:"values,omitempty" db:"-"`
|
||||
|
||||
OwnedBy uint64 `db:"owned_by" json:"ownedBy,string"`
|
||||
CreatedAt time.Time `db:"created_at" json:"createdAt,omitempty"`
|
||||
UpdatedAt *time.Time `db:"updated_at" json:"updatedAt,omitempty"`
|
||||
CreatedBy uint64 `db:"created_by" json:"createdBy,string" `
|
||||
UpdatedAt *time.Time `db:"updated_at" json:"updatedAt,omitempty,omitempty"`
|
||||
UpdatedBy uint64 `db:"updated_by" json:"updatedBy,string,omitempty" `
|
||||
DeletedAt *time.Time `db:"deleted_at" json:"deletedAt,omitempty"`
|
||||
DeletedBy uint64 `db:"deleted_by" json:"deletedBy,string,omitempty" `
|
||||
}
|
||||
|
||||
// RecordValue is a stored row in the `record_value` table
|
||||
@@ -154,7 +150,7 @@ func (set ModuleFieldSet) FilterByModule(moduleID uint64) (ff ModuleFieldSet) {
|
||||
|
||||
// IsRef tells us if value of this field be a reference to something (another record, user)?
|
||||
func (f ModuleField) IsRef() bool {
|
||||
return f.Kind == "Record" || f.Kind == "User"
|
||||
return f.Kind == "Record" || f.Kind == "Owner"
|
||||
}
|
||||
|
||||
// UserIDs returns a slice of user IDs from all items in the set
|
||||
@@ -166,12 +162,12 @@ func (set RecordSet) UserIDs() (IDs []uint64) {
|
||||
loop:
|
||||
for i := range set {
|
||||
for _, id := range IDs {
|
||||
if id == set[i].UserID {
|
||||
if id == set[i].OwnedBy {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
IDs = append(IDs, set[i].UserID)
|
||||
IDs = append(IDs, set[i].OwnedBy)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -4,7 +4,7 @@ package config
|
||||
// type (
|
||||
// // AuthSettings holds configuration settings for auth service
|
||||
// AuthSettings struct {
|
||||
// User struct {
|
||||
// Owner struct {
|
||||
// Handle struct {
|
||||
// // How long can a user's handle be
|
||||
// MaxLength uint `json:"maxLength,omitempty"`
|
||||
@@ -17,8 +17,8 @@ package config
|
||||
// )
|
||||
//
|
||||
// func DefaultAuthSettings() (s AuthSettings) {
|
||||
// s.User.Handle.MaxLength = 20
|
||||
// s.User.Handle.Enabled = true
|
||||
// s.Owner.Handle.MaxLength = 20
|
||||
// s.Owner.Handle.Enabled = true
|
||||
//
|
||||
// return s
|
||||
// }
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestSessions(t *testing.T) {
|
||||
|
||||
// check user has permissions from role
|
||||
{
|
||||
must(t, resources.CheckAccess("test-resource", "view", "test-session"), "User has permission, but CheckAccess reports error")
|
||||
must(t, resources.CheckAccess("test-resource", "view", "test-session"), "Owner has permission, but CheckAccess reports error")
|
||||
mustFail(t, resources.CheckAccess("test-resource", "delete", "test-session"))
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestMentionsExtraction(t *testing.T) {
|
||||
assert(t, len(mm) == len(c.ids), "Number of extracted (%d) and expected (%d) user IDs do not match (%s)", len(mm), len(c.ids), c.text)
|
||||
|
||||
for _, id := range c.ids {
|
||||
assert(t, len(mm.FindByUserID(id)) == 1, "User ID (%d) was not extracted (%s)", id, c.text)
|
||||
assert(t, len(mm.FindByUserID(id)) == 1, "Owner ID (%d) was not extracted (%s)", id, c.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestTeam(t *testing.T) {
|
||||
|
||||
{
|
||||
u1, err := userRepo.Create(user)
|
||||
assert(t, err == nil, "User.Create error: %+v", err)
|
||||
assert(t, err == nil, "Owner.Create error: %+v", err)
|
||||
assert(t, user.ID == u1.ID, "Changes were not stored")
|
||||
}
|
||||
|
||||
@@ -98,6 +98,6 @@ func TestTeam(t *testing.T) {
|
||||
|
||||
{
|
||||
err := userRepo.DeleteByID(user.ID)
|
||||
assert(t, err == nil, "User.DeleteByID error: %+v", err)
|
||||
assert(t, err == nil, "Owner.DeleteByID error: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestUser(t *testing.T) {
|
||||
|
||||
{
|
||||
uu, err := userRepo.Create(user)
|
||||
assert(t, err == nil, "User.Create error: %+v", err)
|
||||
assert(t, err == nil, "Owner.Create error: %+v", err)
|
||||
assert(t, user.ID == uu.ID, "Changes were not stored")
|
||||
}
|
||||
|
||||
@@ -45,14 +45,14 @@ func TestUser(t *testing.T) {
|
||||
|
||||
{
|
||||
uu, err := userRepo.FindByID(user.ID)
|
||||
assert(t, err == nil, "User.FindByID error: %+v", err)
|
||||
assert(t, err == nil, "Owner.FindByID error: %+v", err)
|
||||
assert(t, len(uu.Teams) == 1, "Expected 1 team, got %d", len(uu.Teams))
|
||||
}
|
||||
|
||||
{
|
||||
users, err := userRepo.Find(&types.UserFilter{Query: ""})
|
||||
assert(t, err == nil, "User.Find error: %+v", err)
|
||||
assert(t, len(users) == 1, "User.Find: expected 1 user, got %d", len(users))
|
||||
assert(t, len(users[0].Teams) == 1, "User.Find: expected 1 team, got %d", len(users[0].Teams))
|
||||
assert(t, err == nil, "Owner.Find error: %+v", err)
|
||||
assert(t, len(users) == 1, "Owner.Find: expected 1 user, got %d", len(users))
|
||||
assert(t, len(users[0].Teams) == 1, "Owner.Find: expected 1 team, got %d", len(users[0].Teams))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func (svc *auth) Social(profile goth.User) (u *types.User, err error) {
|
||||
c.ID,
|
||||
)
|
||||
|
||||
// User created
|
||||
// Owner created
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
@@ -153,7 +153,7 @@ func (svc *auth) Social(profile goth.User) (u *types.User, err error) {
|
||||
profile.Provider,
|
||||
)
|
||||
|
||||
// User loaded, carry on.
|
||||
// Owner loaded, carry on.
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package service
|
||||
// mockCtrl := gomock.NewController(t)
|
||||
// defer mockCtrl.Finish()
|
||||
//
|
||||
// usr := &types.User{ID: factory.Sonyflake.NextID()}
|
||||
// usr := &types.Owner{ID: factory.Sonyflake.NextID()}
|
||||
//
|
||||
// usrRpoMock := NewMockRepository(mockCtrl)
|
||||
// usrRpoMock.EXPECT().WithCtx(gomock.Any()).AnyTimes().Return(usrRpoMock)
|
||||
@@ -13,7 +13,7 @@ package service
|
||||
// Times(1).
|
||||
// Return(usr, nil)
|
||||
//
|
||||
// svc := User()
|
||||
// svc := Owner()
|
||||
// svc.rpo = usrRpoMock
|
||||
//
|
||||
// found, err := svc.FindByID(context.Background(), usr.ID)
|
||||
|
||||
Reference in New Issue
Block a user