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

Add support for disabling plugins #4141

Merged
merged 3 commits into from
Oct 16, 2023
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
2 changes: 2 additions & 0 deletions gqlgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ models:
model: github.com/99designs/gqlgen/graphql.Int64
Timestamp:
model: github.com/stashapp/stash/internal/api.Timestamp
BoolMap:
model: github.com/stashapp/stash/internal/api.BoolMap
# define to force resolvers
Image:
model: github.com/stashapp/stash/pkg/models.Image
Expand Down
4 changes: 4 additions & 0 deletions graphql/documents/mutations/plugins.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ mutation RunPluginTask(
) {
runPluginTask(plugin_id: $plugin_id, task_name: $task_name, args: $args)
}

mutation SetPluginsEnabled($enabledMap: BoolMap!) {
setPluginsEnabled(enabledMap: $enabledMap)
}
2 changes: 2 additions & 0 deletions graphql/documents/queries/plugins.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ query Plugins {
plugins {
id
name
enabled
description
url
version
Expand All @@ -26,6 +27,7 @@ query PluginTasks {
plugin {
id
name
enabled
}
}
}
6 changes: 6 additions & 0 deletions graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,12 @@ type Mutation {
"Reload scrapers"
reloadScrapers: Boolean!

"""
Enable/disable plugins - enabledMap is a map of plugin IDs to enabled booleans.
Plugins not in the map are not affected.
"""
setPluginsEnabled(enabledMap: BoolMap!): Boolean!

"Run plugin task. Returns the job ID"
runPluginTask(
plugin_id: ID!
Expand Down
2 changes: 2 additions & 0 deletions graphql/schema/types/plugin.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ type Plugin {
url: String
version: String

enabled: Boolean!

tasks: [PluginTask!]
hooks: [PluginHook!]
}
Expand Down
3 changes: 3 additions & 0 deletions graphql/schema/types/scalars.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ scalar Timestamp
# generic JSON object
scalar Map

# string, boolean map
scalar BoolMap

scalar Any

scalar Int64
38 changes: 38 additions & 0 deletions internal/api/bool_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package api

import (
"encoding/json"
"fmt"
"io"

"github.com/99designs/gqlgen/graphql"
)

func MarshalBoolMap(val map[string]bool) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
err := json.NewEncoder(w).Encode(val)
if err != nil {
panic(err)
}
})
}

func UnmarshalBoolMap(v interface{}) (map[string]bool, error) {
m, ok := v.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%T is not a map", v)
}

result := make(map[string]bool)
for k, v := range m {
key := k
val, ok := v.(bool)
if !ok {
return nil, fmt.Errorf("key %s (%T) is not a bool", k, v)
}

result[key] = val
}

return result, nil
}
53 changes: 0 additions & 53 deletions internal/api/models.go
Original file line number Diff line number Diff line change
@@ -1,64 +1,11 @@
package api

import (
"errors"
"fmt"
"io"
"strconv"
"time"

"github.com/99designs/gqlgen/graphql"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
)

type BaseFile interface{}

type GalleryFile struct {
*models.BaseFile
}

var ErrTimestamp = errors.New("cannot parse Timestamp")

func MarshalTimestamp(t time.Time) graphql.Marshaler {
if t.IsZero() {
return graphql.Null
}

return graphql.WriterFunc(func(w io.Writer) {
_, err := io.WriteString(w, strconv.Quote(t.Format(time.RFC3339Nano)))
if err != nil {
logger.Warnf("could not marshal timestamp: %v", err)
}
})
}

func UnmarshalTimestamp(v interface{}) (time.Time, error) {
if tmpStr, ok := v.(string); ok {
if len(tmpStr) == 0 {
return time.Time{}, fmt.Errorf("%w: empty string", ErrTimestamp)
}

switch tmpStr[0] {
case '>', '<':
d, err := time.ParseDuration(tmpStr[1:])
if err != nil {
return time.Time{}, fmt.Errorf("%w: cannot parse %v-duration: %v", ErrTimestamp, tmpStr[0], err)
}
t := time.Now()
// Compute point in time:
if tmpStr[0] == '<' {
t = t.Add(-d)
} else {
t = t.Add(d)
}

return t, nil
}

return utils.ParseDateStringAsTime(tmpStr)
}

return time.Time{}, fmt.Errorf("%w: not a string", ErrTimestamp)
}
31 changes: 31 additions & 0 deletions internal/api/resolver_mutation_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"

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

