From 4c17bdff1bb871fb31520b7b547f584c53ed854f Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Dec 2017 13:55:41 -0600 Subject: Add plugin slash command support (#7941) * add plugin slash command support * remove unused string * rebase --- plugin/pluginenv/environment.go | 64 +++++++++++++++++++++++++++++++----- plugin/pluginenv/environment_test.go | 48 +++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 9 deletions(-) (limited to 'plugin/pluginenv') diff --git a/plugin/pluginenv/environment.go b/plugin/pluginenv/environment.go index 26511c651..f53021f74 100644 --- a/plugin/pluginenv/environment.go +++ b/plugin/pluginenv/environment.go @@ -223,35 +223,59 @@ func (env *Environment) Shutdown() (errs []error) { return } -type EnvironmentHooks struct { +type MultiPluginHooks struct { env *Environment } -func (env *Environment) Hooks() *EnvironmentHooks { - return &EnvironmentHooks{env} +type SinglePluginHooks struct { + env *Environment + pluginId string } -// OnConfigurationChange invokes the OnConfigurationChange hook for all plugins. Any errors -// encountered will be returned. -func (h *EnvironmentHooks) OnConfigurationChange() (errs []error) { +func (env *Environment) Hooks() *MultiPluginHooks { + return &MultiPluginHooks{ + env: env, + } +} + +func (env *Environment) HooksForPlugin(id string) *SinglePluginHooks { + return &SinglePluginHooks{ + env: env, + pluginId: id, + } +} + +func (h *MultiPluginHooks) invoke(f func(plugin.Hooks) error) (errs []error) { h.env.mutex.RLock() defer h.env.mutex.RUnlock() + for _, activePlugin := range h.env.activePlugins { if activePlugin.Supervisor == nil { continue } - if err := activePlugin.Supervisor.Hooks().OnConfigurationChange(); err != nil { - errs = append(errs, errors.Wrapf(err, "OnConfigurationChange error for %v", activePlugin.BundleInfo.Manifest.Id)) + if err := f(activePlugin.Supervisor.Hooks()); err != nil { + errs = append(errs, errors.Wrapf(err, "hook error for %v", activePlugin.BundleInfo.Manifest.Id)) } } return } +// OnConfigurationChange invokes the OnConfigurationChange hook for all plugins. Any errors +// encountered will be returned. +func (h *MultiPluginHooks) OnConfigurationChange() []error { + return h.invoke(func(hooks plugin.Hooks) error { + if err := hooks.OnConfigurationChange(); err != nil { + return errors.Wrapf(err, "error calling OnConfigurationChange hook") + } + return nil + }) +} + // ServeHTTP invokes the ServeHTTP hook for the plugin identified by the request or responds with a // 404 not found. // // It expects the request's context to have a plugin_id set. -func (h *EnvironmentHooks) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *MultiPluginHooks) ServeHTTP(w http.ResponseWriter, r *http.Request) { if id := r.Context().Value("plugin_id"); id != nil { if idstr, ok := id.(string); ok { h.env.mutex.RLock() @@ -264,3 +288,25 @@ func (h *EnvironmentHooks) ServeHTTP(w http.ResponseWriter, r *http.Request) { } http.NotFound(w, r) } + +func (h *SinglePluginHooks) invoke(f func(plugin.Hooks) error) error { + h.env.mutex.RLock() + defer h.env.mutex.RUnlock() + + if activePlugin, ok := h.env.activePlugins[h.pluginId]; ok && activePlugin.Supervisor != nil { + if err := f(activePlugin.Supervisor.Hooks()); err != nil { + return errors.Wrapf(err, "hook error for plugin: %v", activePlugin.BundleInfo.Manifest.Id) + } + return nil + } + return fmt.Errorf("unable to invoke hook for plugin: %v", h.pluginId) +} + +// ExecuteCommand invokes the ExecuteCommand hook for the plugin. +func (h *SinglePluginHooks) ExecuteCommand(args *model.CommandArgs) (resp *model.CommandResponse, appErr *model.AppError, err error) { + err = h.invoke(func(hooks plugin.Hooks) error { + resp, appErr = hooks.ExecuteCommand(args) + return nil + }) + return +} diff --git a/plugin/pluginenv/environment_test.go b/plugin/pluginenv/environment_test.go index 988e5b08f..2a52b3830 100644 --- a/plugin/pluginenv/environment_test.go +++ b/plugin/pluginenv/environment_test.go @@ -355,3 +355,51 @@ func TestEnvironment_ConcurrentHookInvocations(t *testing.T) { wg.Wait() } + +func TestEnvironment_HooksForPlugins(t *testing.T) { + dir := initTmpDir(t, map[string]string{ + "foo/plugin.json": `{"id": "foo", "backend": {}}`, + }) + defer os.RemoveAll(dir) + + var provider MockProvider + defer provider.AssertExpectations(t) + + env, err := New( + SearchPath(dir), + APIProvider(provider.API), + SupervisorProvider(provider.Supervisor), + ) + require.NoError(t, err) + defer env.Shutdown() + + var api struct{ plugin.API } + var supervisor MockSupervisor + defer supervisor.AssertExpectations(t) + var hooks plugintest.Hooks + defer hooks.AssertExpectations(t) + + provider.On("API").Return(&api, nil) + provider.On("Supervisor").Return(&supervisor, nil) + + supervisor.On("Start", &api).Return(nil) + supervisor.On("Stop").Return(nil) + supervisor.On("Hooks").Return(&hooks) + + hooks.On("OnDeactivate").Return(nil) + hooks.On("ExecuteCommand", mock.AnythingOfType("*model.CommandArgs")).Return(&model.CommandResponse{ + Text: "bar", + }, nil) + + assert.NoError(t, env.ActivatePlugin("foo")) + assert.Equal(t, env.ActivePluginIds(), []string{"foo"}) + + resp, appErr, err := env.HooksForPlugin("foo").ExecuteCommand(&model.CommandArgs{ + Command: "/foo", + }) + assert.Equal(t, "bar", resp.Text) + assert.Nil(t, appErr) + assert.NoError(t, err) + + assert.Empty(t, env.Shutdown()) +} -- cgit v1.2.3-1-g7c22