diff --git a/Makefile b/Makefile index 9d056abe3..8eaa794ee 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ test.events: $(GOTEST) $(GO) tool cover -func=.cover.out | grep --color "^\|[^0-9]0.0%" test.crm: $(GOTEST) - $(GOTEST) -covermode count -coverprofile .cover.out -v ./crm/repository/... + $(GOTEST) -covermode count -coverprofile .cover.out -v ./crm/service/... $(GO) tool cover -func=.cover.out | grep --color "^\|[^0-9]0.0%" test.crm.db: $(GOTEST) diff --git a/crm/repository/content.go b/crm/repository/content.go index f3af235d8..c6f7f121f 100644 --- a/crm/repository/content.go +++ b/crm/repository/content.go @@ -12,7 +12,6 @@ import ( "github.com/titpetric/factory" "github.com/crusttech/crust/crm/types" - systemRepository "github.com/crusttech/crust/system/repository" ) type ( @@ -64,7 +63,7 @@ func (r *content) FindByID(id uint64) (*types.Content, error) { if err := r.db().Get(mod, "SELECT * FROM crm_content WHERE id=? and deleted_at IS NULL", id); err != nil { return nil, err } - return mod, r.prepare(mod, "page", "user", "fields") + return mod, nil } func (r *content) Find(moduleID uint64, query string, page int, perPage int) (*FindResponse, error) { @@ -116,10 +115,6 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int) (*F } } - if err := r.prepareAll(response.Contents, "user", "fields"); err != nil { - return nil, err - } - return response, nil } @@ -154,14 +149,10 @@ func (r *content) Create(mod *types.Content) (*types.Content, error) { if err := r.db().Insert("crm_content", mod); err != nil { return nil, err } - - return mod, r.prepare(mod, "user", "fields") + return mod, nil } func (r *content) Update(mod *types.Content) (*types.Content, error) { - if mod.ID == 0 { - return nil, errors.New("Error when savig content, invalid ID") - } now := time.Now() mod.UpdatedAt = &now @@ -222,44 +213,3 @@ func (r *content) Fields(content *types.Content) ([]*types.ContentColumn, error) } return result, r.db().Select(&result, "select * FROM crm_content_column where content_id=? order by "+order, args...) } - -func (r *content) prepareAll(contents []*types.Content, fields ...string) error { - for _, content := range contents { - if err := r.prepare(content, fields...); err != nil { - return err - } - } - return nil -} - -func (r *content) prepare(content *types.Content, fields ...string) (err error) { - api := Page(r.Context(), r.db()) - usersAPI := systemRepository.User(r.Context(), r.db()) - for _, field := range fields { - switch field { - case "fields": - fields, err := r.Fields(content) - if err != nil { - return err - } - json, err := json.Marshal(fields) - if err != nil { - return err - } - if err := (&content.Fields).Scan(json); err != nil { - return err - } - case "page": - if content.Page, err = api.FindByModuleID(content.ModuleID); err != nil { - return - } - case "user": - if content.UserID > 0 { - if content.User, err = usersAPI.FindByID(content.UserID); err != nil { - return - } - } - } - } - return -} diff --git a/crm/repository/module.go b/crm/repository/module.go index 32c64c255..cad88cfd6 100644 --- a/crm/repository/module.go +++ b/crm/repository/module.go @@ -50,9 +50,6 @@ func (r *module) FindByID(id uint64) (*types.Module, error) { if err := r.db().Get(mod, "SELECT * FROM crm_module WHERE id=?", id); err != nil { return nil, err } - if err := r.fillPage(mod); err != nil { - return nil, err - } return mod, nil } @@ -122,9 +119,3 @@ func (r *module) FieldNames(mod *types.Module) ([]string, error) { return result, nil } } - -func (r *module) fillPage(mod *types.Module) (err error) { - api := Page(r.Context(), r.db()) - mod.Page, err = api.FindByModuleID(mod.ID) - return -} diff --git a/crm/repository/page.go b/crm/repository/page.go index 2c3b4082b..5da919f02 100644 --- a/crm/repository/page.go +++ b/crm/repository/page.go @@ -3,7 +3,6 @@ package repository import ( "context" - "github.com/pkg/errors" "github.com/titpetric/factory" "github.com/crusttech/crust/crm/types" @@ -45,9 +44,6 @@ func (r *page) FindByID(id uint64) (*types.Page, error) { if err := r.db().Get(page, "SELECT * FROM crm_page WHERE id=?", id); err != nil { return page, err } - if err := r.fillModule(page); err != nil { - return page, err - } return page, nil } @@ -64,11 +60,6 @@ func (r *page) FindBySelfID(selfID uint64) (types.PageSet, error) { if err := r.db().Select(&pages, "SELECT * FROM crm_page WHERE self_id = ? ORDER BY weight ASC", selfID); err != nil { return pages, err } - for _, page := range pages { - if err := r.fillModule(page); err != nil { - return pages, err - } - } return pages, nil } @@ -118,9 +109,6 @@ func (r *page) Create(item *types.Page) (*types.Page, error) { } func (r *page) Update(page *types.Page) (*types.Page, error) { - if page.ID == 0 { - return nil, errors.New("Error when savig page, invalid ID") - } return page, r.db().Replace("crm_page", page) } @@ -128,13 +116,3 @@ func (r *page) DeleteByID(id uint64) error { _, err := r.db().Exec("DELETE FROM crm_page WHERE id=?", id) return err } - -func (r *page) fillModule(page *types.Page) error { - if page.ModuleID > 0 { - api := Module(r.Context(), r.db()) - module, err := api.FindByID(page.ModuleID) - page.Module = module - return err - } - return nil -} diff --git a/crm/rest/page.go b/crm/rest/page.go index 3909016a7..44f98d18e 100644 --- a/crm/rest/page.go +++ b/crm/rest/page.go @@ -22,7 +22,7 @@ func (Page) New(pageSvc service.PageService) *Page { } func (ctrl *Page) List(ctx context.Context, r *request.PageList) (interface{}, error) { - return ctrl.page.With(ctx).Find(r.SelfID) + return ctrl.page.With(ctx).FindBySelfID(r.SelfID) } func (ctrl *Page) Tree(ctx context.Context, r *request.PageTree) (interface{}, error) { diff --git a/crm/repository/chart_test.go b/crm/service/chart_test.go similarity index 81% rename from crm/repository/chart_test.go rename to crm/service/chart_test.go index 2da72b89d..8cb5d8569 100644 --- a/crm/repository/chart_test.go +++ b/crm/service/chart_test.go @@ -1,14 +1,14 @@ -package repository +package service import ( - "context" "testing" + "context" "github.com/crusttech/crust/crm/rest/request" ) func TestModuleGraph(t *testing.T) { - repository := Module(context.TODO(), nil).With(context.Background(), nil) + repository := Module().With(context.Background()) params := &request.ModuleChart{ Kind: "line", diff --git a/crm/service/content.go b/crm/service/content.go index 9c771a621..fa98cf0fc 100644 --- a/crm/service/content.go +++ b/crm/service/content.go @@ -3,17 +3,24 @@ package service import ( "context" + "github.com/pkg/errors" "github.com/titpetric/factory" "github.com/crusttech/crust/crm/repository" "github.com/crusttech/crust/crm/types" + + systemService "github.com/crusttech/crust/system/service" ) type ( content struct { - db *factory.DB - ctx context.Context + db *factory.DB + ctx context.Context + repository repository.ContentRepository + pageRepo repository.PageRepository + + userSvc systemService.UserService } ContentService interface { @@ -26,11 +33,15 @@ type ( Create(content *types.Content) (*types.Content, error) Update(content *types.Content) (*types.Content, error) DeleteByID(contentID uint64) error + + Fields(mod *types.Content) ([]*types.ContentColumn, error) } ) func Content() ContentService { - return (&content{}).With(context.Background()) + return (&content{ + userSvc: systemService.DefaultUser, + }).With(context.Background()) } func (s *content) With(ctx context.Context) ContentService { @@ -39,25 +50,49 @@ func (s *content) With(ctx context.Context) ContentService { db: db, ctx: ctx, repository: repository.Content(ctx, db), + pageRepo: repository.Page(ctx, db), + userSvc: s.userSvc.With(ctx), } } func (s *content) FindByID(id uint64) (*types.Content, error) { - return s.repository.FindByID(id) + response, err := s.repository.FindByID(id) + if err != nil { + return nil, err + } + return response, s.preload(response, "page", "user", "fields") } func (s *content) Find(moduleID uint64, query string, page int, perPage int) (*repository.FindResponse, error) { - return s.repository.Find(moduleID, query, page, perPage) + response, err := s.repository.Find(moduleID, query, page, perPage) + if err != nil { + return nil, err + } + if err := s.preloadAll(response.Contents, "user", "fields"); err != nil { + return nil, err + } + return response, nil } func (s *content) Create(mod *types.Content) (*types.Content, error) { - return s.repository.Create(mod) + response, err := s.repository.Create(mod) + if err != nil { + return nil, err + } + return response, s.preload(response, "user", "fields") } func (s *content) Update(mod *types.Content) (*types.Content, error) { + if mod.ID == 0 { + return nil, errors.New("Error when savig content, invalid ID") + } return s.repository.Update(mod) } +func (s *content) Fields(mod *types.Content) ([]*types.ContentColumn, error) { + return s.repository.Fields(mod) +} + func (s *content) DeleteByID(id uint64) error { return s.repository.DeleteByID(id) } diff --git a/crm/repository/content_test.go b/crm/service/content_test.go similarity index 97% rename from crm/repository/content_test.go rename to crm/service/content_test.go index 548542c44..80a0c519f 100644 --- a/crm/repository/content_test.go +++ b/crm/service/content_test.go @@ -1,4 +1,4 @@ -package repository +package service import ( "context" @@ -30,7 +30,7 @@ func TestContent(t *testing.T) { } ctx := auth.SetIdentityToContext(context.Background(), auth.NewIdentity(user.Identity())) - repository := Content(context.TODO(), nil).With(ctx, nil) + repository := Content().With(ctx) fields, err := json.Marshal([]types.Field{ types.Field{ @@ -55,7 +55,7 @@ func TestContent(t *testing.T) { // set up a module { - _, err := Module(context.TODO(), nil).With(context.Background(), nil).Create(module) + _, err := Module().With(context.Background()).Create(module) assert(t, err == nil, "Error when creating module: %+v", err) assert(t, module.ID > 0, "Expected auto generated ID") } diff --git a/crm/service/content_util.go b/crm/service/content_util.go new file mode 100644 index 000000000..6490a8bc6 --- /dev/null +++ b/crm/service/content_util.go @@ -0,0 +1,46 @@ +package service + +import ( + "encoding/json" + + "github.com/crusttech/crust/crm/types" +) + +func (r *content) preloadAll(contents []*types.Content, fields ...string) error { + for _, content := range contents { + if err := r.preload(content, fields...); err != nil { + return err + } + } + return nil +} + +func (r *content) preload(content *types.Content, fields ...string) (err error) { + for _, field := range fields { + switch field { + case "fields": + fields, err := r.Fields(content) + if err != nil { + return err + } + json, err := json.Marshal(fields) + if err != nil { + return err + } + if err := (&content.Fields).Scan(json); err != nil { + return err + } + case "page": + if content.Page, err = r.pageRepo.FindByModuleID(content.ModuleID); err != nil { + return + } + case "user": + if content.UserID > 0 { + if content.User, err = r.userSvc.FindByID(content.UserID); err != nil { + return + } + } + } + } + return +} diff --git a/crm/service/field.go b/crm/service/field.go index 6a3c062f0..764093d6a 100644 --- a/crm/service/field.go +++ b/crm/service/field.go @@ -11,8 +11,9 @@ import ( type ( field struct { - db *factory.DB - ctx context.Context + db *factory.DB + ctx context.Context + repository repository.FieldRepository } diff --git a/crm/repository/field_test.go b/crm/service/field_test.go similarity index 84% rename from crm/repository/field_test.go rename to crm/service/field_test.go index 4ff7baa88..eb6dda2db 100644 --- a/crm/repository/field_test.go +++ b/crm/service/field_test.go @@ -1,4 +1,4 @@ -package repository +package service import ( "context" @@ -6,7 +6,7 @@ import ( ) func TestField(t *testing.T) { - repository := Field(context.TODO(), nil).With(context.Background(), nil) + repository := Field().With(context.Background()) { // fetch all fields diff --git a/crm/repository/main_test.go b/crm/service/main_test.go similarity index 94% rename from crm/repository/main_test.go rename to crm/service/main_test.go index 49ef19ae4..c75e4080d 100644 --- a/crm/repository/main_test.go +++ b/crm/service/main_test.go @@ -1,4 +1,4 @@ -package repository +package service import ( "log" @@ -11,6 +11,7 @@ import ( crmMigrate "github.com/crusttech/crust/crm/db" systemMigrate "github.com/crusttech/crust/system/db" + systemService "github.com/crusttech/crust/system/service" ) func TestMain(m *testing.M) { @@ -57,6 +58,8 @@ func TestMain(m *testing.M) { } } + systemService.Init() + os.Exit(m.Run()) } diff --git a/crm/service/module.go b/crm/service/module.go index 57ce9ab84..8d0b76ca4 100644 --- a/crm/service/module.go +++ b/crm/service/module.go @@ -12,9 +12,11 @@ import ( type ( module struct { - db *factory.DB - ctx context.Context - repository repository.ModuleRepository + db *factory.DB + ctx context.Context + + moduleRepo repository.ModuleRepository + pageRepo repository.PageRepository } ModuleService interface { @@ -40,30 +42,38 @@ func (s *module) With(ctx context.Context) ModuleService { return &module{ db: db, ctx: ctx, - repository: repository.Module(ctx, db), + moduleRepo: repository.Module(ctx, db), + pageRepo: repository.Page(ctx, db), } } func (s *module) FindByID(id uint64) (*types.Module, error) { - return s.repository.FindByID(id) + mod, err := s.moduleRepo.FindByID(id) + if err != nil { + return nil, err + } + if err := s.preload(mod); err != nil { + return nil, err + } + return mod, err } func (s *module) Find() ([]*types.Module, error) { - return s.repository.Find() + return s.moduleRepo.Find() } func (s *module) Chart(r *request.ModuleChart) (interface{}, error) { - return s.repository.Chart(r) + return s.moduleRepo.Chart(r) } func (s *module) Create(mod *types.Module) (*types.Module, error) { - return s.repository.Create(mod) + return s.moduleRepo.Create(mod) } func (s *module) Update(mod *types.Module) (*types.Module, error) { - return s.repository.Update(mod) + return s.moduleRepo.Update(mod) } func (s *module) DeleteByID(id uint64) error { - return s.repository.DeleteByID(id) + return s.moduleRepo.DeleteByID(id) } diff --git a/crm/repository/module_test.go b/crm/service/module_test.go similarity index 95% rename from crm/repository/module_test.go rename to crm/service/module_test.go index 06ac2b665..2fbc3b715 100644 --- a/crm/repository/module_test.go +++ b/crm/service/module_test.go @@ -1,4 +1,4 @@ -package repository +package service import ( "context" @@ -7,7 +7,7 @@ import ( ) func TestModule(t *testing.T) { - repository := Module(context.TODO(), nil).With(context.Background(), nil) + repository := Module().With(context.Background()) // the module object we're working with module := &types.Module{ diff --git a/crm/service/module_util.go b/crm/service/module_util.go new file mode 100644 index 000000000..ab009302c --- /dev/null +++ b/crm/service/module_util.go @@ -0,0 +1,10 @@ +package service + +import ( + "github.com/crusttech/crust/crm/types" +) + +func (r *module) preload(mod *types.Module) (err error) { + mod.Page, err = r.pageRepo.FindByModuleID(mod.ID) + return +} diff --git a/crm/service/page.go b/crm/service/page.go index 75b40a7f6..515a9b03d 100644 --- a/crm/service/page.go +++ b/crm/service/page.go @@ -4,7 +4,6 @@ import ( "context" "errors" - "github.com/davecgh/go-spew/spew" "github.com/titpetric/factory" "github.com/crusttech/crust/crm/repository" @@ -13,9 +12,10 @@ import ( type ( page struct { - db *factory.DB - ctx context.Context - repository repository.PageRepository + db *factory.DB + ctx context.Context + + pageRepo repository.PageRepository moduleRepo repository.ModuleRepository } @@ -23,7 +23,8 @@ type ( With(ctx context.Context) PageService FindByID(pageID uint64) (*types.Page, error) - Find(selfID uint64) (pages types.PageSet, err error) + FindByModuleID(moduleID uint64) (*types.Page, error) + FindBySelfID(selfID uint64) (pages types.PageSet, err error) Tree() (pages types.PageSet, err error) Create(page *types.Page) (*types.Page, error) @@ -41,24 +42,42 @@ func Page() PageService { func (s *page) With(ctx context.Context) PageService { db := repository.DB(ctx) return &page{ - db: db, - ctx: ctx, - repository: repository.Page(ctx, db), + db: db, + ctx: ctx, + pageRepo: repository.Page(ctx, db), moduleRepo: repository.Module(ctx, db), } } func (s *page) FindByID(id uint64) (*types.Page, error) { - return s.repository.FindByID(id) + page, err := s.pageRepo.FindByID(id) + if err != nil { + return nil, err + } + if err := s.preload(page); err != nil { + return nil, err + } + return page, err } -func (s *page) Find(selfID uint64) (pages types.PageSet, err error) { +func (s *page) FindByModuleID(moduleID uint64) (*types.Page, error) { + page, err := s.pageRepo.FindByModuleID(moduleID) + if err != nil { + return nil, err + } + if err := s.preload(page); err != nil { + return nil, err + } + return page, err +} + +func (s *page) FindBySelfID(selfID uint64) (pages types.PageSet, err error) { return pages, s.db.Transaction(func() (err error) { - if pages, err = s.repository.FindBySelfID(selfID); err != nil { + if pages, err = s.pageRepo.FindBySelfID(selfID); err != nil { return } - if err = s.preload(pages); err != nil { + if err = s.preloadAll(pages); err != nil { return } @@ -70,11 +89,11 @@ func (s *page) Tree() (pages types.PageSet, err error) { var tree types.PageSet return tree, s.db.Transaction(func() (err error) { - if pages, err = s.repository.FindAll(); err != nil { + if pages, err = s.pageRepo.FindAll(); err != nil { return } - if err = s.preload(pages); err != nil { + if err = s.preloadAll(pages); err != nil { return } @@ -99,56 +118,54 @@ func (s *page) Tree() (pages types.PageSet, err error) { } func (s *page) Reorder(selfID uint64, pageIDs []uint64) error { - return s.repository.Reorder(selfID, pageIDs) + return s.pageRepo.Reorder(selfID, pageIDs) } -func (s *page) Create(mod *types.Page) (p *types.Page, err error) { - return p, s.db.Transaction(func() (err error) { - if mod.ModuleID > 0 { +func (s *page) Create(page *types.Page) (p *types.Page, err error) { + validate := func() error { + if page.ModuleID > 0 { // @todo check if module exists! - if p, err = s.repository.FindByModuleID(mod.ModuleID); err != nil { + if p, err = s.pageRepo.FindByModuleID(page.ModuleID); err != nil { return err } else if p.ID > 0 { return errors.New("Page for module already exists") } } - - p, err = s.repository.Create(mod) + return nil + } + if err := validate(); err != nil { + return nil, err + } + return p, s.db.Transaction(func() (err error) { + p, err = s.pageRepo.Create(page) return }) } -func (s *page) Update(mod *types.Page) (p *types.Page, err error) { - return p, s.db.Transaction(func() (err error) { - if mod.ModuleID > 0 { +func (s *page) Update(page *types.Page) (p *types.Page, err error) { + validate := func() error { + if page.ID == 0 { + return errors.New("Error when savig page, invalid ID") + } + if page.ModuleID > 0 { // @todo check if module exists! - if p, err = s.repository.FindByModuleID(mod.ModuleID); err != nil { + if p, err = s.pageRepo.FindByModuleID(page.ModuleID); err != nil { return err - } else if p.ID > 0 && mod.ID != p.ID { - spew.Dump(mod, p) + } else if p.ID > 0 && page.ID != p.ID { return errors.New("Page for module already exists") } } - - p, err = s.repository.Update(mod) + return nil + } + if err := validate(); err != nil { + return nil, err + } + return p, s.db.Transaction(func() (err error) { + p, err = s.pageRepo.Update(page) return }) } func (s *page) DeleteByID(id uint64) error { - return s.repository.DeleteByID(id) -} - -// Preloads modules for all pages -func (s *page) preload(pages types.PageSet) error { - if modules, err := s.moduleRepo.Find(); err != nil { - return err - } else { - _ = pages.Walk(func(page *types.Page) error { - page.Module = modules.FindByID(page.ModuleID) - return nil - }) - } - - return nil + return s.pageRepo.DeleteByID(id) } diff --git a/crm/repository/page_test.go b/crm/service/page_test.go similarity index 97% rename from crm/repository/page_test.go rename to crm/service/page_test.go index 37e2ec1f7..e95ecb93e 100644 --- a/crm/repository/page_test.go +++ b/crm/service/page_test.go @@ -1,4 +1,4 @@ -package repository +package service import ( "context" @@ -11,7 +11,7 @@ import ( ) func TestPage(t *testing.T) { - repository := Page(context.TODO(), nil).With(context.Background(), nil) + repository := Page().With(context.Background()) // the page object we're working with page := &types.Page{ diff --git a/crm/service/page_util.go b/crm/service/page_util.go new file mode 100644 index 000000000..c8a19d874 --- /dev/null +++ b/crm/service/page_util.go @@ -0,0 +1,25 @@ +package service + +import ( + "github.com/crusttech/crust/crm/types" +) + +func (s *page) preloadAll(pages types.PageSet) (err error) { + var modules types.ModuleSet + modules, err = s.moduleRepo.Find() + if err != nil { + return err + } + return pages.Walk(func(page *types.Page) error { + page.Module = modules.FindByID(page.ModuleID) + return nil + }) +} + +func (s *page) preload(page *types.Page) (err error) { + if page.ModuleID > 0 { + page.Module, err = s.moduleRepo.FindByID(page.ModuleID) + return + } + return +} diff --git a/crm/service/service.go b/crm/service/service.go new file mode 100644 index 000000000..b6f1c71ba --- /dev/null +++ b/crm/service/service.go @@ -0,0 +1,22 @@ +package service + +import ( + "sync" +) + +var ( + o sync.Once + DefaultContent ContentService + DefaultField FieldService + DefaultModule ModuleService + DefaultPage PageService +) + +func Init() { + o.Do(func() { + DefaultContent = Content() + DefaultField = Field() + DefaultModule = Module() + DefaultPage = Page() + }) +}