[MM-48029] notify admin for plugin install from work templates (#22007)
Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Julien Tant <julien@craftyx.fr>
Этот коммит содержится в:
@@ -573,7 +573,7 @@ type AppIface interface {
|
||||
FillInChannelsProps(c request.CTX, channelList model.ChannelList) *model.AppError
|
||||
FilterUsersByVisible(viewer *model.User, otherUsers []*model.User) ([]*model.User, *model.AppError)
|
||||
FindTeamByName(name string) bool
|
||||
FinishSendAdminNotifyPost(trial bool, now int64)
|
||||
FinishSendAdminNotifyPost(trial bool, now int64, pluginBasedData map[string][]*model.NotifyAdminData)
|
||||
GenerateMfaSecret(userID string) (*model.MfaSecret, *model.AppError)
|
||||
GeneratePublicLink(siteURL string, info *model.FileInfo) string
|
||||
GenerateSupportPacket() []model.FileData
|
||||
@@ -1167,7 +1167,7 @@ type AppIface interface {
|
||||
UpsertGroupMember(groupID string, userID string) (*model.GroupMember, *model.AppError)
|
||||
UpsertGroupMembers(groupID string, userIDs []string) ([]*model.GroupMember, *model.AppError)
|
||||
UpsertGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, *model.AppError)
|
||||
UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostPaidFeature) bool
|
||||
UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostFeature) bool
|
||||
UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError)
|
||||
UserIsFirstAdmin(user *model.User) bool
|
||||
VerifyEmailFromToken(c request.CTX, userSuppliedTokenString string) *model.AppError
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
@@ -19,14 +20,16 @@ import (
|
||||
|
||||
const lastTrialNotificationTimeStamp = "LAST_TRIAL_NOTIFICATION_TIMESTAMP"
|
||||
const lastUpgradeNotificationTimeStamp = "LAST_UPGRADE_NOTIFICATION_TIMESTAMP"
|
||||
const defaultNotifyAdminCoolOffDays = 14
|
||||
const defaultNotifyAdminCoolOffDays = 0.0104166667 // this is a temp change
|
||||
|
||||
func (a *App) SaveAdminNotification(userId string, notifyData *model.NotifyAdminToUpgradeRequest) *model.AppError {
|
||||
requiredFeature := notifyData.RequiredFeature
|
||||
requiredPlan := notifyData.RequiredPlan
|
||||
trial := notifyData.TrialNotification
|
||||
|
||||
if a.UserAlreadyNotifiedOnRequiredFeature(userId, requiredFeature) {
|
||||
isUserAlreadyNotified := a.UserAlreadyNotifiedOnRequiredFeature(userId, requiredFeature)
|
||||
mlog.Info("SaveAdminNotification")
|
||||
if isUserAlreadyNotified {
|
||||
return model.NewAppError("app.SaveAdminNotification", "api.cloud.notify_admin_to_upgrade_error.already_notified", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
@@ -36,6 +39,7 @@ func (a *App) SaveAdminNotification(userId string, notifyData *model.NotifyAdmin
|
||||
RequiredFeature: requiredFeature,
|
||||
Trial: trial,
|
||||
})
|
||||
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
@@ -57,7 +61,9 @@ func (a *App) DoCheckForAdminNotifications(trial bool) *model.AppError {
|
||||
}
|
||||
|
||||
func (a *App) SaveAdminNotifyData(data *model.NotifyAdminData) (*model.NotifyAdminData, *model.AppError) {
|
||||
mlog.Info("Trying to save NotifyAdmin data")
|
||||
d, err := a.Srv().Store().NotifyAdmin().Save(data)
|
||||
mlog.Info("NotifyAdmin data saved")
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
@@ -67,6 +73,7 @@ func (a *App) SaveAdminNotifyData(data *model.NotifyAdminData) (*model.NotifyAdm
|
||||
return nil, model.NewAppError("SaveAdminNotifyData", "app.notify_admin.save.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
mlog.Info("return from SaveAdminNotifyData")
|
||||
|
||||
return d, nil
|
||||
}
|
||||
@@ -81,6 +88,8 @@ func filterNotificationData(data []*model.NotifyAdminData, test func(*model.Noti
|
||||
}
|
||||
|
||||
func (a *App) SendNotifyAdminPosts(c *request.Context, workspaceName string, currentSKU string, trial bool) *model.AppError {
|
||||
mlog.Info("enter SendNotifyAdminPosts")
|
||||
|
||||
if !a.CanNotifyAdmin(trial) {
|
||||
return model.NewAppError("SendNotifyAdminPosts", "app.notify_admin.send_notification_post.app_error", nil, "Cannot notify yet", http.StatusForbidden)
|
||||
}
|
||||
@@ -103,6 +112,8 @@ func (a *App) SendNotifyAdminPosts(c *request.Context, workspaceName string, cur
|
||||
now := model.GetMillis()
|
||||
|
||||
data, err := a.Srv().Store().NotifyAdmin().Get(trial)
|
||||
mlog.Info("SendNotifyAdminPosts")
|
||||
|
||||
if err != nil {
|
||||
return model.NewAppError("SendNotifyAdminPosts", "app.notify_admin.send_notification_post.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -110,58 +121,99 @@ func (a *App) SendNotifyAdminPosts(c *request.Context, workspaceName string, cur
|
||||
data = filterNotificationData(data, func(nad *model.NotifyAdminData) bool { return nad.RequiredPlan != currentSKU })
|
||||
|
||||
if len(data) == 0 {
|
||||
mlog.Warn("No notification data available")
|
||||
a.Log().Warn("No notification data available")
|
||||
return nil
|
||||
}
|
||||
|
||||
userBasedData := a.groupNotifyAdminByUser(data)
|
||||
featureBasedData := a.groupNotifyAdminByFeature(data)
|
||||
props := make(model.StringInterface)
|
||||
userBasedPaidFeatureData, userBasedPluginData := a.groupNotifyAdminByUser(data)
|
||||
featureBasedData := a.groupNotifyAdminByPaidFeature(data)
|
||||
pluginBasedData := a.groupNotifyAdminByPlugin(data)
|
||||
|
||||
for _, admin := range sysadmins {
|
||||
T := i18n.GetUserTranslations(admin.Locale)
|
||||
message := T("app.cloud.upgrade_plan_bot_message", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName})
|
||||
if len(userBasedData) == 1 {
|
||||
message = T("app.cloud.upgrade_plan_bot_message_single", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName}) // todo (allan): investigate if translations library can do this
|
||||
}
|
||||
if trial {
|
||||
message = T("app.cloud.trial_plan_bot_message", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName})
|
||||
if len(userBasedData) == 1 {
|
||||
message = T("app.cloud.trial_plan_bot_message_single", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName})
|
||||
}
|
||||
if len(userBasedPaidFeatureData) > 0 && len(featureBasedData) > 0 {
|
||||
a.upgradePlanAdminNotifyPost(c, workspaceName, userBasedPaidFeatureData, featureBasedData, systemBot, admin, trial)
|
||||
}
|
||||
|
||||
channel, appErr := a.GetOrCreateDirectChannel(c, systemBot.UserId, admin.Id)
|
||||
if appErr != nil {
|
||||
mlog.Warn("Error getting direct channel", mlog.Err(appErr))
|
||||
continue
|
||||
}
|
||||
if len(userBasedPluginData) > 0 {
|
||||
mlog.Info("SendNotifyAdminPosts", mlog.String("length of user based plugin data", fmt.Sprint(len(userBasedPluginData))))
|
||||
|
||||
post := &model.Post{
|
||||
Message: message,
|
||||
UserId: systemBot.UserId,
|
||||
ChannelId: channel.Id,
|
||||
Type: fmt.Sprintf("%sup_notification", model.PostCustomTypePrefix), // webapp will have to create renderer for this custom post type
|
||||
|
||||
}
|
||||
|
||||
props["requested_features"] = featureBasedData
|
||||
props["trial"] = trial
|
||||
post.SetProps(props)
|
||||
|
||||
_, appErr = a.CreatePost(c, post, channel, false, true)
|
||||
if appErr != nil {
|
||||
mlog.Warn("Error creating post", mlog.Err(appErr))
|
||||
continue
|
||||
a.pluginInstallAdminNotifyPost(c, userBasedPluginData, pluginBasedData, systemBot, admin)
|
||||
}
|
||||
}
|
||||
|
||||
a.FinishSendAdminNotifyPost(trial, now)
|
||||
|
||||
a.FinishSendAdminNotifyPost(trial, now, pluginBasedData)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostPaidFeature) bool {
|
||||
func (a *App) pluginInstallAdminNotifyPost(c *request.Context, userBasedData map[string][]*model.NotifyAdminData, pluginBasedPluginData map[string][]*model.NotifyAdminData, systemBot *model.Bot, admin *model.User) {
|
||||
props := make(model.StringInterface)
|
||||
|
||||
channel, appErr := a.GetOrCreateDirectChannel(c, systemBot.UserId, admin.Id)
|
||||
if appErr != nil {
|
||||
a.Log().Warn("Error getting direct channel", mlog.Err(appErr))
|
||||
return
|
||||
}
|
||||
|
||||
post := &model.Post{
|
||||
UserId: systemBot.UserId,
|
||||
ChannelId: channel.Id,
|
||||
Type: fmt.Sprintf("%spl_notification", model.PostCustomTypePrefix), // webapp will have to create renderer for this custom post type
|
||||
}
|
||||
|
||||
props["requested_plugins_by_plugin_ids"] = pluginBasedPluginData
|
||||
props["requested_plugins_by_user_ids"] = userBasedData
|
||||
post.SetProps(props)
|
||||
mlog.Info("pluginInstallAdminNotifyPost: send props")
|
||||
|
||||
_, appErr = a.CreatePost(c, post, channel, false, true)
|
||||
mlog.Info("pluginInstallAdminNotifyPost: post created")
|
||||
|
||||
if appErr != nil {
|
||||
a.Log().Warn("Error creating post", mlog.Err(appErr))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) upgradePlanAdminNotifyPost(c *request.Context, workspaceName string, userBasedData map[string][]*model.NotifyAdminData, featureBasedData map[model.MattermostFeature][]*model.NotifyAdminData, systemBot *model.Bot, admin *model.User, trial bool) {
|
||||
props := make(model.StringInterface)
|
||||
T := i18n.GetUserTranslations(admin.Locale)
|
||||
|
||||
message := T("app.cloud.upgrade_plan_bot_message", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName})
|
||||
if len(userBasedData) == 1 {
|
||||
message = T("app.cloud.upgrade_plan_bot_message_single", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName}) // todo (allan): investigate if translations library can do this
|
||||
}
|
||||
if trial {
|
||||
message = T("app.cloud.trial_plan_bot_message", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName})
|
||||
if len(userBasedData) == 1 {
|
||||
message = T("app.cloud.trial_plan_bot_message_single", map[string]interface{}{"UsersNum": len(userBasedData), "WorkspaceName": workspaceName})
|
||||
}
|
||||
}
|
||||
|
||||
channel, appErr := a.GetOrCreateDirectChannel(c, systemBot.UserId, admin.Id)
|
||||
if appErr != nil {
|
||||
a.Log().Warn("Error getting direct channel", mlog.Err(appErr))
|
||||
return
|
||||
}
|
||||
|
||||
post := &model.Post{
|
||||
Message: message,
|
||||
UserId: systemBot.UserId,
|
||||
ChannelId: channel.Id,
|
||||
Type: fmt.Sprintf("%sup_notification", model.PostCustomTypePrefix), // webapp will have to create renderer for this custom post type
|
||||
|
||||
}
|
||||
|
||||
props["requested_features"] = featureBasedData
|
||||
props["trial"] = trial
|
||||
post.SetProps(props)
|
||||
|
||||
_, appErr = a.CreatePost(c, post, channel, false, true)
|
||||
|
||||
if appErr != nil {
|
||||
a.Log().Warn("Error creating post", mlog.Err(appErr))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostFeature) bool {
|
||||
data, err := a.Srv().Store().NotifyAdmin().GetDataByUserIdAndFeature(user, feature)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -185,13 +237,13 @@ func (a *App) CanNotifyAdmin(trial bool) bool {
|
||||
if errors.As(sysValErr, &nfErr) { // if no timestamps have been recorded before, system is free to notify
|
||||
return true
|
||||
}
|
||||
mlog.Error("Cannot notify", mlog.Err(sysValErr))
|
||||
a.Log().Error("Cannot notify", mlog.Err(sysValErr))
|
||||
return false
|
||||
}
|
||||
|
||||
lastNotificationTimestamp, err := strconv.ParseFloat(sysVal.Value, 64)
|
||||
if err != nil {
|
||||
mlog.Error("Cannot notify", mlog.Err(err))
|
||||
a.Log().Error("Cannot notify", mlog.Err(err))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -205,7 +257,9 @@ func (a *App) CanNotifyAdmin(trial bool) bool {
|
||||
return timeDiff >= int64(daysToMillis)
|
||||
}
|
||||
|
||||
func (a *App) FinishSendAdminNotifyPost(trial bool, now int64) {
|
||||
func (a *App) FinishSendAdminNotifyPost(trial bool, now int64, pluginBasedData map[string][]*model.NotifyAdminData) {
|
||||
mlog.Info("FinishSendAdminNotifyPost")
|
||||
|
||||
systemVarName := lastUpgradeNotificationTimeStamp
|
||||
if trial {
|
||||
systemVarName = lastTrialNotificationTimeStamp
|
||||
@@ -214,30 +268,64 @@ func (a *App) FinishSendAdminNotifyPost(trial bool, now int64) {
|
||||
val := strconv.FormatInt(model.GetMillis(), 10)
|
||||
sysVar := &model.System{Name: systemVarName, Value: val}
|
||||
if err := a.Srv().Store().System().SaveOrUpdate(sysVar); err != nil {
|
||||
mlog.Error("Unable to finish send admin notify post job", mlog.Err(err))
|
||||
a.Log().Error("Unable to finish send admin notify post job", mlog.Err(err))
|
||||
}
|
||||
|
||||
// All the requested features notifications are now sent in a post and can safely be removed except
|
||||
// the plugin notify admin. We keep it as we do not want the same user to send the notification for the same plugin.
|
||||
// We update the NotifyAdmin SentAt to keep track of it.
|
||||
for pluginId := range pluginBasedData {
|
||||
notifications := pluginBasedData[pluginId]
|
||||
for _, notification := range notifications {
|
||||
requiredFeature := notification.RequiredFeature
|
||||
requiredPlan := notification.RequiredPlan
|
||||
userId := notification.UserId
|
||||
if err := a.Srv().Store().NotifyAdmin().Update(userId, requiredPlan, requiredFeature, now); err != nil {
|
||||
a.Log().Error("Unable to update SentAt for work template feature", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all the notifications are now sent in a post and can safely be removed
|
||||
if err := a.Srv().Store().NotifyAdmin().DeleteBefore(trial, now); err != nil {
|
||||
mlog.Error("Unable to finish send admin notify post job", mlog.Err(err))
|
||||
a.Log().Error("Unable to finish send admin notify post job", mlog.Err(err))
|
||||
}
|
||||
mlog.Info("exit FinishSendAdminNotifyPost")
|
||||
|
||||
}
|
||||
|
||||
func (a *App) groupNotifyAdminByUser(data []*model.NotifyAdminData) map[string][]*model.NotifyAdminData {
|
||||
myMap := make(map[string][]*model.NotifyAdminData)
|
||||
func (a *App) groupNotifyAdminByUser(data []*model.NotifyAdminData) (map[string][]*model.NotifyAdminData, map[string][]*model.NotifyAdminData) {
|
||||
userBasedPaidFeatureData := make(map[string][]*model.NotifyAdminData)
|
||||
userBasedPluginData := make(map[string][]*model.NotifyAdminData)
|
||||
for _, d := range data {
|
||||
myMap[d.UserId] = append(myMap[d.UserId], d)
|
||||
if strings.HasPrefix(string(d.RequiredFeature), string(model.PluginFeature)) {
|
||||
userBasedPluginData[d.UserId] = append(userBasedPluginData[d.UserId], d)
|
||||
} else {
|
||||
userBasedPaidFeatureData[d.UserId] = append(userBasedPaidFeatureData[d.UserId], d)
|
||||
}
|
||||
}
|
||||
|
||||
return myMap
|
||||
return userBasedPaidFeatureData, userBasedPluginData
|
||||
}
|
||||
|
||||
func (a *App) groupNotifyAdminByFeature(data []*model.NotifyAdminData) map[model.MattermostPaidFeature][]*model.NotifyAdminData {
|
||||
myMap := make(map[model.MattermostPaidFeature][]*model.NotifyAdminData)
|
||||
func (a *App) groupNotifyAdminByPaidFeature(data []*model.NotifyAdminData) map[model.MattermostFeature][]*model.NotifyAdminData {
|
||||
myMap := make(map[model.MattermostFeature][]*model.NotifyAdminData)
|
||||
for _, d := range data {
|
||||
if strings.HasPrefix(string(d.RequiredFeature), string(model.PluginFeature)) {
|
||||
continue
|
||||
}
|
||||
myMap[d.RequiredFeature] = append(myMap[d.RequiredFeature], d)
|
||||
}
|
||||
|
||||
return myMap
|
||||
}
|
||||
|
||||
func (a *App) groupNotifyAdminByPlugin(data []*model.NotifyAdminData) map[string][]*model.NotifyAdminData {
|
||||
myMap := make(map[string][]*model.NotifyAdminData)
|
||||
for _, d := range data {
|
||||
if strings.HasPrefix(string(d.RequiredFeature), string(model.PluginFeature)) {
|
||||
plugins := strings.Split(d.RequiredPlan, ",")
|
||||
for _, plugin := range plugins {
|
||||
myMap[plugin] = append(myMap[plugin], d)
|
||||
}
|
||||
}
|
||||
}
|
||||
return myMap
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const PluginIdGithub = "github"
|
||||
|
||||
func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
|
||||
t.Run("no error sending upgrade post when no notifications are available", func(t *testing.T) {
|
||||
t.Run("no error sending non trial upgrade post when no notifications are available", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -28,7 +30,7 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("no error sending trial post when do notifications are available", func(t *testing.T) {
|
||||
t.Run("no error sending trial upgrade post when no notifications are available", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -45,7 +47,7 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
// some some notifications
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: model.LicenseShortSkuProfessional,
|
||||
@@ -93,13 +95,13 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
require.Equal(t, "2 members of the test workspace have requested a workspace upgrade for: ", post.Message)
|
||||
})
|
||||
|
||||
t.Run("successfully send trial notification", func(t *testing.T) {
|
||||
t.Run("successfully send trial upgrade notification", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
// some some notifications
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: model.LicenseShortSkuProfessional,
|
||||
@@ -141,13 +143,128 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
require.Equal(t, "1 member of the test workspace has requested starting the Enterprise trial for access to: ", post.Message)
|
||||
})
|
||||
|
||||
t.Run("error when trying to send post before end of cool off period", func(t *testing.T) {
|
||||
t.Run("successfully send install plugin notification", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: PluginIdGithub,
|
||||
RequiredFeature: model.PluginFeature,
|
||||
Trial: false,
|
||||
})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
ctx := request.NewContext(context.Background(), model.NewId(), model.NewId(), model.NewId(), model.NewId(), model.NewId(), model.Session{}, nil)
|
||||
appErr = th.App.SendNotifyAdminPosts(ctx, "", "", false)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
bot, appErr := th.App.GetSystemBot()
|
||||
require.Nil(t, appErr)
|
||||
|
||||
var channel *model.Channel
|
||||
var err error
|
||||
var timeout = 5 * time.Second
|
||||
begin := time.Now()
|
||||
|
||||
for {
|
||||
if time.Since(begin) > timeout {
|
||||
break
|
||||
}
|
||||
channel, err = th.App.Srv().Store().Channel().GetByName("", model.GetDMNameFromIds(bot.UserId, th.SystemAdminUser.Id), false)
|
||||
if err == nil && channel != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
require.NoError(t, err, "Expected message to have been sent within %d seconds", timeout)
|
||||
postList, err := th.App.Srv().Store().Post().GetPosts(model.GetPostsOptions{ChannelId: channel.Id, Page: 0, PerPage: 1}, false, map[string]bool{})
|
||||
require.NoError(t, err)
|
||||
|
||||
post := postList.Posts[postList.Order[0]]
|
||||
|
||||
require.Equal(t, fmt.Sprintf("%spl_notification", model.PostCustomTypePrefix), post.Type)
|
||||
require.Equal(t, bot.UserId, post.UserId)
|
||||
})
|
||||
|
||||
t.Run("persist notify admin data after sending the install plugin notification", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: PluginIdGithub,
|
||||
RequiredFeature: model.PluginFeature,
|
||||
Trial: false,
|
||||
})
|
||||
require.Nil(t, appErr)
|
||||
|
||||
ctx := request.NewContext(context.Background(), model.NewId(), model.NewId(), model.NewId(), model.NewId(), model.NewId(), model.Session{}, nil)
|
||||
appErr = th.App.SendNotifyAdminPosts(ctx, "", "", false)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
bot, appErr := th.App.GetSystemBot()
|
||||
require.Nil(t, appErr)
|
||||
|
||||
var channel *model.Channel
|
||||
var err error
|
||||
var timeout = 5 * time.Second
|
||||
begin := time.Now()
|
||||
|
||||
for {
|
||||
if time.Since(begin) > timeout {
|
||||
break
|
||||
}
|
||||
channel, err = th.App.Srv().Store().Channel().GetByName("", model.GetDMNameFromIds(bot.UserId, th.SystemAdminUser.Id), false)
|
||||
if err == nil && channel != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
require.NoError(t, err, "Expected message to have been sent within %d seconds", timeout)
|
||||
postList, err := th.App.Srv().Store().Post().GetPosts(model.GetPostsOptions{ChannelId: channel.Id, Page: 0, PerPage: 1}, false, map[string]bool{})
|
||||
require.NoError(t, err)
|
||||
|
||||
post := postList.Posts[postList.Order[0]]
|
||||
|
||||
require.Equal(t, fmt.Sprintf("%spl_notification", model.PostCustomTypePrefix), post.Type)
|
||||
require.Equal(t, bot.UserId, post.UserId)
|
||||
|
||||
data, err := th.App.Srv().Store().NotifyAdmin().GetDataByUserIdAndFeature(th.BasicUser.Id, model.PluginFeature)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(data), 1)
|
||||
})
|
||||
|
||||
t.Run("error sending more than one notification to the same user and for the same plugin", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
err := th.App.SaveAdminNotification(th.BasicUser.Id, &model.NotifyAdminToUpgradeRequest{
|
||||
RequiredPlan: PluginIdGithub,
|
||||
RequiredFeature: model.PluginFeature,
|
||||
TrialNotification: false,
|
||||
})
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
err = th.App.SaveAdminNotification(th.BasicUser.Id, &model.NotifyAdminToUpgradeRequest{
|
||||
RequiredPlan: PluginIdGithub,
|
||||
RequiredFeature: model.PluginFeature,
|
||||
TrialNotification: false,
|
||||
})
|
||||
|
||||
require.Equal(t, err.Error(), "app.SaveAdminNotification: Already notified admin")
|
||||
})
|
||||
|
||||
t.Run("error when trying to send upgrade post before end of cool off period", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
// some some notifications
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: model.LicenseShortSkuProfessional,
|
||||
@@ -173,7 +290,7 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
require.Equal(t, appErr.Error(), "SendNotifyAdminPosts: Unable to send notification post., Cannot notify yet")
|
||||
})
|
||||
|
||||
t.Run("can send post at the end of cool off period", func(t *testing.T) {
|
||||
t.Run("can send upgrade post at the end of cool off period", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -182,7 +299,7 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
os.Setenv("MM_NOTIFY_ADMIN_COOL_OFF_DAYS", "0.00003472222222") // set to 3 seconds
|
||||
defer os.Unsetenv("MM_NOTIFY_ADMIN_COOL_OFF_DAYS")
|
||||
|
||||
// some some notifications
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: model.LicenseShortSkuProfessional,
|
||||
@@ -215,7 +332,7 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
// some some notifications
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: model.LicenseShortSkuProfessional,
|
||||
@@ -264,4 +381,68 @@ func Test_SendNotifyAdminPosts(t *testing.T) {
|
||||
require.Equal(t, bot.UserId, post.UserId)
|
||||
require.Equal(t, "1 member of the test workspace has requested a workspace upgrade for: ", post.Message) // expect only one member's notification even though 2 were added
|
||||
})
|
||||
|
||||
t.Run("correctly send upgrade and install plugin post with the correct user request", func(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
os.Setenv("MM_NOTIFY_ADMIN_COOL_OFF_DAYS", "0")
|
||||
defer os.Unsetenv("MM_NOTIFY_ADMIN_COOL_OFF_DAYS")
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
ctx := request.NewContext(context.Background(), model.NewId(), model.NewId(), model.NewId(), model.NewId(), model.NewId(), model.Session{}, nil)
|
||||
|
||||
// some notifications
|
||||
_, appErr := th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: model.LicenseShortSkuProfessional,
|
||||
RequiredFeature: model.PaidFeatureGuestAccounts,
|
||||
Trial: false,
|
||||
})
|
||||
require.Nil(t, appErr)
|
||||
appErr = th.App.SendNotifyAdminPosts(ctx, "test", "", false)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// some notifications
|
||||
_, appErr = th.App.SaveAdminNotifyData(&model.NotifyAdminData{
|
||||
UserId: th.BasicUser.Id,
|
||||
RequiredPlan: PluginIdGithub,
|
||||
RequiredFeature: model.PluginFeature,
|
||||
Trial: false,
|
||||
})
|
||||
require.Nil(t, appErr)
|
||||
appErr = th.App.SendNotifyAdminPosts(ctx, "test", "", false)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
bot, appErr := th.App.GetSystemBot()
|
||||
require.Nil(t, appErr)
|
||||
|
||||
var channel *model.Channel
|
||||
var err error
|
||||
var timeout = 5 * time.Second
|
||||
begin := time.Now()
|
||||
|
||||
for {
|
||||
if time.Since(begin) > timeout {
|
||||
break
|
||||
}
|
||||
channel, err = th.App.Srv().Store().Channel().GetByName("", model.GetDMNameFromIds(bot.UserId, th.SystemAdminUser.Id), false)
|
||||
if err == nil && channel != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
require.NoError(t, err, "Expected message to have been sent within %d seconds", timeout)
|
||||
postList, err := th.App.Srv().Store().Post().GetPosts(model.GetPostsOptions{ChannelId: channel.Id, Page: 0, PerPage: 2}, false, map[string]bool{})
|
||||
require.NoError(t, err)
|
||||
|
||||
installPluginPost := postList.Posts[postList.Order[0]]
|
||||
require.Equal(t, fmt.Sprintf("%spl_notification", model.PostCustomTypePrefix), installPluginPost.Type)
|
||||
require.Equal(t, bot.UserId, installPluginPost.UserId)
|
||||
|
||||
upgradePost := postList.Posts[postList.Order[1]]
|
||||
require.Equal(t, fmt.Sprintf("%sup_notification", model.PostCustomTypePrefix), upgradePost.Type)
|
||||
require.Equal(t, bot.UserId, upgradePost.UserId)
|
||||
require.Equal(t, "1 member of the test workspace has requested a workspace upgrade for: ", upgradePost.Message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4430,7 +4430,7 @@ func (a *OpenTracingAppLayer) FindTeamByName(name string) bool {
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) FinishSendAdminNotifyPost(trial bool, now int64) {
|
||||
func (a *OpenTracingAppLayer) FinishSendAdminNotifyPost(trial bool, now int64, pluginBasedData map[string][]*model.NotifyAdminData) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.FinishSendAdminNotifyPost")
|
||||
|
||||
@@ -4442,7 +4442,7 @@ func (a *OpenTracingAppLayer) FinishSendAdminNotifyPost(trial bool, now int64) {
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
a.app.FinishSendAdminNotifyPost(trial, now)
|
||||
a.app.FinishSendAdminNotifyPost(trial, now, pluginBasedData)
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GenerateMfaSecret(userID string) (*model.MfaSecret, *model.AppError) {
|
||||
@@ -18521,7 +18521,7 @@ func (a *OpenTracingAppLayer) UpsertGroupSyncable(groupSyncable *model.GroupSync
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostPaidFeature) bool {
|
||||
func (a *OpenTracingAppLayer) UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostFeature) bool {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UserAlreadyNotifiedOnRequiredFeature")
|
||||
|
||||
|
||||
@@ -1543,6 +1543,12 @@ func (s *Server) initJobs() {
|
||||
notify_admin.MakeScheduler(s.Jobs, s.License(), model.JobTypeTrialNotifyAdmin),
|
||||
)
|
||||
|
||||
s.Jobs.RegisterJobType(
|
||||
model.JobTypeInstallPluginNotifyAdmin,
|
||||
notify_admin.MakeInstallPluginNotifyWorker(s.Jobs, New(ServerConnector(s.Channels()))),
|
||||
notify_admin.MakeInstallPluginScheduler(s.Jobs, s.License(), model.JobTypeInstallPluginNotifyAdmin),
|
||||
)
|
||||
|
||||
s.Jobs.RegisterJobType(
|
||||
model.JobTypeHostedPurchaseScreening,
|
||||
hosted_purchase_screening.MakeWorker(s.Jobs, s.License(), s.Store().System()),
|
||||
|
||||
Ссылка в новой задаче
Block a user