snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
444
server/system/automation/actionlog_handler.gen.go
generated
Normal file
444
server/system/automation/actionlog_handler.gen.go
generated
Normal file
@@ -0,0 +1,444 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// system/automation/actionlog_handler.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
atypes "github.com/cortezaproject/corteza/server/automation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
actionlogHandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h actionlogHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.Search(),
|
||||
h.Each(),
|
||||
h.Record(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
actionlogSearchArgs struct {
|
||||
hasFromTimestamp bool
|
||||
FromTimestamp *time.Time
|
||||
|
||||
hasToTimestamp bool
|
||||
ToTimestamp *time.Time
|
||||
|
||||
hasBeforeActionID bool
|
||||
BeforeActionID uint64
|
||||
|
||||
hasActorID bool
|
||||
ActorID uint64
|
||||
|
||||
hasOrigin bool
|
||||
Origin string
|
||||
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasAction bool
|
||||
Action string
|
||||
|
||||
hasLimit bool
|
||||
Limit uint64
|
||||
}
|
||||
|
||||
actionlogSearchResults struct {
|
||||
Actions []*actionlog.Action
|
||||
}
|
||||
)
|
||||
|
||||
// Search function Action log search
|
||||
//
|
||||
// expects implementation of search function:
|
||||
//
|
||||
// func (h actionlogHandler) search(ctx context.Context, args *actionlogSearchArgs) (results *actionlogSearchResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h actionlogHandler) Search() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "actionlogSearch",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"actionlog": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Action log search",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "fromTimestamp",
|
||||
Types: []string{"DateTime"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "From timestamp",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "toTimestamp",
|
||||
Types: []string{"DateTime"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "To timestamp",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "beforeActionID",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Before action ID",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "actorID",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Actor ID",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "origin",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Origin",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "action",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Action",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "limit",
|
||||
Types: []string{"UnsignedInteger"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Limit",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "actions",
|
||||
Types: []string{"Action"},
|
||||
IsArray: true,
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &actionlogSearchArgs{
|
||||
hasFromTimestamp: in.Has("fromTimestamp"),
|
||||
hasToTimestamp: in.Has("toTimestamp"),
|
||||
hasBeforeActionID: in.Has("beforeActionID"),
|
||||
hasActorID: in.Has("actorID"),
|
||||
hasOrigin: in.Has("origin"),
|
||||
hasResource: in.Has("resource"),
|
||||
hasAction: in.Has("action"),
|
||||
hasLimit: in.Has("limit"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *actionlogSearchResults
|
||||
if results, err = h.search(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Actions (*actionlog.Action) to Array (of Action)
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
tarr = make([]expr.TypedValue, len(results.Actions))
|
||||
)
|
||||
|
||||
for i := range results.Actions {
|
||||
if tarr[i], err = h.reg.Type("Action").Cast(results.Actions[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if tval, err = expr.NewArray(tarr); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "actions", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
actionlogEachArgs struct {
|
||||
hasFromTimestamp bool
|
||||
FromTimestamp *time.Time
|
||||
|
||||
hasToTimestamp bool
|
||||
ToTimestamp *time.Time
|
||||
|
||||
hasBeforeActionID bool
|
||||
BeforeActionID uint64
|
||||
|
||||
hasActorID bool
|
||||
ActorID uint64
|
||||
|
||||
hasOrigin bool
|
||||
Origin string
|
||||
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasAction bool
|
||||
Action string
|
||||
|
||||
hasLimit bool
|
||||
Limit uint64
|
||||
}
|
||||
|
||||
actionlogEachResults struct {
|
||||
Action *actionlog.Action
|
||||
}
|
||||
)
|
||||
|
||||
// Each function Action log
|
||||
//
|
||||
// expects implementation of each function:
|
||||
//
|
||||
// func (h actionlogHandler) each(ctx context.Context, args *actionlogEachArgs) (results *actionlogEachResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h actionlogHandler) Each() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "actionlogEach",
|
||||
Kind: "iterator",
|
||||
Labels: map[string]string{"actionlog": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Action log",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "fromTimestamp",
|
||||
Types: []string{"DateTime"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "From timestamp",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "toTimestamp",
|
||||
Types: []string{"DateTime"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "To timestamp",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "beforeActionID",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Before action ID",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "actorID",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Actor ID",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "origin",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Origin",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "action",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Action",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "limit",
|
||||
Types: []string{"UnsignedInteger"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Limit",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "action",
|
||||
Types: []string{"Action"},
|
||||
},
|
||||
},
|
||||
|
||||
Iterator: func(ctx context.Context, in *expr.Vars) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
args = &actionlogEachArgs{
|
||||
hasFromTimestamp: in.Has("fromTimestamp"),
|
||||
hasToTimestamp: in.Has("toTimestamp"),
|
||||
hasBeforeActionID: in.Has("beforeActionID"),
|
||||
hasActorID: in.Has("actorID"),
|
||||
hasOrigin: in.Has("origin"),
|
||||
hasResource: in.Has("resource"),
|
||||
hasAction: in.Has("action"),
|
||||
hasLimit: in.Has("limit"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return h.each(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
actionlogRecordArgs struct {
|
||||
hasAction bool
|
||||
Action string
|
||||
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasError bool
|
||||
Error string
|
||||
|
||||
hasSeverity bool
|
||||
Severity string
|
||||
|
||||
hasDescription bool
|
||||
Description string
|
||||
|
||||
hasMeta bool
|
||||
Meta *expr.Vars
|
||||
}
|
||||
)
|
||||
|
||||
// Record function Record action into action log
|
||||
//
|
||||
// expects implementation of record function:
|
||||
//
|
||||
// func (h actionlogHandler) record(ctx context.Context, args *actionlogRecordArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h actionlogHandler) Record() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "actionlogRecord",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"actionlog": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Record action into action log",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "action",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Action",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "error",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Error",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "severity",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Severity",
|
||||
Visual: map[string]interface{}{"options": []interface{}{"emergency", "alert", "critical", "err", "warning", "notice", "info", "debug"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "description",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Description",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "meta",
|
||||
Types: []string{"Vars"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Meta",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &actionlogRecordArgs{
|
||||
hasAction: in.Has("action"),
|
||||
hasResource: in.Has("resource"),
|
||||
hasError: in.Has("error"),
|
||||
hasSeverity: in.Has("severity"),
|
||||
hasDescription: in.Has("description"),
|
||||
hasMeta: in.Has("meta"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return out, h.record(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
154
server/system/automation/actionlog_handler.go
Normal file
154
server/system/automation/actionlog_handler.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
. "github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/id"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
)
|
||||
|
||||
type (
|
||||
actionlogHandler struct {
|
||||
reg actionlogHandlerRegistry
|
||||
svc actionlog.Recorder
|
||||
}
|
||||
|
||||
actionSetIterator struct {
|
||||
// Item buffer, current item pointer, and total items traversed
|
||||
ptr uint
|
||||
buffer actionlog.ActionSet
|
||||
total uint
|
||||
|
||||
// When filter limit is set, this constraints it
|
||||
iterLimit uint
|
||||
useIterLimit bool
|
||||
|
||||
// Item loader for additional chunks
|
||||
filter actionlog.Filter
|
||||
loader func() error
|
||||
}
|
||||
)
|
||||
|
||||
func ActionlogHandler(reg actionlogHandlerRegistry, svc actionlog.Recorder) *actionlogHandler {
|
||||
h := &actionlogHandler{
|
||||
reg: reg,
|
||||
svc: svc,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h actionlogHandler) search(ctx context.Context, args *actionlogSearchArgs) (results *actionlogSearchResults, err error) {
|
||||
results = &actionlogSearchResults{}
|
||||
|
||||
var (
|
||||
f = actionlog.Filter{
|
||||
FromTimestamp: args.FromTimestamp,
|
||||
ToTimestamp: args.ToTimestamp,
|
||||
BeforeActionID: args.BeforeActionID,
|
||||
Origin: args.Origin,
|
||||
Resource: args.Resource,
|
||||
Action: args.Action,
|
||||
Limit: uint(args.Limit),
|
||||
}
|
||||
)
|
||||
|
||||
if args.ActorID > 0 {
|
||||
f.ActorID = id.Strings(args.ActorID)
|
||||
}
|
||||
|
||||
results.Actions, _, err = h.svc.Find(ctx, f)
|
||||
return
|
||||
}
|
||||
|
||||
func (h actionlogHandler) each(ctx context.Context, args *actionlogEachArgs) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
i = &actionSetIterator{}
|
||||
f = actionlog.Filter{
|
||||
FromTimestamp: args.FromTimestamp,
|
||||
ToTimestamp: args.ToTimestamp,
|
||||
BeforeActionID: args.BeforeActionID,
|
||||
Origin: args.Origin,
|
||||
Resource: args.Resource,
|
||||
Action: args.Action,
|
||||
}
|
||||
)
|
||||
|
||||
if args.ActorID > 0 {
|
||||
f.ActorID = id.Strings(args.ActorID)
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
i.useIterLimit = true
|
||||
i.iterLimit = uint(args.Limit)
|
||||
|
||||
f.Limit = uint(args.Limit)
|
||||
|
||||
if args.Limit > uint64(wfexec.MaxIteratorBufferSize) {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
i.iterLimit = uint(args.Limit)
|
||||
} else {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
|
||||
i.filter = f
|
||||
i.loader = func() (err error) {
|
||||
i.total += i.ptr
|
||||
i.ptr = 0
|
||||
|
||||
if len(i.buffer) > 0 {
|
||||
i.filter.BeforeActionID = i.buffer[len(i.buffer)-1].ID
|
||||
}
|
||||
i.buffer, i.filter, err = h.svc.Find(ctx, i.filter)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Initial load
|
||||
return i, i.loader()
|
||||
}
|
||||
|
||||
func (h actionlogHandler) record(ctx context.Context, args *actionlogRecordArgs) (err error) {
|
||||
a := &actionlog.Action{
|
||||
Resource: args.Resource,
|
||||
Action: args.Action,
|
||||
Error: args.Error,
|
||||
Severity: actionlog.NewSeverity(args.Severity),
|
||||
Description: args.Description,
|
||||
}
|
||||
|
||||
if args.Meta != nil {
|
||||
a.Meta = args.Meta.Dict()
|
||||
}
|
||||
|
||||
ctx = actionlog.RequestOriginToContext(ctx, actionlog.RequestOrigin_Automation)
|
||||
|
||||
h.svc.Record(ctx, a)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *actionSetIterator) More(context.Context, *Vars) (bool, error) {
|
||||
return wfexec.GenericResourceNextCheck(i.useIterLimit, i.ptr, uint(len(i.buffer)), i.total, i.iterLimit, i.iterLimit > uint(len(i.buffer))), nil
|
||||
}
|
||||
|
||||
func (i *actionSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
|
||||
|
||||
func (i *actionSetIterator) Next(context.Context, *Vars) (out *Vars, err error) {
|
||||
if len(i.buffer)-int(i.ptr) <= 0 {
|
||||
if err = i.loader(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
out = &Vars{}
|
||||
out.Set("user", Must(NewUser(i.buffer[i.ptr])))
|
||||
out.Set("index", Must(NewInteger(i.total+i.ptr)))
|
||||
|
||||
i.ptr++
|
||||
return out, nil
|
||||
}
|
||||
78
server/system/automation/actionlog_handler.yaml
Normal file
78
server/system/automation/actionlog_handler.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
imports:
|
||||
- time
|
||||
- github.com/cortezaproject/corteza/server/pkg/actionlog
|
||||
|
||||
snippets:
|
||||
filterParams: &filterParams
|
||||
fromTimestamp: { meta: { label: From timestamp }, types: [ { wf: DateTime } ] }
|
||||
toTimestamp: { meta: { label: To timestamp }, types: [ { wf: DateTime } ] }
|
||||
beforeActionID: { meta: { label: Before action ID }, types: [ { wf: ID } ] }
|
||||
actorID: { meta: { label: Actor ID }, types: [ { wf: ID } ] }
|
||||
origin: { meta: { label: Origin }, types: [ { wf: String } ] }
|
||||
resource: { meta: { label: Resource }, types: [ { wf: String } ] }
|
||||
action: { meta: { label: Action }, types: [ { wf: String } ] }
|
||||
limit: { meta: { label: Limit }, types: [ { wf: UnsignedInteger } ] }
|
||||
|
||||
rvAction: &rvAction
|
||||
wf: Action
|
||||
|
||||
labels: &labels
|
||||
actionlog: "step,workflow"
|
||||
|
||||
functions:
|
||||
search:
|
||||
meta:
|
||||
short: Action log search
|
||||
params: *filterParams
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
actions:
|
||||
<<: *rvAction
|
||||
isArray: true
|
||||
|
||||
each:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Action log
|
||||
params: *filterParams
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
action: *rvAction
|
||||
|
||||
record:
|
||||
meta:
|
||||
short: Record action into action log
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
action:
|
||||
meta: { label: Action }
|
||||
types: [ { wf: String } ]
|
||||
resource:
|
||||
meta: { label: Resource }
|
||||
types: [ { wf: String } ]
|
||||
error:
|
||||
meta: { label: Error }
|
||||
types: [ { wf: String } ]
|
||||
severity:
|
||||
types: [ { wf: String } ]
|
||||
meta:
|
||||
label: Severity
|
||||
visual:
|
||||
options:
|
||||
- "emergency"
|
||||
- "alert"
|
||||
- "critical"
|
||||
- "err"
|
||||
- "warning"
|
||||
- "notice"
|
||||
- "info"
|
||||
- "debug"
|
||||
description:
|
||||
meta: { label: Description }
|
||||
types: [ { wf: String } ]
|
||||
meta:
|
||||
meta: { label: Meta }
|
||||
types: [ { wf: Vars, go: '*expr.Vars' } ]
|
||||
1610
server/system/automation/expr_types.gen.go
generated
Normal file
1610
server/system/automation/expr_types.gen.go
generated
Normal file
File diff suppressed because it is too large
Load Diff
325
server/system/automation/expr_types.go
Normal file
325
server/system/automation/expr_types.go
Normal file
@@ -0,0 +1,325 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
renderedDocument struct {
|
||||
Document io.Reader
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
)
|
||||
|
||||
func CastToUser(val interface{}) (out *types.User, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.User{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToUser(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.User:
|
||||
return val, nil
|
||||
case map[string]interface{}:
|
||||
out = &types.User{}
|
||||
m, _ := json.Marshal(val)
|
||||
_ = json.Unmarshal(m, out)
|
||||
return out, nil
|
||||
case nil:
|
||||
return &types.User{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToRole(val interface{}) (out *types.Role, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.Role{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToRole(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Role:
|
||||
return val, nil
|
||||
case map[string]interface{}:
|
||||
out = &types.Role{}
|
||||
m, _ := json.Marshal(val)
|
||||
_ = json.Unmarshal(m, out)
|
||||
return out, nil
|
||||
case nil:
|
||||
return &types.Role{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
func CastToReminder(val interface{}) (out *types.Reminder, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.Reminder{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToReminder(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Reminder:
|
||||
return val, nil
|
||||
case map[string]interface{}:
|
||||
out = &types.Reminder{}
|
||||
// m, _ := json.Marshal(val)
|
||||
// _ = json.Unmarshal(m, out)
|
||||
for k,v := range val {
|
||||
if err = assignToReminder(out, k,v);err != nil{
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
case nil:
|
||||
return &types.Reminder{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToTemplate(val interface{}) (out *types.Template, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.Template{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToTemplate(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Template:
|
||||
return val, nil
|
||||
case map[string]interface{}:
|
||||
out = &types.Template{}
|
||||
m, _ := json.Marshal(val)
|
||||
_ = json.Unmarshal(m, out)
|
||||
return out, nil
|
||||
case nil:
|
||||
return &types.Template{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToTemplateMeta(val interface{}) (out types.TemplateMeta, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = types.TemplateMeta{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToTemplateMeta(&out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case types.TemplateMeta:
|
||||
return val, nil
|
||||
case map[string]interface{}:
|
||||
out = types.TemplateMeta{}
|
||||
m, _ := json.Marshal(val)
|
||||
_ = json.Unmarshal(m, &out)
|
||||
return out, nil
|
||||
case nil:
|
||||
return types.TemplateMeta{}, nil
|
||||
default:
|
||||
return types.TemplateMeta{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
var _ expr.DeepFieldAssigner = &Template{}
|
||||
|
||||
// AssignFieldValue implements expr.DeepFieldAssigner
|
||||
//
|
||||
// Meta is a value struct; assigning to its fields through the generated
|
||||
// selector would modify a copy, so we reroute it to the template's own meta
|
||||
func (t *Template) AssignFieldValue(p expr.Pather, val expr.TypedValue) (err error) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
if p.Get() != "meta" || p.IsLast() {
|
||||
return assignToTemplate(t.value, p.Get(), val)
|
||||
}
|
||||
|
||||
if err = p.Next(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return assignToTemplateMeta(&t.value.Meta, p.Get(), val)
|
||||
}
|
||||
|
||||
func CastToRenderedDocument(val interface{}) (out *renderedDocument, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &renderedDocument{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToRenderedDocument(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *renderedDocument:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToDocumentType(val interface{}) (out types.DocumentType, err error) {
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
return types.DocumentType(val), nil
|
||||
case *expr.String:
|
||||
return types.DocumentType(val.GetValue()), nil
|
||||
default:
|
||||
return "", fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToRenderOptions(val interface{}) (out map[string]string, err error) {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case map[string]string:
|
||||
return val, nil
|
||||
case nil:
|
||||
return make(map[string]string), nil
|
||||
default:
|
||||
out, err = cast.ToStringMapStringE(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *RenderOptions) AssignFieldValue(key string, val expr.TypedValue) error {
|
||||
if t.value == nil {
|
||||
t.value = make(map[string]string)
|
||||
}
|
||||
|
||||
str, err := cast.ToStringE(expr.UntypedValue(val))
|
||||
t.value[key] = str
|
||||
return err
|
||||
}
|
||||
|
||||
func CastToQueueMessage(val interface{}) (out *types.QueueMessage, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.QueueMessage{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToQueueMessage(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.QueueMessage:
|
||||
return val, nil
|
||||
case nil:
|
||||
return &types.QueueMessage{}, nil
|
||||
default:
|
||||
return &types.QueueMessage{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func (doc renderedDocument) String() string {
|
||||
aux, _ := ioutil.ReadAll(doc.Document)
|
||||
return string(aux)
|
||||
}
|
||||
|
||||
func CastToRbacResource(val interface{}) (out rbac.Resource, err error) {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case rbac.Resource:
|
||||
return val, nil
|
||||
case RbacResource:
|
||||
return val.value, nil
|
||||
case string:
|
||||
return rbac.NewResource(val), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToAction(val interface{}) (out *actionlog.Action, err error) {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *actionlog.Action:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Action) Clone() (expr.TypedValue, error) {
|
||||
aux := *v.value
|
||||
return NewAction(&aux)
|
||||
}
|
||||
|
||||
func (v *DocumentType) Clone() (expr.TypedValue, error) {
|
||||
return NewDocumentType(v.value)
|
||||
}
|
||||
|
||||
func (v *QueueMessage) Clone() (expr.TypedValue, error) {
|
||||
aux := *v.value
|
||||
return NewQueueMessage(&aux)
|
||||
}
|
||||
|
||||
func (v *RbacResource) Clone() (expr.TypedValue, error) {
|
||||
return NewRbacResource(v.value)
|
||||
}
|
||||
|
||||
func (v *RenderOptions) Clone() (expr.TypedValue, error) {
|
||||
aux := make(map[string]string)
|
||||
for k, v := range v.value {
|
||||
aux[k] = v
|
||||
}
|
||||
|
||||
return NewRenderOptions(aux)
|
||||
}
|
||||
|
||||
func (v *RenderedDocument) Clone() (expr.TypedValue, error) {
|
||||
aux := *v.value
|
||||
return NewRenderedDocument(&aux)
|
||||
}
|
||||
|
||||
func (v *Reminder) Clone() (expr.TypedValue, error) {
|
||||
if v.value == nil {
|
||||
return NewReminder(nil)
|
||||
}
|
||||
aux := *v.value
|
||||
return NewReminder(&aux)
|
||||
}
|
||||
|
||||
func (v *Role) Clone() (expr.TypedValue, error) {
|
||||
aux := v.value.Clone()
|
||||
return NewRole(aux)
|
||||
}
|
||||
|
||||
func (v *Template) Clone() (expr.TypedValue, error) {
|
||||
aux := v.value.Clone()
|
||||
return NewTemplate(aux)
|
||||
}
|
||||
|
||||
func (v *TemplateMeta) Clone() (expr.TypedValue, error) {
|
||||
return NewTemplateMeta(v.value)
|
||||
}
|
||||
|
||||
func (v *User) Clone() (expr.TypedValue, error) {
|
||||
aux := v.value.Clone()
|
||||
return NewUser(aux)
|
||||
}
|
||||
97
server/system/automation/expr_types.yaml
Normal file
97
server/system/automation/expr_types.yaml
Normal file
@@ -0,0 +1,97 @@
|
||||
package: automation
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/system/types
|
||||
- github.com/cortezaproject/corteza/server/pkg/rbac
|
||||
- github.com/cortezaproject/corteza/server/pkg/actionlog
|
||||
types:
|
||||
Template:
|
||||
as: '*types.Template'
|
||||
# meta is a value struct, assigning to it needs to be rerouted
|
||||
customFieldAssigner: true
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro, alias: 'templateID'}
|
||||
- { name: 'handle', exprType: 'Handle', goType: 'string' }
|
||||
- { name: 'language', exprType: 'String', goType: 'string' }
|
||||
|
||||
- { name: 'type', exprType: 'DocumentType', goType: 'types.DocumentType' }
|
||||
- { name: 'partial', exprType: 'Boolean', goType: 'bool' }
|
||||
|
||||
- { name: 'meta', exprType: 'TemplateMeta', goType: 'types.TemplateMeta' }
|
||||
- { name: 'template', exprType: 'String', goType: 'string' }
|
||||
- { name: 'labels', exprType: 'LabelValue', goType: 'map[string]labelTypes.LabelValue' }
|
||||
|
||||
- { name: 'ownerID', exprType: 'ID', goType: 'uint64', mode: ro}
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro}
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'lastUsedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
User:
|
||||
as: '*types.User'
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro, alias: 'userID' }
|
||||
- { name: 'username', exprType: 'String', goType: 'string' }
|
||||
- { name: 'email', exprType: 'String', goType: 'string' }
|
||||
- { name: 'name', exprType: 'String', goType: 'string' }
|
||||
- { name: 'handle', exprType: 'Handle', goType: 'string' }
|
||||
- { name: 'emailConfirmed', exprType: 'Boolean', goType: 'bool' }
|
||||
- { name: 'labels', exprType: 'LabelValue', goType: 'map[string]labelTypes.LabelValue' }
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro}
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'suspendedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
Role:
|
||||
as: '*types.Role'
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro, alias: 'roleID' }
|
||||
- { name: 'name', exprType: 'String', goType: 'string' }
|
||||
- { name: 'handle', exprType: 'Handle', goType: 'string' }
|
||||
- { name: 'labels', exprType: 'LabelValue', goType: 'map[string]labelTypes.LabelValue' }
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro}
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'archivedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
Reminder:
|
||||
as: '*types.Reminder'
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro, alias: 'reminderID' }
|
||||
- { name: 'resource', exprType: 'String', goType: 'string'}
|
||||
- { name: 'snoozeCount', exprType: 'UnsignedInteger', goType: 'uint', mode: ro }
|
||||
- { name: 'assignedTo', exprType: 'ID', goType: 'uint64' }
|
||||
- { name: 'assignedBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'assignedAt', exprType: 'DateTime', goType: 'time.Time', mode: ro }
|
||||
- { name: 'dismissedBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'dismissedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'remindAt', exprType: 'DateTime', goType: '*time.Time' }
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro }
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'payload', exprType: 'Bytes', goType: 'types.JSONText'}
|
||||
DocumentType:
|
||||
as: 'types.DocumentType'
|
||||
TemplateMeta:
|
||||
as: 'types.TemplateMeta'
|
||||
struct:
|
||||
- { name: 'short', exprType: 'String', goType: 'string'}
|
||||
- { name: 'description', exprType: 'String', goType: 'string'}
|
||||
- { name: 'headerTemplateID', exprType: 'ID', goType: 'uint64'}
|
||||
- { name: 'footerTemplateID', exprType: 'ID', goType: 'uint64'}
|
||||
RenderedDocument:
|
||||
as: '*renderedDocument'
|
||||
struct:
|
||||
- { name: 'document', exprType: 'Reader', goType: 'io.Reader' }
|
||||
- { name: 'name', exprType: 'string', goType: 'string' }
|
||||
- { name: 'type', exprType: 'string', goType: 'string' }
|
||||
RenderOptions:
|
||||
as: 'map[string]string'
|
||||
|
||||
QueueMessage:
|
||||
as: '*types.QueueMessage'
|
||||
struct:
|
||||
- { name: 'Queue', exprType: 'String', goType: 'string' }
|
||||
- { name: 'Payload', exprType: 'Bytes', goType: '[]byte' }
|
||||
|
||||
RbacResource:
|
||||
as: 'rbac.Resource'
|
||||
|
||||
Action:
|
||||
as: '*actionlog.Action'
|
||||
148
server/system/automation/expr_types_test.go
Normal file
148
server/system/automation/expr_types_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUser(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
u, err = NewUser(&types.User{Handle: "handle"})
|
||||
)
|
||||
|
||||
req.NoError(err)
|
||||
req.Equal("handle", u.value.Handle)
|
||||
req.Error(u.AssignFieldValue("some-unexisting-field", nil))
|
||||
req.NoError(u.AssignFieldValue("email", expr.Must(expr.NewString("dummy@domain.tpl"))))
|
||||
req.Equal("dummy@domain.tpl", u.value.Email)
|
||||
}
|
||||
|
||||
func TestUser_Expr(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
u, _ = NewUser(&types.User{Handle: "hendl"})
|
||||
scope = &expr.Vars{}
|
||||
)
|
||||
|
||||
req.NoError(scope.Set("user", u))
|
||||
|
||||
eval, err := expr.NewParser().Parse("user.handle")
|
||||
req.NoError(err)
|
||||
|
||||
res, err := eval.Eval(context.Background(), scope)
|
||||
req.NoError(err)
|
||||
|
||||
req.Equal("hendl", res.(string))
|
||||
}
|
||||
|
||||
func TestTemplate_metaAssign(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
t1, err = NewTemplate(&types.Template{
|
||||
Handle: "invoice",
|
||||
Meta: types.TemplateMeta{Short: "before"},
|
||||
})
|
||||
)
|
||||
|
||||
req.NoError(err)
|
||||
|
||||
// fields of the meta struct
|
||||
req.NoError(expr.Assign(t1, "meta.short", expr.Must(expr.NewString("after"))))
|
||||
req.Equal("after", t1.value.Meta.Short)
|
||||
|
||||
req.NoError(expr.Assign(t1, "meta.headerTemplateID", expr.Must(expr.NewID(42))))
|
||||
req.Equal(uint64(42), t1.value.Meta.HeaderTemplateID)
|
||||
|
||||
req.NoError(expr.Assign(t1, "meta.footerTemplateID", expr.Must(expr.NewID(43))))
|
||||
req.Equal(uint64(43), t1.value.Meta.FooterTemplateID)
|
||||
|
||||
req.Error(expr.Assign(t1, "meta.unknown-field", expr.Must(expr.NewString("nope"))))
|
||||
|
||||
// whole meta struct
|
||||
meta, err := NewTemplateMeta(types.TemplateMeta{Short: "whole", HeaderTemplateID: 99})
|
||||
req.NoError(err)
|
||||
req.NoError(expr.Assign(t1, "meta", meta))
|
||||
req.Equal("whole", t1.value.Meta.Short)
|
||||
req.Equal(uint64(99), t1.value.Meta.HeaderTemplateID)
|
||||
|
||||
// meta from a map, the way it comes in from a workflow
|
||||
fromMap, err := NewTemplateMeta(map[string]interface{}{"short": "mapped", "footerTemplateID": "13"})
|
||||
req.NoError(err)
|
||||
req.NoError(expr.Assign(t1, "meta", fromMap))
|
||||
req.Equal("mapped", t1.value.Meta.Short)
|
||||
req.Equal(uint64(13), t1.value.Meta.FooterTemplateID)
|
||||
|
||||
// other fields still assign
|
||||
req.NoError(expr.Assign(t1, "handle", expr.Must(expr.NewString("receipt"))))
|
||||
req.Equal("receipt", t1.value.Handle)
|
||||
req.Error(expr.Assign(t1, "some-unexisting-field", expr.Must(expr.NewString("nope"))))
|
||||
}
|
||||
|
||||
// Assigning through a scope, the way workflow expression steps do it
|
||||
func TestTemplate_metaAssignInScope(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
t1, err = NewTemplate(&types.Template{Handle: "invoice"})
|
||||
scope = &expr.Vars{}
|
||||
)
|
||||
|
||||
req.NoError(err)
|
||||
req.NoError(scope.Set("template", t1))
|
||||
|
||||
req.NoError(expr.Assign(scope, "template.meta.headerTemplateID", expr.Must(expr.NewID(42))))
|
||||
req.NoError(expr.Assign(scope, "template.meta.short", expr.Must(expr.NewString("scoped"))))
|
||||
|
||||
selected, err := scope.Select("template")
|
||||
req.NoError(err)
|
||||
|
||||
tpl, err := CastToTemplate(selected)
|
||||
req.NoError(err)
|
||||
req.Equal(uint64(42), tpl.Meta.HeaderTemplateID)
|
||||
req.Equal("scoped", tpl.Meta.Short)
|
||||
}
|
||||
|
||||
func TestCastToRbacResource(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
t.Run("string to RbacResource", func(t *testing.T) {
|
||||
resourceString := "corteza::system:user/461133310995726337"
|
||||
resource, err := CastToRbacResource(resourceString)
|
||||
|
||||
req.NoError(err)
|
||||
req.NotNil(resource)
|
||||
req.Equal(resourceString, resource.RbacResource())
|
||||
})
|
||||
|
||||
t.Run("existing RbacResource", func(t *testing.T) {
|
||||
originalResource := rbac.NewResource("corteza::system:role/123")
|
||||
resource, err := CastToRbacResource(originalResource)
|
||||
|
||||
req.NoError(err)
|
||||
req.Equal(originalResource, resource)
|
||||
})
|
||||
|
||||
t.Run("RbacResource expression type", func(t *testing.T) {
|
||||
originalResource := rbac.NewResource("corteza::system:role/123")
|
||||
rbacRes, err := NewRbacResource(originalResource)
|
||||
req.NoError(err)
|
||||
|
||||
resource, err := CastToRbacResource(rbacRes)
|
||||
req.NoError(err)
|
||||
req.Equal(originalResource, resource)
|
||||
})
|
||||
|
||||
t.Run("invalid type", func(t *testing.T) {
|
||||
resource, err := CastToRbacResource(123)
|
||||
req.Error(err)
|
||||
req.Nil(resource)
|
||||
req.Contains(err.Error(), "unable to cast type int to")
|
||||
})
|
||||
}
|
||||
124
server/system/automation/notification_handler.gen.go
generated
Normal file
124
server/system/automation/notification_handler.gen.go
generated
Normal file
@@ -0,0 +1,124 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// system/automation/notification_handler.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
atypes "github.com/cortezaproject/corteza/server/automation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
notificationHandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h notificationHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.Send(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
notificationSendArgs struct {
|
||||
hasRecipient bool
|
||||
Recipient interface{}
|
||||
recipientID uint64
|
||||
recipientHandle string
|
||||
recipientEmail string
|
||||
|
||||
hasTitle bool
|
||||
Title string
|
||||
|
||||
hasDescription bool
|
||||
Description string
|
||||
}
|
||||
)
|
||||
|
||||
func (a notificationSendArgs) GetRecipient() (bool, uint64, string, string) {
|
||||
return a.hasRecipient, a.recipientID, a.recipientHandle, a.recipientEmail
|
||||
}
|
||||
|
||||
// Send function Send simple notification
|
||||
//
|
||||
// expects implementation of send function:
|
||||
//
|
||||
// func (h notificationHandler) send(ctx context.Context, args *notificationSendArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h notificationHandler) Send() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "notificationSend",
|
||||
Kind: "function",
|
||||
Labels: map[string]string(nil),
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Send simple notification",
|
||||
Description: "Sends a simple notification with title and description to a user",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "recipient",
|
||||
Types: []string{"ID", "Handle", "String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Recipient",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "title",
|
||||
Types: []string{"String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Title",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "description",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Description",
|
||||
Visual: map[string]interface{}{"input": map[string]interface{}{"type": "richtext"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = ¬ificationSendArgs{
|
||||
hasRecipient: in.Has("recipient"),
|
||||
hasTitle: in.Has("title"),
|
||||
hasDescription: in.Has("description"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Recipient argument
|
||||
if args.hasRecipient {
|
||||
aux := expr.Must(expr.Select(in, "recipient"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.recipientID = aux.Get().(uint64)
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.recipientHandle = aux.Get().(string)
|
||||
case h.reg.Type("String").Type():
|
||||
args.recipientEmail = aux.Get().(string)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.send(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
104
server/system/automation/notification_handler.go
Normal file
104
server/system/automation/notification_handler.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
|
||||
systemTypes "github.com/cortezaproject/corteza/server/system/types"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
notificationHandler struct {
|
||||
reg notificationHandlerRegistry
|
||||
svc notificationService
|
||||
uSvc notificationUserService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
notificationService interface {
|
||||
Create(context.Context, *systemTypes.Notification) (*systemTypes.Notification, error)
|
||||
}
|
||||
|
||||
// Interface for user service that we need for recipient lookups
|
||||
notificationUserService interface {
|
||||
FindByID(ctx context.Context, userID uint64) (*systemTypes.User, error)
|
||||
FindByHandle(ctx context.Context, handle string) (*systemTypes.User, error)
|
||||
FindByEmail(ctx context.Context, email string) (*systemTypes.User, error)
|
||||
}
|
||||
|
||||
notificationNamespaceService interface {
|
||||
FindByHandle(ctx context.Context, handle string) (*composeTypes.Namespace, error)
|
||||
}
|
||||
|
||||
notificationModuleService interface {
|
||||
FindByHandle(ctx context.Context, handle string) (*composeTypes.Module, error)
|
||||
}
|
||||
)
|
||||
|
||||
// NotificationHandler initializes the notification handler
|
||||
func NotificationHandler(reg notificationHandlerRegistry, ntfSvc notificationService, uSvc notificationUserService, log *zap.Logger) *notificationHandler {
|
||||
h := ¬ificationHandler{
|
||||
reg: reg,
|
||||
svc: ntfSvc,
|
||||
uSvc: uSvc,
|
||||
logger: log.Named("notification"),
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
// send creates and sends a notification
|
||||
func (h notificationHandler) send(ctx context.Context, args *notificationSendArgs) error {
|
||||
// Get the recipient user ID from the input parameter
|
||||
var recipientID uint64
|
||||
|
||||
// Check if we have a direct user object
|
||||
if args.recipientID > 0 {
|
||||
// Direct ID passed
|
||||
recipientID = args.recipientID
|
||||
} else {
|
||||
// Need to look up the user
|
||||
var user *systemTypes.User
|
||||
var err error
|
||||
|
||||
if args.recipientHandle != "" {
|
||||
user, err = h.uSvc.FindByHandle(ctx, args.recipientHandle)
|
||||
} else if args.recipientEmail != "" {
|
||||
user, err = h.uSvc.FindByEmail(ctx, args.recipientEmail)
|
||||
} else {
|
||||
return fmt.Errorf("invalid recipient: unable to determine user ID")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return fmt.Errorf("recipient not found")
|
||||
}
|
||||
|
||||
recipientID = user.ID
|
||||
}
|
||||
|
||||
// Create notification config directly with the expected fields for a simple notification
|
||||
config := systemTypes.NotificationConfig{
|
||||
Simple: systemTypes.SimpleNotificationConfig{
|
||||
Title: args.Title,
|
||||
Description: args.Description,
|
||||
},
|
||||
}
|
||||
|
||||
// Create a simple notification
|
||||
ntf := &systemTypes.Notification{
|
||||
Kind: systemTypes.NotificationKindSimple,
|
||||
Config: config,
|
||||
Recipient: recipientID,
|
||||
}
|
||||
|
||||
// Create the notification
|
||||
_, err := h.svc.Create(ctx, ntf)
|
||||
return err
|
||||
}
|
||||
30
server/system/automation/notification_handler.yaml
Normal file
30
server/system/automation/notification_handler.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: notification
|
||||
|
||||
snippets:
|
||||
lookupUser: &lookupUser
|
||||
meta: { label: Recipient }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID }
|
||||
- { wf: Handle }
|
||||
- { wf: String, suffix: email }
|
||||
|
||||
functions:
|
||||
send:
|
||||
meta:
|
||||
short: Send simple notification
|
||||
description: Sends a simple notification with title and description to a user
|
||||
params:
|
||||
recipient: *lookupUser
|
||||
title:
|
||||
meta: { label: Title }
|
||||
types:
|
||||
- { wf: String }
|
||||
required: true
|
||||
description: The title of the simple notification
|
||||
description:
|
||||
types:
|
||||
- { wf: String }
|
||||
required: false
|
||||
description: Optional description for the simple notification
|
||||
meta: { label: Description, visual: { input: { type: richtext } } }
|
||||
412
server/system/automation/rbac_handler.gen.go
generated
Normal file
412
server/system/automation/rbac_handler.gen.go
generated
Normal file
@@ -0,0 +1,412 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// system/automation/rbac_handler.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
atypes "github.com/cortezaproject/corteza/server/automation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
rbacHandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h rbacHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.Allow(),
|
||||
h.Deny(),
|
||||
h.Inherit(),
|
||||
h.Check(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
rbacAllowArgs struct {
|
||||
hasResource bool
|
||||
Resource rbac.Resource
|
||||
|
||||
hasRole bool
|
||||
Role interface{}
|
||||
roleID uint64
|
||||
roleHandle string
|
||||
roleRes *types.Role
|
||||
|
||||
hasOperation bool
|
||||
Operation string
|
||||
}
|
||||
)
|
||||
|
||||
func (a rbacAllowArgs) GetRole() (bool, uint64, string, *types.Role) {
|
||||
return a.hasRole, a.roleID, a.roleHandle, a.roleRes
|
||||
}
|
||||
|
||||
// Allow function RBAC: Allow operation on resource to a role
|
||||
//
|
||||
// expects implementation of allow function:
|
||||
//
|
||||
// func (h rbacHandler) allow(ctx context.Context, args *rbacAllowArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h rbacHandler) Allow() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "rbacAllow",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"users": "rbac"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "RBAC: Allow operation on resource to a role",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"RbacResource"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "role",
|
||||
Types: []string{"ID", "Handle", "Role"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Role",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "operation",
|
||||
Types: []string{"String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Operation",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &rbacAllowArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasRole: in.Has("role"),
|
||||
hasOperation: in.Has("operation"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Role argument
|
||||
if args.hasRole {
|
||||
aux := expr.Must(expr.Select(in, "role"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.roleID = aux.Get().(uint64)
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.roleHandle = aux.Get().(string)
|
||||
case h.reg.Type("Role").Type():
|
||||
args.roleRes = aux.Get().(*types.Role)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.allow(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
rbacDenyArgs struct {
|
||||
hasResource bool
|
||||
Resource rbac.Resource
|
||||
|
||||
hasRole bool
|
||||
Role interface{}
|
||||
roleID uint64
|
||||
roleHandle string
|
||||
roleRes *types.Role
|
||||
|
||||
hasOperation bool
|
||||
Operation string
|
||||
}
|
||||
)
|
||||
|
||||
func (a rbacDenyArgs) GetRole() (bool, uint64, string, *types.Role) {
|
||||
return a.hasRole, a.roleID, a.roleHandle, a.roleRes
|
||||
}
|
||||
|
||||
// Deny function RBAC: Deny operation on resource to a role
|
||||
//
|
||||
// expects implementation of deny function:
|
||||
//
|
||||
// func (h rbacHandler) deny(ctx context.Context, args *rbacDenyArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h rbacHandler) Deny() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "rbacDeny",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"users": "rbac"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "RBAC: Deny operation on resource to a role",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"RbacResource"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "role",
|
||||
Types: []string{"ID", "Handle", "Role"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Role",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "operation",
|
||||
Types: []string{"String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Operation",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &rbacDenyArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasRole: in.Has("role"),
|
||||
hasOperation: in.Has("operation"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Role argument
|
||||
if args.hasRole {
|
||||
aux := expr.Must(expr.Select(in, "role"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.roleID = aux.Get().(uint64)
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.roleHandle = aux.Get().(string)
|
||||
case h.reg.Type("Role").Type():
|
||||
args.roleRes = aux.Get().(*types.Role)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.deny(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
rbacInheritArgs struct {
|
||||
hasResource bool
|
||||
Resource rbac.Resource
|
||||
|
||||
hasRole bool
|
||||
Role interface{}
|
||||
roleID uint64
|
||||
roleHandle string
|
||||
roleRes *types.Role
|
||||
|
||||
hasOperation bool
|
||||
Operation string
|
||||
}
|
||||
)
|
||||
|
||||
func (a rbacInheritArgs) GetRole() (bool, uint64, string, *types.Role) {
|
||||
return a.hasRole, a.roleID, a.roleHandle, a.roleRes
|
||||
}
|
||||
|
||||
// Inherit function RBAC: Remove allow/deny operation of a role from resource
|
||||
//
|
||||
// expects implementation of inherit function:
|
||||
//
|
||||
// func (h rbacHandler) inherit(ctx context.Context, args *rbacInheritArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h rbacHandler) Inherit() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "rbacInherit",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"users": "rbac"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "RBAC: Remove allow/deny operation of a role from resource",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"RbacResource"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "role",
|
||||
Types: []string{"ID", "Handle", "Role"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Role",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "operation",
|
||||
Types: []string{"String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Operation",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &rbacInheritArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasRole: in.Has("role"),
|
||||
hasOperation: in.Has("operation"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Role argument
|
||||
if args.hasRole {
|
||||
aux := expr.Must(expr.Select(in, "role"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.roleID = aux.Get().(uint64)
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.roleHandle = aux.Get().(string)
|
||||
case h.reg.Type("Role").Type():
|
||||
args.roleRes = aux.Get().(*types.Role)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.inherit(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
rbacCheckArgs struct {
|
||||
hasResource bool
|
||||
Resource rbac.Resource
|
||||
|
||||
hasOperation bool
|
||||
Operation string
|
||||
|
||||
hasUser bool
|
||||
User *types.User
|
||||
}
|
||||
|
||||
rbacCheckResults struct {
|
||||
Can bool
|
||||
}
|
||||
)
|
||||
|
||||
// Check function RBAC: Can user perform an operation on a resource
|
||||
//
|
||||
// expects implementation of check function:
|
||||
//
|
||||
// func (h rbacHandler) check(ctx context.Context, args *rbacCheckArgs) (results *rbacCheckResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h rbacHandler) Check() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "rbacCheck",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"users": "rbac"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "RBAC: Can user perform an operation on a resource",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"RbacResource"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "operation",
|
||||
Types: []string{"String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Operation",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "user",
|
||||
Types: []string{"User"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "can",
|
||||
Types: []string{"Boolean"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &rbacCheckArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasOperation: in.Has("operation"),
|
||||
hasUser: in.Has("user"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *rbacCheckResults
|
||||
if results, err = h.check(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Can (bool) to Boolean
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("Boolean").Cast(results.Can); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "can", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
138
server/system/automation/rbac_handler.go
Normal file
138
server/system/automation/rbac_handler.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
rbacService interface {
|
||||
Can(ses rbac.Session, op string, res rbac.Resource) bool
|
||||
Grant(ctx context.Context, rules ...*rbac.Rule) (err error)
|
||||
}
|
||||
|
||||
rbacUserService interface {
|
||||
FindByAny(ctx context.Context, identifier interface{}) (*types.User, error)
|
||||
}
|
||||
|
||||
rbacHandler struct {
|
||||
reg rbacHandlerRegistry
|
||||
rbac rbacService
|
||||
user rbacUserService
|
||||
role roleService
|
||||
}
|
||||
|
||||
auxResource struct{ res string }
|
||||
)
|
||||
|
||||
var _ rbac.Resource = &auxResource{}
|
||||
|
||||
func (r auxResource) RbacResource() string {
|
||||
return r.res
|
||||
}
|
||||
|
||||
func RbacHandler(reg rbacHandlerRegistry, rbac rbacService, user rbacUserService, role roleService) *rbacHandler {
|
||||
h := &rbacHandler{
|
||||
reg: reg,
|
||||
rbac: rbac,
|
||||
user: user,
|
||||
role: role,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h rbacHandler) allow(ctx context.Context, args *rbacAllowArgs) (err error) {
|
||||
r, err := lookupRole(ctx, h.role, rolesLookupArgs{
|
||||
hasLookup: args.hasRole,
|
||||
Lookup: args.Role,
|
||||
lookupID: args.roleID,
|
||||
lookupHandle: args.roleHandle,
|
||||
lookupRes: args.roleRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return h.grant(ctx, rbac.AllowRule(r.ID, args.Resource.RbacResource(), args.Operation))
|
||||
}
|
||||
|
||||
func (h rbacHandler) deny(ctx context.Context, args *rbacDenyArgs) (err error) {
|
||||
r, err := lookupRole(ctx, h.role, rolesLookupArgs{
|
||||
hasLookup: args.hasRole,
|
||||
Lookup: args.Role,
|
||||
lookupID: args.roleID,
|
||||
lookupHandle: args.roleHandle,
|
||||
lookupRes: args.roleRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return h.grant(ctx, rbac.DenyRule(r.ID, args.Resource.RbacResource(), args.Operation))
|
||||
}
|
||||
|
||||
func (h rbacHandler) inherit(ctx context.Context, args *rbacInheritArgs) (err error) {
|
||||
r, err := lookupRole(ctx, h.role, rolesLookupArgs{
|
||||
hasLookup: args.hasRole,
|
||||
Lookup: args.Role,
|
||||
lookupID: args.roleID,
|
||||
lookupHandle: args.roleHandle,
|
||||
lookupRes: args.roleRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return h.grant(ctx, rbac.InheritRule(r.ID, args.Resource.RbacResource(), args.Operation))
|
||||
}
|
||||
|
||||
// verifies grant op (granter needs to be allowed to grant on the component
|
||||
|
||||
func (h rbacHandler) grant(ctx context.Context, r *rbac.Rule) (err error) {
|
||||
// we can safely create a fake component resource since there are
|
||||
// no extra metadata hidden under it,
|
||||
cmpRes := &auxResource{rbac.ResourceComponent(r.Resource)}
|
||||
|
||||
if !h.rbac.Can(rbac.ContextToSession(ctx), "grant", cmpRes) {
|
||||
return fmt.Errorf("not allowed to grant %s", r.String())
|
||||
}
|
||||
|
||||
return h.rbac.Grant(ctx, r)
|
||||
}
|
||||
|
||||
func (h rbacHandler) check(ctx context.Context, args *rbacCheckArgs) (b *rbacCheckResults, err error) {
|
||||
var secses rbac.Session
|
||||
|
||||
b = &rbacCheckResults{}
|
||||
|
||||
if args.hasUser {
|
||||
// user was explicitly set to rbac checking function
|
||||
//
|
||||
// load all roles that this user is member of
|
||||
// and create new security session out of it
|
||||
var (
|
||||
mm types.RoleMemberSet
|
||||
ids []uint64
|
||||
)
|
||||
|
||||
mm, err = h.role.Membership(ctx, args.User.ID)
|
||||
_ = mm.Walk(func(m *types.RoleMember) error {
|
||||
ids = append(ids, m.RoleID)
|
||||
return nil
|
||||
})
|
||||
|
||||
args.User.SetRoles(ids...)
|
||||
|
||||
secses = rbac.NewSession(ctx, args.User)
|
||||
} else {
|
||||
secses = rbac.ContextToSession(ctx)
|
||||
}
|
||||
|
||||
b.Can = h.rbac.Can(secses, args.Operation, args.Resource)
|
||||
return
|
||||
}
|
||||
72
server/system/automation/rbac_handler.yaml
Normal file
72
server/system/automation/rbac_handler.yaml
Normal file
@@ -0,0 +1,72 @@
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/system/types
|
||||
- github.com/cortezaproject/corteza/server/pkg/rbac
|
||||
|
||||
params:
|
||||
roleLookup: &roleLookup
|
||||
meta: { label: Role }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: Role, suffix: res }
|
||||
|
||||
manageParams: &manageParams
|
||||
resource:
|
||||
meta: { label: Resource }
|
||||
required: true
|
||||
types:
|
||||
# Note that we can not accept string here because
|
||||
# we need whole resource with all the info loaded
|
||||
- { wf: RbacResource }
|
||||
role: *roleLookup
|
||||
operation:
|
||||
meta: { label: Operation }
|
||||
required: true
|
||||
types:
|
||||
- { wf: String }
|
||||
|
||||
labels: &labels
|
||||
users: "rbac"
|
||||
|
||||
functions:
|
||||
allow:
|
||||
meta:
|
||||
short: 'RBAC: Allow operation on resource to a role'
|
||||
labels:
|
||||
<<: *labels
|
||||
params: *manageParams
|
||||
|
||||
deny:
|
||||
meta:
|
||||
short: 'RBAC: Deny operation on resource to a role'
|
||||
labels:
|
||||
<<: *labels
|
||||
params: *manageParams
|
||||
|
||||
inherit:
|
||||
meta:
|
||||
short: 'RBAC: Remove allow/deny operation of a role from resource'
|
||||
labels:
|
||||
<<: *labels
|
||||
params: *manageParams
|
||||
|
||||
check:
|
||||
meta:
|
||||
short: 'RBAC: Can user perform an operation on a resource'
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
resource:
|
||||
meta: { label: Resource }
|
||||
required: true
|
||||
types: [ { wf: RbacResource } ]
|
||||
operation:
|
||||
meta: { label: Operation }
|
||||
required: true
|
||||
types: [ { wf: String } ]
|
||||
user:
|
||||
meta: { label: User }
|
||||
types: [ { wf: User } ]
|
||||
results:
|
||||
can: { wf: Boolean }
|
||||
956
server/system/automation/reminders_handler.gen.go
generated
Normal file
956
server/system/automation/reminders_handler.gen.go
generated
Normal file
@@ -0,0 +1,956 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// system/automation/reminders_handler.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
atypes "github.com/cortezaproject/corteza/server/automation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
remindersHandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h remindersHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.Lookup(),
|
||||
h.Search(),
|
||||
h.Each(),
|
||||
h.Create(),
|
||||
h.Update(),
|
||||
h.Dismiss(),
|
||||
h.Snooze(),
|
||||
h.Delete(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
remindersLookupArgs struct {
|
||||
hasLookup bool
|
||||
Lookup interface{}
|
||||
lookupID uint64
|
||||
lookupRes *types.Reminder
|
||||
}
|
||||
|
||||
remindersLookupResults struct {
|
||||
Reminder *types.Reminder
|
||||
}
|
||||
)
|
||||
|
||||
func (a remindersLookupArgs) GetLookup() (bool, uint64, *types.Reminder) {
|
||||
return a.hasLookup, a.lookupID, a.lookupRes
|
||||
}
|
||||
|
||||
// Lookup function Reminder lookup
|
||||
//
|
||||
// expects implementation of lookup function:
|
||||
//
|
||||
// func (h remindersHandler) lookup(ctx context.Context, args *remindersLookupArgs) (results *remindersLookupResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Lookup() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersLookup",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"reminders": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Reminder lookup",
|
||||
Description: "Find specific reminder by ID",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "lookup",
|
||||
Types: []string{"ID", "Reminder"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Lookup",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "reminder",
|
||||
Types: []string{"Reminder"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersLookupArgs{
|
||||
hasLookup: in.Has("lookup"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Lookup argument
|
||||
if args.hasLookup {
|
||||
aux := expr.Must(expr.Select(in, "lookup"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.lookupID = aux.Get().(uint64)
|
||||
case h.reg.Type("Reminder").Type():
|
||||
args.lookupRes = aux.Get().(*types.Reminder)
|
||||
}
|
||||
}
|
||||
|
||||
var results *remindersLookupResults
|
||||
if results, err = h.lookup(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Reminder (*types.Reminder) to Reminder
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("Reminder").Cast(results.Reminder); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "reminder", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersSearchArgs struct {
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasAssignedTo bool
|
||||
AssignedTo uint64
|
||||
|
||||
hasExcludeDismissed bool
|
||||
ExcludeDismissed bool
|
||||
|
||||
hasScheduledOnly bool
|
||||
ScheduledOnly bool
|
||||
|
||||
hasSort bool
|
||||
Sort string
|
||||
|
||||
hasLimit bool
|
||||
Limit uint64
|
||||
|
||||
hasIncTotal bool
|
||||
IncTotal bool
|
||||
|
||||
hasPageCursor bool
|
||||
PageCursor string
|
||||
}
|
||||
|
||||
remindersSearchResults struct {
|
||||
Reminders []*types.Reminder
|
||||
Total uint64
|
||||
}
|
||||
)
|
||||
|
||||
// Search function Reminders search
|
||||
//
|
||||
// expects implementation of search function:
|
||||
//
|
||||
// func (h remindersHandler) search(ctx context.Context, args *remindersSearchArgs) (results *remindersSearchResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Search() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersSearch",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"reminders": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Reminders search",
|
||||
Description: "Search reminders",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "assignedTo",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Assigned to",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "excludeDismissed",
|
||||
Types: []string{"Boolean"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Exclude dismissed",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "scheduledOnly",
|
||||
Types: []string{"Boolean"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Scheduled only",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "sort",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Sort",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "limit",
|
||||
Types: []string{"UnsignedInteger"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Limit",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "incTotal",
|
||||
Types: []string{"Boolean"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Include total",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "pageCursor",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Page cursor",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "reminders",
|
||||
Types: []string{"Reminder"},
|
||||
IsArray: true,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "total",
|
||||
Types: []string{"UnsignedInteger"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersSearchArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasAssignedTo: in.Has("assignedTo"),
|
||||
hasExcludeDismissed: in.Has("excludeDismissed"),
|
||||
hasScheduledOnly: in.Has("scheduledOnly"),
|
||||
hasSort: in.Has("sort"),
|
||||
hasLimit: in.Has("limit"),
|
||||
hasIncTotal: in.Has("incTotal"),
|
||||
hasPageCursor: in.Has("pageCursor"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *remindersSearchResults
|
||||
if results, err = h.search(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Reminders (*types.Reminder) to Array (of Reminder)
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
tarr = make([]expr.TypedValue, len(results.Reminders))
|
||||
)
|
||||
|
||||
for i := range results.Reminders {
|
||||
if tarr[i], err = h.reg.Type("Reminder").Cast(results.Reminders[i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if tval, err = expr.NewArray(tarr); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "reminders", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// converting results.Total (uint64) to UnsignedInteger
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("UnsignedInteger").Cast(results.Total); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "total", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersEachArgs struct {
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasAssignedTo bool
|
||||
AssignedTo uint64
|
||||
|
||||
hasExcludeDismissed bool
|
||||
ExcludeDismissed bool
|
||||
|
||||
hasScheduledOnly bool
|
||||
ScheduledOnly bool
|
||||
|
||||
hasSort bool
|
||||
Sort string
|
||||
|
||||
hasLimit bool
|
||||
Limit uint64
|
||||
|
||||
hasIncTotal bool
|
||||
IncTotal bool
|
||||
|
||||
hasPageCursor bool
|
||||
PageCursor string
|
||||
}
|
||||
|
||||
remindersEachResults struct {
|
||||
Reminder *types.Reminder
|
||||
Total uint64
|
||||
}
|
||||
)
|
||||
|
||||
// Each function Iterate reminders
|
||||
//
|
||||
// expects implementation of each function:
|
||||
//
|
||||
// func (h remindersHandler) each(ctx context.Context, args *remindersEachArgs) (results *remindersEachResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Each() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersEach",
|
||||
Kind: "iterator",
|
||||
Labels: map[string]string(nil),
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Iterate reminders",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "assignedTo",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Assigned to",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "excludeDismissed",
|
||||
Types: []string{"Boolean"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Exclude dismissed",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "scheduledOnly",
|
||||
Types: []string{"Boolean"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Scheduled only",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "sort",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Sort",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "limit",
|
||||
Types: []string{"UnsignedInteger"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Limit",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "incTotal",
|
||||
Types: []string{"Boolean"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Include total",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "pageCursor",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Page cursor",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "reminder",
|
||||
Types: []string{"Reminder"},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "total",
|
||||
Types: []string{"UnsignedInteger"},
|
||||
},
|
||||
},
|
||||
|
||||
Iterator: func(ctx context.Context, in *expr.Vars) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
args = &remindersEachArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasAssignedTo: in.Has("assignedTo"),
|
||||
hasExcludeDismissed: in.Has("excludeDismissed"),
|
||||
hasScheduledOnly: in.Has("scheduledOnly"),
|
||||
hasSort: in.Has("sort"),
|
||||
hasLimit: in.Has("limit"),
|
||||
hasIncTotal: in.Has("incTotal"),
|
||||
hasPageCursor: in.Has("pageCursor"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return h.each(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersCreateArgs struct {
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasAssignedTo bool
|
||||
AssignedTo uint64
|
||||
|
||||
hasAssignedBy bool
|
||||
AssignedBy uint64
|
||||
|
||||
hasRemindAt bool
|
||||
RemindAt *time.Time
|
||||
|
||||
hasPayload bool
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
remindersCreateResults struct {
|
||||
Reminder *types.Reminder
|
||||
}
|
||||
)
|
||||
|
||||
// Create function Reminder create
|
||||
//
|
||||
// expects implementation of create function:
|
||||
//
|
||||
// func (h remindersHandler) create(ctx context.Context, args *remindersCreateArgs) (results *remindersCreateResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Create() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersCreate",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"create": "step", "reminders": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Reminder create",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "assignedTo",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Assigned to",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "assignedBy",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Assigned by",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remindAt",
|
||||
Types: []string{"DateTime"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Remind at",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "payload",
|
||||
Types: []string{"Bytes"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Payload",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "reminder",
|
||||
Types: []string{"Reminder"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersCreateArgs{
|
||||
hasResource: in.Has("resource"),
|
||||
hasAssignedTo: in.Has("assignedTo"),
|
||||
hasAssignedBy: in.Has("assignedBy"),
|
||||
hasRemindAt: in.Has("remindAt"),
|
||||
hasPayload: in.Has("payload"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *remindersCreateResults
|
||||
if results, err = h.create(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Reminder (*types.Reminder) to Reminder
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("Reminder").Cast(results.Reminder); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "reminder", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersUpdateArgs struct {
|
||||
hasLookup bool
|
||||
Lookup interface{}
|
||||
lookupID uint64
|
||||
lookupRes *types.Reminder
|
||||
|
||||
hasResource bool
|
||||
Resource string
|
||||
|
||||
hasAssignedTo bool
|
||||
AssignedTo uint64
|
||||
|
||||
hasAssignedBy bool
|
||||
AssignedBy uint64
|
||||
|
||||
hasRemindAt bool
|
||||
RemindAt *time.Time
|
||||
|
||||
hasPayload bool
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
remindersUpdateResults struct {
|
||||
Reminder *types.Reminder
|
||||
}
|
||||
)
|
||||
|
||||
func (a remindersUpdateArgs) GetLookup() (bool, uint64, *types.Reminder) {
|
||||
return a.hasLookup, a.lookupID, a.lookupRes
|
||||
}
|
||||
|
||||
// Update function Reminder update
|
||||
//
|
||||
// expects implementation of update function:
|
||||
//
|
||||
// func (h remindersHandler) update(ctx context.Context, args *remindersUpdateArgs) (results *remindersUpdateResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Update() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersUpdate",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"reminders": "step,workflow", "update": "step"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Reminder update",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "lookup",
|
||||
Types: []string{"ID", "Reminder"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Lookup",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "resource",
|
||||
Types: []string{"String"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Resource",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "assignedTo",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Assigned to",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "assignedBy",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Assigned by",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remindAt",
|
||||
Types: []string{"DateTime"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Remind at",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "payload",
|
||||
Types: []string{"Bytes"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Payload",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "reminder",
|
||||
Types: []string{"Reminder"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersUpdateArgs{
|
||||
hasLookup: in.Has("lookup"),
|
||||
hasResource: in.Has("resource"),
|
||||
hasAssignedTo: in.Has("assignedTo"),
|
||||
hasAssignedBy: in.Has("assignedBy"),
|
||||
hasRemindAt: in.Has("remindAt"),
|
||||
hasPayload: in.Has("payload"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Lookup argument
|
||||
if args.hasLookup {
|
||||
aux := expr.Must(expr.Select(in, "lookup"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.lookupID = aux.Get().(uint64)
|
||||
case h.reg.Type("Reminder").Type():
|
||||
args.lookupRes = aux.Get().(*types.Reminder)
|
||||
}
|
||||
}
|
||||
|
||||
var results *remindersUpdateResults
|
||||
if results, err = h.update(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Reminder (*types.Reminder) to Reminder
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("Reminder").Cast(results.Reminder); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "reminder", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersDismissArgs struct {
|
||||
hasLookup bool
|
||||
Lookup interface{}
|
||||
lookupID uint64
|
||||
lookupRes *types.Reminder
|
||||
}
|
||||
)
|
||||
|
||||
func (a remindersDismissArgs) GetLookup() (bool, uint64, *types.Reminder) {
|
||||
return a.hasLookup, a.lookupID, a.lookupRes
|
||||
}
|
||||
|
||||
// Dismiss function Dismiss reminder
|
||||
//
|
||||
// expects implementation of dismiss function:
|
||||
//
|
||||
// func (h remindersHandler) dismiss(ctx context.Context, args *remindersDismissArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Dismiss() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersDismiss",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"dismiss": "step", "reminders": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Dismiss reminder",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "lookup",
|
||||
Types: []string{"ID", "Reminder"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Lookup",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersDismissArgs{
|
||||
hasLookup: in.Has("lookup"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Lookup argument
|
||||
if args.hasLookup {
|
||||
aux := expr.Must(expr.Select(in, "lookup"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.lookupID = aux.Get().(uint64)
|
||||
case h.reg.Type("Reminder").Type():
|
||||
args.lookupRes = aux.Get().(*types.Reminder)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.dismiss(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersSnoozeArgs struct {
|
||||
hasLookup bool
|
||||
Lookup interface{}
|
||||
lookupID uint64
|
||||
lookupRes *types.Reminder
|
||||
|
||||
hasRemindAt bool
|
||||
RemindAt *time.Time
|
||||
}
|
||||
)
|
||||
|
||||
func (a remindersSnoozeArgs) GetLookup() (bool, uint64, *types.Reminder) {
|
||||
return a.hasLookup, a.lookupID, a.lookupRes
|
||||
}
|
||||
|
||||
// Snooze function Snooze reminder
|
||||
//
|
||||
// expects implementation of snooze function:
|
||||
//
|
||||
// func (h remindersHandler) snooze(ctx context.Context, args *remindersSnoozeArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Snooze() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersSnooze",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"reminders": "step,workflow", "snooze": "step"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Snooze reminder",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "lookup",
|
||||
Types: []string{"ID", "Reminder"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Lookup",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remindAt",
|
||||
Types: []string{"DateTime"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Remind at",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersSnoozeArgs{
|
||||
hasLookup: in.Has("lookup"),
|
||||
hasRemindAt: in.Has("remindAt"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Lookup argument
|
||||
if args.hasLookup {
|
||||
aux := expr.Must(expr.Select(in, "lookup"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.lookupID = aux.Get().(uint64)
|
||||
case h.reg.Type("Reminder").Type():
|
||||
args.lookupRes = aux.Get().(*types.Reminder)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.snooze(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
remindersDeleteArgs struct {
|
||||
hasLookup bool
|
||||
Lookup interface{}
|
||||
lookupID uint64
|
||||
lookupRes *types.Reminder
|
||||
}
|
||||
)
|
||||
|
||||
func (a remindersDeleteArgs) GetLookup() (bool, uint64, *types.Reminder) {
|
||||
return a.hasLookup, a.lookupID, a.lookupRes
|
||||
}
|
||||
|
||||
// Delete function Reminder delete
|
||||
//
|
||||
// expects implementation of delete function:
|
||||
//
|
||||
// func (h remindersHandler) delete(ctx context.Context, args *remindersDeleteArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h remindersHandler) Delete() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "remindersDelete",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"delete": "step", "reminders": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Reminder delete",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "lookup",
|
||||
Types: []string{"ID", "Reminder"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Lookup",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &remindersDeleteArgs{
|
||||
hasLookup: in.Has("lookup"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Lookup argument
|
||||
if args.hasLookup {
|
||||
aux := expr.Must(expr.Select(in, "lookup"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.lookupID = aux.Get().(uint64)
|
||||
case h.reg.Type("Reminder").Type():
|
||||
args.lookupRes = aux.Get().(*types.Reminder)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.delete(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
288
server/system/automation/reminders_handler.go
Normal file
288
server/system/automation/reminders_handler.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
intAuth "github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
. "github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
sqlxtypes "github.com/jmoiron/sqlx/types"
|
||||
)
|
||||
|
||||
|
||||
type(
|
||||
reminderService interface {
|
||||
FindByID(ctx context.Context,ID uint64) (*types.Reminder, error)
|
||||
Find(ctx context.Context, filter types.ReminderFilter) (types.ReminderSet,types.ReminderFilter,error)
|
||||
Create(ctx context.Context, reminder *types.Reminder) (*types.Reminder,error)
|
||||
Update(ctx context.Context, reminder *types.Reminder) (*types.Reminder,error)
|
||||
Delete(ctx context.Context, ID uint64) error
|
||||
Dismiss(ctx context.Context, ID uint64) error
|
||||
Snooze(ctx context.Context, ID uint64,remindAt *time.Time) error
|
||||
}
|
||||
|
||||
remindersHandler struct {
|
||||
reg remindersHandlerRegistry
|
||||
rSvc reminderService
|
||||
}
|
||||
|
||||
reminderSetIterator struct {
|
||||
// Item buffer, current item pointer, and total items traversed
|
||||
ptr uint
|
||||
buffer types.ReminderSet
|
||||
total uint
|
||||
|
||||
// When filter limit is set, this constraints it
|
||||
iterLimit uint
|
||||
useIterLimit bool
|
||||
|
||||
// Item loader for additional chunks
|
||||
filter types.ReminderFilter
|
||||
loader func() error
|
||||
|
||||
// Total reported by the first page
|
||||
resTotal uint
|
||||
}
|
||||
reminderLookup interface {
|
||||
GetLookup() (bool, uint64, *types.Reminder)
|
||||
}
|
||||
|
||||
)
|
||||
func RemindersHandler(reg remindersHandlerRegistry,rSvc reminderService) *remindersHandler {
|
||||
h := &remindersHandler{
|
||||
reg : reg,
|
||||
rSvc: rSvc,
|
||||
}
|
||||
h.register()
|
||||
return h
|
||||
|
||||
}
|
||||
|
||||
func (h remindersHandler) lookup(ctx context.Context, args *remindersLookupArgs) (results *remindersLookupResults, err error){
|
||||
results = &remindersLookupResults{}
|
||||
results.Reminder, err = lookupReminder(ctx, h.rSvc, args)
|
||||
return
|
||||
}
|
||||
|
||||
func (h remindersHandler) search(ctx context.Context,args *remindersSearchArgs) (results *remindersSearchResults,err error){
|
||||
results = &remindersSearchResults{}
|
||||
var (
|
||||
f=types.ReminderFilter{
|
||||
Resource: args.Resource,
|
||||
AssignedTo: args.AssignedTo,
|
||||
ExcludeDismissed: args.ExcludeDismissed,
|
||||
ScheduledOnly: args.ScheduledOnly,
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.PageCursor = &filter.PagingCursor{}
|
||||
if err = f.PageCursor.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
f.Limit = uint(args.Limit)
|
||||
}
|
||||
|
||||
// Total cannot be fetched together with a page cursor
|
||||
f.IncTotal = args.IncTotal && f.PageCursor == nil
|
||||
|
||||
var auxf types.ReminderFilter
|
||||
results.Reminders, auxf, err = h.rSvc.Find(ctx, f)
|
||||
results.Total=uint64(auxf.Total)
|
||||
return
|
||||
}
|
||||
func (h remindersHandler) each(ctx context.Context, args *remindersEachArgs) (out wfexec.IteratorHandler, err error){
|
||||
var(
|
||||
i = &reminderSetIterator{}
|
||||
f=types.ReminderFilter{
|
||||
Resource: args.Resource,
|
||||
AssignedTo: args.AssignedTo,
|
||||
ExcludeDismissed: args.ExcludeDismissed,
|
||||
ScheduledOnly: args.ScheduledOnly,
|
||||
}
|
||||
)
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.NextPage = &filter.PagingCursor{}
|
||||
if err = f.NextPage.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
f.IncTotal = args.IncTotal
|
||||
|
||||
if args.hasLimit {
|
||||
i.useIterLimit = true
|
||||
i.iterLimit = uint(args.Limit)
|
||||
f.Limit = uint(args.Limit)
|
||||
if args.Limit > uint64(wfexec.MaxIteratorBufferSize) {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
i.iterLimit = uint(args.Limit)
|
||||
} else {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
i.filter = f
|
||||
i.loader = func() (err error) {
|
||||
if i.filter.PageCursor != nil && i.filter.NextPage == nil {
|
||||
return
|
||||
}
|
||||
i.total += i.ptr
|
||||
i.ptr = 0
|
||||
i.filter.PageCursor = i.filter.NextPage
|
||||
i.filter.NextPage = nil
|
||||
|
||||
// Total is fetched with the first page only; a paged query cannot carry it
|
||||
i.filter.IncTotal = i.filter.IncTotal && i.filter.PageCursor == nil
|
||||
|
||||
i.buffer, i.filter, err = h.rSvc.Find(ctx, i.filter)
|
||||
if i.filter.IncTotal {
|
||||
i.resTotal = i.filter.Total
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
return i, i.loader()
|
||||
}
|
||||
|
||||
func (h remindersHandler) create(ctx context.Context, args *remindersCreateArgs) (results *remindersCreateResults, err error) {
|
||||
|
||||
results = &remindersCreateResults{}
|
||||
currentUser := intAuth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
reminder := &types.Reminder{
|
||||
Resource:args.Resource,
|
||||
AssignedTo: args.AssignedTo,
|
||||
AssignedBy: args.AssignedBy,
|
||||
RemindAt: args.RemindAt,
|
||||
Payload: args.Payload,
|
||||
}
|
||||
if reminder.AssignedTo == 0 {
|
||||
reminder.AssignedTo = currentUser
|
||||
}
|
||||
if reminder.AssignedBy == 0 {
|
||||
reminder.AssignedBy = currentUser
|
||||
}
|
||||
if len(reminder.Payload) == 0 {
|
||||
reminder.Payload = sqlxtypes.JSONText(`{"title": "", "note": ""}`)
|
||||
}
|
||||
if reminder.RemindAt == nil {
|
||||
return nil, fmt.Errorf("remindAt is required")
|
||||
}
|
||||
results.Reminder, err = h.rSvc.Create(ctx, reminder)
|
||||
return
|
||||
}
|
||||
|
||||
func (h remindersHandler) update(ctx context.Context, args *remindersUpdateArgs) (results *remindersUpdateResults, err error) {
|
||||
results = &remindersUpdateResults{}
|
||||
reminder, err := lookupReminder(ctx, h.rSvc, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args.hasResource {
|
||||
reminder.Resource = args.Resource
|
||||
}
|
||||
if args.hasAssignedTo {
|
||||
reminder.AssignedTo = args.AssignedTo
|
||||
}
|
||||
if args.hasAssignedBy {
|
||||
reminder.AssignedBy = args.AssignedBy
|
||||
}
|
||||
if args.hasRemindAt {
|
||||
reminder.RemindAt = args.RemindAt
|
||||
}
|
||||
if args.hasPayload {
|
||||
reminder.Payload = args.Payload
|
||||
}
|
||||
results.Reminder, err = h.rSvc.Update(ctx, reminder)
|
||||
return
|
||||
}
|
||||
func (h remindersHandler) dismiss(ctx context.Context, args *remindersDismissArgs) error {
|
||||
if id, err := getReminderID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Dismiss(ctx, id)
|
||||
}
|
||||
}
|
||||
func (h remindersHandler) snooze(ctx context.Context, args *remindersSnoozeArgs) error {
|
||||
if id, err := getReminderID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Snooze(ctx, id, args.RemindAt)
|
||||
}
|
||||
}
|
||||
func (h remindersHandler) delete(ctx context.Context, args *remindersDeleteArgs) error {
|
||||
if id, err := getReminderID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Delete(ctx, id)
|
||||
}
|
||||
}
|
||||
func getReminderID(ctx context.Context, svc reminderService, args reminderLookup) (uint64, error) {
|
||||
_, ID, reminder := args.GetLookup()
|
||||
|
||||
switch {
|
||||
case reminder != nil:
|
||||
return reminder.ID, nil
|
||||
case ID > 0:
|
||||
return ID, nil
|
||||
}
|
||||
|
||||
lookupResult, err := lookupReminder(ctx, svc, args)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return lookupResult.ID, nil
|
||||
}
|
||||
|
||||
func lookupReminder(ctx context.Context, svc reminderService, args reminderLookup) (*types.Reminder, error) {
|
||||
_, ID, reminder := args.GetLookup()
|
||||
switch {
|
||||
case reminder != nil:
|
||||
return reminder, nil
|
||||
case ID > 0:
|
||||
return svc.FindByID(ctx, ID)
|
||||
}
|
||||
return nil, fmt.Errorf("empty lookup params")
|
||||
}
|
||||
|
||||
func (i *reminderSetIterator) More(context.Context, *expr.Vars) (bool, error) {
|
||||
a := wfexec.GenericResourceNextCheck(i.useIterLimit, i.ptr, uint(len(i.buffer)), i.total, i.iterLimit, i.filter.NextPage != nil)
|
||||
return a, nil
|
||||
}
|
||||
func (i *reminderSetIterator) Start(context.Context, *expr.Vars) error {
|
||||
i.ptr = 0
|
||||
return nil
|
||||
}
|
||||
func (i *reminderSetIterator) Next(context.Context, *expr.Vars) (out *expr.Vars, err error) {
|
||||
if len(i.buffer)-int(i.ptr) <= 0 {
|
||||
if err = i.loader(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
reminder := *i.buffer[i.ptr] // Make a copy
|
||||
reminder.Payload = sqlxtypes.JSONText(string(i.buffer[i.ptr].Payload))
|
||||
out.Set("reminder", Must(NewReminder(&reminder)))
|
||||
out.Set("total", Must(NewInteger(i.resTotal)))
|
||||
|
||||
i.ptr++
|
||||
return out, nil
|
||||
}
|
||||
173
server/system/automation/reminders_handler.yaml
Normal file
173
server/system/automation/reminders_handler.yaml
Normal file
@@ -0,0 +1,173 @@
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/system/types
|
||||
- time
|
||||
|
||||
params:
|
||||
lookup: &lookup
|
||||
meta: { label: Lookup }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: Reminder, suffix: res }
|
||||
reminder: &reminder
|
||||
types:
|
||||
- { wf: Reminder }
|
||||
rvReminder: &rvReminder
|
||||
wf: Reminder
|
||||
rvTotal: &rvTotal
|
||||
wf: UnsignedInteger
|
||||
filterParams: &filterParams
|
||||
resource:
|
||||
meta: { label: Resource }
|
||||
types:
|
||||
- { wf: String }
|
||||
assignedTo:
|
||||
meta: { label: Assigned to }
|
||||
types:
|
||||
- { wf: ID }
|
||||
excludeDismissed:
|
||||
meta: { label: Exclude dismissed }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
scheduledOnly:
|
||||
meta: { label: Scheduled only }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
sort:
|
||||
meta: { label: Sort }
|
||||
types:
|
||||
- { wf: String }
|
||||
limit:
|
||||
meta: { label: Limit }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
incTotal:
|
||||
meta: { label: Include total }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
pageCursor:
|
||||
meta: { label: Page cursor }
|
||||
types:
|
||||
- { wf: String }
|
||||
labels: &labels
|
||||
reminders: "step,workflow"
|
||||
functions:
|
||||
lookup:
|
||||
meta:
|
||||
short: Reminder lookup
|
||||
description: Find specific reminder by ID
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
lookup: *lookup
|
||||
results:
|
||||
reminder: *rvReminder
|
||||
search:
|
||||
meta:
|
||||
short: Reminders search
|
||||
description: Search reminders
|
||||
labels:
|
||||
<<: *labels
|
||||
params: *filterParams
|
||||
results:
|
||||
reminders:
|
||||
<<: *rvReminder
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
each:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Iterate reminders
|
||||
params: *filterParams
|
||||
results:
|
||||
reminder: *rvReminder
|
||||
total: *rvTotal
|
||||
create:
|
||||
meta:
|
||||
short: Reminder create
|
||||
labels:
|
||||
<<: *labels
|
||||
create: "step"
|
||||
params:
|
||||
resource:
|
||||
meta: { label: Resource }
|
||||
types:
|
||||
- { wf: String}
|
||||
assignedTo:
|
||||
meta: { label: Assigned to }
|
||||
types:
|
||||
- { wf: ID }
|
||||
assignedBy:
|
||||
meta: { label: Assigned by }
|
||||
types:
|
||||
- { wf: ID }
|
||||
remindAt:
|
||||
meta: { label: Remind at }
|
||||
types:
|
||||
- { wf: DateTime}
|
||||
required: true
|
||||
payload:
|
||||
meta: { label: Payload }
|
||||
types:
|
||||
- { wf: Bytes }
|
||||
results:
|
||||
reminder: *rvReminder
|
||||
update:
|
||||
meta:
|
||||
short: Reminder update
|
||||
labels:
|
||||
<<: *labels
|
||||
update: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
resource:
|
||||
meta: { label: Resource }
|
||||
types:
|
||||
- { wf: String}
|
||||
assignedTo:
|
||||
meta: { label: Assigned to }
|
||||
types:
|
||||
- { wf: ID }
|
||||
assignedBy:
|
||||
meta: { label: Assigned by }
|
||||
types:
|
||||
- { wf: ID }
|
||||
remindAt:
|
||||
meta: { label: Remind at }
|
||||
types:
|
||||
- { wf: DateTime}
|
||||
payload:
|
||||
meta: { label: Payload }
|
||||
types:
|
||||
- { wf: Bytes }
|
||||
results:
|
||||
reminder: *rvReminder
|
||||
dismiss:
|
||||
meta:
|
||||
short: Dismiss reminder
|
||||
labels:
|
||||
<<: *labels
|
||||
dismiss: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
snooze:
|
||||
meta:
|
||||
short: Snooze reminder
|
||||
labels:
|
||||
<<: *labels
|
||||
snooze: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
remindAt:
|
||||
meta: { label: Remind at }
|
||||
types:
|
||||
- { wf: DateTime }
|
||||
required: true
|
||||
delete:
|
||||
meta:
|
||||
short: Reminder delete
|
||||
labels:
|
||||
<<: *labels
|
||||
delete: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
1424
server/system/automation/roles_handler.gen.go
generated
Normal file
1424
server/system/automation/roles_handler.gen.go
generated
Normal file
File diff suppressed because it is too large
Load Diff
434
server/system/automation/roles_handler.go
Normal file
434
server/system/automation/roles_handler.go
Normal file
@@ -0,0 +1,434 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
roleService interface {
|
||||
FindByID(ctx context.Context, roleID uint64) (*types.Role, error)
|
||||
FindByHandle(ctx context.Context, handle string) (*types.Role, error)
|
||||
Find(ctx context.Context, filter types.RoleFilter) (set types.RoleSet, f types.RoleFilter, err error)
|
||||
|
||||
Create(ctx context.Context, role *types.Role) (*types.Role, error)
|
||||
Update(ctx context.Context, role *types.Role) (*types.Role, error)
|
||||
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
Archive(ctx context.Context, id uint64) error
|
||||
Unarchive(ctx context.Context, id uint64) error
|
||||
Undelete(ctx context.Context, id uint64) error
|
||||
|
||||
Membership(ctx context.Context, userID uint64) (types.RoleMemberSet, error)
|
||||
MemberList(ctx context.Context, roleID uint64) (types.RoleMemberSet, error)
|
||||
MemberAdd(ctx context.Context, roleID, userID uint64) error
|
||||
MemberRemove(ctx context.Context, roleID, userID uint64) error
|
||||
}
|
||||
|
||||
rolesHandler struct {
|
||||
reg rolesHandlerRegistry
|
||||
rSvc roleService
|
||||
uSvc userService
|
||||
}
|
||||
|
||||
roleSetIterator struct {
|
||||
// Item buffer, current item pointer, and total items traversed
|
||||
ptr uint
|
||||
buffer types.RoleSet
|
||||
total uint
|
||||
|
||||
// When filter limit is set, this constraints it
|
||||
iterLimit uint
|
||||
useIterLimit bool
|
||||
|
||||
// Item loader for additional chunks
|
||||
filter types.RoleFilter
|
||||
loader func() error
|
||||
|
||||
// Total reported by the first page
|
||||
resTotal uint
|
||||
}
|
||||
|
||||
roleLookup interface {
|
||||
GetLookup() (bool, uint64, string, *types.Role)
|
||||
}
|
||||
)
|
||||
|
||||
func RolesHandler(reg rolesHandlerRegistry, rSvc roleService, uSvc userService) *rolesHandler {
|
||||
h := &rolesHandler{
|
||||
reg: reg,
|
||||
rSvc: rSvc,
|
||||
uSvc: uSvc,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h rolesHandler) lookup(ctx context.Context, args *rolesLookupArgs) (results *rolesLookupResults, err error) {
|
||||
results = &rolesLookupResults{}
|
||||
results.Role, err = lookupRole(ctx, h.rSvc, args)
|
||||
return
|
||||
}
|
||||
|
||||
func (h rolesHandler) searchMembers(ctx context.Context, args *rolesSearchMembersArgs) (results *rolesSearchMembersResults, err error) {
|
||||
results = &rolesSearchMembersResults{}
|
||||
|
||||
rl, err := lookupRole(ctx, h.rSvc, args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if rl == nil {
|
||||
return nil, fmt.Errorf("role not found")
|
||||
}
|
||||
|
||||
// Get membership info
|
||||
mm, err := h.rSvc.MemberList(ctx, rl.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(mm) == 0 {
|
||||
results.Users = []*types.User{}
|
||||
return
|
||||
}
|
||||
|
||||
// Get actual users
|
||||
uu := make([]string, len(mm))
|
||||
for i, m := range mm {
|
||||
uu[i] = cast.ToString(rbac.ResourceID(m.Resource))
|
||||
}
|
||||
results.Users, _, err = h.uSvc.Find(ctx, types.UserFilter{
|
||||
UserID: uu,
|
||||
})
|
||||
results.Total = uint64(len(results.Users))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h rolesHandler) eachMember(ctx context.Context, args *rolesEachMemberArgs) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
i = &userSetIterator{}
|
||||
)
|
||||
|
||||
rl, err := lookupRole(ctx, h.rSvc, args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if rl == nil {
|
||||
return nil, fmt.Errorf("role not found")
|
||||
}
|
||||
|
||||
// Get membership info
|
||||
mm, err := h.rSvc.MemberList(ctx, rl.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(mm) == 0 {
|
||||
i.buffer = []*types.User{}
|
||||
i.filter = types.UserFilter{}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Get actual users
|
||||
uu := make([]string, len(mm))
|
||||
for i, m := range mm {
|
||||
uu[i] = cast.ToString(rbac.ResourceID(m.Resource))
|
||||
}
|
||||
i.buffer, i.filter, err = h.uSvc.Find(ctx, types.UserFilter{
|
||||
UserID: uu,
|
||||
})
|
||||
return i, err
|
||||
}
|
||||
|
||||
func (h rolesHandler) addMember(ctx context.Context, args *rolesAddMemberArgs) (err error) {
|
||||
role, err := lookupRole(ctx, h.rSvc, &rolesLookupArgs{
|
||||
hasLookup: args.hasRole,
|
||||
Lookup: args.Role,
|
||||
lookupID: args.roleID,
|
||||
lookupHandle: args.roleHandle,
|
||||
lookupRes: args.roleRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if role == nil {
|
||||
return fmt.Errorf("role not found")
|
||||
}
|
||||
|
||||
user, err := lookupUser(ctx, h.uSvc, &usersLookupArgs{
|
||||
hasLookup: args.hasUser,
|
||||
Lookup: args.User,
|
||||
lookupID: args.userID,
|
||||
lookupHandle: args.userHandle,
|
||||
lookupEmail: args.userEmail,
|
||||
lookupRes: args.userRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if role == nil {
|
||||
return fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
return h.rSvc.MemberAdd(ctx, role.ID, user.ID)
|
||||
}
|
||||
|
||||
func (h rolesHandler) removeMember(ctx context.Context, args *rolesRemoveMemberArgs) (err error) {
|
||||
role, err := lookupRole(ctx, h.rSvc, &rolesLookupArgs{
|
||||
hasLookup: args.hasRole,
|
||||
Lookup: args.Role,
|
||||
lookupID: args.roleID,
|
||||
lookupHandle: args.roleHandle,
|
||||
lookupRes: args.roleRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if role == nil {
|
||||
return fmt.Errorf("role not found")
|
||||
}
|
||||
|
||||
user, err := lookupUser(ctx, h.uSvc, &usersLookupArgs{
|
||||
hasLookup: args.hasUser,
|
||||
Lookup: args.User,
|
||||
lookupID: args.userID,
|
||||
lookupHandle: args.userHandle,
|
||||
lookupEmail: args.userEmail,
|
||||
lookupRes: args.userRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if role == nil {
|
||||
return fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
return h.rSvc.MemberRemove(ctx, role.ID, user.ID)
|
||||
}
|
||||
|
||||
func (h rolesHandler) search(ctx context.Context, args *rolesSearchArgs) (results *rolesSearchResults, err error) {
|
||||
results = &rolesSearchResults{}
|
||||
|
||||
var (
|
||||
f = types.RoleFilter{
|
||||
Query: args.Query,
|
||||
MemberID: args.MemberID,
|
||||
Name: args.Name,
|
||||
Handle: args.Handle,
|
||||
Labels: args.Labels,
|
||||
Deleted: filter.State(args.Deleted),
|
||||
Archived: filter.State(args.Archived),
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.PageCursor = &filter.PagingCursor{}
|
||||
if err = f.PageCursor.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
f.Limit = uint(args.Limit)
|
||||
}
|
||||
|
||||
// Total cannot be fetched together with a page cursor
|
||||
f.IncTotal = args.IncTotal && f.PageCursor == nil
|
||||
|
||||
var auxf types.RoleFilter
|
||||
results.Roles, auxf, err = h.rSvc.Find(ctx, f)
|
||||
results.Total = uint64(auxf.Total)
|
||||
return
|
||||
}
|
||||
|
||||
func (h rolesHandler) each(ctx context.Context, args *rolesEachArgs) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
i = &roleSetIterator{}
|
||||
f = types.RoleFilter{
|
||||
Query: args.Query,
|
||||
MemberID: args.MemberID,
|
||||
Name: args.Name,
|
||||
Handle: args.Handle,
|
||||
Labels: args.Labels,
|
||||
Deleted: filter.State(args.Deleted),
|
||||
Archived: filter.State(args.Archived),
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.NextPage = &filter.PagingCursor{}
|
||||
if err = f.NextPage.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
i.useIterLimit = true
|
||||
i.iterLimit = uint(args.Limit)
|
||||
|
||||
f.Limit = uint(args.Limit)
|
||||
|
||||
if args.Limit > uint64(wfexec.MaxIteratorBufferSize) {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
i.iterLimit = uint(args.Limit)
|
||||
} else {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
|
||||
f.IncTotal = args.IncTotal
|
||||
|
||||
i.filter = f
|
||||
i.loader = func() (err error) {
|
||||
// Edgecase
|
||||
if i.filter.PageCursor != nil && i.filter.NextPage == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.total += i.ptr
|
||||
i.ptr = 0
|
||||
|
||||
i.filter.PageCursor = i.filter.NextPage
|
||||
i.filter.NextPage = nil
|
||||
|
||||
// Total is fetched with the first page only; a paged query cannot carry it
|
||||
i.filter.IncTotal = i.filter.IncTotal && i.filter.PageCursor == nil
|
||||
|
||||
i.buffer, i.filter, err = h.rSvc.Find(ctx, i.filter)
|
||||
if i.filter.IncTotal {
|
||||
i.resTotal = i.filter.Total
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Initial load
|
||||
return i, i.loader()
|
||||
}
|
||||
|
||||
func (h rolesHandler) create(ctx context.Context, args *rolesCreateArgs) (results *rolesCreateResults, err error) {
|
||||
results = &rolesCreateResults{}
|
||||
results.Role, err = h.rSvc.Create(ctx, args.Role)
|
||||
return
|
||||
}
|
||||
|
||||
func (h rolesHandler) update(ctx context.Context, args *rolesUpdateArgs) (results *rolesUpdateResults, err error) {
|
||||
results = &rolesUpdateResults{}
|
||||
results.Role, err = h.rSvc.Update(ctx, args.Role)
|
||||
return
|
||||
}
|
||||
|
||||
func (h rolesHandler) delete(ctx context.Context, args *rolesDeleteArgs) error {
|
||||
if id, err := getRoleID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Delete(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h rolesHandler) recover(ctx context.Context, args *rolesRecoverArgs) error {
|
||||
if id, err := getRoleID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Undelete(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h rolesHandler) archive(ctx context.Context, args *rolesArchiveArgs) error {
|
||||
if id, err := getRoleID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Archive(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h rolesHandler) unarchive(ctx context.Context, args *rolesUnarchiveArgs) error {
|
||||
if id, err := getRoleID(ctx, h.rSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rSvc.Unarchive(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func getRoleID(ctx context.Context, svc roleService, args roleLookup) (uint64, error) {
|
||||
_, ID, _, _ := args.GetLookup()
|
||||
|
||||
if ID > 0 {
|
||||
return ID, nil
|
||||
}
|
||||
|
||||
role, err := lookupRole(ctx, svc, args)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return role.ID, nil
|
||||
|
||||
}
|
||||
|
||||
func lookupRole(ctx context.Context, svc roleService, args roleLookup) (*types.Role, error) {
|
||||
_, ID, handle, role := args.GetLookup()
|
||||
|
||||
switch {
|
||||
case role != nil:
|
||||
return role, nil
|
||||
case ID > 0:
|
||||
return svc.FindByID(ctx, ID)
|
||||
case len(handle) > 0:
|
||||
return svc.FindByHandle(ctx, handle)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("empty lookup params")
|
||||
}
|
||||
|
||||
func (i *roleSetIterator) More(context.Context, *Vars) (bool, error) {
|
||||
a := wfexec.GenericResourceNextCheck(i.useIterLimit, i.ptr, uint(len(i.buffer)), i.total, i.iterLimit, i.filter.NextPage != nil)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (i *roleSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
|
||||
|
||||
func (i *roleSetIterator) Next(context.Context, *Vars) (out *Vars, err error) {
|
||||
if len(i.buffer)-int(i.ptr) <= 0 {
|
||||
if err = i.loader(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
out = &Vars{}
|
||||
out.Set("role", Must(NewRole(i.buffer[i.ptr])))
|
||||
out.Set("index", Must(NewInteger(i.total+i.ptr)))
|
||||
out.Set("total", Must(NewInteger(i.resTotal)))
|
||||
|
||||
i.ptr++
|
||||
return out, nil
|
||||
}
|
||||
237
server/system/automation/roles_handler.yaml
Normal file
237
server/system/automation/roles_handler.yaml
Normal file
@@ -0,0 +1,237 @@
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/system/types
|
||||
- labelTypes github.com/cortezaproject/corteza/server/pkg/label/types
|
||||
|
||||
|
||||
params:
|
||||
lookup: &lookup
|
||||
meta: { label: Lookup }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: Role, suffix: res }
|
||||
|
||||
role: &role
|
||||
meta: { label: Role }
|
||||
types:
|
||||
- { wf: Role }
|
||||
|
||||
rvRole: &rvRole
|
||||
wf: Role
|
||||
|
||||
rvUser: &rvUser
|
||||
wf: User
|
||||
|
||||
rvTotal: &rvTotal
|
||||
wf: UnsignedInteger
|
||||
|
||||
rvPageCursor: &rvPageCursor
|
||||
wf: String
|
||||
|
||||
lookupUser: &lookupUser
|
||||
meta: { label: User }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID }
|
||||
- { wf: Handle }
|
||||
- { wf: String, suffix: email }
|
||||
- { wf: User, suffix: res }
|
||||
|
||||
filterParams: &filterParams
|
||||
query:
|
||||
meta: { label: Query }
|
||||
types:
|
||||
- { wf: String }
|
||||
memberID:
|
||||
meta: { label: Member ID }
|
||||
types:
|
||||
- { wf: ID }
|
||||
handle:
|
||||
meta: { label: Handle }
|
||||
types:
|
||||
- { wf: String }
|
||||
name:
|
||||
meta: { label: Name }
|
||||
types:
|
||||
- { wf: String }
|
||||
labels:
|
||||
meta: { label: Labels }
|
||||
types:
|
||||
- { wf: LabelValue }
|
||||
deleted:
|
||||
meta: { label: Deleted }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
archived:
|
||||
meta: { label: Archived }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
sort:
|
||||
meta: { label: Sort }
|
||||
types:
|
||||
- { wf: String }
|
||||
limit:
|
||||
meta: { label: Limit }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
incTotal:
|
||||
meta: { label: Include total }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
incPageNavigation:
|
||||
meta: { label: Include page navigation }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
pageCursor:
|
||||
meta: { label: Page cursor }
|
||||
types:
|
||||
- { wf: String }
|
||||
|
||||
labels: &labels
|
||||
users: "step,workflow"
|
||||
|
||||
functions:
|
||||
lookup:
|
||||
meta:
|
||||
short: Role lookup
|
||||
description: Find specific role by ID or handle
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
lookup: *lookup
|
||||
results:
|
||||
role: *rvRole
|
||||
|
||||
searchMembers:
|
||||
meta:
|
||||
short: Role members search
|
||||
description: Find members for a specific roleby ID or handle
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
lookup: *lookup
|
||||
results:
|
||||
users:
|
||||
<<: *rvUser
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
|
||||
eachMember:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Iterate over role members
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
lookup: *lookup
|
||||
results:
|
||||
user: *rvUser
|
||||
total: *rvTotal
|
||||
|
||||
addMember:
|
||||
meta:
|
||||
short: Role membership add
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
role:
|
||||
<<: *lookup
|
||||
meta: { label: Role }
|
||||
user: *lookupUser
|
||||
|
||||
removeMember:
|
||||
meta:
|
||||
short: Role membership remove
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
role:
|
||||
<<: *lookup
|
||||
meta: { label: Role }
|
||||
user: *lookupUser
|
||||
|
||||
search:
|
||||
meta:
|
||||
short: Roles search
|
||||
labels:
|
||||
<<: *labels
|
||||
params: *filterParams
|
||||
results:
|
||||
roles:
|
||||
<<: *rvRole
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
|
||||
each:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Roles
|
||||
params: *filterParams
|
||||
results:
|
||||
role: *rvRole
|
||||
total: *rvTotal
|
||||
|
||||
create:
|
||||
meta:
|
||||
short: Role creator
|
||||
labels:
|
||||
<<: *labels
|
||||
create: "step"
|
||||
params:
|
||||
role:
|
||||
<<: *role
|
||||
meta: { label: Role }
|
||||
required: true
|
||||
results:
|
||||
role: *rvRole
|
||||
|
||||
update:
|
||||
meta:
|
||||
short: Role update
|
||||
labels:
|
||||
<<: *labels
|
||||
update: "step"
|
||||
params:
|
||||
role:
|
||||
<<: *role
|
||||
meta: { label: Role }
|
||||
required: true
|
||||
results:
|
||||
role: *rvRole
|
||||
|
||||
delete:
|
||||
meta:
|
||||
short: Role delete
|
||||
labels:
|
||||
<<: *labels
|
||||
delete: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
recover:
|
||||
meta:
|
||||
short: Role recover
|
||||
labels:
|
||||
<<: *labels
|
||||
recover: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
archive:
|
||||
meta:
|
||||
short: Role archive
|
||||
labels:
|
||||
<<: *labels
|
||||
archive: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
unarchive:
|
||||
meta:
|
||||
short: Role unarchive
|
||||
labels:
|
||||
<<: *labels
|
||||
unarchive: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
1025
server/system/automation/templates_handler.gen.go
generated
Normal file
1025
server/system/automation/templates_handler.gen.go
generated
Normal file
File diff suppressed because it is too large
Load Diff
341
server/system/automation/templates_handler.go
Normal file
341
server/system/automation/templates_handler.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
. "github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type (
|
||||
templateService interface {
|
||||
FindByID(ctx context.Context, ID uint64) (*types.Template, error)
|
||||
FindByHandle(ct context.Context, handle string) (*types.Template, error)
|
||||
FindByAny(ctx context.Context, identifier interface{}) (*types.Template, error)
|
||||
Search(context.Context, types.TemplateFilter) (types.TemplateSet, types.TemplateFilter, error)
|
||||
|
||||
Create(ctx context.Context, tpl *types.Template) (*types.Template, error)
|
||||
Update(ctx context.Context, tpl *types.Template) (*types.Template, error)
|
||||
|
||||
DeleteByID(ctx context.Context, ID uint64) error
|
||||
UndeleteByID(ctx context.Context, ID uint64) error
|
||||
|
||||
Render(ctx context.Context, templateID uint64, dstType string, variables map[string]interface{}, options map[string]string, aux types.TemplateRenderAux) (io.ReadSeeker, error)
|
||||
}
|
||||
|
||||
templatesHandler struct {
|
||||
reg templatesHandlerRegistry
|
||||
tSvc templateService
|
||||
}
|
||||
|
||||
templateSetIterator struct {
|
||||
// Item buffer, current item pointer, and total items traversed
|
||||
ptr uint
|
||||
buffer types.TemplateSet
|
||||
total uint
|
||||
|
||||
// When filter limit is set, this constraints it
|
||||
iterLimit uint
|
||||
useIterLimit bool
|
||||
|
||||
// Item loader for additional chunks
|
||||
filter types.TemplateFilter
|
||||
loader func() error
|
||||
|
||||
// Total reported by the first page
|
||||
resTotal uint
|
||||
}
|
||||
|
||||
templateLookup interface {
|
||||
GetLookup() (bool, uint64, string, *types.Template)
|
||||
}
|
||||
)
|
||||
|
||||
func TemplatesHandler(reg templatesHandlerRegistry, tSvc templateService) *templatesHandler {
|
||||
h := &templatesHandler{
|
||||
reg: reg,
|
||||
tSvc: tSvc,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h templatesHandler) lookup(ctx context.Context, args *templatesLookupArgs) (results *templatesLookupResults, err error) {
|
||||
results = &templatesLookupResults{}
|
||||
results.Template, err = lookupTemplate(ctx, h.tSvc, args)
|
||||
return
|
||||
}
|
||||
|
||||
func (h templatesHandler) search(ctx context.Context, args *templatesSearchArgs) (results *templatesSearchResults, err error) {
|
||||
results = &templatesSearchResults{}
|
||||
|
||||
var (
|
||||
f = types.TemplateFilter{
|
||||
Handle: args.Handle,
|
||||
Type: args.Type,
|
||||
OwnerID: args.OwnerID,
|
||||
Partial: args.Partial,
|
||||
Labels: args.Labels,
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.PageCursor = &filter.PagingCursor{}
|
||||
if err = f.PageCursor.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
f.Limit = uint(args.Limit)
|
||||
}
|
||||
|
||||
// Total cannot be fetched together with a page cursor
|
||||
f.IncTotal = args.IncTotal && f.PageCursor == nil
|
||||
|
||||
var auxf types.TemplateFilter
|
||||
results.Templates, auxf, err = h.tSvc.Search(ctx, f)
|
||||
results.Total = uint64(auxf.Total)
|
||||
return
|
||||
}
|
||||
|
||||
func (h templatesHandler) each(ctx context.Context, args *templatesEachArgs) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
i = &templateSetIterator{}
|
||||
f = types.TemplateFilter{
|
||||
Handle: args.Handle,
|
||||
Type: args.Type,
|
||||
OwnerID: args.OwnerID,
|
||||
Partial: args.Partial,
|
||||
Labels: args.Labels,
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.NextPage = &filter.PagingCursor{}
|
||||
if err = f.NextPage.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
i.useIterLimit = true
|
||||
i.iterLimit = uint(args.Limit)
|
||||
|
||||
f.Limit = uint(args.Limit)
|
||||
|
||||
if args.Limit > uint64(wfexec.MaxIteratorBufferSize) {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
i.iterLimit = uint(args.Limit)
|
||||
} else {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
|
||||
f.IncTotal = args.IncTotal
|
||||
|
||||
i.filter = f
|
||||
i.loader = func() (err error) {
|
||||
// Edgecase
|
||||
if i.filter.PageCursor != nil && i.filter.NextPage == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.total += i.ptr
|
||||
i.ptr = 0
|
||||
|
||||
i.filter.PageCursor = i.filter.NextPage
|
||||
i.filter.NextPage = nil
|
||||
|
||||
// Total is fetched with the first page only; a paged query cannot carry it
|
||||
i.filter.IncTotal = i.filter.IncTotal && i.filter.PageCursor == nil
|
||||
|
||||
i.buffer, i.filter, err = h.tSvc.Search(ctx, i.filter)
|
||||
if i.filter.IncTotal {
|
||||
i.resTotal = i.filter.Total
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Initial load
|
||||
return i, i.loader()
|
||||
}
|
||||
|
||||
func (h templatesHandler) create(ctx context.Context, args *templatesCreateArgs) (results *templatesCreateResults, err error) {
|
||||
results = &templatesCreateResults{}
|
||||
results.Template, err = h.tSvc.Create(ctx, args.Template)
|
||||
return
|
||||
}
|
||||
|
||||
func (h templatesHandler) update(ctx context.Context, args *templatesUpdateArgs) (results *templatesUpdateResults, err error) {
|
||||
results = &templatesUpdateResults{}
|
||||
results.Template, err = h.tSvc.Update(ctx, args.Template)
|
||||
return
|
||||
}
|
||||
|
||||
func (h templatesHandler) delete(ctx context.Context, args *templatesDeleteArgs) error {
|
||||
if id, err := getTemplateID(ctx, h.tSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.tSvc.DeleteByID(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h templatesHandler) recover(ctx context.Context, args *templatesRecoverArgs) error {
|
||||
if id, err := getTemplateID(ctx, h.tSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.tSvc.UndeleteByID(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h templatesHandler) render(ctx context.Context, args *templatesRenderArgs) (*templatesRenderResults, error) {
|
||||
var err error
|
||||
|
||||
opts := make(map[string]string)
|
||||
if args.hasOptions {
|
||||
opts, err = cast.ToStringMapStringE(args.Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tplID, err := getTemplateID(ctx, h.tSvc, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vars map[string]interface{}
|
||||
if args.hasVariables {
|
||||
vars = args.Variables.Dict()
|
||||
}
|
||||
|
||||
aux := types.TemplateRenderAux{}
|
||||
|
||||
aux.HeaderTemplateID, err = resolveTemplateID(ctx, h.tSvc, args.hasHeaderTemplate, args.headerTemplateID, args.headerTemplateHandle, args.headerTemplateRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aux.FooterTemplateID, err = resolveTemplateID(ctx, h.tSvc, args.hasFooterTemplate, args.footerTemplateID, args.footerTemplateHandle, args.footerTemplateRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc, err := h.tSvc.Render(ctx, tplID, args.DocumentType, vars, opts, aux)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rr := &templatesRenderResults{
|
||||
Document: &renderedDocument{
|
||||
Document: doc,
|
||||
Name: args.DocumentName,
|
||||
Type: args.DocumentType,
|
||||
},
|
||||
}
|
||||
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
func (i *templateSetIterator) More(context.Context, *Vars) (bool, error) {
|
||||
return wfexec.GenericResourceNextCheck(i.useIterLimit, i.ptr, uint(len(i.buffer)), i.total, i.iterLimit, i.filter.NextPage != nil), nil
|
||||
}
|
||||
|
||||
func (i *templateSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
|
||||
|
||||
func (i *templateSetIterator) Next(context.Context, *Vars) (out *Vars, err error) {
|
||||
if len(i.buffer)-int(i.ptr) <= 0 {
|
||||
if err = i.loader(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
out = &Vars{}
|
||||
out.Set("template", Must(NewTemplate(i.buffer[i.ptr])))
|
||||
out.Set("index", Must(NewInteger(i.total+i.ptr)))
|
||||
out.Set("total", Must(NewInteger(i.resTotal)))
|
||||
|
||||
i.ptr++
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func lookupTemplate(ctx context.Context, svc templateService, args templateLookup) (*types.Template, error) {
|
||||
_, ID, handle, template := args.GetLookup()
|
||||
|
||||
switch {
|
||||
case template != nil:
|
||||
return template, nil
|
||||
case ID > 0:
|
||||
return svc.FindByID(ctx, ID)
|
||||
case len(handle) > 0:
|
||||
return svc.FindByHandle(ctx, handle)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("empty lookup params")
|
||||
}
|
||||
|
||||
func getTemplateID(ctx context.Context, svc templateService, args templateLookup) (uint64, error) {
|
||||
_, ID, _, _ := args.GetLookup()
|
||||
|
||||
if ID > 0 {
|
||||
return ID, nil
|
||||
}
|
||||
|
||||
tpl, err := lookupTemplate(ctx, svc, args)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return tpl.ID, nil
|
||||
}
|
||||
|
||||
// resolveTemplateID resolves ID, handle or template argument into a template ID
|
||||
//
|
||||
// Unset argument resolves into zero ID
|
||||
func resolveTemplateID(ctx context.Context, svc templateService, has bool, ID uint64, handle string, tpl *types.Template) (uint64, error) {
|
||||
switch {
|
||||
case !has:
|
||||
return 0, nil
|
||||
case tpl != nil:
|
||||
return tpl.ID, nil
|
||||
case ID > 0:
|
||||
return ID, nil
|
||||
case len(handle) > 0:
|
||||
tpl, err := svc.FindByHandle(ctx, handle)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return tpl.ID, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
207
server/system/automation/templates_handler.yaml
Normal file
207
server/system/automation/templates_handler.yaml
Normal file
@@ -0,0 +1,207 @@
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/system/types
|
||||
- labelTypes github.com/cortezaproject/corteza/server/pkg/label/types
|
||||
|
||||
|
||||
snippets:
|
||||
lookup: &lookup
|
||||
meta: { label: Lookup }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID }
|
||||
- { wf: Handle }
|
||||
- { wf: Template, suffix: res }
|
||||
|
||||
# same as lookup but labeled for the render function where it is the body of the document
|
||||
bodyLookup: &bodyLookup
|
||||
required: true
|
||||
meta:
|
||||
label: Body template
|
||||
description: Template rendered as the body of the document
|
||||
types:
|
||||
- { wf: ID }
|
||||
- { wf: Handle }
|
||||
- { wf: Template, suffix: res }
|
||||
|
||||
auxTemplate: &auxTemplate
|
||||
types:
|
||||
- { wf: ID }
|
||||
- { wf: Handle }
|
||||
- { wf: Template, suffix: res }
|
||||
|
||||
template: &template
|
||||
types:
|
||||
- { wf: Template }
|
||||
|
||||
rvTemplate: &rvTemplate
|
||||
wf: Template
|
||||
|
||||
rvRenderedDocument: &rvRenderedDocument
|
||||
wf: RenderedDocument
|
||||
|
||||
rvTotal: &rvTotal
|
||||
wf: UnsignedInteger
|
||||
|
||||
rvPageCursor: &rvPageCursor
|
||||
wf: String
|
||||
|
||||
filterParams: &filterParams
|
||||
handle:
|
||||
meta: { label: Handle }
|
||||
types:
|
||||
- { wf: String }
|
||||
type:
|
||||
meta: { label: Type }
|
||||
types:
|
||||
- { wf: String }
|
||||
ownerID:
|
||||
meta: { label: Owner ID }
|
||||
types:
|
||||
- { wf: ID }
|
||||
partial:
|
||||
meta: { label: Partial }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
labels:
|
||||
meta: { label: Labels }
|
||||
types:
|
||||
- { wf: LabelValue }
|
||||
sort:
|
||||
meta: { label: Sort }
|
||||
types:
|
||||
- { wf: String }
|
||||
limit:
|
||||
meta: { label: Limit }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
incTotal:
|
||||
meta: { label: Include total }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
incPageNavigation:
|
||||
meta: { label: Include page navigation }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
pageCursor:
|
||||
meta: { label: Page cursor }
|
||||
types:
|
||||
- { wf: String }
|
||||
|
||||
labels: &labels
|
||||
templates: "step,workflow"
|
||||
|
||||
functions:
|
||||
lookup:
|
||||
meta:
|
||||
short: Template lookup
|
||||
description: Find specific template by ID or handle
|
||||
params:
|
||||
lookup: *lookup
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
template: *rvTemplate
|
||||
|
||||
search:
|
||||
meta:
|
||||
short: Templates search
|
||||
params: *filterParams
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
templates:
|
||||
<<: *rvTemplate
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
|
||||
each:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Templates
|
||||
params: *filterParams
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
template: *rvTemplate
|
||||
total: *rvTotal
|
||||
|
||||
create:
|
||||
meta:
|
||||
short: Template create
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
template:
|
||||
<<: *template
|
||||
meta: { label: Template }
|
||||
required: true
|
||||
results:
|
||||
template: *rvTemplate
|
||||
|
||||
update:
|
||||
meta:
|
||||
short: Template update
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
template:
|
||||
<<: *template
|
||||
meta: { label: Template }
|
||||
required: true
|
||||
results:
|
||||
template: *rvTemplate
|
||||
|
||||
delete:
|
||||
labels:
|
||||
<<: *labels
|
||||
delete: "step"
|
||||
meta:
|
||||
short: Template delete
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
recover:
|
||||
meta:
|
||||
short: Template recover
|
||||
labels:
|
||||
<<: *labels
|
||||
recover: "step"
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
render:
|
||||
meta:
|
||||
short: Template render
|
||||
labels:
|
||||
<<: *labels
|
||||
render: "step"
|
||||
params:
|
||||
lookup: *bodyLookup
|
||||
headerTemplate:
|
||||
<<: *auxTemplate
|
||||
meta:
|
||||
label: Header template
|
||||
description: Overrides the header template set on the body template; used by drivers that support it
|
||||
footerTemplate:
|
||||
<<: *auxTemplate
|
||||
meta:
|
||||
label: Footer template
|
||||
description: Overrides the footer template set on the body template; used by drivers that support it
|
||||
documentName:
|
||||
meta: { label: Document name }
|
||||
types:
|
||||
- { wf: String }
|
||||
documentType:
|
||||
meta: { label: Document type }
|
||||
types:
|
||||
- { wf: String }
|
||||
variables:
|
||||
meta: { label: Variables }
|
||||
types:
|
||||
- { wf: Vars, go: '*expr.Vars' }
|
||||
options:
|
||||
meta: { label: Options }
|
||||
types:
|
||||
- { wf: RenderOptions }
|
||||
results:
|
||||
document: *rvRenderedDocument
|
||||
141
server/system/automation/templates_handler_test.go
Normal file
141
server/system/automation/templates_handler_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type (
|
||||
templateServiceMocked struct {
|
||||
templateService
|
||||
|
||||
byHandle func(context.Context, string) (*types.Template, error)
|
||||
rendered func(uint64, types.TemplateRenderAux)
|
||||
}
|
||||
)
|
||||
|
||||
func (svc templateServiceMocked) FindByHandle(ctx context.Context, h string) (*types.Template, error) {
|
||||
return svc.byHandle(ctx, h)
|
||||
}
|
||||
|
||||
func (svc templateServiceMocked) Render(_ context.Context, templateID uint64, _ string, _ map[string]interface{}, _ map[string]string, aux types.TemplateRenderAux) (io.ReadSeeker, error) {
|
||||
svc.rendered(templateID, aux)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestResolveTemplateID(t *testing.T) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
svc = templateServiceMocked{
|
||||
byHandle: func(_ context.Context, h string) (*types.Template, error) {
|
||||
if h != "header" {
|
||||
return nil, fmt.Errorf("unknown template %q", h)
|
||||
}
|
||||
|
||||
return &types.Template{ID: 200}, nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
t.Run("unset argument", func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
ID, err := resolveTemplateID(ctx, svc, false, 0, "", nil)
|
||||
req.NoError(err)
|
||||
req.Zero(ID)
|
||||
})
|
||||
|
||||
t.Run("by ID", func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
ID, err := resolveTemplateID(ctx, svc, true, 100, "", nil)
|
||||
req.NoError(err)
|
||||
req.Equal(uint64(100), ID)
|
||||
})
|
||||
|
||||
t.Run("by handle", func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
ID, err := resolveTemplateID(ctx, svc, true, 0, "header", nil)
|
||||
req.NoError(err)
|
||||
req.Equal(uint64(200), ID)
|
||||
})
|
||||
|
||||
t.Run("by template", func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
ID, err := resolveTemplateID(ctx, svc, true, 0, "", &types.Template{ID: 300})
|
||||
req.NoError(err)
|
||||
req.Equal(uint64(300), ID)
|
||||
})
|
||||
|
||||
t.Run("unknown handle", func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
_, err := resolveTemplateID(ctx, svc, true, 0, "missing", nil)
|
||||
req.Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderPassesAuxTemplates(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
|
||||
renderedID uint64
|
||||
renderedAux types.TemplateRenderAux
|
||||
|
||||
svc = templateServiceMocked{
|
||||
byHandle: func(_ context.Context, h string) (*types.Template, error) {
|
||||
return &types.Template{ID: 200}, nil
|
||||
},
|
||||
rendered: func(ID uint64, aux types.TemplateRenderAux) {
|
||||
renderedID, renderedAux = ID, aux
|
||||
},
|
||||
}
|
||||
|
||||
h = templatesHandler{tSvc: svc}
|
||||
)
|
||||
|
||||
args := &templatesRenderArgs{
|
||||
hasLookup: true,
|
||||
lookupID: 100,
|
||||
|
||||
DocumentType: "application/pdf",
|
||||
|
||||
hasHeaderTemplate: true,
|
||||
headerTemplateID: 300,
|
||||
|
||||
hasFooterTemplate: true,
|
||||
footerTemplateHandle: "footer",
|
||||
}
|
||||
|
||||
_, err := h.render(context.Background(), args)
|
||||
req.NoError(err)
|
||||
|
||||
req.Equal(uint64(100), renderedID)
|
||||
req.Equal(uint64(300), renderedAux.HeaderTemplateID)
|
||||
req.Equal(uint64(200), renderedAux.FooterTemplateID)
|
||||
}
|
||||
|
||||
func TestRenderWithoutAuxTemplates(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
|
||||
renderedAux types.TemplateRenderAux
|
||||
|
||||
svc = templateServiceMocked{
|
||||
rendered: func(_ uint64, aux types.TemplateRenderAux) {
|
||||
renderedAux = aux
|
||||
},
|
||||
}
|
||||
|
||||
h = templatesHandler{tSvc: svc}
|
||||
)
|
||||
|
||||
_, err := h.render(context.Background(), &templatesRenderArgs{hasLookup: true, lookupID: 100})
|
||||
req.NoError(err)
|
||||
|
||||
// nothing set, service falls back to the template's own meta
|
||||
req.Zero(renderedAux.HeaderTemplateID)
|
||||
req.Zero(renderedAux.FooterTemplateID)
|
||||
}
|
||||
1261
server/system/automation/users_handler.gen.go
generated
Normal file
1261
server/system/automation/users_handler.gen.go
generated
Normal file
File diff suppressed because it is too large
Load Diff
383
server/system/automation/users_handler.go
Normal file
383
server/system/automation/users_handler.go
Normal file
@@ -0,0 +1,383 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
. "github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
userService interface {
|
||||
FindByID(ctx context.Context, userID uint64) (*types.User, error)
|
||||
FindByHandle(ctx context.Context, handle string) (*types.User, error)
|
||||
FindByEmail(ctx context.Context, email string) (*types.User, error)
|
||||
Find(ctx context.Context, filter types.UserFilter) (set types.UserSet, f types.UserFilter, err error)
|
||||
|
||||
Create(ctx context.Context, user *types.User) (*types.User, error)
|
||||
Update(ctx context.Context, user *types.User) (*types.User, error)
|
||||
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
Suspend(ctx context.Context, id uint64) error
|
||||
Unsuspend(ctx context.Context, id uint64) error
|
||||
Undelete(ctx context.Context, id uint64) error
|
||||
}
|
||||
|
||||
usersHandler struct {
|
||||
reg usersHandlerRegistry
|
||||
uSvc userService
|
||||
rSvc roleService
|
||||
}
|
||||
|
||||
userSetIterator struct {
|
||||
// Item buffer, current item pointer, and total items traversed
|
||||
ptr uint
|
||||
buffer types.UserSet
|
||||
total uint
|
||||
|
||||
// When filter limit is set, this constraints it
|
||||
iterLimit uint
|
||||
useIterLimit bool
|
||||
|
||||
// Item loader for additional chunks
|
||||
filter types.UserFilter
|
||||
loader func() error
|
||||
|
||||
// Total reported by the first page
|
||||
resTotal uint
|
||||
}
|
||||
|
||||
userLookup interface {
|
||||
GetLookup() (bool, uint64, string, string, *types.User)
|
||||
}
|
||||
)
|
||||
|
||||
func UsersHandler(reg usersHandlerRegistry, uSvc userService, rSvc roleService) *usersHandler {
|
||||
h := &usersHandler{
|
||||
reg: reg,
|
||||
uSvc: uSvc,
|
||||
rSvc: rSvc,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h usersHandler) lookup(ctx context.Context, args *usersLookupArgs) (results *usersLookupResults, err error) {
|
||||
results = &usersLookupResults{}
|
||||
results.User, err = lookupUser(ctx, h.uSvc, args)
|
||||
return
|
||||
}
|
||||
|
||||
func (h usersHandler) searchMembership(ctx context.Context, args *usersSearchMembershipArgs) (results *usersSearchMembershipResults, err error) {
|
||||
results = &usersSearchMembershipResults{}
|
||||
u, err := lookupUser(ctx, h.uSvc, args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if u == nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
// Get the roles
|
||||
mm, err := h.rSvc.Membership(ctx, u.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(mm) == 0 {
|
||||
results.Roles = []*types.Role{}
|
||||
return
|
||||
}
|
||||
|
||||
rr := make([]string, len(mm))
|
||||
for i, r := range mm {
|
||||
rr[i] = strconv.FormatUint(r.RoleID, 10)
|
||||
}
|
||||
|
||||
results.Roles, _, err = h.rSvc.Find(ctx, types.RoleFilter{
|
||||
RoleID: rr,
|
||||
})
|
||||
results.Total = uint64(len(results.Roles))
|
||||
return
|
||||
}
|
||||
|
||||
func (h usersHandler) checkMembership(ctx context.Context, args *usersCheckMembershipArgs) (results *usersCheckMembershipResults, err error) {
|
||||
results = &usersCheckMembershipResults{}
|
||||
|
||||
// Resolve user
|
||||
u, err := lookupUser(ctx, h.uSvc, usersLookupArgs{
|
||||
hasLookup: args.hasUser,
|
||||
Lookup: args.User,
|
||||
lookupID: args.userID,
|
||||
lookupHandle: args.userHandle,
|
||||
lookupEmail: args.userEmail,
|
||||
lookupRes: args.userRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if u == nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
// Get user membershio
|
||||
mm, err := h.rSvc.Membership(ctx, u.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(mm) == 0 {
|
||||
return &usersCheckMembershipResults{
|
||||
Member: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Resolve role
|
||||
r, err := lookupRole(ctx, h.rSvc, rolesLookupArgs{
|
||||
hasLookup: args.hasRole,
|
||||
Lookup: args.Role,
|
||||
lookupID: args.roleID,
|
||||
lookupHandle: args.roleHandle,
|
||||
lookupRes: args.roleRes,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("role not found")
|
||||
}
|
||||
|
||||
// Check if there
|
||||
for _, m := range mm {
|
||||
if m.RoleID == r.ID {
|
||||
return &usersCheckMembershipResults{
|
||||
Member: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &usersCheckMembershipResults{
|
||||
Member: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h usersHandler) search(ctx context.Context, args *usersSearchArgs) (results *usersSearchResults, err error) {
|
||||
results = &usersSearchResults{}
|
||||
|
||||
var (
|
||||
f = types.UserFilter{
|
||||
Query: args.Query,
|
||||
Email: args.Email,
|
||||
Handle: args.Handle,
|
||||
Labels: args.Labels,
|
||||
Deleted: filter.State(args.Deleted),
|
||||
Suspended: filter.State(args.Suspended),
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.PageCursor = &filter.PagingCursor{}
|
||||
if err = f.PageCursor.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
f.Limit = uint(args.Limit)
|
||||
}
|
||||
|
||||
// Total cannot be fetched together with a page cursor
|
||||
f.IncTotal = args.IncTotal && f.PageCursor == nil
|
||||
|
||||
var auxf types.UserFilter
|
||||
results.Users, auxf, err = h.uSvc.Find(ctx, f)
|
||||
results.Total = uint64(auxf.Total)
|
||||
return
|
||||
}
|
||||
|
||||
func (h usersHandler) each(ctx context.Context, args *usersEachArgs) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
i = &userSetIterator{}
|
||||
f = types.UserFilter{
|
||||
Query: args.Query,
|
||||
Email: args.Email,
|
||||
Handle: args.Handle,
|
||||
Labels: args.Labels,
|
||||
Deleted: filter.State(args.Deleted),
|
||||
Suspended: filter.State(args.Suspended),
|
||||
}
|
||||
)
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor && args.PageCursor != "" {
|
||||
f.NextPage = &filter.PagingCursor{}
|
||||
if err = f.NextPage.UnmarshalJSON([]byte(args.PageCursor)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
f.IncTotal = args.IncTotal
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
i.useIterLimit = true
|
||||
i.iterLimit = uint(args.Limit)
|
||||
|
||||
f.Limit = uint(args.Limit)
|
||||
|
||||
if args.Limit > uint64(wfexec.MaxIteratorBufferSize) {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
i.iterLimit = uint(args.Limit)
|
||||
} else {
|
||||
f.Limit = wfexec.MaxIteratorBufferSize
|
||||
}
|
||||
|
||||
i.filter = f
|
||||
i.loader = func() (err error) {
|
||||
// Edgecase
|
||||
if i.filter.PageCursor != nil && i.filter.NextPage == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.total += i.ptr
|
||||
i.ptr = 0
|
||||
|
||||
i.filter.PageCursor = i.filter.NextPage
|
||||
i.filter.NextPage = nil
|
||||
|
||||
// Total is fetched with the first page only; a paged query cannot carry it
|
||||
i.filter.IncTotal = i.filter.IncTotal && i.filter.PageCursor == nil
|
||||
|
||||
i.buffer, i.filter, err = h.uSvc.Find(ctx, i.filter)
|
||||
if i.filter.IncTotal {
|
||||
i.resTotal = i.filter.Total
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Initial load
|
||||
return i, i.loader()
|
||||
}
|
||||
|
||||
func (h usersHandler) create(ctx context.Context, args *usersCreateArgs) (results *usersCreateResults, err error) {
|
||||
results = &usersCreateResults{}
|
||||
results.User, err = h.uSvc.Create(ctx, args.User)
|
||||
return
|
||||
}
|
||||
|
||||
func (h usersHandler) update(ctx context.Context, args *usersUpdateArgs) (results *usersUpdateResults, err error) {
|
||||
results = &usersUpdateResults{}
|
||||
results.User, err = h.uSvc.Update(ctx, args.User)
|
||||
return
|
||||
}
|
||||
|
||||
func (h usersHandler) delete(ctx context.Context, args *usersDeleteArgs) error {
|
||||
if id, err := getUserID(ctx, h.uSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.uSvc.Delete(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h usersHandler) recover(ctx context.Context, args *usersRecoverArgs) error {
|
||||
if id, err := getUserID(ctx, h.uSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.uSvc.Undelete(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h usersHandler) suspend(ctx context.Context, args *usersSuspendArgs) error {
|
||||
if id, err := getUserID(ctx, h.uSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.uSvc.Suspend(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (h usersHandler) unsuspend(ctx context.Context, args *usersUnsuspendArgs) error {
|
||||
if id, err := getUserID(ctx, h.uSvc, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.uSvc.Unsuspend(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
func getUserID(ctx context.Context, svc userService, args userLookup) (uint64, error) {
|
||||
_, ID, _, _, _ := args.GetLookup()
|
||||
|
||||
if ID > 0 {
|
||||
return ID, nil
|
||||
}
|
||||
|
||||
user, err := lookupUser(ctx, svc, args)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return user.ID, nil
|
||||
|
||||
}
|
||||
|
||||
func lookupUser(ctx context.Context, svc userService, args userLookup) (*types.User, error) {
|
||||
_, ID, handle, email, user := args.GetLookup()
|
||||
|
||||
switch {
|
||||
case user != nil:
|
||||
return user, nil
|
||||
case ID > 0:
|
||||
return svc.FindByID(ctx, ID)
|
||||
case len(email) > 0:
|
||||
return svc.FindByEmail(ctx, email)
|
||||
case len(handle) > 0:
|
||||
return svc.FindByHandle(ctx, handle)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("empty lookup params")
|
||||
}
|
||||
|
||||
func (i *userSetIterator) More(context.Context, *Vars) (bool, error) {
|
||||
return wfexec.GenericResourceNextCheck(i.useIterLimit, i.ptr, uint(len(i.buffer)), i.total, i.iterLimit, i.filter.NextPage != nil), nil
|
||||
}
|
||||
|
||||
func (i *userSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
|
||||
|
||||
func (i *userSetIterator) Next(context.Context, *Vars) (out *Vars, err error) {
|
||||
if len(i.buffer)-int(i.ptr) <= 0 {
|
||||
if err = i.loader(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
out = &Vars{}
|
||||
out.Set("user", Must(NewUser(i.buffer[i.ptr])))
|
||||
out.Set("index", Must(NewInteger(i.total+i.ptr)))
|
||||
out.Set("total", Must(NewInteger(i.resTotal)))
|
||||
|
||||
i.ptr++
|
||||
return out, nil
|
||||
}
|
||||
215
server/system/automation/users_handler.yaml
Normal file
215
server/system/automation/users_handler.yaml
Normal file
@@ -0,0 +1,215 @@
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza/server/system/types
|
||||
- labelTypes github.com/cortezaproject/corteza/server/pkg/label/types
|
||||
|
||||
|
||||
snippets:
|
||||
lookup: &lookup
|
||||
meta: { label: Lookup }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID }
|
||||
- { wf: Handle }
|
||||
- { wf: String, suffix: email }
|
||||
- { wf: User, suffix: res }
|
||||
|
||||
lookupRole: &lookupRole
|
||||
meta: { label: Role }
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: Role, suffix: res }
|
||||
|
||||
user: &user
|
||||
types:
|
||||
- { wf: User }
|
||||
|
||||
rvUser: &rvUser
|
||||
wf: User
|
||||
|
||||
rvRole: &rvRole
|
||||
wf: Role
|
||||
|
||||
rvTotal: &rvTotal
|
||||
wf: UnsignedInteger
|
||||
|
||||
rvInclusion: &rvInclusion
|
||||
wf: Boolean
|
||||
|
||||
rvPageCursor: &rvPageCursor
|
||||
wf: String
|
||||
|
||||
filterParams: &filterParams
|
||||
query:
|
||||
meta: { label: Query }
|
||||
types:
|
||||
- { wf: String }
|
||||
email:
|
||||
meta: { label: Email }
|
||||
types:
|
||||
- { wf: String }
|
||||
handle:
|
||||
meta: { label: Handle }
|
||||
types:
|
||||
- { wf: String }
|
||||
labels:
|
||||
meta: { label: Labels }
|
||||
types:
|
||||
- { wf: LabelValue }
|
||||
deleted:
|
||||
meta: { label: Deleted }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
suspended:
|
||||
meta: { label: Suspended }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
sort:
|
||||
meta: { label: Sort }
|
||||
types:
|
||||
- { wf: String }
|
||||
limit:
|
||||
meta: { label: Limit }
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
incTotal:
|
||||
meta: { label: Include total }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
incPageNavigation:
|
||||
meta: { label: Include page navigation }
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
pageCursor:
|
||||
meta: { label: Page cursor }
|
||||
types:
|
||||
- { wf: String }
|
||||
|
||||
labels: &labels
|
||||
users: "step,workflow"
|
||||
|
||||
functions:
|
||||
lookup:
|
||||
meta:
|
||||
short: User lookup
|
||||
description: Find specific user by ID, handle or string
|
||||
params:
|
||||
lookup: *lookup
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
user: *rvUser
|
||||
|
||||
searchMembership:
|
||||
meta:
|
||||
short: User role search
|
||||
description: Search user role membership by ID, handle or string
|
||||
params:
|
||||
lookup: *lookup
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
roles:
|
||||
<<: *rvRole
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
|
||||
checkMembership:
|
||||
meta:
|
||||
short: User membership check
|
||||
description: Find user role membership by ID, handle or string
|
||||
params:
|
||||
user:
|
||||
<<: *lookup
|
||||
meta: { label: User }
|
||||
role: *lookupRole
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
member: *rvInclusion
|
||||
|
||||
search:
|
||||
meta:
|
||||
short: User search
|
||||
params: *filterParams
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
users:
|
||||
<<: *rvUser
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
|
||||
each:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Users
|
||||
params: *filterParams
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
user: *rvUser
|
||||
total: *rvTotal
|
||||
|
||||
create:
|
||||
meta:
|
||||
short: User create
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
user:
|
||||
<<: *user
|
||||
meta: { label: User }
|
||||
required: true
|
||||
results:
|
||||
user: *rvUser
|
||||
|
||||
update:
|
||||
meta:
|
||||
short: User update
|
||||
labels:
|
||||
<<: *labels
|
||||
params:
|
||||
user:
|
||||
<<: *user
|
||||
meta: { label: User }
|
||||
required: true
|
||||
results:
|
||||
user: *rvUser
|
||||
|
||||
delete:
|
||||
labels:
|
||||
<<: *labels
|
||||
delete: "step"
|
||||
meta:
|
||||
short: User delete
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
recover:
|
||||
labels:
|
||||
<<: *labels
|
||||
recover: "step"
|
||||
meta:
|
||||
short: User recover
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
suspend:
|
||||
labels:
|
||||
<<: *labels
|
||||
suspend: "step"
|
||||
meta:
|
||||
short: User suspend
|
||||
params:
|
||||
lookup: *lookup
|
||||
|
||||
unsuspend:
|
||||
labels:
|
||||
<<: *labels
|
||||
unsuspend: "step"
|
||||
meta:
|
||||
short: User unsuspend
|
||||
params:
|
||||
lookup: *lookup
|
||||
113
server/system/automation/valuestore_handler.gen.go
generated
Normal file
113
server/system/automation/valuestore_handler.gen.go
generated
Normal file
@@ -0,0 +1,113 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// system/automation/valuestore_handler.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
atypes "github.com/cortezaproject/corteza/server/automation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza/server/pkg/wfexec"
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
valuestoreHandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h valuestoreHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.Env(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
valuestoreEnvArgs struct {
|
||||
hasKey bool
|
||||
Key string
|
||||
}
|
||||
|
||||
valuestoreEnvResults struct {
|
||||
Value interface{}
|
||||
}
|
||||
)
|
||||
|
||||
// Env function Get ENV variable
|
||||
//
|
||||
// expects implementation of env function:
|
||||
//
|
||||
// func (h valuestoreHandler) env(ctx context.Context, args *valuestoreEnvArgs) (results *valuestoreEnvResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h valuestoreHandler) Env() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "valuestoreEnv",
|
||||
Kind: "function",
|
||||
Labels: map[string]string{"templates": "step,workflow"},
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Get ENV variable",
|
||||
Description: "Get ENV variable for the specified key. If the key doesn't correspond to any value, nil is returned",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "key",
|
||||
Types: []string{"String"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Key",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "value",
|
||||
Types: []string{"Any"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &valuestoreEnvArgs{
|
||||
hasKey: in.Has("key"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *valuestoreEnvResults
|
||||
if results, err = h.env(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Value (interface{}) to Any
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("Any").Cast(results.Value); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "value", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
33
server/system/automation/valuestore_handler.go
Normal file
33
server/system/automation/valuestore_handler.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
envGetter interface {
|
||||
Env(k string) (v any)
|
||||
}
|
||||
|
||||
valuestoreHandler struct {
|
||||
reg valuestoreHandlerRegistry
|
||||
getter envGetter
|
||||
}
|
||||
)
|
||||
|
||||
func ValuestoreHandler(reg valuestoreHandlerRegistry, getter envGetter) *valuestoreHandler {
|
||||
h := &valuestoreHandler{
|
||||
reg: reg,
|
||||
getter: getter,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h valuestoreHandler) env(ctx context.Context, args *valuestoreEnvArgs) (results *valuestoreEnvResults, err error) {
|
||||
results = &valuestoreEnvResults{
|
||||
Value: h.getter.Env(args.Key),
|
||||
}
|
||||
return
|
||||
}
|
||||
21
server/system/automation/valuestore_handler.yaml
Normal file
21
server/system/automation/valuestore_handler.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
snippets:
|
||||
|
||||
labels: &labels
|
||||
templates: "step,workflow"
|
||||
|
||||
functions:
|
||||
env:
|
||||
meta:
|
||||
short: Get ENV variable
|
||||
description: Get ENV variable for the specified key. If the key doesn't correspond to any value, nil is returned
|
||||
params:
|
||||
key:
|
||||
meta: { label: Key }
|
||||
required: true
|
||||
types:
|
||||
- { wf: String }
|
||||
labels:
|
||||
<<: *labels
|
||||
results:
|
||||
value:
|
||||
wf: Any
|
||||
Reference in New Issue
Block a user