Merge branch 'master' of https://github.com/crusttech/crust
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
SAM_HTTP_ADDR=:8082
|
||||
SAM_DB_DSN=crust:crust@tcp(localhost:3306)/crust?collation=utf8mb4_general_ci
|
||||
+4
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 =="
|
||||
|
||||
@@ -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 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package repository
|
||||
|
||||
type (
|
||||
repositoryError string
|
||||
)
|
||||
|
||||
const (
|
||||
ErrDatabaseError = repositoryError("DatabaseError")
|
||||
)
|
||||
|
||||
func (e repositoryError) Error() string {
|
||||
return "repository." + string(e)
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user