From 19a26d02749d1feec3b811facb337051ce7ed459 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Sun, 15 Jul 2018 20:57:37 -0400 Subject: [PATCH] MM-11024 Migrate to use plugin v2 system coming in Mattermost 5.2 (#1) * Migrate to use plugin v2 system coming in Mattermost 5.2 * Update glide --- glide.lock | 101 +++++++++++++++++++++++++++++++++++++++++++++---- main.go | 4 +- plugin.go | 41 +++++--------------- plugin.yaml | 2 +- plugin_test.go | 44 +++++++++------------ 5 files changed, 125 insertions(+), 67 deletions(-) diff --git a/glide.lock b/glide.lock index 4709627a4..f51561261 100644 --- a/glide.lock +++ b/glide.lock @@ -1,23 +1,39 @@ hash: 5f70014e098292babafccc5c2f975cefb83c54c19ccd09b99c986e46dd4e88fc -updated: 2017-12-05T17:37:05.367648-06:00 +updated: 2018-07-15T20:52:37.714560347-04:00 imports: -- name: github.com/alecthomas/log4go - version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c - repo: https://github.com/mattermost/log4go.git - name: github.com/davecgh/go-spew version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 subpackages: - spew +- name: github.com/golang/protobuf + version: 14aad3d5ea4c323bcd7a2137e735da24a76e814c + subpackages: + - proto + - ptypes + - ptypes/any + - ptypes/duration + - ptypes/timestamp - name: github.com/gorilla/websocket version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b +- name: github.com/hashicorp/go-hclog + version: ff2cf002a8dd750586d91dddd4470c341f981fe1 +- name: github.com/hashicorp/go-plugin + version: e8d22c780116115ae5624720c9af0c97afe4f551 +- name: github.com/hashicorp/yamux + version: 3520598351bb3500a49ae9563f5539666ae0a27c - name: github.com/mattermost/mattermost-server - version: eb027c0d3bcd473db17ef6f9c96545b9406b3636 + version: 62c64594ccaa0e634023b358758f2a6bf04164ad subpackages: - model - plugin + - plugin/rpcplugin - plugin/plugintest - plugin/plugintest/mock - - plugin/rpcplugin + - mlog + - utils/jsonutils + - utils/markdown +- name: github.com/mitchellh/go-testing-interface + version: a61a99592b77c9ba629d254a693acffaeb4b7e28 - name: github.com/nicksnyder/go-i18n version: 0dc1626d56435e9d605a29875701721c54bc9bbd subpackages: @@ -25,6 +41,8 @@ imports: - i18n/bundle - i18n/language - i18n/translation +- name: github.com/oklog/run + version: 6934b124db28979da51d3470dadfa34d73d72652 - name: github.com/pborman/uuid version: e790cca94e6cc75c7064b1332e63811d4aae1a53 - name: github.com/pelletier/go-toml @@ -41,13 +59,82 @@ imports: version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 subpackages: - assert - - mock - require + - mock +- name: go.uber.org/atomic + version: 1ea20fb1cbb1cc08cbd0d913a96dead89aa18289 +- name: go.uber.org/multierr + version: 3c4937480c32f4c13a875a1829af76c98ca3d40a +- name: go.uber.org/zap + version: 7e7e266a8dbce911a49554b945538c5b950196b8 + subpackages: + - zapcore + - internal/bufferpool + - buffer + - internal/color + - internal/exit - name: golang.org/x/crypto version: 94eea52f7b742c7cbe0b03b22f0c4c8631ece122 subpackages: - bcrypt - blowfish +- name: golang.org/x/net + version: d0887baf81f4598189d4e12a37c6da86f0bba4d0 + subpackages: + - context + - http2 + - trace + - http/httpguts + - http2/hpack + - idna + - internal/timeseries +- name: golang.org/x/sys + version: ac767d655b305d4e9612f5f6e33120b9176c4ad4 + subpackages: + - unix +- name: golang.org/x/text + version: 0605a8320aceb4207a5fb3521281e17ec2075476 + subpackages: + - secure/bidirule + - unicode/bidi + - unicode/norm + - transform +- name: google.golang.org/genproto + version: e92b116572682a5b432ddd840aeaba2a559eeff1 + subpackages: + - googleapis/rpc/status +- name: google.golang.org/grpc + version: ce6ee6b031cb9e88a81e8d4d502d5b3eafb27f98 + subpackages: + - credentials + - health + - health/grpc_health_v1 + - balancer + - balancer/roundrobin + - codes + - connectivity + - encoding + - encoding/proto + - grpclog + - internal + - internal/backoff + - internal/channelz + - internal/envconfig + - internal/grpcrand + - internal/transport + - keepalive + - metadata + - naming + - peer + - resolver + - resolver/dns + - resolver/passthrough + - stats + - status + - tap + - balancer/base +- name: gopkg.in/natefinch/lumberjack.v2 + version: a96e63847dc3c67d17befa69c303767e2f84e54f - name: gopkg.in/yaml.v2 version: 287cf08546ab5e7e37d55a84f7ed3fd1db036de5 testImports: [] diff --git a/main.go b/main.go index cd8ff1aa7..69cc896ed 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,9 @@ package main import ( - "github.com/mattermost/mattermost-server/plugin/rpcplugin" + "github.com/mattermost/mattermost-server/plugin" ) func main() { - rpcplugin.Main(&Plugin{}) + plugin.ClientMain(&Plugin{}) } diff --git a/plugin.go b/plugin.go index 6ca66bd17..7034709ca 100644 --- a/plugin.go +++ b/plugin.go @@ -4,42 +4,21 @@ import ( "crypto/subtle" "encoding/json" "net/http" - "sync/atomic" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/plugin" ) -type Configuration struct { +type Plugin struct { + plugin.MattermostPlugin + Enabled bool Secret string UserName string } -type Plugin struct { - api plugin.API - configuration atomic.Value -} - -func (p *Plugin) OnActivate(api plugin.API) error { - p.api = api - return p.OnConfigurationChange() -} - -func (p *Plugin) config() *Configuration { - return p.configuration.Load().(*Configuration) -} - -func (p *Plugin) OnConfigurationChange() error { - var configuration Configuration - err := p.api.LoadPluginConfiguration(&configuration) - p.configuration.Store(&configuration) - return err -} - -func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { - config := p.config() - if !config.Enabled || config.Secret == "" || config.UserName == "" { +func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { + if !p.Enabled || p.Secret == "" || p.UserName == "" { http.Error(w, "This plugin is not configured.", http.StatusForbidden) return } else if r.URL.Path != "/webhook" { @@ -48,7 +27,7 @@ func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { } else if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return - } else if subtle.ConstantTimeCompare([]byte(r.URL.Query().Get("secret")), []byte(config.Secret)) != 1 { + } else if subtle.ConstantTimeCompare([]byte(r.URL.Query().Get("secret")), []byte(p.Secret)) != 1 { http.Error(w, "You must provide the configured secret.", http.StatusForbidden) return } @@ -63,13 +42,13 @@ func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } else if r.URL.Query().Get("channel") == "" { http.Error(w, "You must provide a channel.", http.StatusBadRequest) - } else if user, err := p.api.GetUserByUsername(config.UserName); err != nil { + } else if user, err := p.API.GetUserByUsername(p.UserName); err != nil { http.Error(w, err.Message, err.StatusCode) - } else if team, err := p.api.GetTeamByName(r.URL.Query().Get("team")); err != nil { + } else if team, err := p.API.GetTeamByName(r.URL.Query().Get("team")); err != nil { http.Error(w, err.Message, err.StatusCode) - } else if channel, err := p.api.GetChannelByName(r.URL.Query().Get("channel"), team.Id); err != nil { + } else if channel, err := p.API.GetChannelByName(r.URL.Query().Get("channel"), team.Id); err != nil { http.Error(w, err.Message, err.StatusCode) - } else if _, err := p.api.CreatePost(&model.Post{ + } else if _, err := p.API.CreatePost(&model.Post{ ChannelId: channel.Id, Type: model.POST_SLACK_ATTACHMENT, UserId: user.Id, diff --git a/plugin.yaml b/plugin.yaml index 5be6bdf9b..8a863afc4 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -3,7 +3,7 @@ backend: executable: plugin.exe name: JIRA description: Receives webhook events from JIRA and makes Mattermost posts for them. -version: '0.1.2' +version: '1.0.0' settings_schema: settings: - key: Enabled diff --git a/plugin_test.go b/plugin_test.go index 21eb4ce60..4ea9bb6c0 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -3,7 +3,6 @@ package main import ( "bytes" "encoding/json" - "fmt" "io" "io/ioutil" "net/http" @@ -15,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/plugin" "github.com/mattermost/mattermost-server/plugin/plugintest" "github.com/mattermost/mattermost-server/plugin/plugintest/mock" ) @@ -27,40 +27,38 @@ func validRequestBody() io.ReadCloser { } } +type TestConfiguration struct { + Enabled bool + Secret string + UserName string +} + func TestPlugin(t *testing.T) { f, err := os.Open("testdata/webhook_issue_created.json") require.NoError(t, err) defer f.Close() var webhook Webhook require.NoError(t, json.NewDecoder(f).Decode(&webhook)) - expectedAttachment, err := webhook.SlackAttachment() - require.NoError(t, err) - validConfiguration := Configuration{ + validConfiguration := TestConfiguration{ Enabled: true, Secret: "thesecret", UserName: "theuser", } for name, tc := range map[string]struct { - Configuration Configuration - ConfigurationError error + Configuration TestConfiguration Request *http.Request CreatePostError *model.AppError ExpectedStatusCode int }{ "NoConfiguration": { - Configuration: Configuration{}, - Request: httptest.NewRequest("POST", "/webhook?team=theteam&channel=thechannel&secret=thesecret", validRequestBody()), - ExpectedStatusCode: http.StatusForbidden, - }, - "ConfigurationError": { - ConfigurationError: fmt.Errorf("foo"), + Configuration: TestConfiguration{}, Request: httptest.NewRequest("POST", "/webhook?team=theteam&channel=thechannel&secret=thesecret", validRequestBody()), ExpectedStatusCode: http.StatusForbidden, }, "NoUserConfiguration": { - Configuration: Configuration{ + Configuration: TestConfiguration{ Enabled: true, Secret: "thesecret", }, @@ -98,7 +96,7 @@ func TestPlugin(t *testing.T) { ExpectedStatusCode: http.StatusBadRequest, }, "InvalidUser": { - Configuration: Configuration{ + Configuration: TestConfiguration{ Enabled: true, Secret: "thesecret", UserName: "nottheuser", @@ -131,11 +129,6 @@ func TestPlugin(t *testing.T) { t.Run(name, func(t *testing.T) { api := &plugintest.API{} - api.On("LoadPluginConfiguration", mock.AnythingOfType("*main.Configuration")).Return(func(dest interface{}) error { - *dest.(*Configuration) = tc.Configuration - return tc.ConfigurationError - }) - api.On("GetUserByUsername", "theuser").Return(&model.User{ Id: "theuserid", }, (*model.AppError)(nil)) @@ -147,11 +140,7 @@ func TestPlugin(t *testing.T) { api.On("GetTeamByName", "nottheteam").Return((*model.Team)(nil), model.NewAppError("foo", "bar", nil, "", http.StatusBadRequest)) api.On("GetChannelByName", "thechannel", "theteamid").Run(func(args mock.Arguments) { - api.On("CreatePost", mock.AnythingOfType("*model.Post")).Return(func(post *model.Post) (*model.Post, *model.AppError) { - assert.Equal(t, post.ChannelId, "thechannelid") - assert.Equal(t, post.Props["attachments"], []*model.SlackAttachment{expectedAttachment}) - return &model.Post{}, tc.CreatePostError - }) + api.On("CreatePost", mock.AnythingOfType("*model.Post")).Return(&model.Post{}, tc.CreatePostError) }).Return(&model.Channel{ Id: "thechannelid", TeamId: "theteamid", @@ -159,10 +148,13 @@ func TestPlugin(t *testing.T) { api.On("GetChannelByName", "notthechannel", "theteamid").Return((*model.Channel)(nil), model.NewAppError("foo", "bar", nil, "", http.StatusBadRequest)) p := Plugin{} - p.OnActivate(api) + p.Enabled = tc.Configuration.Enabled + p.Secret = tc.Configuration.Secret + p.UserName = tc.Configuration.UserName + p.SetAPI(api) w := httptest.NewRecorder() - p.ServeHTTP(w, tc.Request) + p.ServeHTTP(&plugin.Context{}, w, tc.Request) assert.Equal(t, tc.ExpectedStatusCode, w.Result().StatusCode) }) }