diff --git a/compose/rest.yaml b/compose/rest.yaml index ea8ac226b..6a4e63f09 100644 --- a/compose/rest.yaml +++ b/compose/rest.yaml @@ -215,6 +215,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - name: listTranslations method: GET title: List translation @@ -451,6 +455,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - name: listTranslations method: GET @@ -623,6 +631,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - name: listTranslations method: GET title: List moudle translation @@ -922,6 +934,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - title: Charts path: "/namespace/{namespaceID}/chart" entrypoint: chart @@ -1293,3 +1309,7 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script diff --git a/compose/rest/request/automation.go b/compose/rest/request/automation.go index a2bada0b3..56d5f2856 100644 --- a/compose/rest/request/automation.go +++ b/compose/rest/request/automation.go @@ -87,6 +87,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } ) @@ -268,6 +273,7 @@ func NewAutomationTriggerScript() *AutomationTriggerScript { func (r AutomationTriggerScript) Auditable() map[string]interface{} { return map[string]interface{}{ "script": r.Script, + "args": r.Args, } } @@ -276,6 +282,11 @@ func (r AutomationTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r AutomationTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *AutomationTriggerScript) Fill(req *http.Request) (err error) { @@ -303,6 +314,18 @@ func (r *AutomationTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } return err diff --git a/compose/rest/request/module.go b/compose/rest/request/module.go index c3113d1ef..8d1e66bbf 100644 --- a/compose/rest/request/module.go +++ b/compose/rest/request/module.go @@ -193,6 +193,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } ModuleListTranslations struct { @@ -733,6 +738,7 @@ func (r ModuleTriggerScript) Auditable() map[string]interface{} { "namespaceID": r.NamespaceID, "moduleID": r.ModuleID, "script": r.Script, + "args": r.Args, } } @@ -751,6 +757,11 @@ func (r ModuleTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r ModuleTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *ModuleTriggerScript) Fill(req *http.Request) (err error) { @@ -778,6 +789,18 @@ func (r *ModuleTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/compose/rest/request/namespace.go b/compose/rest/request/namespace.go index 50839c27a..c146a7092 100644 --- a/compose/rest/request/namespace.go +++ b/compose/rest/request/namespace.go @@ -222,6 +222,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } NamespaceListTranslations struct { @@ -980,6 +985,7 @@ func (r NamespaceTriggerScript) Auditable() map[string]interface{} { return map[string]interface{}{ "namespaceID": r.NamespaceID, "script": r.Script, + "args": r.Args, } } @@ -993,6 +999,11 @@ func (r NamespaceTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r NamespaceTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *NamespaceTriggerScript) Fill(req *http.Request) (err error) { @@ -1020,6 +1031,18 @@ func (r *NamespaceTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/compose/rest/request/page.go b/compose/rest/request/page.go index f1cf66d1f..83cf99a86 100644 --- a/compose/rest/request/page.go +++ b/compose/rest/request/page.go @@ -272,6 +272,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } PageListTranslations struct { @@ -1106,6 +1111,7 @@ func (r PageTriggerScript) Auditable() map[string]interface{} { "namespaceID": r.NamespaceID, "pageID": r.PageID, "script": r.Script, + "args": r.Args, } } @@ -1124,6 +1130,11 @@ func (r PageTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r PageTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *PageTriggerScript) Fill(req *http.Request) (err error) { @@ -1151,6 +1162,18 @@ func (r *PageTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/compose/rest/request/record.go b/compose/rest/request/record.go index ef0aa01ac..96e368323 100644 --- a/compose/rest/request/record.go +++ b/compose/rest/request/record.go @@ -418,6 +418,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } ) @@ -1773,6 +1778,7 @@ func (r RecordTriggerScriptOnList) Auditable() map[string]interface{} { "namespaceID": r.NamespaceID, "moduleID": r.ModuleID, "script": r.Script, + "args": r.Args, } } @@ -1791,6 +1797,11 @@ func (r RecordTriggerScriptOnList) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r RecordTriggerScriptOnList) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *RecordTriggerScriptOnList) Fill(req *http.Request) (err error) { @@ -1818,6 +1829,18 @@ func (r *RecordTriggerScriptOnList) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/compose/rest/request/util.go b/compose/rest/request/util.go index 224569807..2ded11b7e 100644 --- a/compose/rest/request/util.go +++ b/compose/rest/request/util.go @@ -3,6 +3,7 @@ package request //lint:file-ignore U1000 Ignore unused code, part of request pkg toolset import ( + "encoding/json" "strconv" "strings" ) @@ -31,3 +32,13 @@ func (args ProcedureArgs) Get(name string) string { return "" } + +func parseMapStringInterface(ss []string) (map[string]interface{}, error) { + if len(ss) == 0 { + return nil, nil + } + + out := make(map[string]interface{}) + + return out, json.Unmarshal([]byte(ss[0]), out) +} diff --git a/pkg/corredor/types.go b/pkg/corredor/types.go index 2c34e9070..94090145b 100644 --- a/pkg/corredor/types.go +++ b/pkg/corredor/types.go @@ -1,8 +1,11 @@ package corredor import ( + "encoding/json" "fmt" "strings" + + "github.com/cortezaproject/corteza-server/pkg/eventbus" ) type ( @@ -34,6 +37,12 @@ type ( Bundle string `json:"bundle,omitempty"` Type string `json:"type,omitempty"` } + + // allows passing extra kv with event arguments + scriptArgs struct { + event ScriptArgs + extra map[string]interface{} + } ) // FindByName returns script from the set if it exists @@ -60,3 +69,62 @@ func (ss *ScriptSecurity) String() (o string) { strings.Join(ss.Deny, ","), ) } + +func ExtendScriptArgs(ev ScriptArgs, args map[string]interface{}) ScriptArgs { + return &scriptArgs{ + event: ev, + extra: args, + } +} + +func (s *scriptArgs) ResourceType() string { + return s.event.ResourceType() + +} + +func (s *scriptArgs) EventType() string { + return s.event.EventType() +} + +func (s *scriptArgs) Match(matcher eventbus.ConstraintMatcher) bool { + return s.event.Match(matcher) +} + +func (s *scriptArgs) Encode() (enc map[string][]byte, err error) { + if enc, err = s.event.Encode(); err != nil { + return nil, err + } + + if enc == nil { + enc = make(map[string][]byte) + } + + for k, v := range s.extra { + if enc[k] != nil { + // skip all that were encoded + // by event encoder + continue + } + + if enc[k], err = json.Marshal(v); err != nil { + return nil, err + } + } + + return +} + +func (s *scriptArgs) Decode(dec map[string][]byte) (err error) { + if err = s.event.Decode(dec); err != nil { + return + } + + for k := range s.extra { + // @todo how do we omit one decoded by event? + if err = json.Unmarshal(dec[k], s.extra[k]); err != nil { + return + } + } + + return +} diff --git a/system/rest.yaml b/system/rest.yaml index ae3da083b..32ed24a66 100644 --- a/system/rest.yaml +++ b/system/rest.yaml @@ -392,6 +392,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - title: Users path: "/users" entrypoint: user @@ -652,6 +656,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - name: sessionsRemove method: DELETE title: Remove all auth sessions of user @@ -873,6 +881,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - name: reorder method: POST title: Reorder applications @@ -1544,6 +1556,10 @@ endpoints: type: string title: Script to execute required: true + - name: args + type: map[string]interface{} + parser: parseMapStringInterface + title: Arguments to pass to the script - title: Action log entrypoint: actionlog path: "/actionlog" diff --git a/system/rest/application.go b/system/rest/application.go index 374c11ff4..f8b3e3a94 100644 --- a/system/rest/application.go +++ b/system/rest/application.go @@ -186,7 +186,7 @@ func (ctrl *Application) TriggerScript(ctx context.Context, r *request.Applicati } // @todo implement same behaviour as we have on record - Application+oldApplication - err = corredor.Service().Exec(ctx, r.Script, event.ApplicationOnManual(application, application)) + err = corredor.Service().Exec(ctx, r.Script, corredor.ExtendScriptArgs(event.ApplicationOnManual(application, application), r.Args)) return application, err } diff --git a/system/rest/automation.go b/system/rest/automation.go index 6ae503336..9022dd170 100644 --- a/system/rest/automation.go +++ b/system/rest/automation.go @@ -2,6 +2,7 @@ package rest import ( "context" + "github.com/cortezaproject/corteza-server/pkg/api" "github.com/cortezaproject/corteza-server/pkg/corredor" "github.com/cortezaproject/corteza-server/system/rest/request" @@ -46,5 +47,5 @@ func (ctrl *Automation) Bundle(ctx context.Context, r *request.AutomationBundle) } func (ctrl *Automation) TriggerScript(ctx context.Context, r *request.AutomationTriggerScript) (interface{}, error) { - return api.OK(), corredor.Service().Exec(ctx, r.Script, event.SystemOnManual()) + return api.OK(), corredor.Service().Exec(ctx, r.Script, corredor.ExtendScriptArgs(event.SystemOnManual(), r.Args)) } diff --git a/system/rest/request/application.go b/system/rest/request/application.go index a7bf84038..a342f58a9 100644 --- a/system/rest/request/application.go +++ b/system/rest/request/application.go @@ -228,6 +228,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } ApplicationReorder struct { @@ -933,6 +938,7 @@ func (r ApplicationTriggerScript) Auditable() map[string]interface{} { return map[string]interface{}{ "applicationID": r.ApplicationID, "script": r.Script, + "args": r.Args, } } @@ -946,6 +952,11 @@ func (r ApplicationTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r ApplicationTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *ApplicationTriggerScript) Fill(req *http.Request) (err error) { @@ -973,6 +984,18 @@ func (r *ApplicationTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/system/rest/request/automation.go b/system/rest/request/automation.go index a2bada0b3..56d5f2856 100644 --- a/system/rest/request/automation.go +++ b/system/rest/request/automation.go @@ -87,6 +87,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } ) @@ -268,6 +273,7 @@ func NewAutomationTriggerScript() *AutomationTriggerScript { func (r AutomationTriggerScript) Auditable() map[string]interface{} { return map[string]interface{}{ "script": r.Script, + "args": r.Args, } } @@ -276,6 +282,11 @@ func (r AutomationTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r AutomationTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *AutomationTriggerScript) Fill(req *http.Request) (err error) { @@ -303,6 +314,18 @@ func (r *AutomationTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } return err diff --git a/system/rest/request/role.go b/system/rest/request/role.go index fa2c69e5a..4892bcb6c 100644 --- a/system/rest/request/role.go +++ b/system/rest/request/role.go @@ -236,6 +236,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } ) @@ -1051,6 +1056,7 @@ func (r RoleTriggerScript) Auditable() map[string]interface{} { return map[string]interface{}{ "roleID": r.RoleID, "script": r.Script, + "args": r.Args, } } @@ -1064,6 +1070,11 @@ func (r RoleTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r RoleTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *RoleTriggerScript) Fill(req *http.Request) (err error) { @@ -1091,6 +1102,18 @@ func (r *RoleTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/system/rest/request/user.go b/system/rest/request/user.go index 6990fe59b..c6549ddfa 100644 --- a/system/rest/request/user.go +++ b/system/rest/request/user.go @@ -266,6 +266,11 @@ type ( // // Script to execute Script string + + // Args POST parameter + // + // Arguments to pass to the script + Args map[string]interface{} } UserSessionsRemove struct { @@ -1139,6 +1144,7 @@ func (r UserTriggerScript) Auditable() map[string]interface{} { return map[string]interface{}{ "userID": r.UserID, "script": r.Script, + "args": r.Args, } } @@ -1152,6 +1158,11 @@ func (r UserTriggerScript) GetScript() string { return r.Script } +// Auditable returns all auditable/loggable parameters +func (r UserTriggerScript) GetArgs() map[string]interface{} { + return r.Args +} + // Fill processes request and fills internal variables func (r *UserTriggerScript) Fill(req *http.Request) (err error) { @@ -1179,6 +1190,18 @@ func (r *UserTriggerScript) Fill(req *http.Request) (err error) { return err } } + + if val, ok := req.Form["args[]"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } else if val, ok := req.Form["args"]; ok { + r.Args, err = parseMapStringInterface(val) + if err != nil { + return err + } + } } { diff --git a/system/rest/request/util.go b/system/rest/request/util.go index 8b6e1d9e4..4845fd381 100644 --- a/system/rest/request/util.go +++ b/system/rest/request/util.go @@ -1,13 +1,15 @@ package request -//lint:file-ignore U1000 Ignore unused code, part of request pkg toolset +import ( + "encoding/json" +) -// is checks if string s is contained in matches -func is(s string, matches ...string) bool { - for _, v := range matches { - if s == v { - return true - } +func parseMapStringInterface(ss []string) (map[string]interface{}, error) { + if len(ss) == 0 { + return nil, nil } - return false + + out := make(map[string]interface{}) + + return out, json.Unmarshal([]byte(ss[0]), out) } diff --git a/system/rest/role.go b/system/rest/role.go index f3e41ade6..ff2fed6c6 100644 --- a/system/rest/role.go +++ b/system/rest/role.go @@ -208,7 +208,7 @@ func (ctrl *Role) TriggerScript(ctx context.Context, r *request.RoleTriggerScrip } // @todo implement same behaviour as we have on record - role+oldRole - err = corredor.Service().Exec(ctx, r.Script, event.RoleOnManual(role, role)) + err = corredor.Service().Exec(ctx, r.Script, corredor.ExtendScriptArgs(event.RoleOnManual(role, role), r.Args)) return role, err } diff --git a/system/rest/user.go b/system/rest/user.go index 5e237f6ca..7cdf047d1 100644 --- a/system/rest/user.go +++ b/system/rest/user.go @@ -231,7 +231,7 @@ func (ctrl *User) TriggerScript(ctx context.Context, r *request.UserTriggerScrip } // @todo implement same behaviour as we have on record - user+oldUser - err = corredor.Service().Exec(ctx, r.Script, event.UserOnManual(user, user)) + err = corredor.Service().Exec(ctx, r.Script, corredor.ExtendScriptArgs(event.UserOnManual(user, user), r.Args)) return user, err }