From bf1f60d309998429b4cd17ea329215b0a0080c40 Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Fri, 6 Aug 2021 11:54:56 +0200 Subject: [PATCH] [MM-2307] Move plugin helpers from mattermost-server repository into mattermost-plugin-api (#17870) --- Makefile | 1 - model/manifest.go | 2 +- plugin/checker/check_helpers.go | 137 ---- plugin/checker/check_helpers_test.go | 48 -- plugin/checker/main.go | 1 - plugin/client.go | 12 +- plugin/client_rpc.go | 2 - plugin/driver.go | 64 ++ plugin/helpers.go | 155 ----- plugin/helpers_bots.go | 318 --------- plugin/helpers_bots_test.go | 579 ----------------- plugin/helpers_config.go | 46 -- plugin/helpers_config_test.go | 123 ---- plugin/helpers_kv.go | 222 ------- plugin/helpers_kv_test.go | 639 ------------------- plugin/helpers_plugin.go | 81 --- plugin/helpers_plugin_test.go | 177 ----- plugin/plugintest/example_hello_user_test.go | 4 - plugin/plugintest/helpers.go | 257 -------- 19 files changed, 67 insertions(+), 2801 deletions(-) delete mode 100644 plugin/checker/check_helpers.go delete mode 100644 plugin/checker/check_helpers_test.go create mode 100644 plugin/driver.go delete mode 100644 plugin/helpers.go delete mode 100644 plugin/helpers_bots.go delete mode 100644 plugin/helpers_bots_test.go delete mode 100644 plugin/helpers_config.go delete mode 100644 plugin/helpers_config_test.go delete mode 100644 plugin/helpers_kv.go delete mode 100644 plugin/helpers_kv_test.go delete mode 100644 plugin/helpers_plugin.go delete mode 100644 plugin/helpers_plugin_test.go delete mode 100644 plugin/plugintest/helpers.go diff --git a/Makefile b/Makefile index e43d46311f..59670427d5 100644 --- a/Makefile +++ b/Makefile @@ -303,7 +303,6 @@ plugin-mocks: ## Creates mock files for plugins. $(GO) get -modfile=go.tools.mod github.com/vektra/mockery/... $(GOBIN)/mockery -dir plugin -name API -output plugin/plugintest -outpkg plugintest -case underscore -note 'Regenerate this file using `make plugin-mocks`.' $(GOBIN)/mockery -dir plugin -name Hooks -output plugin/plugintest -outpkg plugintest -case underscore -note 'Regenerate this file using `make plugin-mocks`.' - $(GOBIN)/mockery -dir plugin -name Helpers -output plugin/plugintest -outpkg plugintest -case underscore -note 'Regenerate this file using `make plugin-mocks`.' $(GOBIN)/mockery -dir plugin -name Driver -output plugin/plugintest -outpkg plugintest -case underscore -note 'Regenerate this file using `make plugin-mocks`.' einterfaces-mocks: ## Creates mock files for einterfaces. diff --git a/model/manifest.go b/model/manifest.go index eb1ebdc39b..b6f4ef7c75 100644 --- a/model/manifest.go +++ b/model/manifest.go @@ -191,7 +191,7 @@ type Manifest struct { // RequiredConfig defines any required server configuration fields for the plugin to function properly. // - // Use the plugin helpers CheckRequiredServerConfiguration method to enforce this. + // Use the pluginapi.Configuration.CheckRequiredServerConfiguration method to enforce this. RequiredConfig *Config `json:"required_configuration,omitempty" yaml:"required_configuration,omitempty"` } diff --git a/plugin/checker/check_helpers.go b/plugin/checker/check_helpers.go deleted file mode 100644 index 61040f2cb3..0000000000 --- a/plugin/checker/check_helpers.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package main - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-server/v6/plugin/checker/internal/asthelpers" - "github.com/mattermost/mattermost-server/v6/plugin/checker/internal/version" -) - -func checkHelpersVersionComments(pkgPath string) (result, error) { - pkg, err := asthelpers.GetPackage(pkgPath) - if err != nil { - return result{}, err - } - - api, apiIdent, err := asthelpers.FindInterfaceWithIdent("API", pkg.Syntax) - if err != nil { - return result{}, err - } - - apiObj := pkg.TypesInfo.ObjectOf(apiIdent) - if apiObj == nil { - return result{}, errors.New("could not find type object for API interface") - } - - helpers, err := asthelpers.FindInterface("Helpers", pkg.Syntax) - if err != nil { - return result{}, err - } - - apiVersions := mapMinimumVersionsByMethodName(api.Methods.List) - - helpersPositions := mapPositionsByMethodName(helpers.Methods.List) - helpersVersions := mapMinimumVersionsByMethodName(helpers.Methods.List) - - implMethods := asthelpers.FindReceiverMethods("HelpersImpl", pkg.Syntax) - implVersions := mapEffectiveVersionByMethod(pkg.TypesInfo, apiObj.Type(), apiVersions, implMethods) - - return validateMethods(pkg.Fset, helpersPositions, helpersVersions, implVersions), nil -} - -func validateMethods( - fset *token.FileSet, - helpersPositions map[string]token.Pos, - helpersVersions map[string]version.V, - implVersions map[string]version.V, -) result { - var res result - - for name, helperVer := range helpersVersions { - pos := helpersPositions[name] - - implVer, ok := implVersions[name] - if !ok { - res.Errors = append(res.Errors, renderWithFilePosition( - fset, - pos, - fmt.Sprintf("missing implementation for method %s", name)), - ) - continue - } - - if helperVer == "" { - res.Errors = append(res.Errors, renderWithFilePosition( - fset, - pos, - fmt.Sprintf("missing a minimum server version comment on method %s", name)), - ) - continue - } - - if helperVer == implVer { - continue - } - - if helperVer.LessThan(implVer) { - res.Errors = append(res.Errors, renderWithFilePosition( - fset, - pos, - fmt.Sprintf("documented minimum server version too low on method %s", name)), - ) - } else { - res.Warnings = append(res.Warnings, renderWithFilePosition( - fset, - pos, - fmt.Sprintf("documented minimum server version too high on method %s", name)), - ) - } - } - - return res -} - -func mapEffectiveVersionByMethod(info *types.Info, apiType types.Type, versions map[string]version.V, methods []*ast.FuncDecl) map[string]version.V { - effectiveVersions := map[string]version.V{} - for _, m := range methods { - apiMethodsCalled := asthelpers.FindMethodsCalledOnType(info, apiType, m) - effectiveVersions[m.Name.Name] = getEffectiveMinimumVersion(versions, apiMethodsCalled) - } - return effectiveVersions -} - -func mapMinimumVersionsByMethodName(methods []*ast.Field) map[string]version.V { - versions := map[string]version.V{} - for _, m := range methods { - versions[m.Names[0].Name] = version.V(version.ExtractMinimumVersionFromComment(m.Doc.Text())) - } - return versions -} - -func mapPositionsByMethodName(methods []*ast.Field) map[string]token.Pos { - pos := map[string]token.Pos{} - for _, m := range methods { - pos[m.Names[0].Name] = m.Pos() - } - return pos -} - -func getEffectiveMinimumVersion(info map[string]version.V, methods []string) version.V { - var highest version.V - for _, m := range methods { - if current, ok := info[m]; ok { - if current.GreaterThanOrEqualTo(highest) { - highest = current - } - } - } - return highest -} diff --git a/plugin/checker/check_helpers_test.go b/plugin/checker/check_helpers_test.go deleted file mode 100644 index 71b117c72e..0000000000 --- a/plugin/checker/check_helpers_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package main - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCheckHelpersVersionComments(t *testing.T) { - testCases := []struct { - name, pkgPath string - expected result - err string - }{ - { - name: "valid versions", - pkgPath: "github.com/mattermost/mattermost-server/v6/plugin/checker/internal/test/valid", - expected: result{}, - }, - { - name: "invalid versions", - pkgPath: "github.com/mattermost/mattermost-server/v6/plugin/checker/internal/test/invalid", - expected: result{ - Errors: []string{"internal/test/invalid/invalid.go:20:2: documented minimum server version too low on method LowerVersionMethod"}, - Warnings: []string{"internal/test/invalid/invalid.go:23:2: documented minimum server version too high on method HigherVersionMethod"}, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert := assert.New(t) - - res, err := checkHelpersVersionComments(tc.pkgPath) - assert.Equal(tc.expected, res) - - if tc.err != "" { - assert.EqualError(err, tc.err) - } else { - assert.NoError(err) - } - - }) - } -} diff --git a/plugin/checker/main.go b/plugin/checker/main.go index 282a325df1..87c1f6b414 100644 --- a/plugin/checker/main.go +++ b/plugin/checker/main.go @@ -21,7 +21,6 @@ type checkFn func(pkgPath string) (result, error) var checks = []checkFn{ checkAPIVersionComments, - checkHelpersVersionComments, } func main() { diff --git a/plugin/client.go b/plugin/client.go index d5a8789943..2ef19567e1 100644 --- a/plugin/client.go +++ b/plugin/client.go @@ -18,13 +18,11 @@ const ( func ClientMain(pluginImplementation interface{}) { if impl, ok := pluginImplementation.(interface { SetAPI(api API) - SetHelpers(helpers Helpers) SetDriver(driver Driver) }); !ok { panic("Plugin implementation given must embed plugin.MattermostPlugin") } else { impl.SetAPI(nil) - impl.SetHelpers(nil) impl.SetDriver(nil) } @@ -40,9 +38,8 @@ func ClientMain(pluginImplementation interface{}) { type MattermostPlugin struct { // API exposes the plugin api, and becomes available just prior to the OnActive hook. - API API - Helpers Helpers - Driver Driver + API API + Driver Driver } // SetAPI persists the given API interface to the plugin. It is invoked just prior to the @@ -51,11 +48,6 @@ func (p *MattermostPlugin) SetAPI(api API) { p.API = api } -// SetHelpers does the same thing as SetAPI except for the plugin helpers. -func (p *MattermostPlugin) SetHelpers(helpers Helpers) { - p.Helpers = helpers -} - // SetDriver sets the RPC client implementation to talk with the server. func (p *MattermostPlugin) SetDriver(driver Driver) { p.Driver = driver diff --git a/plugin/client_rpc.go b/plugin/client_rpc.go index 0baaa671fb..7f04dc406b 100644 --- a/plugin/client_rpc.go +++ b/plugin/client_rpc.go @@ -284,11 +284,9 @@ func (s *hooksRPCServer) OnActivate(args *Z_OnActivateArgs, returns *Z_OnActivat if mmplugin, ok := s.impl.(interface { SetAPI(api API) - SetHelpers(helpers Helpers) SetDriver(driver Driver) }); ok { mmplugin.SetAPI(s.apiRPCClient) - mmplugin.SetHelpers(&HelpersImpl{API: s.apiRPCClient}) mmplugin.SetDriver(dbClient) } diff --git a/plugin/driver.go b/plugin/driver.go new file mode 100644 index 0000000000..68eebeea1c --- /dev/null +++ b/plugin/driver.go @@ -0,0 +1,64 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package plugin + +import ( + "database/sql/driver" +) + +// ResultContainer contains the output from the LastInsertID +// and RowsAffected methods for a given set of rows. +// It is used to embed another round-trip to the server, +// and helping to avoid tracking results on the server. +type ResultContainer struct { + LastID int64 + LastIDError error + RowsAffected int64 + RowsAffectedError error +} + +// Driver is a sql driver interface that is used by plugins to perform +// raw SQL queries without opening DB connections by themselves. This interface +// is not subject to backward compatibility guarantees and is only meant to be +// used by plugins built by the Mattermost team. +type Driver interface { + // Connection + Conn(isMaster bool) (string, error) + ConnPing(connID string) error + ConnClose(connID string) error + ConnQuery(connID, q string, args []driver.NamedValue) (string, error) // rows + ConnExec(connID, q string, args []driver.NamedValue) (ResultContainer, error) // result + + // Transaction + Tx(connID string, opts driver.TxOptions) (string, error) + TxCommit(txID string) error + TxRollback(txID string) error + + // Statement + Stmt(connID, q string) (string, error) + StmtClose(stID string) error + StmtNumInput(stID string) int + StmtQuery(stID string, args []driver.NamedValue) (string, error) // rows + StmtExec(stID string, args []driver.NamedValue) (ResultContainer, error) // result + + // Rows + RowsColumns(rowsID string) []string + RowsClose(rowsID string) error + RowsNext(rowsID string, dest []driver.Value) error + RowsHasNextResultSet(rowsID string) bool + RowsNextResultSet(rowsID string) error + RowsColumnTypeDatabaseTypeName(rowsID string, index int) string + RowsColumnTypePrecisionScale(rowsID string, index int) (int64, int64, bool) + + // TODO: add this + // RowsColumnScanType(rowsID string, index int) reflect.Type + + // Note: the following cannot be implemented because either MySQL or PG + // does not support it. So this implementation has to be a common subset + // of both DB implementations. + // RowsColumnTypeLength(rowsID string, index int) (int64, bool) + // RowsColumnTypeNullable(rowsID string, index int) (bool, bool) + // ResetSession(ctx context.Context) error + // IsValid() bool +} diff --git a/plugin/helpers.go b/plugin/helpers.go deleted file mode 100644 index 946c5bc89c..0000000000 --- a/plugin/helpers.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin - -import ( - "database/sql/driver" - - "github.com/mattermost/mattermost-server/v6/model" -) - -// Helpers provide a common patterns plugins use. -// -// Plugins obtain access to the Helpers by embedding MattermostPlugin. -type Helpers interface { - // EnsureBot either returns an existing bot user matching the given bot, or creates a bot user from the given bot. - // A profile image or icon image may be optionally passed in to be set for the existing or newly created bot. - // Returns the id of the resulting bot. - // - // Minimum server version: 5.10 - EnsureBot(bot *model.Bot, options ...EnsureBotOption) (string, error) - - // KVSetJSON stores a key-value pair, unique per plugin, marshalling the given value as a JSON string. - // - // Deprecated: Use p.API.KVSetWithOptions instead. - // - // Minimum server version: 5.2 - KVSetJSON(key string, value interface{}) error - - // KVCompareAndSetJSON updates a key-value pair, unique per plugin, but only if the current value matches the given oldValue after marshalling as a JSON string. - // Inserts a new key if oldValue == nil. - // Returns (false, err) if DB error occurred - // Returns (false, nil) if current value != oldValue or key already exists when inserting - // Returns (true, nil) if current value == oldValue or new key is inserted - // - // Deprecated: Use p.API.KVSetWithOptions instead. - // - // Minimum server version: 5.12 - KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) - - // KVCompareAndDeleteJSON deletes a key-value pair, unique per plugin, but only if the current value matches the given oldValue after marshalling as a JSON string. - // Returns (false, err) if DB error occurred - // Returns (false, nil) if current value != oldValue or the key was already deleted - // Returns (true, nil) if current value == oldValue - // - // Minimum server version: 5.16 - KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) - - // KVGetJSON retrieves a value based on the key, unique per plugin, unmarshalling the previously set JSON string into the given value. Returns true if the key exists. - // - // Minimum server version: 5.2 - KVGetJSON(key string, value interface{}) (bool, error) - - // KVSetWithExpiryJSON stores a key-value pair with an expiry time, unique per plugin, marshalling the given value as a JSON string. - // - // Deprecated: Use p.API.KVSetWithOptions instead. - // - // Minimum server version: 5.6 - KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error - - // KVListWithOptions returns all keys that match the given options. If no options are provided then all keys are returned. - // - // Minimum server version: 5.6 - KVListWithOptions(options ...KVListOption) ([]string, error) - - // CheckRequiredServerConfiguration checks if the server is configured according to - // plugin requirements. - // - // Minimum server version: 5.2 - CheckRequiredServerConfiguration(req *model.Config) (bool, error) - - // ShouldProcessMessage returns if the message should be processed by a message hook. - // - // Use this method to avoid processing unnecessary messages in a MessageHasBeenPosted - // or MessageWillBePosted hook, and indeed in some cases avoid an infinite loop between - // two automated bots or plugins. - // - // The behaviour is customizable using the given options, since plugin needs may vary. - // By default, system messages and messages from bots will be skipped. - // - // Minimum server version: 5.2 - ShouldProcessMessage(post *model.Post, options ...ShouldProcessMessageOption) (bool, error) - - // InstallPluginFromURL installs the plugin from the provided url. - // - // Minimum server version: 5.18 - InstallPluginFromURL(downloadURL string, replace bool) (*model.Manifest, error) - - // GetPluginAssetURL builds a URL to the given asset in the assets directory. - // Use this URL to link to assets from the webapp, or for third-party integrations with your plugin. - // - // Minimum server version: 5.2 - GetPluginAssetURL(pluginID, asset string) (string, error) -} - -// HelpersImpl implements the helpers interface with an API that retrieves data on behalf of the plugin. -type HelpersImpl struct { - API API -} - -// ResultContainer contains the output from the LastInsertID -// and RowsAffected methods for a given set of rows. -// It is used to embed another round-trip to the server, -// and helping to avoid tracking results on the server. -type ResultContainer struct { - LastID int64 - LastIDError error - RowsAffected int64 - RowsAffectedError error -} - -// Driver is a sql driver interface that is used by plugins to perform -// raw SQL queries without opening DB connections by themselves. This interface -// is not subject to backward compatibility guarantees and is only meant to be -// used by plugins built by the Mattermost team. -type Driver interface { - // Connection - Conn(isMaster bool) (string, error) - ConnPing(connID string) error - ConnClose(connID string) error - ConnQuery(connID, q string, args []driver.NamedValue) (string, error) // rows - ConnExec(connID, q string, args []driver.NamedValue) (ResultContainer, error) // result - - // Transaction - Tx(connID string, opts driver.TxOptions) (string, error) - TxCommit(txID string) error - TxRollback(txID string) error - - // Statement - Stmt(connID, q string) (string, error) - StmtClose(stID string) error - StmtNumInput(stID string) int - StmtQuery(stID string, args []driver.NamedValue) (string, error) // rows - StmtExec(stID string, args []driver.NamedValue) (ResultContainer, error) // result - - // Rows - RowsColumns(rowsID string) []string - RowsClose(rowsID string) error - RowsNext(rowsID string, dest []driver.Value) error - RowsHasNextResultSet(rowsID string) bool - RowsNextResultSet(rowsID string) error - RowsColumnTypeDatabaseTypeName(rowsID string, index int) string - RowsColumnTypePrecisionScale(rowsID string, index int) (int64, int64, bool) - - // TODO: add this - // RowsColumnScanType(rowsID string, index int) reflect.Type - - // Note: the following cannot be implemented because either MySQL or PG - // does not support it. So this implementation has to be a common subset - // of both DB implementations. - // RowsColumnTypeLength(rowsID string, index int) (int64, bool) - // RowsColumnTypeNullable(rowsID string, index int) (bool, bool) - // ResetSession(ctx context.Context) error - // IsValid() bool -} diff --git a/plugin/helpers_bots.go b/plugin/helpers_bots.go deleted file mode 100644 index 011bce85a6..0000000000 --- a/plugin/helpers_bots.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin - -import ( - "io/ioutil" - "path/filepath" - - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/utils" -) - -type ensureBotOptions struct { - ProfileImagePath string - IconImagePath string -} - -type EnsureBotOption func(*ensureBotOptions) - -func ProfileImagePath(path string) EnsureBotOption { - return func(args *ensureBotOptions) { - args.ProfileImagePath = path - } -} - -func IconImagePath(path string) EnsureBotOption { - return func(args *ensureBotOptions) { - args.IconImagePath = path - } -} - -// EnsureBot implements Helpers.EnsureBot -func (p *HelpersImpl) EnsureBot(bot *model.Bot, options ...EnsureBotOption) (retBotID string, retErr error) { - err := p.ensureServerVersion("5.10.0") - if err != nil { - return "", errors.Wrap(err, "failed to ensure bot") - } - - // Default options - o := &ensureBotOptions{ - ProfileImagePath: "", - IconImagePath: "", - } - - for _, setter := range options { - setter(o) - } - - botID, err := p.ensureBot(bot) - if err != nil { - return "", err - } - - err = p.setBotImages(botID, o.ProfileImagePath, o.IconImagePath) - if err != nil { - return "", err - } - return botID, nil -} - -type ShouldProcessMessageOption func(*shouldProcessMessageOptions) - -type shouldProcessMessageOptions struct { - AllowSystemMessages bool - AllowBots bool - AllowWebhook bool - FilterChannelIDs []string - FilterUserIDs []string - OnlyBotDMs bool - BotID string -} - -// AllowSystemMessages configures a call to ShouldProcessMessage to return true for system messages. -// -// As it is typically desirable only to consume messages from users of the system, ShouldProcessMessage ignores system messages by default. -func AllowSystemMessages() ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.AllowSystemMessages = true - } -} - -// AllowBots configures a call to ShouldProcessMessage to return true for bot posts. -// -// As it is typically desirable only to consume messages from human users of the system, ShouldProcessMessage ignores bot messages by default. When allowing bots, take care to avoid a loop where two plugins respond to each others posts repeatedly. -func AllowBots() ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.AllowBots = true - } -} - -// AllowWebhook configures a call to ShouldProcessMessage to return true for posts from webhook. -// -// As it is typically desirable only to consume messages from human users of the system, ShouldProcessMessage ignores webhook messages by default. -func AllowWebhook() ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.AllowWebhook = true - } -} - -// FilterChannelIDs configures a call to ShouldProcessMessage to return true only for the given channels. -// -// By default, posts from all channels are allowed to be processed. -func FilterChannelIDs(filterChannelIDs []string) ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.FilterChannelIDs = filterChannelIDs - } -} - -// FilterUserIDs configures a call to ShouldProcessMessage to return true only for the given users. -// -// By default, posts from all non-bot users are allowed. -func FilterUserIDs(filterUserIDs []string) ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.FilterUserIDs = filterUserIDs - } -} - -// OnlyBotDMs configures a call to ShouldProcessMessage to return true only for direct messages sent to the bot created by EnsureBot. -// -// By default, posts from all channels are allowed. -func OnlyBotDMs() ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.OnlyBotDMs = true - } -} - -// If provided, BotID configures ShouldProcessMessage to skip its retrieval from the store. -// -// By default, posts from all non-bot users are allowed. -func BotID(botID string) ShouldProcessMessageOption { - return func(options *shouldProcessMessageOptions) { - options.BotID = botID - } -} - -// ShouldProcessMessage implements Helpers.ShouldProcessMessage -func (p *HelpersImpl) ShouldProcessMessage(post *model.Post, options ...ShouldProcessMessageOption) (bool, error) { - messageProcessOptions := &shouldProcessMessageOptions{} - for _, option := range options { - option(messageProcessOptions) - } - - var botIDBytes []byte - var kvGetErr *model.AppError - - if messageProcessOptions.BotID != "" { - botIDBytes = []byte(messageProcessOptions.BotID) - } else { - botIDBytes, kvGetErr = p.API.KVGet(BotUserKey) - - if kvGetErr != nil { - return false, errors.Wrap(kvGetErr, "failed to get bot") - } - } - - if botIDBytes != nil { - if post.UserId == string(botIDBytes) { - return false, nil - } - } - - if post.IsSystemMessage() && !messageProcessOptions.AllowSystemMessages { - return false, nil - } - - if !messageProcessOptions.AllowWebhook && post.GetProp("from_webhook") == "true" { - return false, nil - } - - if !messageProcessOptions.AllowBots { - user, appErr := p.API.GetUser(post.UserId) - if appErr != nil { - return false, errors.Wrap(appErr, "unable to get user") - } - - if user.IsBot { - return false, nil - } - } - - if len(messageProcessOptions.FilterChannelIDs) != 0 && !utils.StringInSlice(post.ChannelId, messageProcessOptions.FilterChannelIDs) { - return false, nil - } - - if len(messageProcessOptions.FilterUserIDs) != 0 && !utils.StringInSlice(post.UserId, messageProcessOptions.FilterUserIDs) { - return false, nil - } - - if botIDBytes != nil && messageProcessOptions.OnlyBotDMs { - channel, appErr := p.API.GetChannel(post.ChannelId) - if appErr != nil { - return false, errors.Wrap(appErr, "unable to get channel") - } - - if !model.IsBotDMChannel(channel, string(botIDBytes)) { - return false, nil - } - } - - return true, nil -} - -func (p *HelpersImpl) readFile(path string) ([]byte, error) { - bundlePath, err := p.API.GetBundlePath() - if err != nil { - return nil, errors.Wrap(err, "failed to get bundle path") - } - - imageBytes, err := ioutil.ReadFile(filepath.Join(bundlePath, path)) - if err != nil { - return nil, errors.Wrap(err, "failed to read image") - } - return imageBytes, nil -} - -func (p *HelpersImpl) ensureBot(bot *model.Bot) (retBotID string, retErr error) { - // Must provide a bot with a username - if bot == nil || len(bot.Username) < 1 { - return "", errors.New("passed a bad bot, nil or no username") - } - - // If we fail for any reason, this could be a race between creation of bot and - // retrieval from another EnsureBot. Just try the basic retrieve existing again. - defer func() { - if retBotID == "" || retErr != nil { - var err error - var botIDBytes []byte - - err = utils.ProgressiveRetry(func() error { - var appErr *model.AppError - botIDBytes, appErr = p.API.KVGet(BotUserKey) - if appErr != nil { - return appErr - } - return nil - }) - - if err == nil && botIDBytes != nil { - retBotID = string(botIDBytes) - retErr = nil - } - } - }() - - botIDBytes, kvGetErr := p.API.KVGet(BotUserKey) - if kvGetErr != nil { - return "", errors.Wrap(kvGetErr, "failed to get bot") - } - - // If the bot has already been created, use it - if botIDBytes != nil { - botID := string(botIDBytes) - - // ensure existing bot is synced with what is being created - botPatch := &model.BotPatch{ - Username: &bot.Username, - DisplayName: &bot.DisplayName, - Description: &bot.Description, - } - - if _, err := p.API.PatchBot(botID, botPatch); err != nil { - return "", errors.Wrap(err, "failed to patch bot") - } - - return botID, nil - } - - // Check for an existing bot user with that username. If one exists, then use that. - if user, userGetErr := p.API.GetUserByUsername(bot.Username); userGetErr == nil && user != nil { - if user.IsBot { - if kvSetErr := p.API.KVSet(BotUserKey, []byte(user.Id)); kvSetErr != nil { - p.API.LogWarn("Failed to set claimed bot user id.", "userid", user.Id, "err", kvSetErr) - } - } else { - p.API.LogError("Plugin attempted to use an account that already exists. Convert user to a bot account in the CLI by running 'mattermost user convert --bot'. If the user is an existing user account you want to preserve, change its username and restart the Mattermost server, after which the plugin will create a bot account with that name. For more information about bot accounts, see https://mattermost.com/pl/default-bot-accounts", "username", bot.Username, "user_id", user.Id) - } - return user.Id, nil - } - - // Create a new bot user for the plugin - createdBot, createBotErr := p.API.CreateBot(bot) - if createBotErr != nil { - return "", errors.Wrap(createBotErr, "failed to create bot") - } - - if kvSetErr := p.API.KVSet(BotUserKey, []byte(createdBot.UserId)); kvSetErr != nil { - p.API.LogWarn("Failed to set created bot user id.", "userid", createdBot.UserId, "err", kvSetErr) - } - - return createdBot.UserId, nil -} - -func (p *HelpersImpl) setBotImages(botID, profileImagePath, iconImagePath string) error { - if profileImagePath != "" { - imageBytes, err := p.readFile(profileImagePath) - if err != nil { - return errors.Wrap(err, "failed to read profile image") - } - appErr := p.API.SetProfileImage(botID, imageBytes) - if appErr != nil { - return errors.Wrap(appErr, "failed to set profile image") - } - } - if iconImagePath != "" { - imageBytes, err := p.readFile(iconImagePath) - if err != nil { - return errors.Wrap(err, "failed to read icon image") - } - appErr := p.API.SetBotIconImage(botID, imageBytes) - if appErr != nil { - return errors.Wrap(appErr, "failed to set icon image") - } - } - return nil -} diff --git a/plugin/helpers_bots_test.go b/plugin/helpers_bots_test.go deleted file mode 100644 index a0a35e9bf3..0000000000 --- a/plugin/helpers_bots_test.go +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin_test - -import ( - "io/ioutil" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/plugin" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock" - "github.com/mattermost/mattermost-server/v6/utils/fileutils" -) - -func TestEnsureBot(t *testing.T) { - setupAPI := func() *plugintest.API { - return &plugintest.API{} - } - - testbot := &model.Bot{ - Username: "testbot", - DisplayName: "Test Bot", - Description: "testbotdescription", - } - - t.Run("server version incompatible", func(t *testing.T) { - api := setupAPI() - api.On("GetServerVersion").Return("5.9.0") - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - _, retErr := p.EnsureBot(nil) - - assert.Error(t, retErr) - assert.Equal(t, "failed to ensure bot: incompatible server version for plugin, minimum required version: 5.10.0, current version: 5.9.0", retErr.Error()) - }) - - t.Run("bad parameters", func(t *testing.T) { - t.Run("no bot", func(t *testing.T) { - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - - p := &plugin.HelpersImpl{} - p.API = api - botID, err := p.EnsureBot(nil) - assert.Equal(t, "", botID) - assert.Error(t, err) - }) - t.Run("bad username", func(t *testing.T) { - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - - p := &plugin.HelpersImpl{} - p.API = api - botID, err := p.EnsureBot(&model.Bot{ - Username: "", - }) - assert.Equal(t, "", botID) - assert.Error(t, err) - }) - }) - - t.Run("if bot already exists", func(t *testing.T) { - t.Run("should find and return the existing bot ID", func(t *testing.T) { - expectedBotID := model.NewId() - - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - api.On("PatchBot", expectedBotID, &model.BotPatch{ - Username: &testbot.Username, - DisplayName: &testbot.DisplayName, - Description: &testbot.Description, - }).Return(nil, nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot) - - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should return an error if unable to get bot", func(t *testing.T) { - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return(nil, &model.AppError{}) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot) - - assert.Equal(t, "", botID) - assert.Error(t, err) - }) - - t.Run("should set the bot profile image when specified", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetProfileImage", expectedBotID, imageBytes).Return(nil) - api.On("GetServerVersion").Return("5.10.0") - api.On("PatchBot", expectedBotID, &model.BotPatch{ - Username: &testbot.Username, - DisplayName: &testbot.DisplayName, - Description: &testbot.Description, - }).Return(nil, nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - assert.NoError(t, err) - - botID, err := p.EnsureBot(testbot, plugin.ProfileImagePath(testImage)) - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should set the bot icon image when specified", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - assert.NoError(t, err) - - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetBotIconImage", expectedBotID, imageBytes).Return(nil) - api.On("GetServerVersion").Return("5.10.0") - api.On("PatchBot", expectedBotID, &model.BotPatch{ - Username: &testbot.Username, - DisplayName: &testbot.DisplayName, - Description: &testbot.Description, - }).Return(nil, nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot, plugin.IconImagePath(testImage)) - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should set both the profile image and bot icon image when specified", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - assert.NoError(t, err) - - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetProfileImage", expectedBotID, imageBytes).Return(nil) - api.On("SetBotIconImage", expectedBotID, imageBytes).Return(nil) - api.On("GetServerVersion").Return("5.10.0") - api.On("PatchBot", expectedBotID, &model.BotPatch{ - Username: &testbot.Username, - DisplayName: &testbot.DisplayName, - Description: &testbot.Description, - }).Return(nil, nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot, plugin.ProfileImagePath(testImage), plugin.IconImagePath(testImage)) - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should find and update the bot with new bot details", func(t *testing.T) { - expectedBotID := model.NewId() - - expectedBotUsername := "updated_testbot" - expectedBotDisplayName := "Updated Test Bot" - expectedBotDescription := "updated testbotdescription" - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - assert.NoError(t, err) - - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetProfileImage", expectedBotID, imageBytes).Return(nil) - api.On("SetBotIconImage", expectedBotID, imageBytes).Return(nil) - api.On("PatchBot", expectedBotID, &model.BotPatch{ - Username: &expectedBotUsername, - DisplayName: &expectedBotDisplayName, - Description: &expectedBotDescription, - }).Return(nil, nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - updatedTestbot := &model.Bot{ - Username: "updated_testbot", - DisplayName: "Updated Test Bot", - Description: "updated testbotdescription", - } - botID, err := p.EnsureBot(updatedTestbot, plugin.ProfileImagePath(testImage), plugin.IconImagePath(testImage)) - - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - }) - - t.Run("if bot doesn't exist", func(t *testing.T) { - t.Run("should create the bot and return the ID", func(t *testing.T) { - expectedBotID := model.NewId() - - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(nil, nil) - api.On("CreateBot", testbot).Return(&model.Bot{ - UserId: expectedBotID, - }, nil) - api.On("KVSet", plugin.BotUserKey, []byte(expectedBotID)).Return(nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot) - - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should claim existing bot and return the ID", func(t *testing.T) { - expectedBotID := model.NewId() - - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(&model.User{ - Id: expectedBotID, - IsBot: true, - }, nil) - api.On("KVSet", plugin.BotUserKey, []byte(expectedBotID)).Return(nil) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot) - - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should return the non-bot account but log a message if user exists with the same name and is not a bot", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(&model.User{ - Id: expectedBotID, - IsBot: false, - }, nil) - api.On("LogError", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return() - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot) - - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should fail if create bot fails", func(t *testing.T) { - api := setupAPI() - api.On("GetServerVersion").Return("5.10.0") - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(nil, nil) - api.On("CreateBot", testbot).Return(nil, &model.AppError{}) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot) - - assert.Equal(t, "", botID) - assert.Error(t, err) - }) - - t.Run("should create bot and set the bot profile image when specified", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - assert.NoError(t, err) - - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(nil, nil) - api.On("CreateBot", testbot).Return(&model.Bot{ - UserId: expectedBotID, - }, nil) - api.On("KVSet", plugin.BotUserKey, []byte(expectedBotID)).Return(nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetProfileImage", expectedBotID, imageBytes).Return(nil) - api.On("GetServerVersion").Return("5.10.0") - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot, plugin.ProfileImagePath(testImage)) - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should create bot and set the bot icon image when specified", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - assert.NoError(t, err) - - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(nil, nil) - api.On("CreateBot", testbot).Return(&model.Bot{ - UserId: expectedBotID, - }, nil) - api.On("KVSet", plugin.BotUserKey, []byte(expectedBotID)).Return(nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetBotIconImage", expectedBotID, imageBytes).Return(nil) - api.On("GetServerVersion").Return("5.10.0") - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot, plugin.IconImagePath(testImage)) - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - - t.Run("should create bot and set both the profile image and bot icon image when specified", func(t *testing.T) { - expectedBotID := model.NewId() - api := setupAPI() - - testsDir, _ := fileutils.FindDir("tests") - testImage := filepath.Join(testsDir, "test.png") - imageBytes, err := ioutil.ReadFile(testImage) - assert.NoError(t, err) - - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - api.On("GetUserByUsername", testbot.Username).Return(nil, nil) - api.On("CreateBot", testbot).Return(&model.Bot{ - UserId: expectedBotID, - }, nil) - api.On("KVSet", plugin.BotUserKey, []byte(expectedBotID)).Return(nil) - api.On("GetBundlePath").Return("", nil) - api.On("SetProfileImage", expectedBotID, imageBytes).Return(nil) - api.On("SetBotIconImage", expectedBotID, imageBytes).Return(nil) - api.On("GetServerVersion").Return("5.10.0") - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - botID, err := p.EnsureBot(testbot, plugin.ProfileImagePath(testImage), plugin.IconImagePath(testImage)) - assert.Equal(t, expectedBotID, botID) - assert.NoError(t, err) - }) - }) -} - -func TestShouldProcessMessage(t *testing.T) { - p := &plugin.HelpersImpl{} - expectedBotID := model.NewId() - - setupAPI := func() *plugintest.API { - return &plugintest.API{} - } - - t.Run("should not respond to itself", func(t *testing.T) { - api := setupAPI() - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - p.API = api - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{Type: model.PostTypeHeaderChange, UserId: expectedBotID}, plugin.AllowSystemMessages(), plugin.AllowBots()) - - assert.False(t, shouldProcessMessage) - }) - - t.Run("should not process as the post is generated by system", func(t *testing.T) { - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{Type: model.PostTypeHeaderChange}) - - assert.False(t, shouldProcessMessage) - }) - - t.Run("should not process as the post is sent to another channel", func(t *testing.T) { - channelID := "channel-id" - api := setupAPI() - api.On("GetChannel", channelID).Return(&model.Channel{Id: channelID, Type: model.ChannelTypeGroup}, nil) - p.API = api - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{ChannelId: channelID}, plugin.AllowSystemMessages(), plugin.AllowBots(), plugin.FilterChannelIDs([]string{"another-channel-id"})) - - assert.False(t, shouldProcessMessage) - }) - - t.Run("should not process as the post is created by bot", func(t *testing.T) { - userID := "user-id" - channelID := "1" - api := setupAPI() - p.API = api - api.On("GetUser", userID).Return(&model.User{IsBot: true}, nil) - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{UserId: userID, ChannelId: channelID}, - plugin.AllowSystemMessages(), plugin.FilterUserIDs([]string{"another-user-id"})) - - assert.False(t, shouldProcessMessage) - }) - - t.Run("should not process the message as the post is not in bot dm channel", func(t *testing.T) { - userID := "user-id" - channelID := "1" - channel := model.Channel{ - Name: "user1__" + expectedBotID, - Type: model.ChannelTypeOpen, - } - api := setupAPI() - api.On("GetChannel", channelID).Return(&channel, nil) - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - p.API = api - - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{UserId: userID, ChannelId: channelID}, plugin.AllowSystemMessages(), plugin.AllowBots(), plugin.OnlyBotDMs()) - - assert.False(t, shouldProcessMessage) - }) - - t.Run("should process the message", func(t *testing.T) { - channelID := "1" - api := setupAPI() - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - p.API = api - - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{UserId: "1", Type: model.PostTypeHeaderChange, ChannelId: channelID}, - plugin.AllowSystemMessages(), plugin.FilterChannelIDs([]string{channelID}), plugin.AllowBots(), plugin.FilterUserIDs([]string{"1"})) - - assert.True(t, shouldProcessMessage) - }) - - t.Run("should process the message for plugin without a bot", func(t *testing.T) { - channelID := "1" - api := setupAPI() - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - p.API = api - - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{UserId: "1", Type: model.PostTypeHeaderChange, ChannelId: channelID}, - plugin.AllowSystemMessages(), plugin.FilterChannelIDs([]string{channelID}), plugin.AllowBots(), plugin.FilterUserIDs([]string{"1"})) - - assert.True(t, shouldProcessMessage) - }) - - t.Run("should process the message when filter channel and filter users list is empty", func(t *testing.T) { - channelID := "1" - api := setupAPI() - channel := model.Channel{ - Name: "user1__" + expectedBotID, - Type: model.ChannelTypeDirect, - } - api.On("GetChannel", channelID).Return(&channel, nil) - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - p.API = api - - shouldProcessMessage, _ := p.ShouldProcessMessage(&model.Post{UserId: "1", Type: model.PostTypeHeaderChange, ChannelId: channelID}, - plugin.AllowSystemMessages(), plugin.AllowBots()) - - assert.True(t, shouldProcessMessage) - }) - - t.Run("should not process the message which have from_webhook", func(t *testing.T) { - channelID := "1" - api := setupAPI() - api.On("GetChannel", channelID).Return(&model.Channel{Id: channelID, Type: model.ChannelTypeGroup}, nil) - p.API = api - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - - shouldProcessMessage, err := p.ShouldProcessMessage(&model.Post{ChannelId: channelID, Props: model.StringInterface{"from_webhook": "true"}}, plugin.AllowBots()) - - assert.False(t, shouldProcessMessage) - assert.NoError(t, err) - }) - - t.Run("should process the message which have from_webhook with allow webhook plugin", func(t *testing.T) { - channelID := "1" - api := setupAPI() - api.On("GetChannel", channelID).Return(&model.Channel{Id: channelID, Type: model.ChannelTypeGroup}, nil) - p.API = api - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - - shouldProcessMessage, err := p.ShouldProcessMessage(&model.Post{ChannelId: channelID, Props: model.StringInterface{"from_webhook": "true"}}, plugin.AllowBots(), plugin.AllowWebhook()) - assert.NoError(t, err) - - assert.True(t, shouldProcessMessage) - }) - - t.Run("should process the message where from_webhook is not set", func(t *testing.T) { - channelID := "1" - api := setupAPI() - api.On("GetChannel", channelID).Return(&model.Channel{Id: channelID, Type: model.ChannelTypeGroup}, nil) - p.API = api - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - - shouldProcessMessage, err := p.ShouldProcessMessage(&model.Post{ChannelId: channelID}, plugin.AllowBots()) - assert.NoError(t, err) - - assert.True(t, shouldProcessMessage) - }) - - t.Run("should process the message which have from_webhook false", func(t *testing.T) { - channelID := "1" - api := setupAPI() - api.On("GetChannel", channelID).Return(&model.Channel{Id: channelID, Type: model.ChannelTypeGroup}, nil) - p.API = api - api.On("KVGet", plugin.BotUserKey).Return([]byte(expectedBotID), nil) - - shouldProcessMessage, err := p.ShouldProcessMessage(&model.Post{ChannelId: channelID, Props: model.StringInterface{"from_webhook": "false"}}, plugin.AllowBots()) - assert.NoError(t, err) - - assert.True(t, shouldProcessMessage) - }) - - t.Run("should process the message when we pass the botId as input", func(t *testing.T) { - userID := "user-id" - channelID := "1" - api := setupAPI() - api.On("GetChannel", channelID).Return(&model.Channel{Id: channelID, Type: model.ChannelTypeGroup}, nil) - p.API = api - api.On("GetUser", userID).Return(&model.User{IsBot: false}, nil) - - // we should skip the store Get - api.On("KVGet", plugin.BotUserKey).Return(nil, nil) - - shouldProcessMessage, err := p.ShouldProcessMessage(&model.Post{ChannelId: channelID, UserId: userID}, plugin.BotID(expectedBotID)) - assert.NoError(t, err) - - assert.True(t, shouldProcessMessage) - }) -} diff --git a/plugin/helpers_config.go b/plugin/helpers_config.go deleted file mode 100644 index 7fe6d6dbbb..0000000000 --- a/plugin/helpers_config.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin - -import ( - "bytes" - "encoding/json" - "fmt" - - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/utils" -) - -// CheckRequiredServerConfiguration implements Helpers.CheckRequiredServerConfiguration -func (p *HelpersImpl) CheckRequiredServerConfiguration(req *model.Config) (bool, error) { - if req == nil { - return true, nil - } - - cfg := p.API.GetConfig() - - mc, err := utils.Merge(cfg, req, nil) - if err != nil { - return false, errors.Wrap(err, "could not merge configurations") - } - - mergedCfg := mc.(model.Config) - cfgBuf, err := json.Marshal(cfg) - if err != nil { - return false, fmt.Errorf("failed to marshal config: %v", err) - } - - mergedCfgBuf, err := json.Marshal(mergedCfg) - if err != nil { - return false, fmt.Errorf("failed to marshal merged config: %v", err) - } - - if !bytes.Equal(cfgBuf, mergedCfgBuf) { - return false, nil - } - - return true, nil -} diff --git a/plugin/helpers_config_test.go b/plugin/helpers_config_test.go deleted file mode 100644 index 458c3aa4f4..0000000000 --- a/plugin/helpers_config_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/plugin" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest" -) - -func TestCheckRequiredServerConfiguration(t *testing.T) { - for name, test := range map[string]struct { - SetupAPI func(*plugintest.API) *plugintest.API - Input *model.Config - ShouldReturn bool - ShouldError bool - }{ - "no required config therefore it should be compatible": { - SetupAPI: func(api *plugintest.API) *plugintest.API { - return api - }, - Input: nil, - ShouldReturn: true, - ShouldError: false, - }, - "contains required configuration": { - SetupAPI: func(api *plugintest.API) *plugintest.API { - api.On("GetConfig").Return(&model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(true), - }, - TeamSettings: model.TeamSettings{ - EnableUserCreation: model.NewBool(true), - }, - }) - - return api - }, - Input: &model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(true), - }, - }, - ShouldReturn: true, - ShouldError: false, - }, - "does not contain required configuration": { - SetupAPI: func(api *plugintest.API) *plugintest.API { - api.On("GetConfig").Return(&model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(true), - }, - }) - - return api - }, - Input: &model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(true), - }, - TeamSettings: model.TeamSettings{ - EnableUserCreation: model.NewBool(true), - }, - }, - ShouldReturn: false, - ShouldError: false, - }, - "different configurations": { - SetupAPI: func(api *plugintest.API) *plugintest.API { - api.On("GetConfig").Return(&model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(false), - }, - }) - - return api - }, - Input: &model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(true), - }, - }, - ShouldReturn: false, - ShouldError: false, - }, - "non-existent configuration": { - SetupAPI: func(api *plugintest.API) *plugintest.API { - api.On("GetConfig").Return(&model.Config{}) - - return api - }, - Input: &model.Config{ - ServiceSettings: model.ServiceSettings{ - EnableCommands: model.NewBool(true), - }, - }, - ShouldReturn: false, - ShouldError: false, - }, - } { - t.Run(name, func(t *testing.T) { - api := test.SetupAPI(&plugintest.API{}) - defer api.AssertExpectations(t) - - p := &plugin.HelpersImpl{} - p.API = api - - ok, err := p.CheckRequiredServerConfiguration(test.Input) - - assert.Equal(t, test.ShouldReturn, ok) - if test.ShouldError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/plugin/helpers_kv.go b/plugin/helpers_kv.go deleted file mode 100644 index 890e8121e4..0000000000 --- a/plugin/helpers_kv.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin - -import ( - "encoding/json" - "strings" - - "github.com/pkg/errors" -) - -// KVSetJSON implements Helpers.KVSetJSON. -func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error { - err := p.ensureServerVersion("5.2.0") - if err != nil { - return err - } - - data, err := json.Marshal(value) - if err != nil { - return err - } - - appErr := p.API.KVSet(key, data) - if appErr != nil { - return appErr - } - - return nil -} - -// KVCompareAndSetJSON implements Helpers.KVCompareAndSetJSON. -func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) { - var err error - - err = p.ensureServerVersion("5.12.0") - if err != nil { - return false, err - } - var oldData, newData []byte - - if oldValue != nil { - oldData, err = json.Marshal(oldValue) - if err != nil { - return false, errors.Wrap(err, "unable to marshal old value") - } - } - - if newValue != nil { - newData, err = json.Marshal(newValue) - if err != nil { - return false, errors.Wrap(err, "unable to marshal new value") - } - } - - set, appErr := p.API.KVCompareAndSet(key, oldData, newData) - if appErr != nil { - return set, appErr - } - - return set, nil -} - -// KVCompareAndDeleteJSON implements Helpers.KVCompareAndDeleteJSON. -func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) { - var err error - - err = p.ensureServerVersion("5.16.0") - if err != nil { - return false, err - } - - var oldData []byte - - if oldValue != nil { - oldData, err = json.Marshal(oldValue) - if err != nil { - return false, errors.Wrap(err, "unable to marshal old value") - } - } - - deleted, appErr := p.API.KVCompareAndDelete(key, oldData) - if appErr != nil { - return deleted, appErr - } - - return deleted, nil -} - -// KVGetJSON implements Helpers.KVGetJSON. -func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) { - err := p.ensureServerVersion("5.2.0") - if err != nil { - return false, err - } - - data, appErr := p.API.KVGet(key) - if appErr != nil { - return false, appErr - } - if data == nil { - return false, nil - } - - err = json.Unmarshal(data, value) - if err != nil { - return false, err - } - - return true, nil -} - -// KVSetWithExpiryJSON is a wrapper around KVSetWithExpiry to simplify atomically writing a JSON object with expiry to the key value store. -func (p *HelpersImpl) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error { - err := p.ensureServerVersion("5.6.0") - if err != nil { - return err - } - - data, err := json.Marshal(value) - if err != nil { - return err - } - - appErr := p.API.KVSetWithExpiry(key, data, expireInSeconds) - if appErr != nil { - return appErr - } - - return nil -} - -type kvListOptions struct { - checkers []func(key string) (keep bool, err error) -} - -func (o *kvListOptions) checkAll(key string) (keep bool, err error) { - for _, check := range o.checkers { - keep, err := check(key) - if err != nil { - return false, err - } - if !keep { - return false, nil - } - } - - // key made it through all checkers - return true, nil -} - -// KVListOption represents a single input option for KVListWithOptions -type KVListOption func(*kvListOptions) - -// WithPrefix only return keys that start with the given string. -func WithPrefix(prefix string) KVListOption { - return WithChecker(func(key string) (keep bool, err error) { - return strings.HasPrefix(key, prefix), nil - }) -} - -// WithChecker allows for a custom filter function to determine which keys to return. -// Returning true will keep the key and false will filter it out. Returning an error -// will halt KVListWithOptions immediately and pass the error up (with no other results). -func WithChecker(f func(key string) (keep bool, err error)) KVListOption { - return func(args *kvListOptions) { - args.checkers = append(args.checkers, f) - } -} - -// kvListPerPage is the number of keys KVListWithOptions gets per request -const kvListPerPage = 100 - -// KVListWithOptions implements Helpers.KVListWithOptions. -func (p *HelpersImpl) KVListWithOptions(options ...KVListOption) ([]string, error) { - err := p.ensureServerVersion("5.6.0") - if err != nil { - return nil, err - } - // convert functional options into args struct - args := &kvListOptions{} - for _, opt := range options { - opt(args) - } - ret := make([]string, 0) - - // get our keys a batch at a time, filter out the ones we don't want based on our args - // any errors will hault the whole process and return the error raw - for i := 0; ; i++ { - keys, appErr := p.API.KVList(i, kvListPerPage) - if appErr != nil { - return nil, appErr - } - - if len(args.checkers) == 0 { - // no checkers, just append the whole block at once - ret = append(ret, keys...) - } else { - // we have a filter, so check each key, all checkers must say key - // for us to keep a key - for _, key := range keys { - keep, err := args.checkAll(key) - if err != nil { - return nil, err - } - if !keep { - continue - } - - // didn't get filtered out, add to our return - ret = append(ret, key) - } - } - - if len(keys) < kvListPerPage { - break - } - } - - return ret, nil -} diff --git a/plugin/helpers_kv_test.go b/plugin/helpers_kv_test.go deleted file mode 100644 index b08c2cef84..0000000000 --- a/plugin/helpers_kv_test.go +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin_test - -import ( - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/plugin" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest" -) - -func TestKVGetJSON(t *testing.T) { - t.Run("incompatible server version", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.1.0") - - p := &plugin.HelpersImpl{API: api} - var dat map[string]interface{} - - ok, err := p.KVGetJSON("test-key", dat) - - api.AssertExpectations(t) - assert.False(t, ok) - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.2.0, current version: 5.1.0", err.Error()) - }) - - t.Run("KVGet error", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.2.0") - api.On("KVGet", "test-key").Return(nil, &model.AppError{}) - p.API = api - - var dat map[string]interface{} - - ok, err := p.KVGetJSON("test-key", dat) - api.AssertExpectations(t) - assert.False(t, ok) - assert.Error(t, err) - assert.Nil(t, dat) - }) - - t.Run("unknown key", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.2.0") - api.On("KVGet", "test-key").Return(nil, nil) - p.API = api - - var dat map[string]interface{} - - ok, err := p.KVGetJSON("test-key", dat) - api.AssertExpectations(t) - assert.False(t, ok) - assert.NoError(t, err) - assert.Nil(t, dat) - }) - - t.Run("malformed JSON", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.2.0") - api.On("KVGet", "test-key").Return([]byte(`{{:}"val-a": 10}`), nil) - p.API = api - - var dat map[string]interface{} - - ok, err := p.KVGetJSON("test-key", &dat) - api.AssertExpectations(t) - assert.False(t, ok) - assert.Error(t, err) - assert.Nil(t, dat) - }) - - t.Run("wellformed JSON", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.2.0") - api.On("KVGet", "test-key").Return([]byte(`{"val-a": 10}`), nil) - p.API = api - - var dat map[string]interface{} - - ok, err := p.KVGetJSON("test-key", &dat) - assert.True(t, ok) - api.AssertExpectations(t) - assert.NoError(t, err) - assert.Equal(t, map[string]interface{}{ - "val-a": float64(10), - }, dat) - }) -} - -func TestKVSetJSON(t *testing.T) { - t.Run("incompatible server version", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.1.0") - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetJSON("test-key", map[string]interface{}{ - "val-a": float64(10), - }) - - api.AssertExpectations(t) - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.2.0, current version: 5.1.0", err.Error()) - }) - - t.Run("JSON marshal error", func(t *testing.T) { - api := &plugintest.API{} - api.AssertNotCalled(t, "KVSet") - api.On("GetServerVersion").Return("5.2.0") - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetJSON("test-key", func() {}) - api.AssertExpectations(t) - assert.Error(t, err) - }) - - t.Run("KVSet error", func(t *testing.T) { - api := &plugintest.API{} - api.On("KVSet", "test-key", []byte(`{"val-a":10}`)).Return(&model.AppError{}) - api.On("GetServerVersion").Return("5.2.0") - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetJSON("test-key", map[string]interface{}{ - "val-a": float64(10), - }) - - api.AssertExpectations(t) - assert.Error(t, err) - }) - - t.Run("marshallable struct", func(t *testing.T) { - api := &plugintest.API{} - api.On("KVSet", "test-key", []byte(`{"val-a":10}`)).Return(nil) - api.On("GetServerVersion").Return("5.2.0") - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetJSON("test-key", map[string]interface{}{ - "val-a": float64(10), - }) - - api.AssertExpectations(t) - assert.NoError(t, err) - }) -} - -func TestKVCompareAndSetJSON(t *testing.T) { - t.Run("incompatible server version", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.10.0") - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", nil, map[string]interface{}{ - "val-b": 20, - }) - - assert.Equal(t, false, ok) - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.12.0, current version: 5.10.0", err.Error()) - }) - - t.Run("old value JSON marshal error", func(t *testing.T) { - api := &plugintest.API{} - api.AssertNotCalled(t, "KVCompareAndSet") - api.On("GetServerVersion").Return("5.12.0") - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", func() {}, map[string]interface{}{}) - - api.AssertExpectations(t) - assert.Equal(t, false, ok) - assert.Error(t, err) - }) - - t.Run("new value JSON marshal error", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.12.0") - api.AssertNotCalled(t, "KVCompareAndSet") - - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", map[string]interface{}{}, func() {}) - - api.AssertExpectations(t) - assert.False(t, ok) - assert.Error(t, err) - }) - - t.Run("KVCompareAndSet error", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.12.0") - api.On("KVCompareAndSet", "test-key", []byte(`{"val-a":10}`), []byte(`{"val-b":20}`)).Return(false, &model.AppError{}) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", map[string]interface{}{ - "val-a": 10, - }, map[string]interface{}{ - "val-b": 20, - }) - - api.AssertExpectations(t) - assert.False(t, ok) - assert.Error(t, err) - }) - - t.Run("old value nil", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.12.0") - api.On("KVCompareAndSet", "test-key", []byte(nil), []byte(`{"val-b":20}`)).Return(true, nil) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", nil, map[string]interface{}{ - "val-b": 20, - }) - - api.AssertExpectations(t) - assert.True(t, ok) - assert.NoError(t, err) - }) - - t.Run("old value non-nil", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.12.0") - api.On("KVCompareAndSet", "test-key", []byte(`{"val-a":10}`), []byte(`{"val-b":20}`)).Return(true, nil) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", map[string]interface{}{ - "val-a": 10, - }, map[string]interface{}{ - "val-b": 20, - }) - - api.AssertExpectations(t) - assert.True(t, ok) - assert.NoError(t, err) - }) - - t.Run("new value nil", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.12.0") - api.On("KVCompareAndSet", "test-key", []byte(`{"val-a":10}`), []byte(nil)).Return(true, nil) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndSetJSON("test-key", map[string]interface{}{ - "val-a": 10, - }, nil) - - api.AssertExpectations(t) - assert.True(t, ok) - assert.NoError(t, err) - }) -} - -func TestKVCompareAndDeleteJSON(t *testing.T) { - t.Run("incompatible server version", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.10.0") - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndDeleteJSON("test-key", map[string]interface{}{ - "val-a": 10, - }) - - assert.Equal(t, false, ok) - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.16.0, current version: 5.10.0", err.Error()) - }) - - t.Run("old value JSON marshal error", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.16.0") - api.AssertNotCalled(t, "KVCompareAndDelete") - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndDeleteJSON("test-key", func() {}) - - api.AssertExpectations(t) - assert.Equal(t, false, ok) - assert.Error(t, err) - }) - - t.Run("KVCompareAndDelete error", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.16.0") - api.On("KVCompareAndDelete", "test-key", []byte(`{"val-a":10}`)).Return(false, &model.AppError{}) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndDeleteJSON("test-key", map[string]interface{}{ - "val-a": 10, - }) - - api.AssertExpectations(t) - assert.False(t, ok) - assert.Error(t, err) - }) - - t.Run("old value nil", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.16.0") - api.On("KVCompareAndDelete", "test-key", []byte(nil)).Return(true, nil) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndDeleteJSON("test-key", nil) - - api.AssertExpectations(t) - assert.True(t, ok) - assert.NoError(t, err) - }) - - t.Run("old value non-nil", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.16.0") - api.On("KVCompareAndDelete", "test-key", []byte(`{"val-a":10}`)).Return(true, nil) - p := &plugin.HelpersImpl{API: api} - - ok, err := p.KVCompareAndDeleteJSON("test-key", map[string]interface{}{ - "val-a": 10, - }) - - api.AssertExpectations(t) - assert.True(t, ok) - assert.NoError(t, err) - }) -} - -func TestKVSetWithExpiryJSON(t *testing.T) { - t.Run("incompatible server version", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.4.0") - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetWithExpiryJSON("test-key", map[string]interface{}{ - "val-a": float64(10), - }, 100) - - api.AssertExpectations(t) - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.6.0, current version: 5.4.0", err.Error()) - }) - - t.Run("JSON marshal error", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.AssertNotCalled(t, "KVSetWithExpiry") - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetWithExpiryJSON("test-key", func() {}, 100) - - api.AssertExpectations(t) - assert.Error(t, err) - }) - - t.Run("KVSetWithExpiry error", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVSetWithExpiry", "test-key", []byte(`{"val-a":10}`), int64(100)).Return(&model.AppError{}) - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetWithExpiryJSON("test-key", map[string]interface{}{ - "val-a": float64(10), - }, 100) - - api.AssertExpectations(t) - assert.Error(t, err) - }) - - t.Run("wellformed JSON", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVSetWithExpiry", "test-key", []byte(`{"val-a":10}`), int64(100)).Return(nil) - - p := &plugin.HelpersImpl{API: api} - - err := p.KVSetWithExpiryJSON("test-key", map[string]interface{}{ - "val-a": float64(10), - }, 100) - - api.AssertExpectations(t) - assert.NoError(t, err) - }) -} - -func TestKVListWithOptions(t *testing.T) { - t.Run("incompatible server version", func(t *testing.T) { - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.1.0") - - p := &plugin.HelpersImpl{API: api} - - keys, err := p.KVListWithOptions() - - api.AssertExpectations(t) - assert.Nil(t, keys) - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.6.0, current version: 5.1.0", err.Error()) - }) - - t.Run("KVList error", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return([]string{}, &model.AppError{}) - p.API = api - - keys, err := p.KVListWithOptions() - api.AssertExpectations(t) - assert.Empty(t, keys) - assert.Error(t, err) - }) - - t.Run("No keys", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(nil, nil) - p.API = api - - keys, err := p.KVListWithOptions() - api.AssertExpectations(t) - assert.Empty(t, keys) - assert.NoError(t, err) - }) - - t.Run("Basic Success, one page", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return([]string{"key1", "key2"}, nil) - p.API = api - - keys, err := p.KVListWithOptions() - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, []string{"key1", "key2"}) - assert.NoError(t, err) - }) - - t.Run("Basic Success, two page", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - keys, err := p.KVListWithOptions() - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, getKeys(101)) - assert.NoError(t, err) - }) - - t.Run("error on second page", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, &model.AppError{}) - p.API = api - - keys, err := p.KVListWithOptions() - api.AssertExpectations(t) - assert.Empty(t, keys) - assert.Error(t, err) - }) - - t.Run("success, two page, filter prefix, one", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - keys, err := p.KVListWithOptions(plugin.WithPrefix("key99")) - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, []string{"key99"}) - assert.NoError(t, err) - }) - - t.Run("success, two page, filter prefix, all", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - keys, err := p.KVListWithOptions(plugin.WithPrefix("notkey")) - api.AssertExpectations(t) - assert.Empty(t, keys) - assert.NoError(t, err) - }) - - t.Run("success, two page, filter prefix, none", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - keys, err := p.KVListWithOptions(plugin.WithPrefix("key")) - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, getKeys(101)) - assert.NoError(t, err) - }) - - t.Run("success, two page, checker func, one", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - check := func(key string) (bool, error) { - if key == "key1" { - return true, nil - } - return false, nil - } - - keys, err := p.KVListWithOptions(plugin.WithChecker(check)) - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, []string{"key1"}) - assert.NoError(t, err) - }) - - t.Run("success, two page, checker func, all", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - check := func(key string) (bool, error) { - return false, nil - } - - keys, err := p.KVListWithOptions(plugin.WithChecker(check)) - api.AssertExpectations(t) - assert.Empty(t, keys) - assert.NoError(t, err) - }) - - t.Run("success, two page, checker func, none", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return(getKeys(100), nil) - api.On("KVList", 1, 100).Return([]string{"key100"}, nil) - p.API = api - - check := func(key string) (bool, error) { - return true, nil - } - - keys, err := p.KVListWithOptions(plugin.WithChecker(check)) - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, getKeys(101)) - assert.NoError(t, err) - }) - - t.Run("error, checker func", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return([]string{"key1"}, nil) - p.API = api - - check := func(key string) (bool, error) { - return true, &model.AppError{} - } - - keys, err := p.KVListWithOptions(plugin.WithChecker(check)) - api.AssertExpectations(t) - assert.Empty(t, keys) - assert.Error(t, err) - }) - - t.Run("success, filter and checker func, partial on both", func(t *testing.T) { - p := &plugin.HelpersImpl{} - - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.6.0") - api.On("KVList", 0, 100).Return([]string{"key1", "key2", "notkey3", "key4", "key5"}, nil) - p.API = api - - check := func(key string) (bool, error) { - if key == "key1" || key == "key5" { - return false, nil - } - return true, nil - } - - keys, err := p.KVListWithOptions(plugin.WithPrefix("key"), plugin.WithChecker(check)) - api.AssertExpectations(t) - assert.ElementsMatch(t, keys, []string{"key2", "key4"}) - assert.NoError(t, err) - }) -} - -func getKeys(count int) []string { - ret := make([]string, count) - for i := 0; i < count; i++ { - ret[i] = "key" + strconv.Itoa(i) - } - return ret -} diff --git a/plugin/helpers_plugin.go b/plugin/helpers_plugin.go deleted file mode 100644 index d64d9add2e..0000000000 --- a/plugin/helpers_plugin.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin - -import ( - "net/http" - "net/url" - "path" - "time" - - "github.com/blang/semver" - "github.com/pkg/errors" - - "github.com/mattermost/mattermost-server/v6/model" -) - -// InstallPluginFromURL implements Helpers.InstallPluginFromURL. -func (p *HelpersImpl) InstallPluginFromURL(downloadURL string, replace bool) (*model.Manifest, error) { - err := p.ensureServerVersion("5.18.0") - if err != nil { - return nil, err - } - - parsedURL, err := url.Parse(downloadURL) - if err != nil { - return nil, errors.Wrap(err, "error while parsing url") - } - - client := &http.Client{Timeout: time.Hour} - response, err := client.Get(parsedURL.String()) - if err != nil { - return nil, errors.Wrap(err, "unable to download the plugin") - } - defer response.Body.Close() - - if response.StatusCode != http.StatusOK { - return nil, errors.Errorf("received %d status code while downloading plugin from server", response.StatusCode) - } - - manifest, installError := p.API.InstallPlugin(response.Body, replace) - if installError != nil { - return nil, errors.Wrap(err, "unable to install plugin on server") - } - - return manifest, nil -} - -func (p *HelpersImpl) ensureServerVersion(required string) error { - serverVersion := p.API.GetServerVersion() - currentVersion := semver.MustParse(serverVersion) - requiredVersion := semver.MustParse(required) - - if currentVersion.LT(requiredVersion) { - return errors.Errorf("incompatible server version for plugin, minimum required version: %s, current version: %s", required, serverVersion) - } - return nil -} - -// GetPluginAssetURL implements GetPluginAssetURL. -func (p *HelpersImpl) GetPluginAssetURL(pluginID, asset string) (string, error) { - if pluginID == "" { - return "", errors.New("empty pluginID provided") - } - - if asset == "" { - return "", errors.New("empty asset name provided") - } - - siteURL := *p.API.GetConfig().ServiceSettings.SiteURL - if siteURL == "" { - return "", errors.New("no SiteURL configured by the server") - } - - u, err := url.Parse(siteURL + path.Join("/", pluginID, asset)) - if err != nil { - return "", err - } - - return u.String(), nil -} diff --git a/plugin/helpers_plugin_test.go b/plugin/helpers_plugin_test.go deleted file mode 100644 index 79e5e00947..0000000000 --- a/plugin/helpers_plugin_test.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package plugin_test - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/plugin" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock" - "github.com/mattermost/mattermost-server/v6/utils/fileutils" -) - -func TestInstallPluginFromURL(t *testing.T) { - replace := true - - t.Run("incompatible server version", func(t *testing.T) { - h := &plugin.HelpersImpl{} - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.1.0") - h.API = api - - _, err := h.InstallPluginFromURL("", true) - - assert.Error(t, err) - assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.18.0, current version: 5.1.0", err.Error()) - }) - - t.Run("error while parsing the download url", func(t *testing.T) { - h := &plugin.HelpersImpl{} - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.19.0") - h.API = api - _, err := h.InstallPluginFromURL("http://%41:8080/", replace) - - assert.Error(t, err) - assert.Equal(t, "error while parsing url: parse \"http://%41:8080/\": invalid URL escape \"%41\"", err.Error()) - }) - - t.Run("errors out while downloading file", func(t *testing.T) { - h := &plugin.HelpersImpl{} - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.19.0") - h.API = api - testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.WriteHeader(http.StatusInternalServerError) - })) - defer testServer.Close() - url := testServer.URL - - _, err := h.InstallPluginFromURL(url, replace) - - assert.Error(t, err) - assert.Equal(t, "received 500 status code while downloading plugin from server", err.Error()) - }) - - t.Run("downloads the file successfully", func(t *testing.T) { - h := &plugin.HelpersImpl{} - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.19.0") - h.API = api - path, _ := fileutils.FindDir("tests") - tarData, err := ioutil.ReadFile(filepath.Join(path, "testplugin.tar.gz")) - require.NoError(t, err) - expectedManifest := &model.Manifest{Id: "testplugin"} - api.On("InstallPlugin", mock.Anything, false).Return(expectedManifest, nil) - - testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.WriteHeader(http.StatusOK) - _, _ = res.Write(tarData) - })) - defer testServer.Close() - url := testServer.URL - - manifest, err := h.InstallPluginFromURL(url, false) - - assert.NoError(t, err) - assert.Equal(t, "testplugin", manifest.Id) - }) - - t.Run("the url pointing to server is incorrect", func(t *testing.T) { - h := &plugin.HelpersImpl{} - api := &plugintest.API{} - api.On("GetServerVersion").Return("5.19.0") - h.API = api - testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.WriteHeader(http.StatusNotFound) - })) - defer testServer.Close() - url := testServer.URL - - _, err := h.InstallPluginFromURL(url, false) - - assert.Error(t, err) - assert.Equal(t, "received 404 status code while downloading plugin from server", err.Error()) - }) -} - -func TestGetPluginAssetURL(t *testing.T) { - siteURL := "https://mattermost.example.com" - api := &plugintest.API{} - api.On("GetConfig").Return(&model.Config{ServiceSettings: model.ServiceSettings{SiteURL: &siteURL}}) - - p := &plugin.HelpersImpl{API: api} - - t.Run("Valid asset directory was provided", func(t *testing.T) { - pluginID := "mattermost-1234" - dir := "assets" - wantedURL := "https://mattermost.example.com/mattermost-1234/assets" - gotURL, err := p.GetPluginAssetURL(pluginID, dir) - - assert.Equalf(t, wantedURL, gotURL, "GetPluginAssetURL(%q, %q) got=%q; want=%v", pluginID, dir, gotURL, wantedURL) - assert.NoError(t, err) - }) - - t.Run("Valid asset directory path was provided", func(t *testing.T) { - pluginID := "mattermost-1234" - dirPath := "/mattermost/assets" - wantedURL := "https://mattermost.example.com/mattermost-1234/mattermost/assets" - gotURL, err := p.GetPluginAssetURL(pluginID, dirPath) - - assert.Equalf(t, wantedURL, gotURL, "GetPluginAssetURL(%q, %q) got=%q; want=%q", pluginID, dirPath, gotURL, wantedURL) - assert.NoError(t, err) - }) - - t.Run("Valid pluginID was provided", func(t *testing.T) { - pluginID := "mattermost-1234" - dir := "assets" - wantedURL := "https://mattermost.example.com/mattermost-1234/assets" - gotURL, err := p.GetPluginAssetURL(pluginID, dir) - - assert.Equalf(t, wantedURL, gotURL, "GetPluginAssetURL(%q, %q) got=%q; want=%q", pluginID, dir, gotURL, wantedURL) - assert.NoError(t, err) - }) - - t.Run("Invalid asset directory name was provided", func(t *testing.T) { - pluginID := "mattermost-1234" - dir := "" - want := "" - gotURL, err := p.GetPluginAssetURL(pluginID, dir) - - assert.Emptyf(t, gotURL, "GetPluginAssetURL(%q, %q) got=%s; want=%q", pluginID, dir, gotURL, want) - assert.Error(t, err) - }) - - t.Run("Invalid pluginID was provided", func(t *testing.T) { - pluginID := "" - dir := "assets" - want := "" - gotURL, err := p.GetPluginAssetURL(pluginID, dir) - - assert.Emptyf(t, gotURL, "GetPluginAssetURL(%q, %q) got=%q; want=%q", pluginID, dir, gotURL, want) - assert.Error(t, err) - }) - - siteURL = "" - api.On("GetConfig").Return(&model.Config{ServiceSettings: model.ServiceSettings{SiteURL: &siteURL}}) - - t.Run("Empty SiteURL was configured", func(t *testing.T) { - pluginID := "mattermost-1234" - dir := "assets" - want := "" - gotURL, err := p.GetPluginAssetURL(pluginID, dir) - - assert.Emptyf(t, gotURL, "GetPluginAssetURL(%q, %q) got=%q; want=%q", pluginID, dir, gotURL, want) - assert.Error(t, err) - }) -} diff --git a/plugin/plugintest/example_hello_user_test.go b/plugin/plugintest/example_hello_user_test.go index 06b395d65d..36b57f65b2 100644 --- a/plugin/plugintest/example_hello_user_test.go +++ b/plugin/plugintest/example_hello_user_test.go @@ -45,12 +45,8 @@ func Example() { api.On("GetUser", user.Id).Return(user, nil) defer api.AssertExpectations(t) - helpers := &plugintest.Helpers{} - defer helpers.AssertExpectations(t) - p := &HelloUserPlugin{} p.SetAPI(api) - p.SetHelpers(helpers) w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) diff --git a/plugin/plugintest/helpers.go b/plugin/plugintest/helpers.go deleted file mode 100644 index 0af1321810..0000000000 --- a/plugin/plugintest/helpers.go +++ /dev/null @@ -1,257 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -// Regenerate this file using `make plugin-mocks`. - -package plugintest - -import ( - model "github.com/mattermost/mattermost-server/v6/model" - plugin "github.com/mattermost/mattermost-server/v6/plugin" - mock "github.com/stretchr/testify/mock" -) - -// Helpers is an autogenerated mock type for the Helpers type -type Helpers struct { - mock.Mock -} - -// CheckRequiredServerConfiguration provides a mock function with given fields: req -func (_m *Helpers) CheckRequiredServerConfiguration(req *model.Config) (bool, error) { - ret := _m.Called(req) - - var r0 bool - if rf, ok := ret.Get(0).(func(*model.Config) bool); ok { - r0 = rf(req) - } else { - r0 = ret.Get(0).(bool) - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.Config) error); ok { - r1 = rf(req) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EnsureBot provides a mock function with given fields: bot, options -func (_m *Helpers) EnsureBot(bot *model.Bot, options ...plugin.EnsureBotOption) (string, error) { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, bot) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 string - if rf, ok := ret.Get(0).(func(*model.Bot, ...plugin.EnsureBotOption) string); ok { - r0 = rf(bot, options...) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.Bot, ...plugin.EnsureBotOption) error); ok { - r1 = rf(bot, options...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetPluginAssetURL provides a mock function with given fields: pluginID, asset -func (_m *Helpers) GetPluginAssetURL(pluginID string, asset string) (string, error) { - ret := _m.Called(pluginID, asset) - - var r0 string - if rf, ok := ret.Get(0).(func(string, string) string); ok { - r0 = rf(pluginID, asset) - } else { - r0 = ret.Get(0).(string) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(pluginID, asset) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// InstallPluginFromURL provides a mock function with given fields: downloadURL, replace -func (_m *Helpers) InstallPluginFromURL(downloadURL string, replace bool) (*model.Manifest, error) { - ret := _m.Called(downloadURL, replace) - - var r0 *model.Manifest - if rf, ok := ret.Get(0).(func(string, bool) *model.Manifest); ok { - r0 = rf(downloadURL, replace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Manifest) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, bool) error); ok { - r1 = rf(downloadURL, replace) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// KVCompareAndDeleteJSON provides a mock function with given fields: key, oldValue -func (_m *Helpers) KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) { - ret := _m.Called(key, oldValue) - - var r0 bool - if rf, ok := ret.Get(0).(func(string, interface{}) bool); ok { - r0 = rf(key, oldValue) - } else { - r0 = ret.Get(0).(bool) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, interface{}) error); ok { - r1 = rf(key, oldValue) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// KVCompareAndSetJSON provides a mock function with given fields: key, oldValue, newValue -func (_m *Helpers) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) { - ret := _m.Called(key, oldValue, newValue) - - var r0 bool - if rf, ok := ret.Get(0).(func(string, interface{}, interface{}) bool); ok { - r0 = rf(key, oldValue, newValue) - } else { - r0 = ret.Get(0).(bool) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, interface{}, interface{}) error); ok { - r1 = rf(key, oldValue, newValue) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// KVGetJSON provides a mock function with given fields: key, value -func (_m *Helpers) KVGetJSON(key string, value interface{}) (bool, error) { - ret := _m.Called(key, value) - - var r0 bool - if rf, ok := ret.Get(0).(func(string, interface{}) bool); ok { - r0 = rf(key, value) - } else { - r0 = ret.Get(0).(bool) - } - - var r1 error - if rf, ok := ret.Get(1).(func(string, interface{}) error); ok { - r1 = rf(key, value) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// KVListWithOptions provides a mock function with given fields: options -func (_m *Helpers) KVListWithOptions(options ...plugin.KVListOption) ([]string, error) { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 []string - if rf, ok := ret.Get(0).(func(...plugin.KVListOption) []string); ok { - r0 = rf(options...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(...plugin.KVListOption) error); ok { - r1 = rf(options...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// KVSetJSON provides a mock function with given fields: key, value -func (_m *Helpers) KVSetJSON(key string, value interface{}) error { - ret := _m.Called(key, value) - - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(key, value) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// KVSetWithExpiryJSON provides a mock function with given fields: key, value, expireInSeconds -func (_m *Helpers) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error { - ret := _m.Called(key, value, expireInSeconds) - - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}, int64) error); ok { - r0 = rf(key, value, expireInSeconds) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ShouldProcessMessage provides a mock function with given fields: post, options -func (_m *Helpers) ShouldProcessMessage(post *model.Post, options ...plugin.ShouldProcessMessageOption) (bool, error) { - _va := make([]interface{}, len(options)) - for _i := range options { - _va[_i] = options[_i] - } - var _ca []interface{} - _ca = append(_ca, post) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 bool - if rf, ok := ret.Get(0).(func(*model.Post, ...plugin.ShouldProcessMessageOption) bool); ok { - r0 = rf(post, options...) - } else { - r0 = ret.Get(0).(bool) - } - - var r1 error - if rf, ok := ret.Get(1).(func(*model.Post, ...plugin.ShouldProcessMessageOption) error); ok { - r1 = rf(post, options...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -}