Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MM-36587: Enable development #29

Merged
merged 3 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ module github.com/mattermost/mattermost-plugin-channel-export
go 1.14

require (
github.com/golang/mock v1.4.3
github.com/gorilla/mux v1.7.4
github.com/mattermost/mattermost-plugin-api v0.0.11
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20200723144633-ed34468996e6
github.com/go-gorp/gorp v2.2.0+incompatible // indirect
github.com/golang/mock v1.4.4
github.com/gorilla/mux v1.8.0
github.com/mattermost/mattermost-plugin-api v0.0.16-0.20210617150024-a576c5d40298
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210514083559-0bf7aed02e2c
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.6.0
github.com/stretchr/testify v1.6.1
github.com/poy/onpar v1.0.0 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
)
914 changes: 786 additions & 128 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (h *Handler) hasPermissionToChannel(userID, channelID string) (*model.Chann
// Export handles /api/v1/export, exporting the requested channel.
func (h *Handler) Export(w http.ResponseWriter, r *http.Request) {
license := h.client.System.GetLicense()
if !isLicensed(license) {
if !isLicensed(license, h.client) {
handleError(w, http.StatusBadRequest, "the channel export plugin requires a valid E20 license.")
return
}
Expand Down
82 changes: 72 additions & 10 deletions server/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestHandler(t *testing.T) {
require.EqualError(t, err, "failed with status code 401")
})

t.Run("missing e20 license", func(t *testing.T) {
t.Run("missing e20 license and no Testing nor Developer modes enabled", func(t *testing.T) {
mockCtrl := gomock.NewController(t)

mockChannel := mock_pluginapi.NewMockChannel(mockCtrl)
Expand All @@ -66,12 +66,66 @@ func TestHandler(t *testing.T) {
client := NewClient(address)
client.SetToken("token")

mockSystem.EXPECT().GetLicense().Return(nil)
mockSystem.EXPECT().GetLicense().Return(nil).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{
ServiceSettings: model.ServiceSettings{
EnableTesting: &falseValue,
EnableDeveloper: &falseValue,
},
}).Times(1)

err := client.ExportChannel(ioutil.Discard, "channel_id", FormatCSV)
require.EqualError(t, err, "the channel export plugin requires a valid E20 license.")
})

t.Run("missing e20 license with Testing and Developer modes enabled", func(t *testing.T) {
mockCtrl := gomock.NewController(t)

mockChannel := mock_pluginapi.NewMockChannel(mockCtrl)
mockFile := mock_pluginapi.NewMockFile(mockCtrl)
mockLog := mock_pluginapi.NewMockLog(mockCtrl)
mockPost := mock_pluginapi.NewMockPost(mockCtrl)
mockSlashCommand := mock_pluginapi.NewMockSlashCommand(mockCtrl)
mockUser := mock_pluginapi.NewMockUser(mockCtrl)
mockSystem := mock_pluginapi.NewMockSystem(mockCtrl)
mockConfiguration := mock_pluginapi.NewMockConfiguration(mockCtrl)

mockAPI := pluginapi.CustomWrapper(mockChannel, mockFile, mockLog, mockPost, mockSlashCommand, mockUser, mockSystem, mockConfiguration)

now := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.FixedZone("UTC-8", -8*60*60))
userID := "user_id"
channelID := "channel_id"
address := setupAPI(t, mockAPI, now, userID, channelID)
client := NewClient(address)
client.SetToken("token")

mockSystem.EXPECT().GetLicense().Return(nil).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{
ServiceSettings: model.ServiceSettings{
EnableTesting: &trueValue,
EnableDeveloper: &trueValue,
},
}).Times(1)
mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1)
mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PERMISSION_READ_CHANNEL).Return(true).Times(1)
mockUser.EXPECT().HasPermissionTo(userID, model.PERMISSION_MANAGE_SYSTEM).Return(false).Times(1)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{
PrivacySettings: model.PrivacySettings{
ShowEmailAddress: &trueValue,
},
})

var buffer bytes.Buffer
err := client.ExportChannel(&buffer, channelID, FormatCSV)
require.NoError(t, err)

expected := `Post Creation Time,User Id,User Email,User Type,User Name,Post Id,Parent Post Id,Post Message,Post Type
2009-11-11 07:00:00 +0000 UTC,post_user_id,post_user_email,user,post_user_nickname,post_id,post_parent_id,post_message,message
`

require.Equal(t, expected, buffer.String())
})

t.Run("missing channel_id", func(t *testing.T) {
mockCtrl := gomock.NewController(t)

Expand All @@ -92,7 +146,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)

err := client.ExportChannel(ioutil.Discard, "", FormatCSV)
require.EqualError(t, err, "missing channel_id parameter")
Expand All @@ -118,7 +173,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)

