[MM-2307] Move plugin helpers from mattermost-server repository into mattermost-plugin-api (#17870)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
28137b3048
Коммит
bf1f60d309
1
Makefile
1
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.
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ type checkFn func(pkgPath string) (result, error)
|
||||
|
||||
var checks = []checkFn{
|
||||
checkAPIVersionComments,
|
||||
checkHelpersVersionComments,
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
64
plugin/driver.go
Обычный файл
64
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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 <username> --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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Ссылка в новой задаче
Block a user