diff --git a/crm/repository/constants.go b/crm/repository/constants.go new file mode 100644 index 000000000..8799fedf6 --- /dev/null +++ b/crm/repository/constants.go @@ -0,0 +1,7 @@ +package repository + +const ( + // @todo root should be configurable + // @todo move this to db or stack it inside the binary or container + fieldPath = "crm/data/%s.json" +) diff --git a/crm/repository/field.go b/crm/repository/field.go index c1b2e822b..43f5e1c34 100644 --- a/crm/repository/field.go +++ b/crm/repository/field.go @@ -10,27 +10,40 @@ import ( "path/filepath" ) -const ( - // @todo root should be configurable - // @todo move this to db or stack it inside the binary or container - fieldPath = "crm/data/%s.json" -) - type ( - field struct{} + Field interface { + With(ctx context.Context) Field + + FindByName(name string) (*types.Field, error) + Find() ([]*types.Field, error) + } + + field struct { + *repository + } ) -func Field() field { - return field{} +func NewField(ctx context.Context) Field { + return &field{ + repository: &repository{ + ctx: ctx, + }, + } +} + +func (r *field) With(ctx context.Context) Field { + return &field{ + repository: r.repository.With(ctx), + } } // Finds field by it's name and returns it -func (repo field) FindByName(ctx context.Context, name string) (*types.Field, error) { - return repo.decode(fmt.Sprintf(fieldPath, name)) +func (f *field) FindByName(name string) (*types.Field, error) { + return f.fieldDecode(fmt.Sprintf(fieldPath, name)) } // Returns all known fields -func (repo field) Find(ctx context.Context) ([]*types.Field, error) { +func (f *field) Find() ([]*types.Field, error) { matches, err := filepath.Glob(fmt.Sprintf(fieldPath, "*")) if err != nil { return nil, err @@ -38,14 +51,14 @@ func (repo field) Find(ctx context.Context) ([]*types.Field, error) { res := make([]*types.Field, len(matches)) for i, match := range matches { - if res[i], err = repo.decode(match); err != nil { + if res[i], err = f.fieldDecode(match); err != nil { return nil, err } } return res, nil } -func (repo field) decode(filepath string) (*types.Field, error) { +func (f *field) fieldDecode(filepath string) (*types.Field, error) { file, err := os.Open(filepath) if err != nil { // @todo wrap error @@ -54,9 +67,15 @@ func (repo field) decode(filepath string) (*types.Field, error) { defer file.Close() + // Removes path and extension from full filename + fieldTypeFromPath := func(filepath string) string { + t := path.Base(filepath) + return t[:len(t)-5] + } + // Preset field's type with name of the file (sans .json) // if type is explicitly set within the file, it will be overwritten - field := &types.Field{Type: repo.typeFromPath(filepath)} + field := &types.Field{Type: fieldTypeFromPath(filepath)} if err := json.NewDecoder(file).Decode(&field); err != nil { // @todo wrap error return nil, err @@ -64,9 +83,3 @@ func (repo field) decode(filepath string) (*types.Field, error) { return field, nil } - -// Removes path and extension from full filename -func (repo field) typeFromPath(filepath string) string { - t := path.Base(filepath) - return t[:len(t)-5] -} diff --git a/crm/repository/module.go b/crm/repository/module.go index cd55d4e28..42192bb20 100644 --- a/crm/repository/module.go +++ b/crm/repository/module.go @@ -7,17 +7,39 @@ import ( ) type ( - module struct{} + Module interface { + With(ctx context.Context) Module + + FindByID(id uint64) (*types.Module, error) + Find() ([]*types.Module, error) + Create(mod *types.Module) (*types.Module, error) + Update(mod *types.Module) (*types.Module, error) + DeleteByID(id uint64) error + } + + module struct { + *repository + } ) -func Module() module { - return module{} +func NewModule(ctx context.Context) Module { + return &module{ + repository: &repository{ + ctx: ctx, + }, + } } -func (r module) FindByID(ctx context.Context, id uint64) (*types.Module, error) { - db := factory.Database.MustGet() +func (r *module) With(ctx context.Context) Module { + return &module{ + repository: r.repository.With(ctx), + } +} + + +func (r *module) FindByID(id uint64) (*types.Module, error) { mod := &types.Module{} - if err := db.Get(mod, "SELECT * FROM crm_module WHERE id = ?", id); err != nil { + if err := r.db().Get(mod, "SELECT * FROM crm_module WHERE id = ?", id); err != nil { println(err.Error()) return nil, ErrDatabaseError } else { @@ -25,10 +47,9 @@ func (r module) FindByID(ctx context.Context, id uint64) (*types.Module, error) } } -func (r module) Find(ctx context.Context) ([]*types.Module, error) { - db := factory.Database.MustGet() +func (r *module) Find() ([]*types.Module, error) { mod := make([]*types.Module, 0) - if err := db.With(ctx).Select(&mod, "SELECT * FROM crm_module ORDER BY name ASC"); err != nil { + if err := r.db().Select(&mod, "SELECT * FROM crm_module ORDER BY name ASC"); err != nil { println(err.Error()) return nil, ErrDatabaseError } else { @@ -36,26 +57,25 @@ func (r module) Find(ctx context.Context) ([]*types.Module, error) { } } -func (r module) Create(ctx context.Context, mod *types.Module) (*types.Module, error) { +func (r *module) Create(mod *types.Module) (*types.Module, error) { mod.ID = factory.Sonyflake.NextID() - return mod, factory.Database.MustGet().With(ctx).Insert("crm_module", mod) + return mod, r.db().Insert("crm_module", mod) } -func (r module) Update(ctx context.Context, mod *types.Module) (*types.Module, error) { - return mod, factory.Database.MustGet().With(ctx).Replace("crm_module", mod) +func (r *module) Update(mod *types.Module) (*types.Module, error) { + return mod, r.db().Replace("crm_module", mod) } -func (r module) DeleteByID(ctx context.Context, id uint64) error { - db := factory.Database.MustGet() - if _, err := db.Exec("DELETE FROM crm_module WHERE ID = ?", id); err != nil { +func (r *module) DeleteByID(id uint64) error { + if _, err := r.db().Exec("DELETE FROM crm_module WHERE ID = ?", id); err != nil { return ErrDatabaseError } else { return nil } } -//func (r module) Edit(r *moduleEditRequest) (interface{}, error) { +//func (r *module) Edit(r *moduleEditRequest) (interface{}, error) { // db := factory.Database.MustGet() // m := module{}.New() // m.SetID(r.id).SetName(r.name) @@ -66,7 +86,7 @@ func (r module) DeleteByID(ctx context.Context, id uint64) error { // return m, db.With(ctx).Insert("crm_module", m) //} // -//func (r module) ContentList(r *moduleContentListRequest) (interface{}, error) { +//func (r *module) ContentList(r *moduleContentListRequest) (interface{}, error) { // db := factory.Database.MustGet() // if r.id > 0 { // m := ModuleContentRow{}.New() @@ -78,10 +98,10 @@ func (r module) DeleteByID(ctx context.Context, id uint64) error { // return res, err //} // -//func (r module) ContentEdit(r *moduleContentEditRequest) (interface{}, error) { +//func (r *module) ContentEdit(r *moduleContentEditRequest) (interface{}, error) { // return nil, errors.New("Not implemented: module.content/edit") //} // -//func (r module) ContentDelete(r *moduleContentDeleteRequest) (interface{}, error) { +//func (r *module) ContentDelete(r *moduleContentDeleteRequest) (interface{}, error) { // return nil, errors.New("Not implemented: module.content/delete") //} diff --git a/crm/repository/repository.go b/crm/repository/repository.go new file mode 100644 index 000000000..ad50d76ee --- /dev/null +++ b/crm/repository/repository.go @@ -0,0 +1,43 @@ +package repository + +import ( + "context" + "github.com/pkg/errors" + "github.com/titpetric/factory" +) + +type ( + repository struct { + ctx context.Context + + // Current transaction + tx *factory.DB + } +) + +// With updates repository and database contexts +func (r *repository) With(ctx context.Context) *repository { + return &repository{ + ctx: ctx, + tx: r.db().With(r.ctx), + } +} + +func (r *repository) Begin() error { + return r.db().Begin() +} + +func (r *repository) Commit() error { + return errors.Wrap(r.db().Commit(), "Can not commit changes") +} + +func (r *repository) Rollback() error { + return errors.Wrap(r.db().Rollback(), "Can not rollback changes") +} + +func (r *repository) db() *factory.DB { + if r.tx == nil { + r.tx = factory.Database.MustGet().With(r.ctx) + } + return r.tx +} diff --git a/crm/rest/field.go b/crm/rest/field.go index 42794e535..379760d63 100644 --- a/crm/rest/field.go +++ b/crm/rest/field.go @@ -6,31 +6,32 @@ import ( "context" "github.com/crusttech/crust/crm/rest/server" "github.com/crusttech/crust/crm/types" + "github.com/crusttech/crust/crm/service" ) var _ = errors.Wrap type ( Field struct { - svc fieldService + field service.FieldService } - fieldService interface { + FieldService interface { FindByName(ctx context.Context, name string) (*types.Field, error) Find(ctx context.Context) ([]*types.Field, error) } ) -func (Field) New(fieldSvc fieldService) *Field { - var ctrl = &Field{} - ctrl.svc = fieldSvc - return ctrl +func (Field) New() server.FieldAPI { + return &Field{ + field: service.Field(), + } } -func (self *Field) List(ctx context.Context, _ *server.FieldListRequest) (interface{}, error) { - return self.svc.Find(ctx) +func (s *Field) List(ctx context.Context, _ *server.FieldListRequest) (interface{}, error) { + return s.field.With(ctx).Find() } -func (self *Field) Type(ctx context.Context, r *server.FieldTypeRequest) (interface{}, error) { - return self.svc.FindByName(ctx, r.ID) +func (s *Field) Type(ctx context.Context, r *server.FieldTypeRequest) (interface{}, error) { + return s.field.With(ctx).FindByName(r.ID) } diff --git a/crm/rest/module.go b/crm/rest/module.go index 40cf8198c..82bb4961a 100644 --- a/crm/rest/module.go +++ b/crm/rest/module.go @@ -8,51 +8,43 @@ import ( "context" "github.com/crusttech/crust/crm/rest/server" "github.com/crusttech/crust/crm/types" + "github.com/crusttech/crust/crm/service" ) var _ = errors.Wrap type ( Module struct { - svc moduleService - } - - moduleService interface { - FindByID(ctx context.Context, moduleID uint64) (*types.Module, error) - Find(ctx context.Context) ([]*types.Module, error) - - Create(ctx context.Context, module *types.Module) (*types.Module, error) - Update(ctx context.Context, module *types.Module) (*types.Module, error) - DeleteByID(ctx context.Context, moduleID uint64) error + module service.ModuleService } ) -func (Module) New(moduleSvc moduleService) *Module { - var ctrl = &Module{} - ctrl.svc = moduleSvc - return ctrl +func (Module) New() server.ModuleAPI { + return &Module{ + module: service.Module(), + } } -func (c *Module) List(ctx context.Context, r *server.ModuleListRequest) (interface{}, error) { - return c.svc.Find(ctx) +func (s *Module) List(ctx context.Context, r *server.ModuleListRequest) (interface{}, error) { + return s.module.With(ctx).Find() } -func (c *Module) Read(ctx context.Context, r *server.ModuleReadRequest) (interface{}, error) { - return c.svc.FindByID(ctx, r.ID) +func (s *Module) Read(ctx context.Context, r *server.ModuleReadRequest) (interface{}, error) { + return s.module.With(ctx).FindByID(r.ID) } -func (c *Module) Delete(ctx context.Context, r *server.ModuleDeleteRequest) (interface{}, error) { - return resputil.OK(), c.svc.DeleteByID(ctx, r.ID) +func (s *Module) Delete(ctx context.Context, r *server.ModuleDeleteRequest) (interface{}, error) { + return resputil.OK(), s.module.With(ctx).DeleteByID(r.ID) } -func (c *Module) Create(ctx context.Context, r *server.ModuleCreateRequest) (interface{}, error) { +func (s *Module) Create(ctx context.Context, r *server.ModuleCreateRequest) (interface{}, error) { m := &types.Module{Name: r.Name} - return c.svc.Create(ctx, m) + return s.module.With(ctx).Create(m) } -func (c *Module) Edit(ctx context.Context, r *server.ModuleEditRequest) (interface{}, error) { +func (s *Module) Edit(ctx context.Context, r *server.ModuleEditRequest) (interface{}, error) { m := &types.Module{ID: r.ID, Name: r.Name} - return c.svc.Update(ctx, m) + return s.module.With(ctx).Update(m) } func (*Module) ContentList(ctx context.Context, r *server.ModuleContentListRequest) (interface{}, error) { diff --git a/crm/rest/rest.go b/crm/rest/rest.go index 1e834de34..021350e43 100644 --- a/crm/rest/rest.go +++ b/crm/rest/rest.go @@ -3,7 +3,6 @@ package rest import ( "github.com/crusttech/crust/auth" "github.com/crusttech/crust/crm/rest/server" - "github.com/crusttech/crust/crm/service" "github.com/go-chi/chi" ) @@ -14,12 +13,6 @@ type ( ) func MountRoutes(jwtAuth authTokenEncoder) func(chi.Router) { - // Initialize services - var ( - fieldSvc = service.Field() - moduleSvc = service.Module() - ) - // @todo pass jwtAuth to auth handlers (signUp) for JWT generation // Initialize handers & controllers. @@ -27,11 +20,11 @@ func MountRoutes(jwtAuth authTokenEncoder) func(chi.Router) { r.Use(auth.AuthenticationMiddlewareValidOnly) (&server.FieldHandlers{ - Field: (&Field{}).New(fieldSvc), + Field: Field{}.New(), }).MountRoutes(r) (&server.ModuleHandlers{ - Module: (&Module{}).New(moduleSvc), + Module: Module{}.New(), }).MountRoutes(r) } } diff --git a/crm/service/field.go b/crm/service/field.go index b2e1d2152..70418ff66 100644 --- a/crm/service/field.go +++ b/crm/service/field.go @@ -7,26 +7,33 @@ import ( ) type ( - fieldType struct { - repository fieldTypeRepository + field struct { + repository repository.Field } - fieldTypeRepository interface { - FindByName(ctx context.Context, name string) (*types.Field, error) - Find(ctx context.Context) ([]*types.Field, error) + FieldService interface { + With(ctx context.Context) FieldService + FindByName(name string) (*types.Field, error) + Find() ([]*types.Field, error) } ) -func Field() fieldType { - return fieldType{ - repository: repository.Field(), +func Field() FieldService { + return &field{ + repository: repository.NewField(context.Background()), } } -func (svc fieldType) FindByName(ctx context.Context, name string) (*types.Field, error) { - return svc.repository.FindByName(ctx, name) +func (s *field) With(ctx context.Context) FieldService { + return &field{ + repository: s.repository.With(ctx), + } } -func (svc fieldType) Find(ctx context.Context) ([]*types.Field, error) { - return svc.repository.Find(ctx) +func (s *field) FindByName(name string) (*types.Field, error) { + return s.repository.FindByName(name) +} + +func (s *field) Find() ([]*types.Field, error) { + return s.repository.Find() } diff --git a/crm/service/module.go b/crm/service/module.go index d13fce79b..5268d82d3 100644 --- a/crm/service/module.go +++ b/crm/service/module.go @@ -8,41 +8,49 @@ import ( type ( module struct { - repository moduleRepository + repository repository.Module } - moduleRepository interface { - FindByID(ctx context.Context, moduleID uint64) (*types.Module, error) - Find(ctx context.Context) ([]*types.Module, error) + ModuleService interface { + With(ctx context.Context) ModuleService - Create(ctx context.Context, module *types.Module) (*types.Module, error) - Update(ctx context.Context, module *types.Module) (*types.Module, error) - DeleteByID(ctx context.Context, moduleID uint64) error + FindByID(moduleID uint64) (*types.Module, error) + Find() ([]*types.Module, error) + + Create(module *types.Module) (*types.Module, error) + Update(module *types.Module) (*types.Module, error) + DeleteByID(moduleID uint64) error } ) -func Module() module { - return module{ - repository: repository.Module(), +func Module() ModuleService { + return &module{ + repository: repository.NewModule(context.Background()), } } -func (svc module) FindByID(ctx context.Context, id uint64) (*types.Module, error) { - return svc.repository.FindByID(ctx, id) +func (s *module) With(ctx context.Context) ModuleService { + return &module{ + repository: s.repository.With(ctx), + } } -func (svc module) Find(ctx context.Context) ([]*types.Module, error) { - return svc.repository.Find(ctx) +func (s *module) FindByID(id uint64) (*types.Module, error) { + return s.repository.FindByID(id) } -func (svc module) Create(ctx context.Context, mod *types.Module) (*types.Module, error) { - return svc.repository.Create(ctx, mod) +func (s *module) Find() ([]*types.Module, error) { + return s.repository.Find() } -func (svc module) Update(ctx context.Context, mod *types.Module) (*types.Module, error) { - return svc.repository.Update(ctx, mod) +func (s *module) Create(mod *types.Module) (*types.Module, error) { + return s.repository.Create(mod) } -func (svc module) DeleteByID(ctx context.Context, id uint64) error { - return svc.repository.DeleteByID(ctx, id) +func (s *module) Update(mod *types.Module) (*types.Module, error) { + return s.repository.Update(mod) +} + +func (s *module) DeleteByID(id uint64) error { + return s.repository.DeleteByID(id) }