From df6a7f8b19e2381ee57f946d5b184185653b2ee1 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Tue, 15 May 2018 13:33:47 -0700 Subject: MM-10249 Adding plugin ability to intercept posts before they reach the DB. (#8791) * Adding plugin ability to intercept posts before they reach the DB. * s/envoked/invoked/ --- plugin/hooks.go | 33 ++ plugin/pluginenv/environment.go | 59 +++ plugin/plugintest/api.go | 834 +++++++++++++++++++++++++---------- plugin/plugintest/apioverride.go | 18 + plugin/plugintest/hooks.go | 152 +++++-- plugin/plugintest/key_value_store.go | 70 +++ plugin/rpcplugin/api_test.go | 60 +-- plugin/rpcplugin/hooks.go | 106 +++++ plugin/rpcplugin/hooks_test.go | 24 + 9 files changed, 1059 insertions(+), 297 deletions(-) create mode 100644 plugin/plugintest/apioverride.go create mode 100644 plugin/plugintest/key_value_store.go (limited to 'plugin') diff --git a/plugin/hooks.go b/plugin/hooks.go index 814609e8c..e41081e48 100644 --- a/plugin/hooks.go +++ b/plugin/hooks.go @@ -36,4 +36,37 @@ type Hooks interface { // ExecuteCommand executes a command that has been previously registered via the RegisterCommand // API. ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) + + // MessageWillBePosted is invoked when a message is posted by a user before it is commited + // to the database. If you also want to act on edited posts, see MessageWillBeUpdated. + // Return values should be the modified post or nil if rejected and an explanation for the user. + // + // If you don't need to modify or reject posts, use MessageHasBeenPosted instead. + // + // Note that this method will be called for posts created by plugins, including the plugin that + // created the post. + MessageWillBePosted(post *model.Post) (*model.Post, string) + + // MessageWillBeUpdated is invoked when a message is updated by a user before it is commited + // to the database. If you also want to act on new posts, see MessageWillBePosted. + // Return values should be the modified post or nil if rejected and an explanation for the user. + // On rejection, the post will be kept in its previous state. + // + // If you don't need to modify or rejected updated posts, use MessageHasBeenUpdated instead. + // + // Note that this method will be called for posts updated by plugins, including the plugin that + // updated the post. + MessageWillBeUpdated(newPost, oldPost *model.Post) (*model.Post, string) + + // MessageHasBeenPosted is invoked after the message has been commited to the databse. + // If you need to modify or reject the post, see MessageWillBePosted + // Note that this method will be called for posts created by plugins, including the plugin that + // created the post. + MessageHasBeenPosted(post *model.Post) + + // MessageHasBeenUpdated is invoked after a message is updated and has been updated in the databse. + // If you need to modify or reject the post, see MessageWillBeUpdated + // Note that this method will be called for posts created by plugins, including the plugin that + // created the post. + MessageHasBeenUpdated(newPost, oldPost *model.Post) } diff --git a/plugin/pluginenv/environment.go b/plugin/pluginenv/environment.go index adc02e885..947eda86d 100644 --- a/plugin/pluginenv/environment.go +++ b/plugin/pluginenv/environment.go @@ -306,6 +306,65 @@ func (h *MultiPluginHooks) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) } +// MessageWillBePosted invokes the MessageWillBePosted hook for all plugins. Ordering +// is not guaranteed and the next plugin will get the previous one's modifications. +// if a plugin rejects a post, the rest of the plugins will not know that an attempt was made. +// Returns the final result post, or nil if the post was rejected and a string with a reason +// for the user the message was rejected. +func (h *MultiPluginHooks) MessageWillBePosted(post *model.Post) (*model.Post, string) { + h.env.mutex.RLock() + defer h.env.mutex.RUnlock() + + for _, activePlugin := range h.env.activePlugins { + if activePlugin.Supervisor == nil { + continue + } + var rejectionReason string + post, rejectionReason = activePlugin.Supervisor.Hooks().MessageWillBePosted(post) + if post == nil { + return nil, rejectionReason + } + } + return post, "" +} + +// MessageWillBeUpdated invokes the MessageWillBeUpdated hook for all plugins. Ordering +// is not guaranteed and the next plugin will get the previous one's modifications. +// if a plugin rejects a post, the rest of the plugins will not know that an attempt was made. +// Returns the final result post, or nil if the post was rejected and a string with a reason +// for the user the message was rejected. +func (h *MultiPluginHooks) MessageWillBeUpdated(newPost, oldPost *model.Post) (*model.Post, string) { + h.env.mutex.RLock() + defer h.env.mutex.RUnlock() + + post := newPost + for _, activePlugin := range h.env.activePlugins { + if activePlugin.Supervisor == nil { + continue + } + var rejectionReason string + post, rejectionReason = activePlugin.Supervisor.Hooks().MessageWillBeUpdated(post, oldPost) + if post == nil { + return nil, rejectionReason + } + } + return post, "" +} + +func (h *MultiPluginHooks) MessageHasBeenPosted(post *model.Post) { + h.invoke(func(hooks plugin.Hooks) error { + hooks.MessageHasBeenPosted(post) + return nil + }) +} + +func (h *MultiPluginHooks) MessageHasBeenUpdated(newPost, oldPost *model.Post) { + h.invoke(func(hooks plugin.Hooks) error { + hooks.MessageHasBeenUpdated(newPost, oldPost) + return nil + }) +} + func (h *SinglePluginHooks) invoke(f func(plugin.Hooks) error) error { h.env.mutex.RLock() defer h.env.mutex.RUnlock() diff --git a/plugin/plugintest/api.go b/plugin/plugintest/api.go index 8f9f4a604..f1281a5ff 100644 --- a/plugin/plugintest/api.go +++ b/plugin/plugintest/api.go @@ -1,344 +1,702 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. +// Code generated by mockery v1.0.0. DO NOT EDIT. -package plugintest +// Regenerate this file using `make plugin-mocks`. -import ( - "github.com/stretchr/testify/mock" +package plugintest - "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/plugin" -) +import mock "github.com/stretchr/testify/mock" +import model "github.com/mattermost/mattermost-server/model" +import plugin "github.com/mattermost/mattermost-server/plugin" -type API struct { +// APIMOCKINTERNAL is an autogenerated mock type for the APIMOCKINTERNAL type +type APIMOCKINTERNAL struct { mock.Mock - Store *KeyValueStore } -type KeyValueStore struct { - mock.Mock -} +// AddChannelMember provides a mock function with given fields: channelId, userId +func (_m *APIMOCKINTERNAL) AddChannelMember(channelId string, userId string) (*model.ChannelMember, *model.AppError) { + ret := _m.Called(channelId, userId) -var _ plugin.API = (*API)(nil) -var _ plugin.KeyValueStore = (*KeyValueStore)(nil) + var r0 *model.ChannelMember + if rf, ok := ret.Get(0).(func(string, string) *model.ChannelMember); ok { + r0 = rf(channelId, userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.ChannelMember) + } + } -func (m *API) LoadPluginConfiguration(dest interface{}) error { - ret := m.Called(dest) - if f, ok := ret.Get(0).(func(interface{}) error); ok { - return f(dest) + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok { + r1 = rf(channelId, userId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - return ret.Error(0) + + return r0, r1 } -func (m *API) RegisterCommand(command *model.Command) error { - ret := m.Called(command) - if f, ok := ret.Get(0).(func(*model.Command) error); ok { - return f(command) +// CreateChannel provides a mock function with given fields: channel +func (_m *APIMOCKINTERNAL) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { + ret := _m.Called(channel) + + var r0 *model.Channel + if rf, ok := ret.Get(0).(func(*model.Channel) *model.Channel); ok { + r0 = rf(channel) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Channel) + } } - return ret.Error(0) -} -func (m *API) UnregisterCommand(teamId, trigger string) error { - ret := m.Called(teamId, trigger) - if f, ok := ret.Get(0).(func(string, string) error); ok { - return f(teamId, trigger) + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.Channel) *model.AppError); ok { + r1 = rf(channel) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - return ret.Error(0) + + return r0, r1 } -func (m *API) CreateUser(user *model.User) (*model.User, *model.AppError) { - ret := m.Called(user) - if f, ok := ret.Get(0).(func(*model.User) (*model.User, *model.AppError)); ok { - return f(user) +// CreatePost provides a mock function with given fields: post +func (_m *APIMOCKINTERNAL) CreatePost(post *model.Post) (*model.Post, *model.AppError) { + ret := _m.Called(post) + + var r0 *model.Post + if rf, ok := ret.Get(0).(func(*model.Post) *model.Post); ok { + r0 = rf(post) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Post) + } } - userOut, _ := ret.Get(0).(*model.User) - err, _ := ret.Get(1).(*model.AppError) - return userOut, err -} -func (m *API) DeleteUser(userId string) *model.AppError { - ret := m.Called(userId) - if f, ok := ret.Get(0).(func(string) *model.AppError); ok { - return f(userId) + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.Post) *model.AppError); ok { + r1 = rf(post) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - err, _ := ret.Get(0).(*model.AppError) - return err + + return r0, r1 } -func (m *API) GetUser(userId string) (*model.User, *model.AppError) { - ret := m.Called(userId) - if f, ok := ret.Get(0).(func(string) (*model.User, *model.AppError)); ok { - return f(userId) +// CreateTeam provides a mock function with given fields: team +func (_m *APIMOCKINTERNAL) CreateTeam(team *model.Team) (*model.Team, *model.AppError) { + ret := _m.Called(team) + + var r0 *model.Team + if rf, ok := ret.Get(0).(func(*model.Team) *model.Team); ok { + r0 = rf(team) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Team) + } } - user, _ := ret.Get(0).(*model.User) - err, _ := ret.Get(1).(*model.AppError) - return user, err -} -func (m *API) GetUserByEmail(email string) (*model.User, *model.AppError) { - ret := m.Called(email) - if f, ok := ret.Get(0).(func(string) (*model.User, *model.AppError)); ok { - return f(email) + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.Team) *model.AppError); ok { + r1 = rf(team) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - user, _ := ret.Get(0).(*model.User) - err, _ := ret.Get(1).(*model.AppError) - return user, err + + return r0, r1 } -func (m *API) GetUserByUsername(name string) (*model.User, *model.AppError) { - ret := m.Called(name) - if f, ok := ret.Get(0).(func(string) (*model.User, *model.AppError)); ok { - return f(name) +// CreateUser provides a mock function with given fields: user +func (_m *APIMOCKINTERNAL) CreateUser(user *model.User) (*model.User, *model.AppError) { + ret := _m.Called(user) + + var r0 *model.User + if rf, ok := ret.Get(0).(func(*model.User) *model.User); ok { + r0 = rf(user) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.User) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.User) *model.AppError); ok { + r1 = rf(user) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - user, _ := ret.Get(0).(*model.User) - err, _ := ret.Get(1).(*model.AppError) - return user, err + + return r0, r1 } -func (m *API) UpdateUser(user *model.User) (*model.User, *model.AppError) { - ret := m.Called(user) - if f, ok := ret.Get(0).(func(*model.User) (*model.User, *model.AppError)); ok { - return f(user) +// DeleteChannel provides a mock function with given fields: channelId +func (_m *APIMOCKINTERNAL) DeleteChannel(channelId string) *model.AppError { + ret := _m.Called(channelId) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string) *model.AppError); ok { + r0 = rf(channelId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } } - userOut, _ := ret.Get(0).(*model.User) - err, _ := ret.Get(1).(*model.AppError) - return userOut, err + + return r0 } -func (m *API) CreateTeam(team *model.Team) (*model.Team, *model.AppError) { - ret := m.Called(team) - if f, ok := ret.Get(0).(func(*model.Team) (*model.Team, *model.AppError)); ok { - return f(team) +// DeleteChannelMember provides a mock function with given fields: channelId, userId +func (_m *APIMOCKINTERNAL) DeleteChannelMember(channelId string, userId string) *model.AppError { + ret := _m.Called(channelId, userId) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string, string) *model.AppError); ok { + r0 = rf(channelId, userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } } - teamOut, _ := ret.Get(0).(*model.Team) - err, _ := ret.Get(1).(*model.AppError) - return teamOut, err + + return r0 } -func (m *API) DeleteTeam(teamId string) *model.AppError { - ret := m.Called(teamId) - if f, ok := ret.Get(0).(func(string) *model.AppError); ok { - return f(teamId) +// DeletePost provides a mock function with given fields: postId +func (_m *APIMOCKINTERNAL) DeletePost(postId string) *model.AppError { + ret := _m.Called(postId) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string) *model.AppError); ok { + r0 = rf(postId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } } - err, _ := ret.Get(0).(*model.AppError) - return err + + return r0 } -func (m *API) GetTeam(teamId string) (*model.Team, *model.AppError) { - ret := m.Called(teamId) - if f, ok := ret.Get(0).(func(string) (*model.Team, *model.AppError)); ok { - return f(teamId) +// DeleteTeam provides a mock function with given fields: teamId +func (_m *APIMOCKINTERNAL) DeleteTeam(teamId string) *model.AppError { + ret := _m.Called(teamId) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string) *model.AppError); ok { + r0 = rf(teamId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } } - team, _ := ret.Get(0).(*model.Team) - err, _ := ret.Get(1).(*model.AppError) - return team, err + + return r0 } -func (m *API) GetTeamByName(name string) (*model.Team, *model.AppError) { - ret := m.Called(name) - if f, ok := ret.Get(0).(func(string) (*model.Team, *model.AppError)); ok { - return f(name) +// DeleteUser provides a mock function with given fields: userId +func (_m *APIMOCKINTERNAL) DeleteUser(userId string) *model.AppError { + ret := _m.Called(userId) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string) *model.AppError); ok { + r0 = rf(userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } } - team, _ := ret.Get(0).(*model.Team) - err, _ := ret.Get(1).(*model.AppError) - return team, err + + return r0 } -func (m *API) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { - ret := m.Called(team) - if f, ok := ret.Get(0).(func(*model.Team) (*model.Team, *model.AppError)); ok { - return f(team) +// GetChannel provides a mock function with given fields: channelId +func (_m *APIMOCKINTERNAL) GetChannel(channelId string) (*model.Channel, *model.AppError) { + ret := _m.Called(channelId) + + var r0 *model.Channel + if rf, ok := ret.Get(0).(func(string) *model.Channel); ok { + r0 = rf(channelId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Channel) + } } - teamOut, _ := ret.Get(0).(*model.Team) - err, _ := ret.Get(1).(*model.AppError) - return teamOut, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(channelId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { - ret := m.Called(channel) - if f, ok := ret.Get(0).(func(*model.Channel) (*model.Channel, *model.AppError)); ok { - return f(channel) +// GetChannelByName provides a mock function with given fields: name, teamId +func (_m *APIMOCKINTERNAL) GetChannelByName(name string, teamId string) (*model.Channel, *model.AppError) { + ret := _m.Called(name, teamId) + + var r0 *model.Channel + if rf, ok := ret.Get(0).(func(string, string) *model.Channel); ok { + r0 = rf(name, teamId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Channel) + } } - channelOut, _ := ret.Get(0).(*model.Channel) - err, _ := ret.Get(1).(*model.AppError) - return channelOut, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok { + r1 = rf(name, teamId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) DeleteChannel(channelId string) *model.AppError { - ret := m.Called(channelId) - if f, ok := ret.Get(0).(func(string) *model.AppError); ok { - return f(channelId) +// GetChannelMember provides a mock function with given fields: channelId, userId +func (_m *APIMOCKINTERNAL) GetChannelMember(channelId string, userId string) (*model.ChannelMember, *model.AppError) { + ret := _m.Called(channelId, userId) + + var r0 *model.ChannelMember + if rf, ok := ret.Get(0).(func(string, string) *model.ChannelMember); ok { + r0 = rf(channelId, userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.ChannelMember) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok { + r1 = rf(channelId, userId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - err, _ := ret.Get(0).(*model.AppError) - return err + + return r0, r1 } -func (m *API) GetChannel(channelId string) (*model.Channel, *model.AppError) { - ret := m.Called(channelId) - if f, ok := ret.Get(0).(func(string) (*model.Channel, *model.AppError)); ok { - return f(channelId) +// GetDirectChannel provides a mock function with given fields: userId1, userId2 +func (_m *APIMOCKINTERNAL) GetDirectChannel(userId1 string, userId2 string) (*model.Channel, *model.AppError) { + ret := _m.Called(userId1, userId2) + + var r0 *model.Channel + if rf, ok := ret.Get(0).(func(string, string) *model.Channel); ok { + r0 = rf(userId1, userId2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Channel) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok { + r1 = rf(userId1, userId2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - channel, _ := ret.Get(0).(*model.Channel) - err, _ := ret.Get(1).(*model.AppError) - return channel, err + + return r0, r1 } -func (m *API) GetChannelByName(name, teamId string) (*model.Channel, *model.AppError) { - ret := m.Called(name, teamId) - if f, ok := ret.Get(0).(func(_, _ string) (*model.Channel, *model.AppError)); ok { - return f(name, teamId) +// GetGroupChannel provides a mock function with given fields: userIds +func (_m *APIMOCKINTERNAL) GetGroupChannel(userIds []string) (*model.Channel, *model.AppError) { + ret := _m.Called(userIds) + + var r0 *model.Channel + if rf, ok := ret.Get(0).(func([]string) *model.Channel); ok { + r0 = rf(userIds) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Channel) + } } - channel, _ := ret.Get(0).(*model.Channel) - err, _ := ret.Get(1).(*model.AppError) - return channel, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func([]string) *model.AppError); ok { + r1 = rf(userIds) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) { - ret := m.Called(userId1, userId2) - if f, ok := ret.Get(0).(func(_, _ string) (*model.Channel, *model.AppError)); ok { - return f(userId1, userId2) +// GetPost provides a mock function with given fields: postId +func (_m *APIMOCKINTERNAL) GetPost(postId string) (*model.Post, *model.AppError) { + ret := _m.Called(postId) + + var r0 *model.Post + if rf, ok := ret.Get(0).(func(string) *model.Post); ok { + r0 = rf(postId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Post) + } } - channel, _ := ret.Get(0).(*model.Channel) - err, _ := ret.Get(1).(*model.AppError) - return channel, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(postId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) GetGroupChannel(userIds []string) (*model.Channel, *model.AppError) { - ret := m.Called(userIds) - if f, ok := ret.Get(0).(func([]string) (*model.Channel, *model.AppError)); ok { - return f(userIds) +// GetTeam provides a mock function with given fields: teamId +func (_m *APIMOCKINTERNAL) GetTeam(teamId string) (*model.Team, *model.AppError) { + ret := _m.Called(teamId) + + var r0 *model.Team + if rf, ok := ret.Get(0).(func(string) *model.Team); ok { + r0 = rf(teamId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Team) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(teamId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - channel, _ := ret.Get(0).(*model.Channel) - err, _ := ret.Get(1).(*model.AppError) - return channel, err + + return r0, r1 } -func (m *API) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { - ret := m.Called(channel) - if f, ok := ret.Get(0).(func(*model.Channel) (*model.Channel, *model.AppError)); ok { - return f(channel) +// GetTeamByName provides a mock function with given fields: name +func (_m *APIMOCKINTERNAL) GetTeamByName(name string) (*model.Team, *model.AppError) { + ret := _m.Called(name) + + var r0 *model.Team + if rf, ok := ret.Get(0).(func(string) *model.Team); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Team) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(name) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - channelOut, _ := ret.Get(0).(*model.Channel) - err, _ := ret.Get(1).(*model.AppError) - return channelOut, err + + return r0, r1 } -func (m *API) AddChannelMember(channelId, userId string) (*model.ChannelMember, *model.AppError) { - ret := m.Called(channelId, userId) - if f, ok := ret.Get(0).(func(_, _ string) (*model.ChannelMember, *model.AppError)); ok { - return f(channelId, userId) +// GetUser provides a mock function with given fields: userId +func (_m *APIMOCKINTERNAL) GetUser(userId string) (*model.User, *model.AppError) { + ret := _m.Called(userId) + + var r0 *model.User + if rf, ok := ret.Get(0).(func(string) *model.User); ok { + r0 = rf(userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.User) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(userId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - member, _ := ret.Get(0).(*model.ChannelMember) - err, _ := ret.Get(1).(*model.AppError) - return member, err + + return r0, r1 } -func (m *API) GetChannelMember(channelId, userId string) (*model.ChannelMember, *model.AppError) { - ret := m.Called(channelId, userId) - if f, ok := ret.Get(0).(func(_, _ string) (*model.ChannelMember, *model.AppError)); ok { - return f(channelId, userId) +// GetUserByEmail provides a mock function with given fields: email +func (_m *APIMOCKINTERNAL) GetUserByEmail(email string) (*model.User, *model.AppError) { + ret := _m.Called(email) + + var r0 *model.User + if rf, ok := ret.Get(0).(func(string) *model.User); ok { + r0 = rf(email) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.User) + } } - member, _ := ret.Get(0).(*model.ChannelMember) - err, _ := ret.Get(1).(*model.AppError) - return member, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(email) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) UpdateChannelMemberRoles(channelId, userId, newRoles string) (*model.ChannelMember, *model.AppError) { - ret := m.Called(channelId, userId, newRoles) - if f, ok := ret.Get(0).(func(_, _, _ string) (*model.ChannelMember, *model.AppError)); ok { - return f(channelId, userId, newRoles) +// GetUserByUsername provides a mock function with given fields: name +func (_m *APIMOCKINTERNAL) GetUserByUsername(name string) (*model.User, *model.AppError) { + ret := _m.Called(name) + + var r0 *model.User + if rf, ok := ret.Get(0).(func(string) *model.User); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.User) + } } - member, _ := ret.Get(0).(*model.ChannelMember) - err, _ := ret.Get(1).(*model.AppError) - return member, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(name) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) UpdateChannelMemberNotifications(channelId, userId string, notifications map[string]string) (*model.ChannelMember, *model.AppError) { - ret := m.Called(channelId, userId, notifications) - if f, ok := ret.Get(0).(func(_, _ string, _ map[string]string) (*model.ChannelMember, *model.AppError)); ok { - return f(channelId, userId, notifications) +// KeyValueStore provides a mock function with given fields: +func (_m *APIMOCKINTERNAL) KeyValueStore() plugin.KeyValueStore { + ret := _m.Called() + + var r0 plugin.KeyValueStore + if rf, ok := ret.Get(0).(func() plugin.KeyValueStore); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(plugin.KeyValueStore) + } } - member, _ := ret.Get(0).(*model.ChannelMember) - err, _ := ret.Get(1).(*model.AppError) - return member, err + + return r0 } -func (m *API) DeleteChannelMember(channelId, userId string) *model.AppError { - ret := m.Called(channelId, userId) - if f, ok := ret.Get(0).(func(_, _ string) *model.AppError); ok { - return f(channelId, userId) +// LoadPluginConfiguration provides a mock function with given fields: dest +func (_m *APIMOCKINTERNAL) LoadPluginConfiguration(dest interface{}) error { + ret := _m.Called(dest) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(dest) + } else { + r0 = ret.Error(0) } - err, _ := ret.Get(0).(*model.AppError) - return err + + return r0 } -func (m *API) CreatePost(post *model.Post) (*model.Post, *model.AppError) { - ret := m.Called(post) - if f, ok := ret.Get(0).(func(*model.Post) (*model.Post, *model.AppError)); ok { - return f(post) +// RegisterCommand provides a mock function with given fields: command +func (_m *APIMOCKINTERNAL) RegisterCommand(command *model.Command) error { + ret := _m.Called(command) + + var r0 error + if rf, ok := ret.Get(0).(func(*model.Command) error); ok { + r0 = rf(command) + } else { + r0 = ret.Error(0) } - postOut, _ := ret.Get(0).(*model.Post) - err, _ := ret.Get(1).(*model.AppError) - return postOut, err + + return r0 } -func (m *API) DeletePost(postId string) *model.AppError { - ret := m.Called(postId) - if f, ok := ret.Get(0).(func(string) *model.AppError); ok { - return f(postId) +// UnregisterCommand provides a mock function with given fields: teamId, trigger +func (_m *APIMOCKINTERNAL) UnregisterCommand(teamId string, trigger string) error { + ret := _m.Called(teamId, trigger) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(teamId, trigger) + } else { + r0 = ret.Error(0) } - err, _ := ret.Get(0).(*model.AppError) - return err + + return r0 } -func (m *API) GetPost(postId string) (*model.Post, *model.AppError) { - ret := m.Called(postId) - if f, ok := ret.Get(0).(func(string) (*model.Post, *model.AppError)); ok { - return f(postId) +// UpdateChannel provides a mock function with given fields: channel +func (_m *APIMOCKINTERNAL) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { + ret := _m.Called(channel) + + var r0 *model.Channel + if rf, ok := ret.Get(0).(func(*model.Channel) *model.Channel); ok { + r0 = rf(channel) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Channel) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.Channel) *model.AppError); ok { + r1 = rf(channel) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - post, _ := ret.Get(0).(*model.Post) - err, _ := ret.Get(1).(*model.AppError) - return post, err + + return r0, r1 } -func (m *API) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { - ret := m.Called(post) - if f, ok := ret.Get(0).(func(*model.Post) (*model.Post, *model.AppError)); ok { - return f(post) +// UpdateChannelMemberNotifications provides a mock function with given fields: channelId, userId, notifications +func (_m *APIMOCKINTERNAL) UpdateChannelMemberNotifications(channelId string, userId string, notifications map[string]string) (*model.ChannelMember, *model.AppError) { + ret := _m.Called(channelId, userId, notifications) + + var r0 *model.ChannelMember + if rf, ok := ret.Get(0).(func(string, string, map[string]string) *model.ChannelMember); ok { + r0 = rf(channelId, userId, notifications) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.ChannelMember) + } } - postOut, _ := ret.Get(0).(*model.Post) - err, _ := ret.Get(1).(*model.AppError) - return postOut, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string, map[string]string) *model.AppError); ok { + r1 = rf(channelId, userId, notifications) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *API) KeyValueStore() plugin.KeyValueStore { - return m.Store +// UpdateChannelMemberRoles provides a mock function with given fields: channelId, userId, newRoles +func (_m *APIMOCKINTERNAL) UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) { + ret := _m.Called(channelId, userId, newRoles) + + var r0 *model.ChannelMember + if rf, ok := ret.Get(0).(func(string, string, string) *model.ChannelMember); ok { + r0 = rf(channelId, userId, newRoles) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.ChannelMember) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string, string, string) *model.AppError); ok { + r1 = rf(channelId, userId, newRoles) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *KeyValueStore) Set(key string, value []byte) *model.AppError { - ret := m.Called(key, value) - if f, ok := ret.Get(0).(func(string, []byte) *model.AppError); ok { - return f(key, value) +// UpdatePost provides a mock function with given fields: post +func (_m *APIMOCKINTERNAL) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { + ret := _m.Called(post) + + var r0 *model.Post + if rf, ok := ret.Get(0).(func(*model.Post) *model.Post); ok { + r0 = rf(post) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Post) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.Post) *model.AppError); ok { + r1 = rf(post) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } } - err, _ := ret.Get(0).(*model.AppError) - return err + + return r0, r1 } -func (m *KeyValueStore) Get(key string) ([]byte, *model.AppError) { - ret := m.Called(key) - if f, ok := ret.Get(0).(func(string) ([]byte, *model.AppError)); ok { - return f(key) +// UpdateTeam provides a mock function with given fields: team +func (_m *APIMOCKINTERNAL) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { + ret := _m.Called(team) + + var r0 *model.Team + if rf, ok := ret.Get(0).(func(*model.Team) *model.Team); ok { + r0 = rf(team) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Team) + } } - psv, _ := ret.Get(0).([]byte) - err, _ := ret.Get(1).(*model.AppError) - return psv, err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.Team) *model.AppError); ok { + r1 = rf(team) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } -func (m *KeyValueStore) Delete(key string) *model.AppError { - ret := m.Called(key) - if f, ok := ret.Get(0).(func(string) *model.AppError); ok { - return f(key) +// UpdateUser provides a mock function with given fields: user +func (_m *APIMOCKINTERNAL) UpdateUser(user *model.User) (*model.User, *model.AppError) { + ret := _m.Called(user) + + var r0 *model.User + if rf, ok := ret.Get(0).(func(*model.User) *model.User); ok { + r0 = rf(user) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.User) + } } - err, _ := ret.Get(0).(*model.AppError) - return err + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.User) *model.AppError); ok { + r1 = rf(user) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } diff --git a/plugin/plugintest/apioverride.go b/plugin/plugintest/apioverride.go new file mode 100644 index 000000000..54cfe27bc --- /dev/null +++ b/plugin/plugintest/apioverride.go @@ -0,0 +1,18 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package plugintest + +import "github.com/mattermost/mattermost-server/plugin" + +type API struct { + APIMOCKINTERNAL + Store *KeyValueStore +} + +var _ plugin.API = (*API)(nil) +var _ plugin.KeyValueStore = (*KeyValueStore)(nil) + +func (m *API) KeyValueStore() plugin.KeyValueStore { + return m.Store +} diff --git a/plugin/plugintest/hooks.go b/plugin/plugintest/hooks.go index 9ea11a9fb..3de257c76 100644 --- a/plugin/plugintest/hooks.go +++ b/plugin/plugintest/hooks.go @@ -1,49 +1,143 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. +// Code generated by mockery v1.0.0. DO NOT EDIT. -package plugintest - -import ( - "net/http" +// Regenerate this file using `make plugin-mocks`. - "github.com/stretchr/testify/mock" +package plugintest - "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/plugin" -) +import http "net/http" +import mock "github.com/stretchr/testify/mock" +import model "github.com/mattermost/mattermost-server/model" +import plugin "github.com/mattermost/mattermost-server/plugin" +// Hooks is an autogenerated mock type for the Hooks type type Hooks struct { mock.Mock } -var _ plugin.Hooks = (*Hooks)(nil) +// ExecuteCommand provides a mock function with given fields: args +func (_m *Hooks) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { + ret := _m.Called(args) + + var r0 *model.CommandResponse + if rf, ok := ret.Get(0).(func(*model.CommandArgs) *model.CommandResponse); ok { + r0 = rf(args) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.CommandResponse) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(*model.CommandArgs) *model.AppError); ok { + r1 = rf(args) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + +// MessageHasBeenPosted provides a mock function with given fields: post +func (_m *Hooks) MessageHasBeenPosted(post *model.Post) { + _m.Called(post) +} + +// MessageHasBeenUpdated provides a mock function with given fields: newPost, oldPost +func (_m *Hooks) MessageHasBeenUpdated(newPost *model.Post, oldPost *model.Post) { + _m.Called(newPost, oldPost) +} + +// MessageWillBePosted provides a mock function with given fields: post +func (_m *Hooks) MessageWillBePosted(post *model.Post) (*model.Post, string) { + ret := _m.Called(post) + + var r0 *model.Post + if rf, ok := ret.Get(0).(func(*model.Post) *model.Post); ok { + r0 = rf(post) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Post) + } + } -func (m *Hooks) OnActivate(api plugin.API) error { - ret := m.Called(api) - if f, ok := ret.Get(0).(func(plugin.API) error); ok { - return f(api) + var r1 string + if rf, ok := ret.Get(1).(func(*model.Post) string); ok { + r1 = rf(post) + } else { + r1 = ret.Get(1).(string) } - return ret.Error(0) + + return r0, r1 } -func (m *Hooks) OnDeactivate() error { - return m.Called().Error(0) +// MessageWillBeUpdated provides a mock function with given fields: newPost, oldPost +func (_m *Hooks) MessageWillBeUpdated(newPost *model.Post, oldPost *model.Post) (*model.Post, string) { + ret := _m.Called(newPost, oldPost) + + var r0 *model.Post + if rf, ok := ret.Get(0).(func(*model.Post, *model.Post) *model.Post); ok { + r0 = rf(newPost, oldPost) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Post) + } + } + + var r1 string + if rf, ok := ret.Get(1).(func(*model.Post, *model.Post) string); ok { + r1 = rf(newPost, oldPost) + } else { + r1 = ret.Get(1).(string) + } + + return r0, r1 } -func (m *Hooks) OnConfigurationChange() error { - return m.Called().Error(0) +// OnActivate provides a mock function with given fields: _a0 +func (_m *Hooks) OnActivate(_a0 plugin.API) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(plugin.API) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (m *Hooks) ServeHTTP(w http.ResponseWriter, r *http.Request) { - m.Called(w, r) +// OnConfigurationChange provides a mock function with given fields: +func (_m *Hooks) OnConfigurationChange() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 } -func (m *Hooks) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { - ret := m.Called(args) - if f, ok := ret.Get(0).(func(*model.CommandArgs) (*model.CommandResponse, *model.AppError)); ok { - return f(args) +// OnDeactivate provides a mock function with given fields: +func (_m *Hooks) OnDeactivate() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) } - resp, _ := ret.Get(0).(*model.CommandResponse) - err, _ := ret.Get(1).(*model.AppError) - return resp, err + + return r0 +} + +// ServeHTTP provides a mock function with given fields: _a0, _a1 +func (_m *Hooks) ServeHTTP(_a0 http.ResponseWriter, _a1 *http.Request) { + _m.Called(_a0, _a1) } diff --git a/plugin/plugintest/key_value_store.go b/plugin/plugintest/key_value_store.go new file mode 100644 index 000000000..30d60d708 --- /dev/null +++ b/plugin/plugintest/key_value_store.go @@ -0,0 +1,70 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +// Regenerate this file using `make plugin-mocks`. + +package plugintest + +import mock "github.com/stretchr/testify/mock" +import model "github.com/mattermost/mattermost-server/model" + +// KeyValueStore is an autogenerated mock type for the KeyValueStore type +type KeyValueStore struct { + mock.Mock +} + +// Delete provides a mock function with given fields: key +func (_m *KeyValueStore) Delete(key string) *model.AppError { + ret := _m.Called(key) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string) *model.AppError); ok { + r0 = rf(key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } + } + + return r0 +} + +// Get provides a mock function with given fields: key +func (_m *KeyValueStore) Get(key string) ([]byte, *model.AppError) { + ret := _m.Called(key) + + var r0 []byte + if rf, ok := ret.Get(0).(func(string) []byte); ok { + r0 = rf(key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + var r1 *model.AppError + if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { + r1 = rf(key) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 +} + +// Set provides a mock function with given fields: key, value +func (_m *KeyValueStore) Set(key string, value []byte) *model.AppError { + ret := _m.Called(key, value) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string, []byte) *model.AppError); ok { + r0 = rf(key, value) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } + } + + return r0 +} diff --git a/plugin/rpcplugin/api_test.go b/plugin/rpcplugin/api_test.go index d7b3733ea..04d8e5d86 100644 --- a/plugin/rpcplugin/api_test.go +++ b/plugin/rpcplugin/api_test.go @@ -90,10 +90,10 @@ func TestAPI(t *testing.T) { api.On("UnregisterCommand", "team", "trigger").Return(nil).Once() assert.NoError(t, remote.UnregisterCommand("team", "trigger")) - api.On("CreateChannel", mock.AnythingOfType("*model.Channel")).Return(func(c *model.Channel) (*model.Channel, *model.AppError) { + api.On("CreateChannel", mock.AnythingOfType("*model.Channel")).Return(func(c *model.Channel) *model.Channel { c.Id = "thechannelid" - return c, nil - }).Once() + return c + }, nil).Once() channel, err := remote.CreateChannel(testChannel) assert.Equal(t, "thechannelid", channel.Id) assert.Nil(t, err) @@ -121,9 +121,9 @@ func TestAPI(t *testing.T) { assert.Equal(t, testChannel, channel) assert.Nil(t, err) - api.On("UpdateChannel", mock.AnythingOfType("*model.Channel")).Return(func(c *model.Channel) (*model.Channel, *model.AppError) { - return c, nil - }).Once() + api.On("UpdateChannel", mock.AnythingOfType("*model.Channel")).Return(func(c *model.Channel) *model.Channel { + return c + }, nil).Once() channel, err = remote.UpdateChannel(testChannel) assert.Equal(t, testChannel, channel) assert.Nil(t, err) @@ -154,10 +154,10 @@ func TestAPI(t *testing.T) { err = remote.DeleteChannelMember("thechannelid", "theuserid") assert.Nil(t, err) - api.On("CreateUser", mock.AnythingOfType("*model.User")).Return(func(u *model.User) (*model.User, *model.AppError) { + api.On("CreateUser", mock.AnythingOfType("*model.User")).Return(func(u *model.User) *model.User { u.Id = "theuserid" - return u, nil - }).Once() + return u + }, nil).Once() user, err := remote.CreateUser(testUser) assert.Equal(t, "theuserid", user.Id) assert.Nil(t, err) @@ -180,17 +180,17 @@ func TestAPI(t *testing.T) { assert.Equal(t, testUser, user) assert.Nil(t, err) - api.On("UpdateUser", mock.AnythingOfType("*model.User")).Return(func(u *model.User) (*model.User, *model.AppError) { - return u, nil - }).Once() + api.On("UpdateUser", mock.AnythingOfType("*model.User")).Return(func(u *model.User) *model.User { + return u + }, nil).Once() user, err = remote.UpdateUser(testUser) assert.Equal(t, testUser, user) assert.Nil(t, err) - api.On("CreateTeam", mock.AnythingOfType("*model.Team")).Return(func(t *model.Team) (*model.Team, *model.AppError) { + api.On("CreateTeam", mock.AnythingOfType("*model.Team")).Return(func(t *model.Team) *model.Team { t.Id = "theteamid" - return t, nil - }).Once() + return t + }, nil).Once() team, err := remote.CreateTeam(testTeam) assert.Equal(t, "theteamid", team.Id) assert.Nil(t, err) @@ -213,17 +213,17 @@ func TestAPI(t *testing.T) { assert.Nil(t, team) assert.Equal(t, teamNotFoundError, err) - api.On("UpdateTeam", mock.AnythingOfType("*model.Team")).Return(func(t *model.Team) (*model.Team, *model.AppError) { - return t, nil - }).Once() + api.On("UpdateTeam", mock.AnythingOfType("*model.Team")).Return(func(t *model.Team) *model.Team { + return t + }, nil).Once() team, err = remote.UpdateTeam(testTeam) assert.Equal(t, testTeam, team) assert.Nil(t, err) - api.On("CreatePost", mock.AnythingOfType("*model.Post")).Return(func(p *model.Post) (*model.Post, *model.AppError) { + api.On("CreatePost", mock.AnythingOfType("*model.Post")).Return(func(p *model.Post) *model.Post { p.Id = "thepostid" - return p, nil - }).Once() + return p + }, nil).Once() post, err := remote.CreatePost(testPost) require.Nil(t, err) assert.NotEmpty(t, post.Id) @@ -237,9 +237,9 @@ func TestAPI(t *testing.T) { assert.Equal(t, testPost, post) assert.Nil(t, err) - api.On("UpdatePost", mock.AnythingOfType("*model.Post")).Return(func(p *model.Post) (*model.Post, *model.AppError) { - return p, nil - }).Once() + api.On("UpdatePost", mock.AnythingOfType("*model.Post")).Return(func(p *model.Post) *model.Post { + return p + }, nil).Once() post, err = remote.UpdatePost(testPost) assert.Equal(t, testPost, post) assert.Nil(t, err) @@ -248,9 +248,9 @@ func TestAPI(t *testing.T) { err = remote.KeyValueStore().Set("thekey", []byte("thevalue")) assert.Nil(t, err) - api.KeyValueStore().(*plugintest.KeyValueStore).On("Get", "thekey").Return(func(key string) ([]byte, *model.AppError) { - return []byte("thevalue"), nil - }).Once() + api.KeyValueStore().(*plugintest.KeyValueStore).On("Get", "thekey").Return(func(key string) []byte { + return []byte("thevalue") + }, nil).Once() ret, err := remote.KeyValueStore().Get("thekey") assert.Nil(t, err) assert.Equal(t, []byte("thevalue"), ret) @@ -267,10 +267,10 @@ func TestAPI_GobRegistration(t *testing.T) { defer api.AssertExpectations(t) testAPIRPC(&api, func(remote plugin.API) { - api.On("CreatePost", mock.AnythingOfType("*model.Post")).Return(func(p *model.Post) (*model.Post, *model.AppError) { + api.On("CreatePost", mock.AnythingOfType("*model.Post")).Return(func(p *model.Post) *model.Post { p.Id = "thepostid" - return p, nil - }).Once() + return p + }, nil).Once() _, err := remote.CreatePost(&model.Post{ Message: "hello", Props: map[string]interface{}{ diff --git a/plugin/rpcplugin/hooks.go b/plugin/rpcplugin/hooks.go index 90734fd1c..6af98873a 100644 --- a/plugin/rpcplugin/hooks.go +++ b/plugin/rpcplugin/hooks.go @@ -141,6 +141,52 @@ func (h *LocalHooks) ExecuteCommand(args *model.CommandArgs, reply *HooksExecute return nil } +type MessageWillBeReply struct { + Post *model.Post + RejectionReason string +} + +type MessageUpdatedArgs struct { + NewPost *model.Post + OldPost *model.Post +} + +func (h *LocalHooks) MessageWillBePosted(args *model.Post, reply *MessageWillBeReply) error { + if hook, ok := h.hooks.(interface { + MessageWillBePosted(*model.Post) (*model.Post, string) + }); ok { + reply.Post, reply.RejectionReason = hook.MessageWillBePosted(args) + } + return nil +} + +func (h *LocalHooks) MessageWillBeUpdated(args *MessageUpdatedArgs, reply *MessageWillBeReply) error { + if hook, ok := h.hooks.(interface { + MessageWillBeUpdated(*model.Post, *model.Post) (*model.Post, string) + }); ok { + reply.Post, reply.RejectionReason = hook.MessageWillBeUpdated(args.NewPost, args.OldPost) + } + return nil +} + +func (h *LocalHooks) MessageHasBeenPosted(args *model.Post, reply *struct{}) error { + if hook, ok := h.hooks.(interface { + MessageHasBeenPosted(*model.Post) + }); ok { + hook.MessageHasBeenPosted(args) + } + return nil +} + +func (h *LocalHooks) MessageHasBeenUpdated(args *MessageUpdatedArgs, reply *struct{}) error { + if hook, ok := h.hooks.(interface { + MessageHasBeenUpdated(*model.Post, *model.Post) + }); ok { + hook.MessageHasBeenUpdated(args.NewPost, args.OldPost) + } + return nil +} + func ServeHooks(hooks interface{}, conn io.ReadWriteCloser, muxer *Muxer) { server := rpc.NewServer() server.Register(&LocalHooks{ @@ -158,6 +204,10 @@ const ( remoteServeHTTP = 2 remoteOnConfigurationChange = 3 remoteExecuteCommand = 4 + remoteMessageWillBePosted = 5 + remoteMessageWillBeUpdated = 6 + remoteMessageHasBeenPosted = 7 + remoteMessageHasBeenUpdated = 8 maxRemoteHookCount = iota ) @@ -255,6 +305,54 @@ func (h *RemoteHooks) ExecuteCommand(args *model.CommandArgs) (*model.CommandRes return reply.Response, reply.Error } +func (h *RemoteHooks) MessageWillBePosted(args *model.Post) (*model.Post, string) { + if !h.implemented[remoteMessageWillBePosted] { + return args, "" + } + var reply MessageWillBeReply + if err := h.client.Call("LocalHooks.MessageWillBePosted", args, &reply); err != nil { + return nil, "" + } + return reply.Post, reply.RejectionReason +} + +func (h *RemoteHooks) MessageWillBeUpdated(newPost, oldPost *model.Post) (*model.Post, string) { + if !h.implemented[remoteMessageWillBeUpdated] { + return newPost, "" + } + var reply MessageWillBeReply + args := &MessageUpdatedArgs{ + NewPost: newPost, + OldPost: oldPost, + } + if err := h.client.Call("LocalHooks.MessageWillBeUpdated", args, &reply); err != nil { + return nil, "" + } + return reply.Post, reply.RejectionReason +} + +func (h *RemoteHooks) MessageHasBeenPosted(args *model.Post) { + if !h.implemented[remoteMessageHasBeenPosted] { + return + } + if err := h.client.Call("LocalHooks.MessageHasBeenPosted", args, nil); err != nil { + return + } +} + +func (h *RemoteHooks) MessageHasBeenUpdated(newPost, oldPost *model.Post) { + if !h.implemented[remoteMessageHasBeenUpdated] { + return + } + args := &MessageUpdatedArgs{ + NewPost: newPost, + OldPost: oldPost, + } + if err := h.client.Call("LocalHooks.MessageHasBeenUpdated", args, nil); err != nil { + return + } +} + func (h *RemoteHooks) Close() error { if h.apiCloser != nil { h.apiCloser.Close() @@ -286,6 +384,14 @@ func ConnectHooks(conn io.ReadWriteCloser, muxer *Muxer, pluginId string) (*Remo remote.implemented[remoteServeHTTP] = true case "ExecuteCommand": remote.implemented[remoteExecuteCommand] = true + case "MessageWillBePosted": + remote.implemented[remoteMessageWillBePosted] = true + case "MessageWillBeUpdated": + remote.implemented[remoteMessageWillBeUpdated] = true + case "MessageHasBeenPosted": + remote.implemented[remoteMessageHasBeenPosted] = true + case "MessageHasBeenUpdated": + remote.implemented[remoteMessageHasBeenUpdated] = true } } return remote, nil diff --git a/plugin/rpcplugin/hooks_test.go b/plugin/rpcplugin/hooks_test.go index c404442b7..a7bac982e 100644 --- a/plugin/rpcplugin/hooks_test.go +++ b/plugin/rpcplugin/hooks_test.go @@ -91,6 +91,30 @@ func TestHooks(t *testing.T) { }) assert.Equal(t, "bar", commandResponse.Text) assert.Nil(t, appErr) + + hooks.On("MessageWillBePosted", mock.AnythingOfType("*model.Post")).Return(func(post *model.Post) *model.Post { + post.Message += "_testing" + return post + }, "changemessage") + post, changemessage := remote.MessageWillBePosted(&model.Post{Id: "1", Message: "base"}) + assert.Equal(t, "changemessage", changemessage) + assert.Equal(t, "base_testing", post.Message) + assert.Equal(t, "1", post.Id) + + hooks.On("MessageWillBeUpdated", mock.AnythingOfType("*model.Post"), mock.AnythingOfType("*model.Post")).Return(func(newPost, oldPost *model.Post) *model.Post { + newPost.Message += "_testing" + return newPost + }, "changemessage2") + post2, changemessage2 := remote.MessageWillBeUpdated(&model.Post{Id: "2", Message: "base2"}, &model.Post{Id: "OLD", Message: "OLDMESSAGE"}) + assert.Equal(t, "changemessage2", changemessage2) + assert.Equal(t, "base2_testing", post2.Message) + assert.Equal(t, "2", post2.Id) + + hooks.On("MessageHasBeenPosted", mock.AnythingOfType("*model.Post")).Return(nil) + remote.MessageHasBeenPosted(&model.Post{}) + + hooks.On("MessageHasBeenUpdated", mock.AnythingOfType("*model.Post"), mock.AnythingOfType("*model.Post")).Return(nil) + remote.MessageHasBeenUpdated(&model.Post{}, &model.Post{}) })) } -- cgit v1.2.3-1-g7c22