func (r *mutationResolver) RunPluginTask(ctx context.Context, pluginID string, taskName string, args []*plugin.PluginArgInput) (string, error) {
Expand All @@ -22,3 +24,32 @@ func (r *mutationResolver) ReloadPlugins(ctx context.Context) (bool, error) {

return true, nil
}

func (r *mutationResolver) SetPluginsEnabled(ctx context.Context, enabledMap map[string]bool) (bool, error) {
c := config.GetInstance()

existingDisabled := c.GetDisabledPlugins()
var newDisabled []string

// remove plugins that are no longer disabled
for _, disabledID := range existingDisabled {
if enabled, found := enabledMap[disabledID]; !enabled || !found {
newDisabled = append(newDisabled, disabledID)
}
}

// add plugins that are newly disabled
for pluginID, enabled := range enabledMap {
if !enabled {
newDisabled = stringslice.StrAppendUnique(newDisabled, pluginID)
}
}

c.Set(config.DisabledPlugins, newDisabled)

if err := c.Write(); err != nil {
return false, err
}

return true, nil
}
8 changes: 8 additions & 0 deletions internal/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ func cssHandler(c *config.Instance, pluginCache *plugin.Cache) func(w http.Respo
var paths []string

for _, p := range pluginCache.ListPlugins() {
if !p.Enabled {
continue
}

paths = append(paths, p.UI.CSS...)
}

Expand All @@ -344,6 +348,10 @@ func javascriptHandler(c *config.Instance, pluginCache *plugin.Cache) func(w htt
var paths []string

for _, p := range pluginCache.ListPlugins() {
if !p.Enabled {
continue
}

paths = append(paths, p.UI.Javascript...)
}

Expand Down
57 changes: 57 additions & 0 deletions internal/api/timestamp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package api

import (
"errors"
"fmt"
"io"
"strconv"
"time"

"github.com/99designs/gqlgen/graphql"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/utils"
)

var ErrTimestamp = errors.New("cannot parse Timestamp")

func MarshalTimestamp(t time.Time) graphql.Marshaler {
if t.IsZero() {
return graphql.Null
}

return graphql.WriterFunc(func(w io.Writer) {
_, err := io.WriteString(w, strconv.Quote(t.Format(time.RFC3339Nano)))
if err != nil {
logger.Warnf("could not marshal timestamp: %v", err)
}
})
}

func UnmarshalTimestamp(v interface{}) (time.Time, error) {
if tmpStr, ok := v.(string); ok {
if len(tmpStr) == 0 {
return time.Time{}, fmt.Errorf("%w: empty string", ErrTimestamp)
}

switch tmpStr[0] {
case '>', '<':
d, err := time.ParseDuration(tmpStr[1:])
if err != nil {
return time.Time{}, fmt.Errorf("%w: cannot parse %v-duration: %v", ErrTimestamp, tmpStr[0], err)
}
t := time.Now()
// Compute point in time:
if tmpStr[0] == '<' {
t = t.Add(-d)
} else {
t = t.Add(d)
}

return t, nil
}

return utils.ParseDateStringAsTime(tmpStr)
}

return time.Time{}, fmt.Errorf("%w: not a string", ErrTimestamp)
}
File renamed without changes.
7 changes: 6 additions & 1 deletion internal/manager/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ const (
PythonPath = "python_path"

// plugin options
PluginsPath = "plugins_path"
PluginsPath = "plugins_path"
DisabledPlugins = "plugins.disabled"

// i18n
Language = "language"
Expand Down Expand Up @@ -722,6 +723,10 @@ func (i *Instance) GetPluginsPath() string {
return i.getString(PluginsPath)
}

func (i *Instance) GetDisabledPlugins() []string {
return i.getStringSlice(DisabledPlugins)
}

func (i *Instance) GetPythonPath() string {
return i.getString(PythonPath)
}
Expand Down
Loading