err := client.ExportChannel(ioutil.Discard, "channel_id", "")
require.EqualError(t, err, "missing format parameter")
Expand All @@ -144,7 +200,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)

err := client.ExportChannel(ioutil.Discard, "channel_id", "pdf2")
require.EqualError(t, err, "unsupported format parameter 'pdf2'")
Expand All @@ -171,7 +228,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get(channelID).Return(nil, &model.AppError{StatusCode: http.StatusNotFound}).Times(1)

err := client.ExportChannel(ioutil.Discard, channelID, FormatCSV)
Expand Down Expand Up @@ -199,7 +257,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get(channelID).Return(nil, &model.AppError{StatusCode: http.StatusInternalServerError}).Times(1)

err := client.ExportChannel(ioutil.Discard, channelID, FormatCSV)
Expand Down Expand Up @@ -228,7 +287,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1)
mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PERMISSION_READ_CHANNEL).Return(false).Times(1)

Expand Down Expand Up @@ -259,7 +319,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1)
mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PERMISSION_READ_CHANNEL).Return(true).Times(1)
mockUser.EXPECT().HasPermissionTo(userID, model.PERMISSION_MANAGE_SYSTEM).Return(false).Times(1)
Expand Down Expand Up @@ -303,7 +364,8 @@ func TestHandler(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1)
mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PERMISSION_READ_CHANNEL).Return(true).Times(1)
mockUser.EXPECT().HasPermissionTo(userID, model.PERMISSION_MANAGE_SYSTEM).Return(false).Times(1)
Expand Down
2 changes: 1 addition & 1 deletion server/command_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo

