3
0

Add (record change) triggers

This commit is contained in:
Denis Arh
2019-01-09 19:55:04 +01:00
parent 74fd548fa9
commit 19abcb5a89
17 changed files with 1166 additions and 42 deletions
+146
View File
@@ -605,5 +605,151 @@
}
}
]
},
{
"title": "Triggers",
"description": "CRM Triggers",
"package": "crm",
"entrypoint": "trigger",
"path": "/trigger",
"authentication": [],
"apis": [
{
"name": "list",
"method": "GET",
"path": "/",
"title": "List available triggers",
"parameters": {
"get": [
{
"type": "uint64",
"name": "moduleID",
"required": false,
"title": "Filter triggers by module"
}
]
}
},
{
"name": "create",
"method": "POST",
"title": "Create trigger",
"path": "/",
"parameters": {
"post": [
{
"type": "uint64",
"name": "moduleID",
"required": false,
"title": "Module ID"
},
{
"type": "string",
"name": "name",
"required": true,
"title": "Name"
},
{
"type": "[]string",
"name": "actions",
"required": false,
"title": "Actions that trigger this trigger"
},
{
"type": "bool",
"name": "enabled",
"required": false,
"title": "Enabled"
},
{
"type": "string",
"name": "source",
"required": false,
"title": "Trigger source code"
}
]
}
},
{
"name": "read",
"path": "/{triggerID}",
"method": "GET",
"title": "Get trigger details",
"parameters": {
"path": [
{
"type": "uint64",
"name": "triggerID",
"required": true,
"title": "Trigger ID"
}
]
}
},
{
"name": "edit",
"method": "POST",
"title": "Edit trigger",
"path": "/{triggerID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "triggerID",
"required": true,
"title": "Trigger ID"
}
],
"post": [
{
"type": "uint64",
"name": "moduleID",
"required": false,
"title": "Module ID"
},
{
"type": "string",
"name": "name",
"required": true,
"title": "Name"
},
{
"type": "[]string",
"name": "actions",
"required": false,
"title": "Actions that trigger this trigger"
},
{
"type": "bool",
"name": "enabled",
"required": false,
"title": "Enabled"
},
{
"type": "string",
"name": "source",
"required": false,
"title": "Trigger source code"
}
]
}
},
{
"name": "delete",
"path": "/{triggerID}",
"method": "Delete",
"title": "Delete trigger",
"parameters": {
"path": [
{
"type": "uint64",
"name": "triggerID",
"required": true,
"title": "Trigger ID"
}
]
}
}
]
}
]
+149
View File
@@ -0,0 +1,149 @@
{
"Title": "Triggers",
"Description": "CRM Triggers",
"Package": "crm",
"Interface": "Trigger",
"Struct": null,
"Parameters": null,
"Protocol": "",
"Authentication": [],
"Path": "/trigger",
"APIs": [
{
"Name": "list",
"Method": "GET",
"Title": "List available triggers",
"Path": "/",
"Parameters": {
"get": [
{
"name": "moduleID",
"required": false,
"title": "Filter triggers by module",
"type": "uint64"
}
]
}
},
{
"Name": "create",
"Method": "POST",
"Title": "Create trigger",
"Path": "/",
"Parameters": {
"post": [
{
"name": "moduleID",
"required": false,
"title": "Module ID",
"type": "uint64"
},
{
"name": "name",
"required": true,
"title": "Name",
"type": "string"
},
{
"name": "actions",
"required": false,
"title": "Actions that trigger this trigger",
"type": "[]string"
},
{
"name": "enabled",
"required": false,
"title": "Enabled",
"type": "bool"
},
{
"name": "source",
"required": false,
"title": "Trigger source code",
"type": "string"
}
]
}
},
{
"Name": "read",
"Method": "GET",
"Title": "Get trigger details",
"Path": "/{triggerID}",
"Parameters": {
"path": [
{
"name": "triggerID",
"required": true,
"title": "Trigger ID",
"type": "uint64"
}
]
}
},
{
"Name": "edit",
"Method": "POST",
"Title": "Edit trigger",
"Path": "/{triggerID}",
"Parameters": {
"path": [
{
"name": "triggerID",
"required": true,
"title": "Trigger ID",
"type": "uint64"
}
],
"post": [
{
"name": "moduleID",
"required": false,
"title": "Module ID",
"type": "uint64"
},
{
"name": "name",
"required": true,
"title": "Name",
"type": "string"
},
{
"name": "actions",
"required": false,
"title": "Actions that trigger this trigger",
"type": "[]string"
},
{
"name": "enabled",
"required": false,
"title": "Enabled",
"type": "bool"
},
{
"name": "source",
"required": false,
"title": "Trigger source code",
"type": "string"
}
]
}
},
{
"Name": "delete",
"Method": "Delete",
"Title": "Delete trigger",
"Path": "/{triggerID}",
"Parameters": {
"path": [
{
"name": "triggerID",
"required": true,
"title": "Trigger ID",
"type": "uint64"
}
]
}
}
]
}
+1 -1
View File
@@ -37,7 +37,7 @@ function types {
CGO_ENABLED=0 go build -o ./build/gen-type-set codegen/v2/type-set.go
fi
./build/gen-type-set --types Module,Page,Chart -no-pk-types ModuleField --output crm/types/type.gen.go
./build/gen-type-set --types Module,Page,Chart,Trigger -no-pk-types ModuleField --output crm/types/type.gen.go
./build/gen-type-set --types MessageAttachment --output sam/types/attachment.gen.go
./build/gen-type-set --types Channel --output sam/types/channel.gen.go
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
CREATE TABLE `crm_trigger` (
`id` BIGINT(20) UNSIGNED NOT NULL,
`name` VARCHAR(64) NOT NULL COMMENT 'The name of the trigger',
`enabled` BOOLEAN NOT NULL COMMENT 'Trigger enabled?',
`actions` TEXT NOT NULL COMMENT 'All actions that trigger it',
`source` TEXT NOT NULL COMMENT 'Trigger source',
`rel_module` BIGINT(20) UNSIGNED NULL COMMENT 'Primary module',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT NULL,
`deleted_at` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+94
View File
@@ -0,0 +1,94 @@
package repository
import (
"context"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/titpetric/factory"
"gopkg.in/Masterminds/squirrel.v1"
"github.com/crusttech/crust/crm/types"
)
type (
TriggerRepository interface {
With(ctx context.Context, db *factory.DB) TriggerRepository
FindByID(id uint64) (*types.Trigger, error)
Find(filter types.TriggerFilter) (types.TriggerSet, error)
Create(mod *types.Trigger) (*types.Trigger, error)
Update(mod *types.Trigger) (*types.Trigger, error)
DeleteByID(id uint64) error
}
trigger struct {
*repository
}
)
func Trigger(ctx context.Context, db *factory.DB) TriggerRepository {
return (&trigger{}).With(ctx, db)
}
func (r *trigger) With(ctx context.Context, db *factory.DB) TriggerRepository {
return &trigger{
repository: r.repository.With(ctx, db),
}
}
func (r *trigger) FindByID(id uint64) (*types.Trigger, error) {
mod := &types.Trigger{}
query := r.query().Where("id = ?", id)
if sql, args, err := query.ToSql(); err != nil {
return nil, err
} else {
return mod, r.db().Get(mod, sql, args...)
}
}
func (r *trigger) Find(filter types.TriggerFilter) (mod types.TriggerSet, err error) {
query := r.query()
if filter.ModuleID > 0 {
query = query.Where("rel_module = ?", filter.ModuleID)
}
spew.Dump(filter)
spew.Dump(query.ToSql())
if sql, args, err := query.ToSql(); err != nil {
return nil, err
} else {
return mod, r.db().Select(&mod, sql, args...)
}
}
func (r trigger) query() (query squirrel.SelectBuilder) {
query = squirrel.Select().
Columns(
"id", "name", "actions", "enabled", "source", "rel_module",
"created_at", "updated_at", "deleted_at").
From("crm_trigger").
OrderBy("id DESC")
return
}
func (r *trigger) Create(mod *types.Trigger) (*types.Trigger, error) {
mod.ID = factory.Sonyflake.NextID()
mod.CreatedAt = time.Now()
return mod, r.db().Insert("crm_trigger", mod)
}
func (r *trigger) Update(mod *types.Trigger) (*types.Trigger, error) {
now := time.Now()
mod.UpdatedAt = &now
return mod, r.db().Replace("crm_trigger", mod)
}
func (r *trigger) DeleteByID(id uint64) error {
_, err := r.db().Exec("DELETE FROM crm_trigger WHERE id = ?", id)
return err
}
+97
View File
@@ -0,0 +1,97 @@
package handlers
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `trigger.go`, `trigger.util.go` or `trigger_test.go` to
implement your API calls, helper functions and tests. The file `trigger.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"context"
"github.com/go-chi/chi"
"net/http"
"github.com/titpetric/factory/resputil"
"github.com/crusttech/crust/crm/rest/request"
)
// Internal API interface
type TriggerAPI interface {
List(context.Context, *request.TriggerList) (interface{}, error)
Create(context.Context, *request.TriggerCreate) (interface{}, error)
Read(context.Context, *request.TriggerRead) (interface{}, error)
Edit(context.Context, *request.TriggerEdit) (interface{}, error)
Delete(context.Context, *request.TriggerDelete) (interface{}, error)
}
// HTTP API interface
type Trigger struct {
List func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Edit func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
}
func NewTrigger(th TriggerAPI) *Trigger {
return &Trigger{
List: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewTriggerList()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
return th.List(r.Context(), params)
})
},
Create: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewTriggerCreate()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
return th.Create(r.Context(), params)
})
},
Read: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewTriggerRead()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
return th.Read(r.Context(), params)
})
},
Edit: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewTriggerEdit()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
return th.Edit(r.Context(), params)
})
},
Delete: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewTriggerDelete()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
return th.Delete(r.Context(), params)
})
},
}
}
func (th *Trigger) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
r.Group(func(r chi.Router) {
r.Use(middlewares...)
r.Route("/trigger", func(r chi.Router) {
r.Get("/", th.List)
r.Post("/", th.Create)
r.Get("/{triggerID}", th.Read)
r.Post("/{triggerID}", th.Edit)
r.Delete("/{triggerID}", th.Delete)
})
})
}
+288
View File
@@ -0,0 +1,288 @@
package request
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `trigger.go`, `trigger.util.go` or `trigger_test.go` to
implement your API calls, helper functions and tests. The file `trigger.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"encoding/json"
"io"
"mime/multipart"
"net/http"
"strings"
"github.com/go-chi/chi"
"github.com/pkg/errors"
)
var _ = chi.URLParam
var _ = multipart.FileHeader{}
// Trigger list request parameters
type TriggerList struct {
ModuleID uint64 `json:",string"`
}
func NewTriggerList() *TriggerList {
return &TriggerList{}
}
func (t *TriggerList) Fill(r *http.Request) (err error) {
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(r.Body).Decode(t)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = r.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := get["moduleID"]; ok {
t.ModuleID = parseUInt64(val)
}
return err
}
var _ RequestFiller = NewTriggerList()
// Trigger create request parameters
type TriggerCreate struct {
ModuleID uint64 `json:",string"`
Name string
Actions []string
Enabled bool
Source string
}
func NewTriggerCreate() *TriggerCreate {
return &TriggerCreate{}
}
func (t *TriggerCreate) Fill(r *http.Request) (err error) {
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(r.Body).Decode(t)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = r.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := post["moduleID"]; ok {
t.ModuleID = parseUInt64(val)
}
if val, ok := post["name"]; ok {
t.Name = val
}
if val, ok := post["enabled"]; ok {
t.Enabled = parseBool(val)
}
if val, ok := post["source"]; ok {
t.Source = val
}
return err
}
var _ RequestFiller = NewTriggerCreate()
// Trigger read request parameters
type TriggerRead struct {
TriggerID uint64 `json:",string"`
}
func NewTriggerRead() *TriggerRead {
return &TriggerRead{}
}
func (t *TriggerRead) Fill(r *http.Request) (err error) {
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(r.Body).Decode(t)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = r.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
t.TriggerID = parseUInt64(chi.URLParam(r, "triggerID"))
return err
}
var _ RequestFiller = NewTriggerRead()
// Trigger edit request parameters
type TriggerEdit struct {
TriggerID uint64 `json:",string"`
ModuleID uint64 `json:",string"`
Name string
Actions []string
Enabled bool
Source string
}
func NewTriggerEdit() *TriggerEdit {
return &TriggerEdit{}
}
func (t *TriggerEdit) Fill(r *http.Request) (err error) {
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(r.Body).Decode(t)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = r.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
t.TriggerID = parseUInt64(chi.URLParam(r, "triggerID"))
if val, ok := post["moduleID"]; ok {
t.ModuleID = parseUInt64(val)
}
if val, ok := post["name"]; ok {
t.Name = val
}
if val, ok := post["enabled"]; ok {
t.Enabled = parseBool(val)
}
if val, ok := post["source"]; ok {
t.Source = val
}
return err
}
var _ RequestFiller = NewTriggerEdit()
// Trigger delete request parameters
type TriggerDelete struct {
TriggerID uint64 `json:",string"`
}
func NewTriggerDelete() *TriggerDelete {
return &TriggerDelete{}
}
func (t *TriggerDelete) Fill(r *http.Request) (err error) {
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(r.Body).Decode(t)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = r.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := r.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := r.Form
for name, param := range postVars {
post[name] = string(param[0])
}
t.TriggerID = parseUInt64(chi.URLParam(r, "triggerID"))
return err
}
var _ RequestFiller = NewTriggerDelete()
+5 -7
View File
@@ -9,11 +9,10 @@ import (
func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) {
var (
module = Module{}.New()
page = Page{}.New()
chart = Chart{}.New()
workflow = Workflow{}.New()
job = Job{}.New()
module = Module{}.New()
page = Page{}.New()
chart = Chart{}.New()
trigger = Trigger{}.New()
)
// Initialize handers & controllers.
@@ -25,8 +24,7 @@ func MountRoutes(jwtAuth auth.TokenEncoder) func(chi.Router) {
handlers.NewPage(page).MountRoutes(r)
handlers.NewModule(module).MountRoutes(r)
handlers.NewChart(chart).MountRoutes(r)
handlers.NewWorkflow(workflow).MountRoutes(r)
handlers.NewJob(job).MountRoutes(r)
handlers.NewTrigger(trigger).MountRoutes(r)
})
}
}
+75
View File
@@ -0,0 +1,75 @@
package rest
import (
"context"
"github.com/titpetric/factory/resputil"
"github.com/crusttech/crust/crm/rest/request"
"github.com/crusttech/crust/crm/service"
"github.com/crusttech/crust/crm/types"
"github.com/pkg/errors"
)
var _ = errors.Wrap
type Trigger struct {
trigger service.TriggerService
}
func (Trigger) New() *Trigger {
return &Trigger{
trigger: service.DefaultTrigger,
}
}
func (ctrl *Trigger) List(ctx context.Context, r *request.TriggerList) (interface{}, error) {
filter := types.TriggerFilter{}
if r.ModuleID > 0 {
filter.ModuleID = r.ModuleID
}
return ctrl.trigger.With(ctx).Find(filter)
}
func (ctrl *Trigger) Create(ctx context.Context, r *request.TriggerCreate) (interface{}, error) {
trigger := &types.Trigger{
Name: r.Name,
Actions: r.Actions,
Enabled: r.Enabled,
Source: r.Source,
}
if r.ModuleID > 0 {
trigger.ModuleID = r.ModuleID
}
return ctrl.trigger.With(ctx).Create(trigger)
}
func (ctrl *Trigger) Read(ctx context.Context, r *request.TriggerRead) (interface{}, error) {
return ctrl.trigger.With(ctx).FindByID(r.TriggerID)
}
func (ctrl *Trigger) Edit(ctx context.Context, r *request.TriggerEdit) (interface{}, error) {
if trigger, err := ctrl.trigger.FindByID(r.TriggerID); err != nil {
return nil, err
} else {
trigger.Name = r.Name
trigger.Actions = r.Actions
trigger.Enabled = r.Enabled
trigger.Source = r.Source
if r.ModuleID > 0 {
trigger.ModuleID = r.ModuleID
}
return ctrl.trigger.With(ctx).Update(trigger)
}
}
func (ctrl *Trigger) Delete(ctx context.Context, r *request.TriggerDelete) (interface{}, error) {
return resputil.OK(), ctrl.trigger.With(ctx).DeleteByID(r.TriggerID)
}
+1 -1
View File
@@ -50,7 +50,7 @@ func TestMain(m *testing.M) {
// clean up tables
{
for _, name := range []string{"crm_module", "crm_module_form", "crm_record", "crm_record_column", "crm_page", "sys_user"} {
for _, name := range []string{"crm_chart", "crm_trigger", "crm_module", "crm_module_form", "crm_record", "crm_record_column", "crm_page", "sys_user"} {
_, err := db.Exec("truncate " + name)
if err != nil {
panic("Error when clearing " + name + ": " + err.Error())
+7 -9
View File
@@ -5,22 +5,20 @@ import (
)
var (
o sync.Once
DefaultRecord RecordService
DefaultModule ModuleService
DefaultChart ChartService
DefaultPage PageService
DefaultWorkflow WorkflowService
DefaultJob JobService
o sync.Once
DefaultRecord RecordService
DefaultModule ModuleService
DefaultTrigger TriggerService
DefaultChart ChartService
DefaultPage PageService
)
func Init() {
o.Do(func() {
DefaultRecord = Record()
DefaultModule = Module()
DefaultTrigger = Trigger()
DefaultPage = Page()
DefaultChart = Chart()
DefaultWorkflow = Workflow()
DefaultJob = Job()
})
}
+87
View File
@@ -0,0 +1,87 @@
package service
import (
"context"
"errors"
"github.com/titpetric/factory"
"github.com/crusttech/crust/crm/repository"
"github.com/crusttech/crust/crm/types"
)
type (
trigger struct {
db *factory.DB
ctx context.Context
triggerRepo repository.TriggerRepository
moduleRepo repository.ModuleRepository
}
TriggerService interface {
With(ctx context.Context) TriggerService
FindByID(triggerID uint64) (*types.Trigger, error)
Find(filter types.TriggerFilter) (set types.TriggerSet, err error)
Create(trigger *types.Trigger) (*types.Trigger, error)
Update(trigger *types.Trigger) (*types.Trigger, error)
DeleteByID(triggerID uint64) error
}
)
func Trigger() TriggerService {
return (&trigger{}).With(context.Background())
}
func (s *trigger) With(ctx context.Context) TriggerService {
db := repository.DB(ctx)
return &trigger{
db: db,
ctx: ctx,
triggerRepo: repository.Trigger(ctx, db),
moduleRepo: repository.Module(ctx, db),
}
}
func (s *trigger) FindByID(id uint64) (*types.Trigger, error) {
return s.triggerRepo.FindByID(id)
}
func (s *trigger) Find(filter types.TriggerFilter) (types.TriggerSet, error) {
return s.triggerRepo.Find(filter)
}
func (s *trigger) Create(trigger *types.Trigger) (p *types.Trigger, err error) {
validate := func() error {
return nil
}
if err := validate(); err != nil {
return nil, err
}
return p, s.db.Transaction(func() (err error) {
p, err = s.triggerRepo.Create(trigger)
return
})
}
func (s *trigger) Update(trigger *types.Trigger) (p *types.Trigger, err error) {
validate := func() error {
if trigger.ID == 0 {
return errors.New("could not update trigger, invalid ID")
}
return nil
}
if err := validate(); err != nil {
return nil, err
}
return p, s.db.Transaction(func() (err error) {
p, err = s.triggerRepo.Update(trigger)
return
})
}
func (s *trigger) DeleteByID(id uint64) error {
return s.triggerRepo.DeleteByID(id)
}
+68
View File
@@ -0,0 +1,68 @@
package service
import (
"context"
"testing"
"github.com/pkg/errors"
"github.com/crusttech/crust/crm/types"
)
func TestTrigger(t *testing.T) {
repository := Trigger().With(context.Background())
// the trigger object we're working with
trigger := &types.Trigger{
Name: "Test",
ModuleID: 123,
}
{
{
m, err := repository.Update(trigger)
assert(t, m == nil, "Expected empty return for invalid update, got %#v", m)
assert(t, err != nil, "Expected error when updating invalid content")
}
// create trigger
m, err := repository.Create(trigger)
assert(t, err == nil, "Error when creating trigger: %+v", err)
assert(t, m.ID > 0, "Expected auto generated ID")
{
_, err := repository.Create(trigger)
assert(t, err == nil, "Unexpected error when creating trigger, %+v", err)
}
// fetch created trigger
{
ms, err := repository.FindByID(m.ID)
assert(t, err == nil, "Error when retrieving trigger by id: %+v", err)
assert(t, ms.ID == m.ID, "Expected ID from database to match, %+v", errors.Errorf("%d != %d", m.ID, ms.ID))
assert(t, ms.Name == m.Name, "Expected Name from database to match, %+v", errors.Errorf("%s != %s", m.Name, ms.Name))
}
// update created trigger
{
m.Name = "Updated test"
_, err := repository.Update(m)
assert(t, err == nil, "Error when updating trigger, %+v", err)
}
// re-fetch trigger
{
ms, err := repository.FindByID(m.ID)
assert(t, err == nil, "Error when retrieving trigger by id: %+v", err)
assert(t, ms.ID == m.ID, "re-fetch: Expected ID from database to match, %d != %d", m.ID, ms.ID)
assert(t, ms.Name == m.Name, "Expected Name from database to match, %s != %s", m.Name, ms.Name)
}
// delete trigger
{
err := repository.DeleteByID(m.ID)
assert(t, err == nil, "Error when deleting trigger by id: %+v", err)
}
}
}
+45
View File
@@ -0,0 +1,45 @@
package types
import (
"database/sql/driver"
"strings"
"time"
)
type (
ActionSet []string
Trigger struct {
ID uint64 `json:"triggerID,string" db:"id"`
ModuleID uint64 `json:"moduleID,string,omitempty" db:"rel_module"`
Name string `json:"name" db:"name"`
Actions ActionSet `json:"actions" db:"actions"`
Enabled bool `json:"enabled" db:"enabled"`
Source string `json:"source" db:"source"`
CreatedAt time.Time `db:"created_at" json:"createdAt,omitempty"`
UpdatedAt *time.Time `db:"updated_at" json:"updatedAt,omitempty"`
DeletedAt *time.Time `db:"deleted_at" json:"deletedAt,omitempty"`
}
TriggerFilter struct {
ModuleID uint64 `json:"moduleID,string"`
}
)
func (set *ActionSet) Scan(src interface{}) error {
if ser, ok := src.([]uint8); ok {
var tmp = make([]string, 0)
for _, a := range strings.Split(string(ser), ",") {
if a = strings.TrimSpace(a); len(a) > 0 {
tmp = append(tmp, a)
}
}
*set = ActionSet(tmp)
}
return nil
}
func (set ActionSet) Value() (driver.Value, error) {
return strings.Trim(strings.Join(set, ","), " ,"), nil
}
+61
View File
@@ -19,6 +19,11 @@ type (
// This type is auto-generated.
ChartSet []*Chart
// TriggerSet slice of Trigger
//
// This type is auto-generated.
TriggerSet []*Trigger
// ModuleFieldSet slice of ModuleField
//
// This type is auto-generated.
@@ -193,6 +198,62 @@ func (set ChartSet) IDs() (IDs []uint64) {
return
}
// Walk iterates through every slice item and calls w(Trigger) err
//
// This function is auto-generated.
func (set TriggerSet) Walk(w func(*Trigger) error) (err error) {
for i := range set {
if err = w(set[i]); err != nil {
return
}
}
return
}
// Filter iterates through every slice item, calls f(Trigger) (bool, err) and return filtered slice
//
// This function is auto-generated.
func (set TriggerSet) Filter(f func(*Trigger) (bool, error)) (out TriggerSet, err error) {
var ok bool
out = TriggerSet{}
for i := range set {
if ok, err = f(set[i]); err != nil {
return
} else if ok {
out = append(out, set[i])
}
}
return
}
// FindByID finds items from slice by its ID property
//
// This function is auto-generated.
func (set TriggerSet) FindByID(ID uint64) *Trigger {
for i := range set {
if set[i].ID == ID {
return set[i]
}
}
return nil
}
// IDs returns a slice of uint64s from all items in the set
//
// This function is auto-generated.
func (set TriggerSet) IDs() (IDs []uint64) {
IDs = make([]uint64, len(set))
for i := range set {
IDs[i] = set[i].ID
}
return
}
// Walk iterates through every slice item and calls w(ModuleField) err
//
// This function is auto-generated.
+26 -23
View File
@@ -369,82 +369,85 @@ CRM module pages
# Workflows
# Triggers
CRM workflow definitions
CRM Triggers
## List available workflows
## List available triggers
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/workflow/` | HTTP/S | GET | |
| `/trigger/` | HTTP/S | GET | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| moduleID | uint64 | GET | Filter triggers by module | N/A | NO |
## Create new workflow
## Create trigger
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/workflow/` | HTTP/S | POST | |
| `/trigger/` | HTTP/S | POST | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| name | string | POST | Workflow name | N/A | YES |
| tasks | types.WorkflowTaskSet | POST | Type ID | N/A | NO |
| onError | types.WorkflowTaskSet | POST | Type ID | N/A | NO |
| timeout | int | POST | Timeout in seconds | N/A | NO |
| moduleID | uint64 | POST | Module ID | N/A | NO |
| name | string | POST | Name | N/A | YES |
| actions | []string | POST | Actions that trigger this trigger | N/A | NO |
| enabled | bool | POST | Enabled | N/A | NO |
| source | string | POST | Trigger source code | N/A | NO |
## Get workflow details
## Get trigger details
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/workflow/{workflowID}` | HTTP/S | GET | |
| `/trigger/{triggerID}` | HTTP/S | GET | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| workflowID | string | PATH | Workflow ID | N/A | YES |
| triggerID | uint64 | PATH | Trigger ID | N/A | YES |
## Update workflow details
## Edit trigger
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/workflow/{workflowID}` | HTTP/S | POST | |
| `/trigger/{triggerID}` | HTTP/S | POST | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| workflowID | string | PATH | Workflow ID | N/A | YES |
| name | string | POST | Workflow name | N/A | YES |
| tasks | types.WorkflowTaskSet | POST | Type ID | N/A | NO |
| onError | types.WorkflowTaskSet | POST | Type ID | N/A | NO |
| timeout | int | POST | Timeout in seconds | N/A | NO |
| triggerID | uint64 | PATH | Trigger ID | N/A | YES |
| moduleID | uint64 | POST | Module ID | N/A | NO |
| name | string | POST | Name | N/A | YES |
| actions | []string | POST | Actions that trigger this trigger | N/A | NO |
| enabled | bool | POST | Enabled | N/A | NO |
| source | string | POST | Trigger source code | N/A | NO |
## Delete workflow
## Delete trigger
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/workflow/{workflowID}` | HTTP/S | DELETE | |
| `/trigger/{triggerID}` | HTTP/S | Delete | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| workflowID | string | PATH | Workflow ID | N/A | YES |
| triggerID | uint64 | PATH | Trigger ID | N/A | YES |