This commit is contained in:
Tit Petric
2018-07-17 11:10:55 +02:00
16 changed files with 395 additions and 112 deletions
-2
View File
@@ -1,2 +0,0 @@
SAM_HTTP_ADDR=:8082
SAM_DB_DSN=crust:crust@tcp(localhost:3306)/crust?collation=utf8mb4_general_ci
+4 -1
View File
@@ -1,2 +1,5 @@
SAM_HTTP_ADDR=:3000
SAM_DB_DSN=crust:crust@tcp(db1:3306)/crust?collation=utf8mb4_general_ci
SAM_DB_DSN=crust:crust@tcp(localhost:3306)/crust?collation=utf8mb4_general_ci
CRM_HTTP_ADDR=:3001
CRM_DB_DSN=crust:crust@tcp(localhost:3306)/crust?collation=utf8mb4_general_ci
+22 -1
View File
@@ -18,7 +18,7 @@ settings:
server:
status: false
schema:
- name: crust
- name: sam
path: cmd/sam
commands:
vet:
@@ -39,3 +39,24 @@ schema:
- .git
- .realize
- vendor
- name: crm
path: cmd/crm
commands:
vet:
status: true
test:
status: false
run:
status: true
args:
- api
watcher:
paths:
- .
- ../../crm
extensions:
- go
ignored_paths:
- .git
- .realize
- vendor
+3 -4
View File
@@ -1,4 +1,4 @@
.PHONY: build realize dep spec protobuf qa qa.test qa.vet
.PHONY: build realize dep spec protobuf qa qa.test qa.vet codegen
PKG = "github.com/$(shell cat .project)"
@@ -28,9 +28,8 @@ dep.update:
dep:
dep ensure -v
spec: $(SPEC)
cd sam/docs/src && $(SPEC)
cd sam/ && ./_gen.sh
codegen: $(SPEC)
./codegen.sh
protobuf: $(PROTOC)
# @todo this needs work (it hangs and outputs nothing)
+8 -3
View File
@@ -9,13 +9,18 @@ import (
"github.com/go-chi/chi"
"github.com/namsral/flag"
"github.com/crusttech/crust/rbac"
"github.com/crusttech/crust/crm/rest"
"github.com/titpetric/factory"
)
const (
defaultAddr = ":3000"
defaultDsn = "crust:crust@tcp(db1:3306)/crust?collation=utf8mb4_general_ci"
envVarKey_HTTP_ADDR = "CRM_HTTP_ADDR"
envVarKey_DB_DSN = "CRM_DB_DSN"
)
func handleError(err error, message string) {
if message == "" {
message = "Error making API call"
+3 -2
View File
@@ -1,14 +1,15 @@
#!/bin/bash
set -e
set -x
_PWD=$PWD
SPECS=$(find $PWD -name 'spec.json' | xargs -n1 dirname)
for SPEC in $SPECS; do
echo "=== spec $SPEC ==="
cd $SPEC && rm -rf spec && spec && cd $_PWD
cd $SPEC && rm -rf spec && ${GOPATH}/bin/spec && cd $_PWD
SRC=$(dirname $(dirname $SPEC))
echo "=== codegen $SRC ==="
GOPATHS=$(codegen/codegen.php $(basename $SRC) | tee -a /dev/stderr | xargs --no-run-if-empty -n1 dirname | sort | uniq)
GOPATHS=$(codegen/codegen.php $(basename $SRC) | tee -a /dev/stderr | xargs -n1 dirname | sort | uniq)
for FOLDER in $GOPATHS; do
if [[ $FOLDER != "." ]]; then
echo "== go fmt $FOLDER =="
+5 -2
View File
@@ -9,7 +9,10 @@
"struct": [
{
"name": "Field",
"fields": []
"fields": [
{ "name": "Name", "type": "string", "tag": "json:\"name\"" },
{ "name": "Type", "type": "string", "tag": "json:\"type\"" }
]
}
],
"apis": [
@@ -114,4 +117,4 @@
}
]
}
]
]
+12 -1
View File
@@ -5,7 +5,18 @@
"Interface": "Field",
"Struct": [
{
"fields": [],
"fields": [
{
"name": "Name",
"tag": "json:\"name\"",
"type": "string"
},
{
"name": "Type",
"tag": "json:\"type\"",
"type": "string"
}
],
"name": "Field"
}
],
+13
View File
@@ -0,0 +1,13 @@
package repository
type (
repositoryError string
)
const (
ErrDatabaseError = repositoryError("DatabaseError")
)
func (e repositoryError) Error() string {
return "repository." + string(e)
}
+72
View File
@@ -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]
}
+124
View File
@@ -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")
//}
+9 -7
View File
@@ -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)
}
+27 -39
View File
@@ -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) {
+20 -50
View File
@@ -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)
}
+48
View File
@@ -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)
}
+25
View File
@@ -18,6 +18,9 @@ package types
type (
// Fields
Field struct {
Name string `json:"name" db:"name"`
Type string `json:"type" db:"type"`
changed []string
}
)
@@ -28,3 +31,25 @@ func (Field) New() *Field {
}
/* Getters/setters */
func (f *Field) GetName() string {
return f.Name
}
func (f *Field) SetName(value string) *Field {
if f.Name != value {
f.changed = append(f.changed, "Name")
f.Name = value
}
return f
}
func (f *Field) GetType() string {
return f.Type
}
func (f *Field) SetType(value string) *Field {
if f.Type != value {
f.changed = append(f.changed, "Type")
f.Type = value
}
return f
}