From 9c05a271f71d0400cc1f3507147715763bca561b Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Tue, 17 Jul 2018 11:06:40 +0200 Subject: [PATCH] Service & repository implementation for CRM field & module --- crm/repository/error.go | 13 ++++ crm/repository/field.go | 72 +++++++++++++++++++++++ crm/repository/module.go | 124 +++++++++++++++++++++++++++++++++++++++ crm/rest/field.go | 16 ++--- crm/rest/module.go | 66 +++++++++------------ crm/service/field.go | 70 +++++++--------------- crm/service/module.go | 48 +++++++++++++++ 7 files changed, 313 insertions(+), 96 deletions(-) create mode 100644 crm/repository/error.go create mode 100644 crm/repository/field.go create mode 100644 crm/repository/module.go create mode 100644 crm/service/module.go diff --git a/crm/repository/error.go b/crm/repository/error.go new file mode 100644 index 000000000..85114c9fe --- /dev/null +++ b/crm/repository/error.go @@ -0,0 +1,13 @@ +package repository + +type ( + repositoryError string +) + +const ( + ErrDatabaseError = repositoryError("DatabaseError") +) + +func (e repositoryError) Error() string { + return "repository." + string(e) +} diff --git a/crm/repository/field.go b/crm/repository/field.go new file mode 100644 index 000000000..c1b2e822b --- /dev/null +++ b/crm/repository/field.go @@ -0,0 +1,72 @@ +package repository + +import ( + "context" + "encoding/json" + "fmt" + "github.com/crusttech/crust/crm/types" + "os" + "path" + "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{} +) + +func Field() field { + return field{} +} + +// 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)) +} + +// Returns all known fields +func (repo field) Find(ctx context.Context) ([]*types.Field, error) { + matches, err := filepath.Glob(fmt.Sprintf(fieldPath, "*")) + if err != nil { + return nil, err + } + + res := make([]*types.Field, len(matches)) + for i, match := range matches { + if res[i], err = repo.decode(match); err != nil { + return nil, err + } + } + return res, nil +} + +func (repo field) decode(filepath string) (*types.Field, error) { + file, err := os.Open(filepath) + if err != nil { + // @todo wrap error + return nil, err + } + + defer file.Close() + + // 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)} + if err := json.NewDecoder(file).Decode(&field); err != nil { + // @todo wrap error + return nil, err + } + + 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 new file mode 100644 index 000000000..5ca494e47 --- /dev/null +++ b/crm/repository/module.go @@ -0,0 +1,124 @@ +package repository + +import ( + "context" + "github.com/crusttech/crust/crm/types" + "github.com/titpetric/factory" +) + +type ( + module struct{} +) + +func Module() module { + return module{} +} + +func (r module) FindById(ctx context.Context, id uint64) (*types.Module, error) { + db, err := factory.Database.Get() + if err != nil { + return nil, ErrDatabaseError + } + + mod := &types.Module{} + if err := db.Get(mod, "SELECT * FROM crm_module WHERE id = ?", id); err != nil { + println(err.Error()) + return nil, ErrDatabaseError + } else { + return mod, nil + } +} + +func (r module) Find(ctx context.Context) ([]*types.Module, error) { + db, err := factory.Database.Get() + if err != nil { + return nil, ErrDatabaseError + } + + mod := make([]*types.Module, 0) + if err := db.Select(&mod, "SELECT * FROM crm_module ORDER BY name ASC"); err != nil { + println(err.Error()) + return nil, ErrDatabaseError + } else { + return mod, nil + } +} + +func (r module) Create(ctx context.Context, mod *types.Module) (*types.Module, error) { + db, err := factory.Database.Get() + if err != nil { + return nil, ErrDatabaseError + } + + mod.SetID(factory.Sonyflake.NextID()) + if err := db.Insert("crm_module", mod); err != nil { + return nil, ErrDatabaseError + } else { + return mod, nil + } +} + +func (r module) Update(ctx context.Context, mod *types.Module) (*types.Module, error) { + db, err := factory.Database.Get() + if err != nil { + return nil, ErrDatabaseError + } + + if err := db.Replace("crm_module", mod); err != nil { + return nil, ErrDatabaseError + } else { + return mod, nil + } +} + +func (r module) Delete(ctx context.Context, mod *types.Module) error { + db, err := factory.Database.Get() + if err != nil { + return ErrDatabaseError + } + + if _, err := db.Exec("DELETE FROM crm_module WHERE ID = ?", mod.ID); err != nil { + return ErrDatabaseError + } else { + return nil + } +} + +//func (r module) Edit(r *moduleEditRequest) (interface{}, error) { +// db, err := factory.Database.Get() +// if err != nil { +// return nil, err +// } +// +// m := module{}.New() +// m.SetID(r.id).SetName(r.name) +// if m.GetID() > 0 { +// return m, db.Replace("crm_module", m) +// } +// m.SetID(factory.Sonyflake.NextID()) +// return m, db.Insert("crm_module", m) +//} +// +//func (r module) ContentList(r *moduleContentListRequest) (interface{}, error) { +// db, err := factory.Database.Get() +// if err != nil { +// return nil, err +// } +// +// if r.id > 0 { +// m := ModuleContentRow{}.New() +// return m, db.Get(m, "select * from crm_module id=?", r.id) +// } +// +// res := make([]ModuleContentRow, 0) +// err = db.Select(&res, "select * from crm_module order by name asc") +// return res, err +//} +// +//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) { +// return nil, errors.New("Not implemented: module.content/delete") +//} diff --git a/crm/rest/field.go b/crm/rest/field.go index 98f0c0af1..8a36b48b6 100644 --- a/crm/rest/field.go +++ b/crm/rest/field.go @@ -3,31 +3,33 @@ package rest import ( "github.com/pkg/errors" + "context" "github.com/crusttech/crust/crm/rest/server" "github.com/crusttech/crust/crm/service" + "github.com/crusttech/crust/crm/types" ) var _ = errors.Wrap type ( Field struct { - service FieldInterface + service fieldService } - FieldInterface interface { - List() (interface{}, error) - Type(id string) (interface{}, error) + fieldService interface { + FindByName(context.Context, string) (*types.Field, error) + Find(context.Context) ([]*types.Field, error) } ) func (Field) New() *Field { - return &Field{service.Field{}.New()} + return &Field{service: service.Field()} } func (self *Field) List(_ *server.FieldListRequest) (interface{}, error) { - return self.service.List() + return self.service.Find(context.TODO()) } func (self *Field) Type(r *server.FieldTypeRequest) (interface{}, error) { - return self.service.Type(r.ID) + return self.service.FindByName(context.TODO(), r.ID) } diff --git a/crm/rest/module.go b/crm/rest/module.go index 001b65a42..ed09b6651 100644 --- a/crm/rest/module.go +++ b/crm/rest/module.go @@ -2,65 +2,53 @@ package rest import ( "github.com/pkg/errors" - "github.com/titpetric/factory" + "context" "github.com/crusttech/crust/crm/rest/server" + "github.com/crusttech/crust/crm/service" "github.com/crusttech/crust/crm/types" ) var _ = errors.Wrap -type Module struct{} +type ( + Module struct { + service moduleService + } + + moduleService interface { + FindById(context.Context, uint64) (*types.Module, error) + Find(context.Context) ([]*types.Module, error) + + Create(context.Context, *types.Module) (*types.Module, error) + Update(context.Context, *types.Module) (*types.Module, error) + Delete(context.Context, *types.Module) error + } +) func (Module) New() *Module { - return &Module{} + return &Module{ + service: service.Module(), + } } -func (*Module) List(r *server.ModuleListRequest) (interface{}, error) { - db, err := factory.Database.Get() - if err != nil { - return nil, err - } - - if r.ID > 0 { - m := types.Module{}.New() - return m, db.Get(m, "select * from crm_module id=?", r.ID) - } - - res := make([]Module, 0) - err = db.Select(&res, "select * from crm_module order by name asc") - return res, err +func (c *Module) List(r *server.ModuleListRequest) (interface{}, error) { + return c.service.Find(context.TODO()) } -func (*Module) Edit(r *server.ModuleEditRequest) (interface{}, error) { - db, err := factory.Database.Get() - if err != nil { - return nil, err - } - +func (c *Module) Edit(r *server.ModuleEditRequest) (interface{}, error) { m := types.Module{}.New() m.SetID(r.ID).SetName(r.Name) + if m.GetID() > 0 { - return m, db.Replace("crm_module", m) + return c.service.Update(context.TODO(), m) } - m.SetID(factory.Sonyflake.NextID()) - return m, db.Insert("crm_module", m) + + return c.service.Create(context.TODO(), m) } func (*Module) ContentList(r *server.ModuleContentListRequest) (interface{}, error) { - db, err := factory.Database.Get() - if err != nil { - return nil, err - } - - if r.ID > 0 { - m := types.ModuleContentRow{}.New() - return m, db.Get(m, "select * from crm_module id=?", r.ID) - } - - res := make([]types.ModuleContentRow, 0) - err = db.Select(&res, "select * from crm_module order by name asc") - return res, err + return nil, errors.New("Not implemented: Module.content/edit") } func (*Module) ContentEdit(r *server.ModuleContentEditRequest) (interface{}, error) { diff --git a/crm/service/field.go b/crm/service/field.go index 9009e5799..e635c35ef 100644 --- a/crm/service/field.go +++ b/crm/service/field.go @@ -1,62 +1,32 @@ package service import ( - "encoding/json" - "os" - "path" - "path/filepath" - - "github.com/pkg/errors" - - _ "github.com/crusttech/crust/crm/types" + "context" + "github.com/crusttech/crust/crm/repository" + "github.com/crusttech/crust/crm/types" ) -var _ = errors.Wrap +type ( + fieldType struct { + repository fieldTypeRepository + } -type Field struct{} + fieldTypeRepository interface { + FindByName(context.Context, string) (*types.Field, error) + Find(context.Context) ([]*types.Field, error) + } +) -func (Field) New() *Field { - return &Field{} +func Field() fieldType { + return fieldType{ + repository: repository.Field(), + } } -func (*Field) List() (interface{}, error) { - matches, err := filepath.Glob("../crm/data/*.json") - if err != nil { - return nil, err - } - - res := make([]interface{}, 0) - for _, match := range matches { - t := path.Base(match) - t = t[:len(t)-5] - params, err := decodeJSON(match) - if err != nil { - return nil, errors.Wrap(err, "Error when parsing "+match) - } - params["type"] = t - res = append(res, params) - } - return res, nil +func (svc fieldType) FindByName(ctx context.Context, name string) (*types.Field, error) { + return svc.repository.FindByName(ctx, name) } -func (*Field) Type(id string) (interface{}, error) { - if id == "" { - return nil, errors.New("Missing id parameter") - } - params, err := decodeJSON("../crm/data/" + id + ".json") - if err != nil { - return nil, errors.Wrap(err, "Error reading field type: "+id) - } - params["type"] = id - return params, nil -} - -func decodeJSON(filename string) (map[string]interface{}, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - result := make(map[string]interface{}) - return result, json.NewDecoder(file).Decode(&result) +func (svc fieldType) Find(ctx context.Context) ([]*types.Field, error) { + return svc.repository.Find(ctx) } diff --git a/crm/service/module.go b/crm/service/module.go new file mode 100644 index 000000000..c730eb2f0 --- /dev/null +++ b/crm/service/module.go @@ -0,0 +1,48 @@ +package service + +import ( + "context" + "github.com/crusttech/crust/crm/repository" + "github.com/crusttech/crust/crm/types" +) + +type ( + module struct { + repository moduleRepository + } + + moduleRepository interface { + FindById(context.Context, uint64) (*types.Module, error) + Find(context.Context) ([]*types.Module, error) + + Create(context.Context, *types.Module) (*types.Module, error) + Update(context.Context, *types.Module) (*types.Module, error) + Delete(context.Context, *types.Module) error + } +) + +func Module() module { + return module{ + repository: repository.Module(), + } +} + +func (svc module) FindById(ctx context.Context, id uint64) (*types.Module, error) { + return svc.repository.FindById(ctx, id) +} + +func (svc module) Find(ctx context.Context) ([]*types.Module, error) { + return svc.repository.Find(ctx) +} + +func (svc module) Create(ctx context.Context, mod *types.Module) (*types.Module, error) { + return svc.repository.Create(ctx, mod) +} + +func (svc module) Update(ctx context.Context, mod *types.Module) (*types.Module, error) { + return svc.repository.Update(ctx, mod) +} + +func (svc module) Delete(ctx context.Context, mod *types.Module) error { + return svc.repository.Delete(ctx, mod) +}