summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorGeorge Goldberg <george@gberg.me>2018-03-13 13:36:23 +0000
committerGeorge Goldberg <george@gberg.me>2018-03-13 13:36:23 +0000
commitfadcdd271a68b38571b75d1d38ab023f940ac83a (patch)
tree7f413edd35401b54b2f4eb4a687da1ac273bcd4b /app
parentb66e4bc932ed76c1cfd2b5f4ec0cfce70cd9fbb4 (diff)
parentf2d26801b9647715fb43af873354d8def753868b (diff)
downloadchat-fadcdd271a68b38571b75d1d38ab023f940ac83a.tar.gz
chat-fadcdd271a68b38571b75d1d38ab023f940ac83a.tar.bz2
chat-fadcdd271a68b38571b75d1d38ab023f940ac83a.zip
Merge branch 'master' into advanced-permissions-phase-1
Diffstat (limited to 'app')
-rw-r--r--app/admin.go10
-rw-r--r--app/app.go18
-rw-r--r--app/apptestlib.go24
-rw-r--r--app/auto_constants.go22
-rw-r--r--app/auto_posts.go15
-rw-r--r--app/channel.go45
-rw-r--r--app/channel_test.go69
-rw-r--r--app/command_expand_collapse.go4
-rw-r--r--app/config.go15
-rw-r--r--app/config_test.go10
-rw-r--r--app/diagnostics.go14
-rw-r--r--app/emoji.go1
-rw-r--r--app/login.go14
-rw-r--r--app/post.go18
-rw-r--r--app/preference.go8
-rw-r--r--app/server.go47
-rw-r--r--app/server_test.go2
-rw-r--r--app/session.go3
-rw-r--r--app/slackimport.go12
-rw-r--r--app/status.go44
-rw-r--r--app/status_test.go40
-rw-r--r--app/team.go9
-rw-r--r--app/user.go31
-rw-r--r--app/user_test.go129
-rw-r--r--app/webhook.go2
-rw-r--r--app/webhook_test.go58
26 files changed, 455 insertions, 209 deletions
diff --git a/app/admin.go b/app/admin.go
index 154fa8899..22928390e 100644
--- a/app/admin.go
+++ b/app/admin.go
@@ -15,7 +15,6 @@ import (
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/store/sqlstore"
"github.com/mattermost/mattermost-server/utils"
)
@@ -141,10 +140,11 @@ func (a *App) InvalidateAllCachesSkipSend() {
l4g.Info(utils.T("api.context.invalidate_all_caches"))
a.sessionCache.Purge()
ClearStatusCache()
- sqlstore.ClearChannelCaches()
- sqlstore.ClearUserCaches()
- sqlstore.ClearPostCaches()
- sqlstore.ClearWebhookCaches()
+ a.Srv.Store.Channel().ClearCaches()
+ a.Srv.Store.User().ClearCaches()
+ a.Srv.Store.Post().ClearCaches()
+ a.Srv.Store.FileInfo().ClearCaches()
+ a.Srv.Store.Webhook().ClearCaches()
a.LoadLicense()
}
diff --git a/app/app.go b/app/app.go
index 5bc418e0d..c4d11cb63 100644
--- a/app/app.go
+++ b/app/app.go
@@ -133,8 +133,24 @@ func New(options ...Option) (outApp *App, outErr error) {
app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
app.configOrLicenseListener()
+
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil)
+
+ message.Add("config", app.ClientConfigWithNoAccounts())
+ app.Go(func() {
+ app.Publish(message)
+ })
+ })
+ app.licenseListenerId = app.AddLicenseListener(func() {
+ app.configOrLicenseListener()
+
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil)
+ message.Add("license", app.GetSanitizedClientLicense())
+ app.Go(func() {
+ app.Publish(message)
+ })
+
})
- app.licenseListenerId = app.AddLicenseListener(app.configOrLicenseListener)
app.regenerateClientConfig()
l4g.Info(utils.T("api.server.new_server.init.info"))
diff --git a/app/apptestlib.go b/app/apptestlib.go
index 3402f1f79..6c2273c6e 100644
--- a/app/apptestlib.go
+++ b/app/apptestlib.go
@@ -143,10 +143,6 @@ func (me *TestHelper) InitBasic() *TestHelper {
return me
}
-func (me *TestHelper) MakeUsername() string {
- return "un_" + model.NewId()
-}
-
func (me *TestHelper) MakeEmail() string {
return "success_" + model.NewId() + "@simulator.amazonses.com"
}
@@ -199,10 +195,6 @@ func (me *TestHelper) CreateChannel(team *model.Team) *model.Channel {
return me.createChannel(team, model.CHANNEL_OPEN)
}
-func (me *TestHelper) CreatePrivateChannel(team *model.Team) *model.Channel {
- return me.createChannel(team, model.CHANNEL_PRIVATE)
-}
-
func (me *TestHelper) createChannel(team *model.Team, channelType string) *model.Channel {
id := model.NewId()
@@ -262,6 +254,22 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
utils.EnableDebugLogForTest()
}
+func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
+ utils.DisableDebugLogForTest()
+
+ member, err := me.App.AddUserToChannel(user, channel)
+ if err != nil {
+ l4g.Error(err.Error())
+ l4g.Close()
+ time.Sleep(time.Second)
+ panic(err)
+ }
+
+ utils.EnableDebugLogForTest()
+
+ return member
+}
+
func (me *TestHelper) TearDown() {
me.App.Shutdown()
os.Remove(me.tempConfigPath)
diff --git a/app/auto_constants.go b/app/auto_constants.go
index c52eb6243..520d4e363 100644
--- a/app/auto_constants.go
+++ b/app/auto_constants.go
@@ -9,16 +9,15 @@ import (
)
const (
- USER_PASSWORD = "passwd"
- CHANNEL_TYPE = model.CHANNEL_OPEN
- FUZZ_USER_EMAIL_PREFIX_LEN = 10
- BTEST_TEAM_DISPLAY_NAME = "TestTeam"
- BTEST_TEAM_NAME = "z-z-testdomaina"
- BTEST_TEAM_EMAIL = "test@nowhere.com"
- BTEST_TEAM_TYPE = model.TEAM_OPEN
- BTEST_USER_NAME = "Mr. Testing Tester"
- BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
- BTEST_USER_PASSWORD = "passwd"
+ USER_PASSWORD = "passwd"
+ CHANNEL_TYPE = model.CHANNEL_OPEN
+ BTEST_TEAM_DISPLAY_NAME = "TestTeam"
+ BTEST_TEAM_NAME = "z-z-testdomaina"
+ BTEST_TEAM_EMAIL = "test@nowhere.com"
+ BTEST_TEAM_TYPE = model.TEAM_OPEN
+ BTEST_USER_NAME = "Mr. Testing Tester"
+ BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
+ BTEST_USER_PASSWORD = "passwd"
)
var (
@@ -29,8 +28,5 @@ var (
USER_EMAIL_LEN = utils.Range{Begin: 15, End: 30}
CHANNEL_DISPLAY_NAME_LEN = utils.Range{Begin: 10, End: 20}
CHANNEL_NAME_LEN = utils.Range{Begin: 5, End: 20}
- POST_MESSAGE_LEN = utils.Range{Begin: 100, End: 400}
- POST_HASHTAGS_NUM = utils.Range{Begin: 5, End: 10}
- POST_MENTIONS_NUM = utils.Range{Begin: 0, End: 3}
TEST_IMAGE_FILENAMES = []string{"test.png", "testjpg.jpg", "testgif.gif"}
)
diff --git a/app/auto_posts.go b/app/auto_posts.go
index 6d1e352e5..379c74ab7 100644
--- a/app/auto_posts.go
+++ b/app/auto_posts.go
@@ -90,18 +90,3 @@ func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) {
}
return result.Data.(*model.Post), true
}
-
-func (cfg *AutoPostCreator) CreateTestPosts(rangePosts utils.Range) ([]*model.Post, bool) {
- numPosts := utils.RandIntFromRange(rangePosts)
- posts := make([]*model.Post, numPosts)
-
- for i := 0; i < numPosts; i++ {
- var err bool
- posts[i], err = cfg.CreateRandomPost()
- if !err {
- return posts, false
- }
- }
-
- return posts, true
-}
diff --git a/app/channel.go b/app/channel.go
index af36774de..cd694af0f 100644
--- a/app/channel.go
+++ b/app/channel.go
@@ -225,6 +225,14 @@ func (a *App) createDirectChannel(userId string, otherUserId string) (*model.Cha
}
} else {
channel := result.Data.(*model.Channel)
+
+ if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId, channel.Id, model.GetMillis()); result.Err != nil {
+ l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ }
+ if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(otherUserId, channel.Id, model.GetMillis()); result.Err != nil {
+ l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ }
+
return channel, nil
}
}
@@ -369,7 +377,7 @@ func (a *App) postChannelPrivacyMessage(user *model.User, channel *model.Channel
})[channel.Type]
post := &model.Post{
ChannelId: channel.Id,
- Message: fmt.Sprintf(utils.T("api.channel.change_channel_privacy." + privacy)),
+ Message: utils.T("api.channel.change_channel_privacy." + privacy),
Type: model.POST_CHANGE_CHANNEL_PRIVACY,
UserId: user.Id,
Props: model.StringInterface{
@@ -549,7 +557,6 @@ func (a *App) DeleteChannel(channel *model.Channel, userId string) *model.AppErr
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, channel.TeamId, "", "", nil)
message.Add("channel_id", channel.Id)
-
a.Publish(message)
}
@@ -1059,7 +1066,7 @@ func (a *App) LeaveChannel(channelId string, userId string) *model.AppError {
return err
}
- if channel.Name == model.DEFAULT_CHANNEL && *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages == false {
+ if channel.Name == model.DEFAULT_CHANNEL && !*a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
return nil
}
@@ -1097,7 +1104,9 @@ func (a *App) PostAddToChannelMessage(user *model.User, addedUser *model.User, c
UserId: user.Id,
RootId: postRootId,
Props: model.StringInterface{
+ "userId": user.Id,
"username": user.Username,
+ "addedUserId": addedUser.Id,
"addedUsername": addedUser.Username,
},
}
@@ -1117,7 +1126,9 @@ func (a *App) postAddToTeamMessage(user *model.User, addedUser *model.User, chan
UserId: user.Id,
RootId: postRootId,
Props: model.StringInterface{
+ "userId": user.Id,
"username": user.Username,
+ "addedUserId": addedUser.Id,
"addedUsername": addedUser.Username,
},
}
@@ -1136,6 +1147,7 @@ func (a *App) postRemoveFromChannelMessage(removerUserId string, removedUser *mo
Type: model.POST_REMOVE_FROM_CHANNEL,
UserId: removerUserId,
Props: model.StringInterface{
+ "removedUserId": removedUser.Id,
"removedUsername": removedUser.Username,
},
}
@@ -1170,17 +1182,13 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string,
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
message.Add("user_id", userIdToRemove)
message.Add("remover_id", removerUserId)
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
// because the removed user no longer belongs to the channel we need to send a separate websocket event
userMsg := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", "", userIdToRemove, nil)
userMsg.Add("channel_id", channel.Id)
userMsg.Add("remover_id", removerUserId)
- a.Go(func() {
- a.Publish(userMsg)
- })
+ a.Publish(userMsg)
return nil
}
@@ -1250,9 +1258,7 @@ func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *mod
for _, channelId := range channelIds {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil)
message.Add("channel_id", channelId)
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
}
@@ -1329,9 +1335,7 @@ func (a *App) ViewChannel(view *model.ChannelView, userId string, clearPushNotif
if *a.Config().ServiceSettings.EnableChannelViewedMessages && model.IsValidId(view.ChannelId) {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil)
message.Add("channel_id", view.ChannelId)
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
return times, nil
@@ -1434,7 +1438,16 @@ func (a *App) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.
}
a.InvalidateCacheForUser(userId1)
a.InvalidateCacheForUser(userId2)
- return result.Data.(*model.Channel), nil
+
+ channel := result.Data.(*model.Channel)
+ if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId1, channel.Id, model.GetMillis()); result.Err != nil {
+ l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ }
+ if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId2, channel.Id, model.GetMillis()); result.Err != nil {
+ l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ }
+
+ return channel, nil
} else if result.Err != nil {
return nil, model.NewAppError("GetOrCreateDMChannel", "web.incoming_webhook.channel.app_error", nil, "err="+result.Err.Message, result.Err.StatusCode)
}
diff --git a/app/channel_test.go b/app/channel_test.go
index e4a0e4320..69efaeca7 100644
--- a/app/channel_test.go
+++ b/app/channel_test.go
@@ -110,7 +110,7 @@ func TestMoveChannel(t *testing.T) {
}
}
-func TestJoinDefaultChannelsTownSquare(t *testing.T) {
+func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordTownSquare(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -136,7 +136,7 @@ func TestJoinDefaultChannelsTownSquare(t *testing.T) {
assert.True(t, found)
}
-func TestJoinDefaultChannelsOffTopic(t *testing.T) {
+func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordOffTopic(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -162,7 +162,7 @@ func TestJoinDefaultChannelsOffTopic(t *testing.T) {
assert.True(t, found)
}
-func TestCreateChannelPublic(t *testing.T) {
+func TestCreateChannelPublicCreatesChannelMemberHistoryRecord(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -176,7 +176,7 @@ func TestCreateChannelPublic(t *testing.T) {
assert.Equal(t, publicChannel.Id, histories[0].ChannelId)
}
-func TestCreateChannelPrivate(t *testing.T) {
+func TestCreateChannelPrivateCreatesChannelMemberHistoryRecord(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -205,7 +205,7 @@ func TestUpdateChannelPrivacy(t *testing.T) {
}
}
-func TestCreateGroupChannel(t *testing.T) {
+func TestCreateGroupChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -233,7 +233,62 @@ func TestCreateGroupChannel(t *testing.T) {
}
}
-func TestAddUserToChannel(t *testing.T) {
+func TestCreateDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
+ th := Setup().InitBasic()
+ defer th.TearDown()
+
+ user1 := th.CreateUser()
+ user2 := th.CreateUser()
+
+ if channel, err := th.App.CreateDirectChannel(user1.Id, user2.Id); err != nil {
+ t.Fatal("Failed to create direct channel. Error: " + err.Message)
+ } else {
+ // there should be a ChannelMemberHistory record for both users
+ histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)).([]*model.ChannelMemberHistoryResult)
+ assert.Len(t, histories, 2)
+
+ historyId0 := histories[0].UserId
+ historyId1 := histories[1].UserId
+ switch historyId0 {
+ case user1.Id:
+ assert.Equal(t, user2.Id, historyId1)
+ case user2.Id:
+ assert.Equal(t, user1.Id, historyId1)
+ default:
+ t.Fatal("Unexpected user id " + historyId0 + " in ChannelMemberHistory table")
+ }
+ }
+}
+
+func TestGetDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
+ th := Setup().InitBasic()
+ defer th.TearDown()
+
+ user1 := th.CreateUser()
+ user2 := th.CreateUser()
+
+ // this function call implicitly creates a direct channel between the two users if one doesn't already exist
+ if channel, err := th.App.GetDirectChannel(user1.Id, user2.Id); err != nil {
+ t.Fatal("Failed to create direct channel. Error: " + err.Message)
+ } else {
+ // there should be a ChannelMemberHistory record for both users
+ histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)).([]*model.ChannelMemberHistoryResult)
+ assert.Len(t, histories, 2)
+
+ historyId0 := histories[0].UserId
+ historyId1 := histories[1].UserId
+ switch historyId0 {
+ case user1.Id:
+ assert.Equal(t, user2.Id, historyId1)
+ case user2.Id:
+ assert.Equal(t, user1.Id, historyId1)
+ default:
+ t.Fatal("Unexpected user id " + historyId0 + " in ChannelMemberHistory table")
+ }
+ }
+}
+
+func TestAddUserToChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
@@ -263,7 +318,7 @@ func TestAddUserToChannel(t *testing.T) {
assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
}
-func TestRemoveUserFromChannel(t *testing.T) {
+func TestRemoveUserFromChannelUpdatesChannelMemberHistoryRecord(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
diff --git a/app/command_expand_collapse.go b/app/command_expand_collapse.go
index a8eb3bc1f..638490c6c 100644
--- a/app/command_expand_collapse.go
+++ b/app/command_expand_collapse.go
@@ -74,9 +74,7 @@ func (a *App) setCollapsePreference(args *model.CommandArgs, isCollapse bool) *m
socketMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCE_CHANGED, "", "", args.UserId, nil)
socketMessage.Add("preference", pref.ToJson())
- a.Go(func() {
- a.Publish(socketMessage)
- })
+ a.Publish(socketMessage)
var rmsg string
diff --git a/app/config.go b/app/config.go
index 35a0c9a3f..460d580d8 100644
--- a/app/config.go
+++ b/app/config.go
@@ -14,6 +14,7 @@ import (
"fmt"
"net/url"
"runtime/debug"
+ "strconv"
"strings"
l4g "github.com/alecthomas/log4go"
@@ -34,6 +35,7 @@ func (a *App) UpdateConfig(f func(*model.Config)) {
updated := old.Clone()
f(updated)
a.config.Store(updated)
+
a.InvokeConfigListeners(old, updated)
}
@@ -269,3 +271,16 @@ func (a *App) GetCookieDomain() string {
func (a *App) GetSiteURL() string {
return a.siteURL
}
+
+// ClientConfigWithNoAccounts gets the configuration in a format suitable for sending to the client.
+func (a *App) ClientConfigWithNoAccounts() map[string]string {
+ respCfg := map[string]string{}
+ for k, v := range a.ClientConfig() {
+ respCfg[k] = v
+ }
+
+ // NoAccounts is not actually part of the configuration, but is expected by the client.
+ respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
+
+ return respCfg
+}
diff --git a/app/config_test.go b/app/config_test.go
index 5ee999f0f..051fa8fd8 100644
--- a/app/config_test.go
+++ b/app/config_test.go
@@ -63,3 +63,13 @@ func TestAsymmetricSigningKey(t *testing.T) {
assert.NotNil(t, th.App.AsymmetricSigningKey())
assert.NotEmpty(t, th.App.ClientConfig()["AsymmetricSigningPublicKey"])
}
+
+func TestClientConfigWithNoAccounts(t *testing.T) {
+ th := Setup().InitBasic()
+ defer th.TearDown()
+
+ config := th.App.ClientConfigWithNoAccounts()
+ if _, ok := config["NoAccounts"]; !ok {
+ t.Fatal("expected NoAccounts in returned config")
+ }
+}
diff --git a/app/diagnostics.go b/app/diagnostics.go
index ddea3289f..b08c4f86b 100644
--- a/app/diagnostics.go
+++ b/app/diagnostics.go
@@ -502,11 +502,15 @@ func (a *App) trackConfig() {
})
a.SendDiagnostic(TRACK_CONFIG_MESSAGE_EXPORT, map[string]interface{}{
- "enable_message_export": *cfg.MessageExportSettings.EnableExport,
- "export_format": *cfg.MessageExportSettings.ExportFormat,
- "daily_run_time": *cfg.MessageExportSettings.DailyRunTime,
- "default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp,
- "batch_size": *cfg.MessageExportSettings.BatchSize,
+ "enable_message_export": *cfg.MessageExportSettings.EnableExport,
+ "export_format": *cfg.MessageExportSettings.ExportFormat,
+ "daily_run_time": *cfg.MessageExportSettings.DailyRunTime,
+ "default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp,
+ "batch_size": *cfg.MessageExportSettings.BatchSize,
+ "global_relay_customer_type": *cfg.MessageExportSettings.GlobalRelaySettings.CustomerType,
+ "is_default_global_relay_smtp_username": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SmtpUsername, ""),
+ "is_default_global_relay_smtp_password": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SmtpPassword, ""),
+ "is_default_global_relay_email_address": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.EmailAddress, ""),
})
}
diff --git a/app/emoji.go b/app/emoji.go
index 20d4bb44d..eebe59ccf 100644
--- a/app/emoji.go
+++ b/app/emoji.go
@@ -60,7 +60,6 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
} else {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EMOJI_ADDED, "", "", "", nil)
message.Add("emoji", emoji.ToJson())
-
a.Publish(message)
return result.Data.(*model.Emoji), nil
}
diff --git a/app/login.go b/app/login.go
index e01566bcd..43b022749 100644
--- a/app/login.go
+++ b/app/login.go
@@ -9,8 +9,8 @@ import (
"strings"
"time"
+ "github.com/avct/uasurfer"
"github.com/mattermost/mattermost-server/model"
- "github.com/mssola/user_agent"
)
func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken, deviceId string, ldapOnly bool) (*model.User, *model.AppError) {
@@ -71,19 +71,19 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthWebInDays)
}
- ua := user_agent.New(r.UserAgent())
+ ua := uasurfer.Parse(r.UserAgent())
- plat := ua.Platform()
+ plat := ua.OS.Platform.String()
if plat == "" {
plat = "unknown"
}
- os := ua.OS()
+ os := ua.OS.Name.String()
if os == "" {
os = "unknown"
}
- bname, bversion := ua.Browser()
+ bname := ua.Browser.Name.String()
if bname == "" {
bname = "unknown"
}
@@ -92,9 +92,7 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
bname = "Desktop App"
}
- if bversion == "" {
- bversion = "0.0"
- }
+ bversion := ua.Browser.Version
session.AddProp(model.SESSION_PROP_PLATFORM, plat)
session.AddProp(model.SESSION_PROP_OS, os)
diff --git a/app/post.go b/app/post.go
index 3f157672b..1d7bf974d 100644
--- a/app/post.go
+++ b/app/post.go
@@ -84,9 +84,7 @@ func (a *App) CreatePostAsUser(post *model.Post) (*model.Post, *model.AppError)
if *a.Config().ServiceSettings.EnableChannelViewedMessages {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", post.UserId, nil)
message.Add("channel_id", post.ChannelId)
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
}
@@ -314,10 +312,7 @@ func (a *App) SendEphemeralPost(userId string, post *model.Post) *model.Post {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, "", post.ChannelId, userId, nil)
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
-
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
return post
}
@@ -417,10 +412,7 @@ func (a *App) PatchPost(postId string, patch *model.PostPatch) (*model.Post, *mo
func (a *App) sendUpdatedPostEvent(post *model.Post) {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", post.ChannelId, "", nil)
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
-
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
func (a *App) GetPostsPage(channelId string, page int, perPage int) (*model.PostList, *model.AppError) {
@@ -560,11 +552,9 @@ func (a *App) DeletePost(postId string) (*model.Post, *model.AppError) {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil)
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
+ a.Publish(message)
a.Go(func() {
- a.Publish(message)
- })
- a.Go(func() {
a.DeletePostFiles(post)
})
a.Go(func() {
diff --git a/app/preference.go b/app/preference.go
index 9ca1f474c..eb41992da 100644
--- a/app/preference.go
+++ b/app/preference.go
@@ -55,9 +55,7 @@ func (a *App) UpdatePreferences(userId string, preferences model.Preferences) *m
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_CHANGED, "", "", userId, nil)
message.Add("preferences", preferences.ToJson())
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
return nil
}
@@ -80,9 +78,7 @@ func (a *App) DeletePreferences(userId string, preferences model.Preferences) *m
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_DELETED, "", "", userId, nil)
message.Add("preferences", preferences.ToJson())
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
return nil
}
diff --git a/app/server.go b/app/server.go
index 93804a372..0c6c25ba5 100644
--- a/app/server.go
+++ b/app/server.go
@@ -84,28 +84,6 @@ func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second
-type VaryBy struct {
- useIP bool
- useAuth bool
-}
-
-func (m *VaryBy) Key(r *http.Request) string {
- key := ""
-
- if m.useAuth {
- token, tokenLocation := ParseAuthTokenFromRequest(r)
- if tokenLocation != TokenLocationNotFound {
- key += token
- } else if m.useIP { // If we don't find an authentication token and IP based is enabled, fall back to IP
- key += utils.GetIpAddress(r)
- }
- } else if m.useIP { // Only if Auth based is not enabed do we use a plain IP based
- key = utils.GetIpAddress(r)
- }
-
- return key
-}
-
func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
if r.Host == "" {
http.Error(w, "Not Found", http.StatusNotFound)
@@ -223,31 +201,6 @@ func (a *App) StartServer() error {
return nil
}
-type tcpKeepAliveListener struct {
- *net.TCPListener
-}
-
-func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
- tc, err := ln.AcceptTCP()
- if err != nil {
- return
- }
- tc.SetKeepAlive(true)
- tc.SetKeepAlivePeriod(3 * time.Minute)
- return tc, nil
-}
-
-func (a *App) Listen(addr string) (net.Listener, error) {
- if addr == "" {
- addr = ":http"
- }
- ln, err := net.Listen("tcp", addr)
- if err != nil {
- return nil, err
- }
- return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil
-}
-
func (a *App) StopServer() {
if a.Srv.Server != nil {
ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
diff --git a/app/server_test.go b/app/server_test.go
index de358b976..94771a44e 100644
--- a/app/server_test.go
+++ b/app/server_test.go
@@ -26,7 +26,7 @@ func TestStartServerRateLimiterCriticalError(t *testing.T) {
// Attempt to use Rate Limiter with an invalid config
a.UpdateConfig(func(cfg *model.Config) {
- *cfg.RateLimitSettings.Enable = true
+ *cfg.RateLimitSettings.Enable = true
*cfg.RateLimitSettings.MaxBurst = -100
})
diff --git a/app/session.go b/app/session.go
index 459618439..88f52477f 100644
--- a/app/session.go
+++ b/app/session.go
@@ -138,6 +138,9 @@ func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
session := ts.(*model.Session)
if session.UserId == userId {
a.sessionCache.Remove(key)
+ if a.Metrics != nil {
+ a.Metrics.IncrementMemCacheInvalidationCounterSession()
+ }
}
}
}
diff --git a/app/slackimport.go b/app/slackimport.go
index 9d1b4cf9c..ed522671a 100644
--- a/app/slackimport.go
+++ b/app/slackimport.go
@@ -109,13 +109,11 @@ func SlackParseUsers(data io.Reader) ([]SlackUser, error) {
decoder := json.NewDecoder(data)
var users []SlackUser
- if err := decoder.Decode(&users); err != nil {
- // This actually returns errors that are ignored.
- // In this case it is erroring because of a null that Slack
- // introduced. So we just return the users here.
- return users, err
- }
- return users, nil
+ err := decoder.Decode(&users)
+ // This actually returns errors that are ignored.
+ // In this case it is erroring because of a null that Slack
+ // introduced. So we just return the users here.
+ return users, err
}
func SlackParsePosts(data io.Reader) ([]SlackPost, error) {
diff --git a/app/status.go b/app/status.go
index 1ef7aef0f..c8bff0d1a 100644
--- a/app/status.go
+++ b/app/status.go
@@ -221,9 +221,7 @@ func (a *App) BroadcastStatus(status *model.Status) {
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
event.Add("status", status.Status)
event.Add("user_id", status.UserId)
- a.Go(func() {
- a.Publish(event)
- })
+ a.Publish(event)
}
func (a *App) SetStatusOffline(userId string, manual bool) {
@@ -238,18 +236,7 @@ func (a *App) SetStatusOffline(userId string, manual bool) {
status = &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: manual, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
- a.AddStatusCache(status)
-
- if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
- l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
- }
-
- event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
- event.Add("status", model.STATUS_OFFLINE)
- event.Add("user_id", status.UserId)
- a.Go(func() {
- a.Publish(event)
- })
+ a.SaveAndBroadcastStatus(status)
}
func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) {
@@ -281,18 +268,7 @@ func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) {
status.Manual = manual
status.ActiveChannel = ""
- a.AddStatusCache(status)
-
- if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
- l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
- }
-
- event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
- event.Add("status", model.STATUS_AWAY)
- event.Add("user_id", status.UserId)
- a.Go(func() {
- a.Publish(event)
- })
+ a.SaveAndBroadcastStatus(status)
}
func (a *App) SetStatusDoNotDisturb(userId string) {
@@ -309,18 +285,22 @@ func (a *App) SetStatusDoNotDisturb(userId string) {
status.Status = model.STATUS_DND
status.Manual = true
+ a.SaveAndBroadcastStatus(status)
+}
+
+func (a *App) SaveAndBroadcastStatus(status *model.Status) *model.AppError {
a.AddStatusCache(status)
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
- l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
+ l4g.Error(utils.T("api.status.save_status.error"), status.UserId, result.Err)
}
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
- event.Add("status", model.STATUS_DND)
+ event.Add("status", status.Status)
event.Add("user_id", status.UserId)
- a.Go(func() {
- a.Publish(event)
- })
+ a.Publish(event)
+
+ return nil
}
func GetStatusFromCache(userId string) *model.Status {
diff --git a/app/status_test.go b/app/status_test.go
new file mode 100644
index 000000000..bf5736a48
--- /dev/null
+++ b/app/status_test.go
@@ -0,0 +1,40 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "testing"
+
+ "github.com/mattermost/mattermost-server/model"
+)
+
+func TestSaveStatus(t *testing.T) {
+ th := Setup().InitBasic()
+ defer th.TearDown()
+
+ user := th.BasicUser
+
+ for _, statusString := range []string{
+ model.STATUS_ONLINE,
+ model.STATUS_AWAY,
+ model.STATUS_DND,
+ model.STATUS_OFFLINE,
+ } {
+ t.Run(statusString, func(t *testing.T) {
+ status := &model.Status{
+ UserId: user.Id,
+ Status: statusString,
+ }
+
+ th.App.SaveAndBroadcastStatus(status)
+
+ after, err := th.App.GetStatus(user.Id)
+ if err != nil {
+ t.Fatalf("failed to get status after save: %v", err)
+ } else if after.Status != statusString {
+ t.Fatalf("failed to save status, got %v, expected %v", after.Status, statusString)
+ }
+ })
+ }
+}
diff --git a/app/team.go b/app/team.go
index a6e79e9a6..a7b32af33 100644
--- a/app/team.go
+++ b/app/team.go
@@ -139,9 +139,7 @@ func (a *App) sendTeamEvent(team *model.Team, event string) {
message := model.NewWebSocketEvent(event, "", "", "", nil)
message.Add("team", sanitizedTeam.ToJson())
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) {
@@ -182,10 +180,7 @@ func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles strin
func (a *App) sendUpdatedMemberRoleEvent(userId string, member *model.TeamMember) {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_MEMBERROLE_UPDATED, "", "", userId, nil)
message.Add("member", member.ToJson())
-
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
func (a *App) AddUserToTeam(teamId string, userId string, userRequestorId string) (*model.Team, *model.AppError) {
diff --git a/app/user.go b/app/user.go
index 2b160f9f5..1a444e123 100644
--- a/app/user.go
+++ b/app/user.go
@@ -34,7 +34,6 @@ const (
TOKEN_TYPE_PASSWORD_RECOVERY = "password_recovery"
TOKEN_TYPE_VERIFY_EMAIL = "verify_email"
PASSWORD_RECOVER_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
- VERIFY_EMAIL_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
IMAGE_PROFILE_PIXEL_DIMENSION = 128
)
@@ -202,9 +201,7 @@ func (a *App) CreateUser(user *model.User) (*model.User, *model.AppError) {
// This message goes to everyone, so the teamId, channelId and userId are irrelevant
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_NEW_USER, "", "", "", nil)
message.Add("user_id", ruser.Id)
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
return ruser, nil
}
@@ -508,6 +505,14 @@ func (a *App) GetUsersInChannel(channelId string, offset int, limit int) ([]*mod
}
}
+func (a *App) GetUsersInChannelByStatus(channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
+ if result := <-a.Srv.Store.User().GetProfilesInChannelByStatus(channelId, offset, limit); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.User), nil
+ }
+}
+
func (a *App) GetUsersInChannelMap(channelId string, offset int, limit int, asAdmin bool) (map[string]*model.User, *model.AppError) {
users, err := a.GetUsersInChannel(channelId, offset, limit)
if err != nil {
@@ -533,6 +538,15 @@ func (a *App) GetUsersInChannelPage(channelId string, page int, perPage int, asA
return a.sanitizeProfiles(users, asAdmin), nil
}
+func (a *App) GetUsersInChannelPageByStatus(channelId string, page int, perPage int, asAdmin bool) ([]*model.User, *model.AppError) {
+ users, err := a.GetUsersInChannelByStatus(channelId, page*perPage, perPage)
+ if err != nil {
+ return nil, err
+ }
+
+ return a.sanitizeProfiles(users, asAdmin), nil
+}
+
func (a *App) GetUsersNotInChannel(teamId string, channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
if result := <-a.Srv.Store.User().GetProfilesNotInChannel(teamId, channelId, offset, limit); result.Err != nil {
return nil, result.Err
@@ -832,7 +846,6 @@ func (a *App) SetProfileImageFromFile(userId string, file multipart.File) *model
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
message.Add("user", user)
-
a.Publish(message)
}
@@ -901,10 +914,6 @@ func (a *App) UpdateActive(user *model.User, active bool) (*model.User, *model.A
}
}
- if extra := <-a.Srv.Store.Channel().ExtraUpdateByUser(user.Id, model.GetMillis()); extra.Err != nil {
- return nil, extra.Err
- }
-
ruser := result.Data.([2]*model.User)[0]
options := a.Config().GetSanitizeOptions()
options["passwordupdate"] = false
@@ -1002,9 +1011,7 @@ func (a *App) sendUpdatedUserEvent(user model.User, asAdmin bool) {
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
message.Add("user", user)
- a.Go(func() {
- a.Publish(message)
- })
+ a.Publish(message)
}
func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User, *model.AppError) {
diff --git a/app/user_test.go b/app/user_test.go
index 38ff286b3..94052da61 100644
--- a/app/user_test.go
+++ b/app/user_test.go
@@ -299,3 +299,132 @@ func createGitlabUser(t *testing.T, a *App, email string, username string) (*mod
return user, gitlabUserObj
}
+
+func TestGetUsersByStatus(t *testing.T) {
+ th := Setup()
+ defer th.TearDown()
+
+ team := th.CreateTeam()
+ channel, err := th.App.CreateChannel(&model.Channel{
+ DisplayName: "dn_" + model.NewId(),
+ Name: "name_" + model.NewId(),
+ Type: model.CHANNEL_OPEN,
+ TeamId: team.Id,
+ CreatorId: model.NewId(),
+ }, false)
+ if err != nil {
+ t.Fatalf("failed to create channel: %v", err)
+ }
+
+ createUserWithStatus := func(username string, status string) *model.User {
+ id := model.NewId()
+
+ user, err := th.App.CreateUser(&model.User{
+ Email: "success+" + id + "@simulator.amazonses.com",
+ Username: "un_" + username + "_" + id,
+ Nickname: "nn_" + id,
+ Password: "Password1",
+ })
+ if err != nil {
+ t.Fatalf("failed to create user: %v", err)
+ }
+
+ th.LinkUserToTeam(user, team)
+ th.AddUserToChannel(user, channel)
+
+ th.App.SaveAndBroadcastStatus(&model.Status{
+ UserId: user.Id,
+ Status: status,
+ Manual: true,
+ })
+
+ return user
+ }
+
+ // Creating these out of order in case that affects results
+ awayUser1 := createUserWithStatus("away1", model.STATUS_AWAY)
+ awayUser2 := createUserWithStatus("away2", model.STATUS_AWAY)
+ dndUser1 := createUserWithStatus("dnd1", model.STATUS_DND)
+ dndUser2 := createUserWithStatus("dnd2", model.STATUS_DND)
+ offlineUser1 := createUserWithStatus("offline1", model.STATUS_OFFLINE)
+ offlineUser2 := createUserWithStatus("offline2", model.STATUS_OFFLINE)
+ onlineUser1 := createUserWithStatus("online1", model.STATUS_ONLINE)
+ onlineUser2 := createUserWithStatus("online2", model.STATUS_ONLINE)
+
+ t.Run("sorting by status then alphabetical", func(t *testing.T) {
+ usersByStatus, err := th.App.GetUsersInChannelPageByStatus(channel.Id, 0, 8, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expectedUsersByStatus := []*model.User{
+ onlineUser1,
+ onlineUser2,
+ awayUser1,
+ awayUser2,
+ dndUser1,
+ dndUser2,
+ offlineUser1,
+ offlineUser2,
+ }
+
+ if len(usersByStatus) != len(expectedUsersByStatus) {
+ t.Fatalf("received only %v users, expected %v", len(usersByStatus), len(expectedUsersByStatus))
+ }
+
+ for i := range usersByStatus {
+ if usersByStatus[i].Id != expectedUsersByStatus[i].Id {
+ t.Fatalf("received user %v at index %v, expected %v", usersByStatus[i].Username, i, expectedUsersByStatus[i].Username)
+ }
+ }
+ })
+
+ t.Run("paging", func(t *testing.T) {
+ usersByStatus, err := th.App.GetUsersInChannelPageByStatus(channel.Id, 0, 3, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(usersByStatus) != 3 {
+ t.Fatal("received too many users")
+ }
+
+ if usersByStatus[0].Id != onlineUser1.Id && usersByStatus[1].Id != onlineUser2.Id {
+ t.Fatal("expected to receive online users first")
+ }
+
+ if usersByStatus[2].Id != awayUser1.Id {
+ t.Fatal("expected to receive away users second")
+ }
+
+ usersByStatus, err = th.App.GetUsersInChannelPageByStatus(channel.Id, 1, 3, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if usersByStatus[0].Id != awayUser2.Id {
+ t.Fatal("expected to receive away users second")
+ }
+
+ if usersByStatus[1].Id != dndUser1.Id && usersByStatus[2].Id != dndUser2.Id {
+ t.Fatal("expected to receive dnd users third")
+ }
+
+ usersByStatus, err = th.App.GetUsersInChannelPageByStatus(channel.Id, 1, 4, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(usersByStatus) != 4 {
+ t.Fatal("received too many users")
+ }
+
+ if usersByStatus[0].Id != dndUser1.Id && usersByStatus[1].Id != dndUser2.Id {
+ t.Fatal("expected to receive dnd users third")
+ }
+
+ if usersByStatus[2].Id != offlineUser1.Id && usersByStatus[3].Id != offlineUser2.Id {
+ t.Fatal("expected to receive offline users last")
+ }
+ })
+}
diff --git a/app/webhook.go b/app/webhook.go
index f3777ab48..abfc388b5 100644
--- a/app/webhook.go
+++ b/app/webhook.go
@@ -225,7 +225,7 @@ func SplitWebhookPost(post *model.Post) ([]*model.Post, *model.AppError) {
func (a *App) CreateWebhookPost(userId string, channel *model.Channel, text, overrideUsername, overrideIconUrl string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError) {
// parse links into Markdown format
- linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
+ linkWithTextRegex := regexp.MustCompile(`<([^\n<\|>]+)\|([^\n>]+)>`)
text = linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
post := &model.Post{UserId: userId, ChannelId: channel.Id, Message: text, Type: postType, RootId: postRootId}
diff --git a/app/webhook_test.go b/app/webhook_test.go
index 850e74efc..4d2bc58fa 100644
--- a/app/webhook_test.go
+++ b/app/webhook_test.go
@@ -317,6 +317,64 @@ func TestCreateWebhookPost(t *testing.T) {
if err == nil {
t.Fatal("should have failed - bad post type")
}
+
+ expectedText := "`<>|<>|`"
+ post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
+ "attachments": []*model.SlackAttachment{
+ {
+ Text: "text",
+ },
+ },
+ "webhook_display_name": hook.DisplayName,
+ }, model.POST_SLACK_ATTACHMENT, "")
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ assert.Equal(t, expectedText, post.Message)
+
+ expectedText = "< | \n|\n>"
+ post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
+ "attachments": []*model.SlackAttachment{
+ {
+ Text: "text",
+ },
+ },
+ "webhook_display_name": hook.DisplayName,
+ }, model.POST_SLACK_ATTACHMENT, "")
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ assert.Equal(t, expectedText, post.Message)
+
+ expectedText = `commit bc95839e4a430ace453e8b209a3723c000c1729a
+Author: foo <foo@example.org>
+Date: Thu Mar 1 19:46:54 2018 +0300
+
+ commit message 2
+
+ test | 1 +
+ 1 file changed, 1 insertion(+)
+
+commit 5df78b7139b543997838071cd912e375d8bd69b2
+Author: foo <foo@example.org>
+Date: Thu Mar 1 19:46:48 2018 +0300
+
+ commit message 1
+
+ test | 3 +++
+ 1 file changed, 3 insertions(+)`
+ post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
+ "attachments": []*model.SlackAttachment{
+ {
+ Text: "text",
+ },
+ },
+ "webhook_display_name": hook.DisplayName,
+ }, model.POST_SLACK_ATTACHMENT, "")
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+ assert.Equal(t, expectedText, post.Message)
}
func TestSplitWebhookPost(t *testing.T) {