Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin API improvements #4603

Merged
merged 5 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -389,12 +389,29 @@ type Mutation {
"""
setPluginsEnabled(enabledMap: BoolMap!): Boolean!

"Run plugin task. Returns the job ID"
"""
Run a plugin task.
If task_name is provided, then the task must exist in the plugin config and the tasks configuration
will be used to run the plugin.
If no task_name is provided, then the plugin will be executed with the arguments provided only.
Returns the job ID
"""
runPluginTask(
plugin_id: ID!
task_name: String!
args: [PluginArgInput!]
"if provided, then the default args will be applied"
task_name: String
"displayed in the task queue"
description: String
args: [PluginArgInput!] @deprecated(reason: "Use args_map instead")
args_map: Map
): ID!

"""
Runs a plugin operation. The operation is run immediately and does not use the job queue.
Returns a map of the result.
"""
runPluginOperation(plugin_id: ID!, args: Map): Any

reloadPlugins: Boolean!

"""
Expand Down
69 changes: 66 additions & 3 deletions internal/api/resolver_mutation_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,80 @@ package api

import (
"context"
"strconv"

"github.com/stashapp/stash/internal/manager"
"github.com/stashapp/stash/internal/manager/config"
"github.com/stashapp/stash/pkg/plugin"
"github.com/stashapp/stash/pkg/sliceutil"
)

func (r *mutationResolver) RunPluginTask(ctx context.Context, pluginID string, taskName string, args []*plugin.PluginArgInput) (string, error) {
func toPluginArgs(args []*plugin.PluginArgInput) plugin.OperationInput {
ret := make(plugin.OperationInput)
for _, a := range args {
ret[a.Key] = toPluginArgValue(a.Value)
}

return ret
}

func toPluginArgValue(arg *plugin.PluginValueInput) interface{} {
if arg == nil {
return nil
}

switch {
case arg.Str != nil:
return *arg.Str
case arg.I != nil:
return *arg.I
case arg.B != nil:
return *arg.B
case arg.F != nil:
return *arg.F
case arg.O != nil:
return toPluginArgs(arg.O)
case arg.A != nil:
var ret []interface{}
for _, v := range arg.A {
ret = append(ret, toPluginArgValue(v))
}
return ret
}

return nil
}

func (r *mutationResolver) RunPluginTask(
ctx context.Context,
pluginID string,
taskName *string,
description *string,
args []*plugin.PluginArgInput,
argsMap map[string]interface{},
) (string, error) {
if argsMap == nil {
// convert args to map
// otherwise ignore args in favour of args map
argsMap = toPluginArgs(args)
}

m := manager.GetInstance()
jobID := m.RunPluginTask(ctx, pluginID, taskName, description, argsMap)
return strconv.Itoa(jobID), nil
}

func (r *mutationResolver) RunPluginOperation(
ctx context.Context,
pluginID string,
args map[string]interface{},
) (interface{}, error) {
if args == nil {
args = make(map[string]interface{})
}

m := manager.GetInstance()
m.RunPluginTask(ctx, pluginID, taskName, args)
return "todo", nil
return m.PluginCache.RunPlugin(ctx, pluginID, args)
}

func (r *mutationResolver) ReloadPlugins(ctx context.Context) (bool, error) {
Expand Down
17 changes: 15 additions & 2 deletions internal/manager/task_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import (
"github.com/stashapp/stash/pkg/plugin"
)

func (s *Manager) RunPluginTask(ctx context.Context, pluginID string, taskName string, args []*plugin.PluginArgInput) int {
func (s *Manager) RunPluginTask(
ctx context.Context,
pluginID string,
taskName *string,
description *string,
args plugin.OperationInput,
) int {
j := job.MakeJobExec(func(jobCtx context.Context, progress *job.Progress) {
pluginProgress := make(chan float64)
task, err := s.PluginCache.CreateTask(ctx, pluginID, taskName, args, pluginProgress)
Expand Down Expand Up @@ -56,5 +62,12 @@ func (s *Manager) RunPluginTask(ctx context.Context, pluginID string, taskName s
}
})

return s.JobManager.Add(ctx, fmt.Sprintf("Running plugin task: %s", taskName), j)
displayName := pluginID
if taskName != nil {
displayName = *taskName
}
if description != nil {
displayName = *description
}
return s.JobManager.Add(ctx, fmt.Sprintf("Running plugin task: %s", displayName), j)
}
27 changes: 6 additions & 21 deletions pkg/plugin/args.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package plugin

type OperationInput map[string]interface{}

type PluginArgInput struct {
Key string `json:"key"`
Value *PluginValueInput `json:"value"`
Expand All @@ -14,28 +16,11 @@ type PluginValueInput struct {
A []*PluginValueInput `json:"a"`
}

func findArg(args []*PluginArgInput, name string) *PluginArgInput {
for _, v := range args {
if v.Key == name {
return v
}
}

return nil
}

func applyDefaultArgs(args []*PluginArgInput, defaultArgs map[string]string) []*PluginArgInput {
func applyDefaultArgs(args OperationInput, defaultArgs map[string]string) {
for k, v := range defaultArgs {
if arg := findArg(args, k); arg == nil {
v := v // Copy v, because it's being exported out of the loop
args = append(args, &PluginArgInput{
Key: k,
Value: &PluginValueInput{
Str: &v,
},
})
_, found := args[k]
if !found {
args[k] = v
}
}

return args
}
4 changes: 3 additions & 1 deletion pkg/plugin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,9 @@ func (c Config) getConfigPath() string {
func (c Config) getExecCommand(task *OperationConfig) []string {
ret := c.Exec

ret = append(ret, task.ExecArgs...)
if task != nil {
ret = append(ret, task.ExecArgs...)
}

if len(ret) > 0 {
_, err := exec.LookPath(ret[0])
Expand Down
33 changes: 3 additions & 30 deletions pkg/plugin/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,11 @@ import (
"github.com/stashapp/stash/pkg/plugin/common"
)

func toPluginArgs(args []*PluginArgInput) common.ArgsMap {
func toPluginArgs(args OperationInput) common.ArgsMap {
ret := make(common.ArgsMap)
for _, a := range args {
ret[a.Key] = toPluginArgValue(a.Value)
for k, a := range args {
ret[k] = common.PluginArgValue(a)
}

return ret
}

func toPluginArgValue(arg *PluginValueInput) common.PluginArgValue {
if arg == nil {
return nil
}

switch {
case arg.Str != nil:
return common.PluginArgValue(*arg.Str)
case arg.I != nil:
return common.PluginArgValue(*arg.I)
case arg.B != nil:
return common.PluginArgValue(*arg.B)
case arg.F != nil:
return common.PluginArgValue(*arg.F)
case arg.O != nil:
return common.PluginArgValue(toPluginArgs(arg.O))
case arg.A != nil:
var ret []common.PluginArgValue
for _, v := range arg.A {
ret = append(ret, toPluginArgValue(v))
}
return common.PluginArgValue(ret)
}

return nil
}
42 changes: 28 additions & 14 deletions pkg/plugin/examples/js/js.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,40 @@ var tagName = "Hawwwwt"

function main() {
var modeArg = input.Args.mode;
try {
if (modeArg == "" || modeArg == "add") {
addTag();
} else if (modeArg == "remove") {
removeTag();
} else if (modeArg == "long") {
doLongTask();
} else if (modeArg == "indef") {
doIndefiniteTask();
} else if (modeArg == "hook") {
doHookTask();
if (modeArg !== undefined) {
try {
if (modeArg == "" || modeArg == "add") {
addTag();
} else if (modeArg == "remove") {
removeTag();
} else if (modeArg == "long") {
doLongTask();
} else if (modeArg == "indef") {
doIndefiniteTask();
} else if (modeArg == "hook") {
doHookTask();
}
} catch (err) {
return {
Error: err
};
}
} catch (err) {

return {
Output: "ok"
};
}

if (input.Args.error) {
return {
Error: err
Error: input.Args.error
};
}

// immediate mode
// just return the args
return {
Output: "ok"
Output: input.Args
};
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/plugin/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ func (t *jsPluginTask) makeOutput(o otto.Value) {
return
}

t.result.Output, _ = asObj.Get("Output")
output, _ := asObj.Get("Output")
t.result.Output, _ = output.Export()

err, _ := asObj.Get("Error")
if !err.IsUndefined() {
errStr := err.String()
Expand Down
Loading
Loading