func (p *Plugin) executeCommandExport(args *model.CommandArgs) *model.CommandResponse {
license := p.client.System.GetLicense()
if !isLicensed(license) {
if !isLicensed(license, p.client) {
return &model.CommandResponse{
ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL,
Text: "The channel export plugin requires a valid E20 license.",
Expand Down
86 changes: 80 additions & 6 deletions server/command_hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func setupPlugin(t *testing.T, mockAPI *pluginapi.Wrapper, now time.Time) (*Plug

func TestExecuteCommand(t *testing.T) {
trueValue := true
falseValue := false

t.Run("unexpected trigger", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
Expand All @@ -52,7 +53,7 @@ func TestExecuteCommand(t *testing.T) {
assert.Equal(t, "Unknown command: /unknown", commandResponse.Text)
})

t.Run("missing e20 license", func(t *testing.T) {
t.Run("missing e20 license and no Testing nor Developer modes enabled", func(t *testing.T) {
mockCtrl := gomock.NewController(t)

mockChannel := mock_pluginapi.NewMockChannel(mockCtrl)
Expand All @@ -68,7 +69,13 @@ func TestExecuteCommand(t *testing.T) {

plugin, pluginContext := setupPlugin(t, mockAPI, time.Now())

mockSystem.EXPECT().GetLicense().Return(nil)
mockSystem.EXPECT().GetLicense().Return(nil).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{
ServiceSettings: model.ServiceSettings{
EnableTesting: &falseValue,
EnableDeveloper: &falseValue,
},
}).Times(1)

commandResponse, appError := plugin.ExecuteCommand(pluginContext, &model.CommandArgs{
Command: "/export",
Expand All @@ -79,6 +86,69 @@ func TestExecuteCommand(t *testing.T) {
assert.Equal(t, "The channel export plugin requires a valid E20 license.", commandResponse.Text)
})

t.Run("missing e20 license with Testing and Developer modes enabled", func(t *testing.T) {
mockCtrl := gomock.NewController(t)

mockChannel := mock_pluginapi.NewMockChannel(mockCtrl)
mockFile := mock_pluginapi.NewMockFile(mockCtrl)
mockLog := mock_pluginapi.NewMockLog(mockCtrl)
mockPost := mock_pluginapi.NewMockPost(mockCtrl)
mockSlashCommand := mock_pluginapi.NewMockSlashCommand(mockCtrl)
mockUser := mock_pluginapi.NewMockUser(mockCtrl)
mockSystem := mock_pluginapi.NewMockSystem(mockCtrl)
mockConfiguration := mock_pluginapi.NewMockConfiguration(mockCtrl)

mockAPI := pluginapi.CustomWrapper(mockChannel, mockFile, mockLog, mockPost, mockSlashCommand, mockUser, mockSystem, mockConfiguration)

now := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.FixedZone("UTC-8", -8*60*60))
plugin, pluginContext := setupPlugin(t, mockAPI, now)

mockSystem.EXPECT().GetLicense().Return(nil).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{
ServiceSettings: model.ServiceSettings{
EnableTesting: &trueValue,
EnableDeveloper: &trueValue,
},
}).Times(1)
mockChannel.EXPECT().Get("channel_id").Return(&model.Channel{Id: "channel_id", Name: "channel_name"}, nil)
mockChannel.EXPECT().GetDirect("user_id", "bot_id").Return(&model.Channel{Id: "direct"}, nil)
mockUser.EXPECT().HasPermissionTo("user_id", model.PERMISSION_MANAGE_SYSTEM).Return(false).Times(1)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{
PrivacySettings: model.PrivacySettings{
ShowEmailAddress: &trueValue,
},
})
mockFile.EXPECT().Upload(gomock.Any(), "channel_name.csv", "direct").Do(func(reader io.Reader, fileName, channelID string) {
contents, err := ioutil.ReadAll(reader)
require.NoError(t, err)
expected := `Post Creation Time,User Id,User Email,User Type,User Name,Post Id,Parent Post Id,Post Message,Post Type
2009-11-11 07:00:00 +0000 UTC,post_user_id,post_user_email,user,post_user_nickname,post_id,post_parent_id,post_message,message
`

require.Equal(t, expected, string(contents))
}).Return(&model.FileInfo{Id: "file_id"}, nil)
mockPost.EXPECT().CreatePost(&model.Post{
UserId: "bot_id",
ChannelId: "direct",
Message: "Channel ~channel_name exported:",
FileIds: []string{"file_id"},
})

commandResponse, appError := plugin.ExecuteCommand(pluginContext, &model.CommandArgs{
Command: "/export",
ChannelId: "channel_id",
UserId: "user_id",
})

require.Nil(t, appError)
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, commandResponse.ResponseType)
assert.Equal(t, "Exporting ~channel_name. @channelexport will send you a direct message when the export is ready.", commandResponse.Text)

// Export runs asynchronuosly, so give time for that to occur and complete above
// mock assertions.
time.Sleep(1 * time.Second)
})

t.Run("failed channel fetch", func(t *testing.T) {
mockCtrl := gomock.NewController(t)

Expand All @@ -97,7 +167,8 @@ func TestExecuteCommand(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get("channel_id").Return(nil, errors.New("failed"))
mockLog.EXPECT().Error("unable to retrieve the channel to export", "Channel ID", "channel_id", "Error", gomock.Any())

Expand Down Expand Up @@ -129,7 +200,8 @@ func TestExecuteCommand(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get("channel_id").Return(&model.Channel{Id: "channel_id"}, nil)
mockChannel.EXPECT().GetDirect("user_id", "bot_id").Return(nil, errors.New("failed"))
mockLog.EXPECT().Error("unable to create a direct message channel between the bot and the user", "Bot ID", "bot_id", "User ID", "user_id", "Error", gomock.Any())
Expand Down Expand Up @@ -164,7 +236,8 @@ func TestExecuteCommand(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get("channel_id").Return(&model.Channel{Id: "channel_id", Name: "channel_name"}, nil)
mockChannel.EXPECT().GetDirect("user_id", "bot_id").Return(&model.Channel{Id: "direct"}, nil)
mockUser.EXPECT().HasPermissionTo("user_id", model.PERMISSION_MANAGE_SYSTEM).Return(false).Times(1)
Expand Down Expand Up @@ -219,7 +292,8 @@ func TestExecuteCommand(t *testing.T) {

mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{
FutureFeatures: &trueValue,
}})
}}).Times(2)
mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1)
mockChannel.EXPECT().Get("channel_id").Return(&model.Channel{Id: "channel_id", Name: "channel_name"}, nil)
mockChannel.EXPECT().GetDirect("user_id", "bot_id").Return(&model.Channel{Id: "direct"}, nil)
mockUser.EXPECT().HasPermissionTo("user_id", model.PERMISSION_MANAGE_SYSTEM).Return(false).Times(1)
Expand Down
23 changes: 5 additions & 18 deletions server/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,11 @@ package main

import (
"github.com/mattermost/mattermost-server/v5/model"
)

func isLicensed(license *model.License) bool {
if license == nil {
return false
}

if license.Features == nil {
return false
}

if license.Features.FutureFeatures == nil {
return false
}

if !*license.Features.FutureFeatures {
return false
}
originalapi "github.com/mattermost/mattermost-plugin-api"
"github.com/mattermost/mattermost-plugin-channel-export/server/pluginapi"
)

return true
func isLicensed(license *model.License, api *pluginapi.Wrapper) bool {
return originalapi.IsE20LicensedOrDevelopment(api.Configuration.GetConfig(), api.System.GetLicense())
}
Loading