Commit 365b8b46 authored by Joram Wilander's avatar Joram Wilander Committed by GitHub

Merging performance branch into master (#4268)

* improve performance on sendNotifications

* Fix SQL queries

* Remove get direct profiles, not needed anymore

* Add raw data to error details if AppError fails to decode

* men

* Fix decode (#4052)

* Fixing json decode

* Adding unit test

* Initial work for client scaling (#4051)

* Begin adding paging to profiles API

* Added more paging functionality

* Finish hooking up admin console user lists

* Add API for searching users and add searching to all user lists

* Add lazy loading of profiles

* Revert config.json

* Fix unit tests and some style issues

* Add GetProfilesFromList to Go driver and fix web unit test

* Update etag for GetProfiles

* Updating ui for filters and pagination (#4044)

* Updating UI for pagination

* Adjusting margins for filter row

* Adjusting margin for specific modals

* Adding relative padding to system console

* Adjusting responsive view

* Update client user tests

* Minor fixes for direct messages modal (#4056)

* Remove some unneeded initial load calls (#4057)

* UX updates to user lists, added smart counts and bug fixes (#4059)

* Improved getExplicitMentions and unit tests (#4064)

* Refactor getting posts to lazy load profiles correctly (#4062)

* Comment out SetActiveChannel test (#4066)

* Profiler cpu, block, and memory profiler. (#4081)

* Fix TestSetActiveChannel unit test (#4071)

* Fixing build failure caused by dependancies updating (#4076)

* Adding profiler

* Fix admin_team_member_dropdown eslint errors

* Bumping session cache size (#4077)

* Bumping session cache size

* Bumping status cache

* Refactor how the client handles channel members to be large team friendly (#4106)

* Refactor how the client handles channel members to be large team friendly

* Change Id to ChannelId in ChannelStats model

* Updated getChannelMember and getProfilesByIds routes to match proposal

* Performance improvements (#4100)

* Performance improvements

* Fixing re-connect issue

* Fixing error message

* Some other minor perf tweaks

* Some other minor perf tweaks

* Fixing config file

* Fixing buffer size

* Fixing web socket send message

* adding some error logging

* fix getMe to be user required

* Fix websocket event for new user

* Fixing shutting down

* Reverting web socket changes

* Fixing logging lvl

* Adding caching to GetMember

* Adding some logging

* Fixing caching

* Fixing caching invalidate

* Fixing direct message caching

* Fixing caching

* Fixing caching

* Remove GetDirectProfiles from initial load

* Adding logging and fixing websocket client

* Adding back caching from bad merge.

* Explicitly close go driver requests (#4162)

* Refactored how the client handles team members to be more large team friendly (#4159)

* Refactor getProfilesForDirectMessageList API into getAllProfiles API

* Refactored how the client handles team members to be more large team friendly

* Fix js error when receiving a notification

* Fix JS error caused by current user being overwritten with sanitized version (#4165)

* Adding error message to status failure (#4167)

* Fix a few bugs caused by client scaling refactoring (#4170)

* When there is no read replica, don't open a second set of connections to the master database (#4173)

* Adding connection tacking to stats (#4174)

* Reduce DB writes for statuses and other status related changes (#4175)

* Fix bug preventing opening of DM channels from more modal (#4181)

* 	Fixing socket timing error (#4183)

* Fixing ping/pong handler

* Fixing socket timing error

* Commenting out status broadcasting

* Removing user status changes

* Removing user status changes

* Removing user status changes

* Removing user status changes

* Adding DoPreComputeJson()

* Performance improvements (#4194)

* * Fix System Console Analytics queries
* Add db.SetConnMaxLifetime to 15 minutes
* Add "net/http/pprof" for profiling
* Add FreeOSMemory() to manually release memory on reload config

* Add flag to enable http profiler

* Fix memory leak (#4197)

* Fix memory leak

* removed unneeded nil assignment

* Fixing go routine leak (#4208)

* Merge fixes

* Merge fix

* Refactored statuses to be queried by the client rather than broadcast by the server (#4212)

* Refactored server code to reduce status broadcasts and to allow getting statuses by IDs

* Refactor client code to periodically fetch statuses

* Add store unit test for getting statuses by ids

* Fix status unit test

* Add getStatusesByIds REST API and move the client over to use that instead of the WebSocket

* Adding multiple threads to websocket hub (#4230)

* Adding multiple threads to websocket hub

* Fixing unit tests

* Fixing so websocket connections from the same user end up in the same… (#4240)

* Fixing so websocket connections from the same user end up in the same list

* Removing old comment

* Refactor user autocomplete to query the server (#4239)

* Add API for autocompleting users

* Converted at mention autocomplete to query server

* Converted user search autocomplete to query server

* Switch autocomplete API naming to use term instead of username

* Split autocomplete API into two, one for channels and for teams

* Fix copy/paste error

* Some final client scaling fixes (#4246)

* Add lazy loading of profiles to integration pages

* Add lazy loading of profiles to emoji page

* Fix JS error when receiving post in select team menu and also clean up channel store
parent 0512bd26
......@@ -361,7 +361,7 @@ run-server: prepare-enterprise start-docker
run-cli: prepare-enterprise start-docker
@echo Running mattermost for development
@echo Example should be like >'make ARGS="-version" run-cli'
@echo Example should be like 'make ARGS="-version" run-cli'
$(GO) run $(GOFLAGS) $(GO_LINKER_FLAGS) *.go ${ARGS}
......
......@@ -20,6 +20,7 @@ import (
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
"runtime/debug"
)
func InitAdmin() {
......@@ -48,7 +49,7 @@ func InitAdmin() {
BaseRoutes.Admin.Handle("/remove_certificate", ApiAdminSystemRequired(removeCertificate)).Methods("POST")
BaseRoutes.Admin.Handle("/saml_cert_status", ApiAdminSystemRequired(samlCertificateStatus)).Methods("GET")
BaseRoutes.Admin.Handle("/cluster_status", ApiAdminSystemRequired(getClusterStatus)).Methods("GET")
BaseRoutes.Admin.Handle("/recently_active_users/{team_id:[A-Za-z0-9]+}", ApiUserRequiredActivity(getRecentlyActiveUsers, false)).Methods("GET")
BaseRoutes.Admin.Handle("/recently_active_users/{team_id:[A-Za-z0-9]+}", ApiUserRequired(getRecentlyActiveUsers)).Methods("GET")
}
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -134,6 +135,7 @@ func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
}
func reloadConfig(c *Context, w http.ResponseWriter, r *http.Request) {
debug.FreeOSMemory()
utils.LoadConfig(utils.CfgFileName)
// start/restart email batching job if necessary
......@@ -338,12 +340,15 @@ func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
name := params["name"]
if name == "standard" {
var rows model.AnalyticsRows = make([]*model.AnalyticsRow, 5)
var rows model.AnalyticsRows = make([]*model.AnalyticsRow, 8)
rows[0] = &model.AnalyticsRow{"channel_open_count", 0}
rows[1] = &model.AnalyticsRow{"channel_private_count", 0}
rows[2] = &model.AnalyticsRow{"post_count", 0}
rows[3] = &model.AnalyticsRow{"unique_user_count", 0}
rows[4] = &model.AnalyticsRow{"team_count", 0}
rows[5] = &model.AnalyticsRow{"total_websocket_connections", 0}
rows[6] = &model.AnalyticsRow{"total_master_db_connections", 0}
rows[7] = &model.AnalyticsRow{"total_read_db_connections", 0}
openChan := Srv.Store.Channel().AnalyticsTypeCount(teamId, model.CHANNEL_OPEN)
privateChan := Srv.Store.Channel().AnalyticsTypeCount(teamId, model.CHANNEL_PRIVATE)
......@@ -386,6 +391,10 @@ func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
rows[4].Value = float64(r.Data.(int64))
}
rows[5].Value = float64(TotalWebsocketConnections())
rows[6].Value = float64(Srv.Store.TotalMasterDbConnections())
rows[7].Value = float64(Srv.Store.TotalReadDbConnections())
w.Write([]byte(rows.ToJson()))
} else if name == "post_counts_day" {
if r := <-Srv.Store.Post().AnalyticsPostCountsByDay(teamId); r.Err != nil {
......@@ -706,32 +715,14 @@ func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) {
}
func getRecentlyActiveUsers(c *Context, w http.ResponseWriter, r *http.Request) {
statusMap := map[string]interface{}{}
if result := <-Srv.Store.Status().GetAllFromTeam(c.TeamId); result.Err != nil {
c.Err = result.Err
return
} else {
statuses := result.Data.([]*model.Status)
for _, s := range statuses {
statusMap[s.UserId] = s.LastActivityAt
}
}
if result := <-Srv.Store.User().GetProfiles(c.TeamId); result.Err != nil {
if result := <-Srv.Store.User().GetRecentlyActiveUsersForTeam(c.TeamId); result.Err != nil {
c.Err = result.Err
return
} else {
profiles := result.Data.(map[string]*model.User)
for k, p := range profiles {
p = sanitizeProfile(c, p)
if lastActivityAt, ok := statusMap[p.Id].(int64); ok {
p.LastActivityAt = lastActivityAt
}
profiles[k] = p
for _, p := range profiles {
sanitizeProfile(c, p)
}
w.Write([]byte(model.UserMapToJson(profiles)))
......
......@@ -527,17 +527,13 @@ func TestAdminLdapSyncNow(t *testing.T) {
}
}
// Needs more work
func TestGetRecentlyActiveUsers(t *testing.T) {
th := Setup().InitBasic()
user1Id := th.BasicUser.Id
user2Id := th.BasicUser2.Id
if userMap, err := th.BasicClient.GetRecentlyActiveUsers(th.BasicTeam.Id); err != nil {
t.Fatal(err)
} else if len(userMap.Data.(map[string]*model.User)) != 2 {
t.Fatal("should have been 2")
} else if userMap.Data.(map[string]*model.User)[user1Id].Id != user1Id || userMap.Data.(map[string]*model.User)[user2Id].Id != user2Id {
t.Fatal("should have been valid")
} else if len(userMap.Data.(map[string]*model.User)) >= 2 {
t.Fatal("should have been at least 2")
}
}
......@@ -36,7 +36,7 @@ func SetupEnterprise() *TestHelper {
*utils.Cfg.RateLimitSettings.Enable = false
utils.DisableDebugLogForTest()
utils.License.Features.SetDefaults()
NewServer()
NewServer(false)
StartServer()
utils.InitHTML()
InitApi()
......@@ -57,7 +57,7 @@ func Setup() *TestHelper {
utils.Cfg.TeamSettings.MaxUsersPerTeam = 50
*utils.Cfg.RateLimitSettings.Enable = false
utils.DisableDebugLogForTest()
NewServer()
NewServer(false)
StartServer()
InitApi()
utils.EnableDebugLogForTest()
......
......@@ -5,6 +5,7 @@ package api
import (
"net/http"
"strings"
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/model"
......@@ -65,15 +66,16 @@ func HasPermissionToTeam(user *model.User, teamMember *model.TeamMember, permiss
}
func HasPermissionToChannelContext(c *Context, channelId string, permission *model.Permission) bool {
cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId)
cmc := Srv.Store.Channel().GetAllChannelMembersForUser(c.Session.UserId, true)
var channelRoles []string
if cmcresult := <-cmc; cmcresult.Err == nil {
channelMember := cmcresult.Data.(model.ChannelMember)
channelRoles = channelMember.GetRoles()
if CheckIfRolesGrantPermission(channelRoles, permission.Id) {
return true
ids := cmcresult.Data.(map[string]string)
if roles, ok := ids[channelId]; ok {
channelRoles = strings.Fields(roles)
if CheckIfRolesGrantPermission(channelRoles, permission.Id) {
return true
}
}
}
......
......@@ -80,6 +80,13 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
ruser := result.Data.(*model.User)
status := &model.Status{ruser.Id, model.STATUS_ONLINE, false, model.GetMillis(), ""}
if result := <-Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
result.Err.Translate(utils.T)
l4g.Error(result.Err.Error())
return nil, false
}
// We need to cheat to verify the user's email
store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
......
......@@ -6,7 +6,6 @@ package api
import (
"fmt"
"net/http"
"strconv"
"strings"
l4g "github.com/alecthomas/log4go"
......@@ -16,16 +15,12 @@ import (
"github.com/mattermost/platform/utils"
)
const (
defaultExtraMemberLimit = 100
)
func InitChannel() {
l4g.Debug(utils.T("api.channel.init.debug"))
BaseRoutes.Channels.Handle("/", ApiUserRequiredActivity(getChannels, false)).Methods("GET")
BaseRoutes.Channels.Handle("/", ApiUserRequired(getChannels)).Methods("GET")
BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET")
BaseRoutes.Channels.Handle("/counts", ApiUserRequiredActivity(getChannelCounts, false)).Methods("GET")
BaseRoutes.Channels.Handle("/counts", ApiUserRequired(getChannelCounts)).Methods("GET")
BaseRoutes.Channels.Handle("/create", ApiUserRequired(createChannel)).Methods("POST")
BaseRoutes.Channels.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST")
BaseRoutes.Channels.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST")
......@@ -35,9 +30,9 @@ func InitChannel() {
BaseRoutes.NeedChannelName.Handle("/join", ApiUserRequired(join)).Methods("POST")
BaseRoutes.NeedChannel.Handle("/", ApiUserRequiredActivity(getChannel, false)).Methods("GET")
BaseRoutes.NeedChannel.Handle("/extra_info", ApiUserRequired(getChannelExtraInfo)).Methods("GET")
BaseRoutes.NeedChannel.Handle("/extra_info/{member_limit:-?[0-9]+}", ApiUserRequired(getChannelExtraInfo)).Methods("GET")
BaseRoutes.NeedChannel.Handle("/", ApiUserRequired(getChannel)).Methods("GET")
BaseRoutes.NeedChannel.Handle("/stats", ApiUserRequired(getChannelStats)).Methods("GET")
BaseRoutes.NeedChannel.Handle("/members/{user_id:[A-Za-z0-9]+}", ApiUserRequired(getChannelMember)).Methods("GET")
BaseRoutes.NeedChannel.Handle("/join", ApiUserRequired(join)).Methods("POST")
BaseRoutes.NeedChannel.Handle("/leave", ApiUserRequired(leave)).Methods("POST")
BaseRoutes.NeedChannel.Handle("/delete", ApiUserRequired(deleteChannel)).Methods("POST")
......@@ -150,11 +145,14 @@ func CreateDirectChannel(userId string, otherUserId string) (*model.Channel, *mo
} else {
channel := result.Data.(*model.Channel)
InvalidateCacheForUser(userId)
InvalidateCacheForUser(otherUserId)
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_DIRECT_ADDED, "", channel.Id, "", nil)
message.Add("teammate_id", otherUserId)
go Publish(message)
return result.Data.(*model.Channel), nil
return channel, nil
}
}
......@@ -566,6 +564,7 @@ func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelM
go func() {
InvalidateCacheForUser(user.Id)
Srv.Store.User().InvalidateProfilesInChannelCache(channel.Id)
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_ADDED, "", channel.Id, "", nil)
message.Add("user_id", user.Id)
......@@ -609,6 +608,8 @@ func JoinDefaultChannels(teamId string, user *model.User, channelRole string) *m
if _, err := CreatePost(fakeContext, post, false); err != nil {
l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
}
Srv.Store.User().InvalidateProfilesInChannelCache(result.Data.(*model.Channel).Id)
}
if result := <-Srv.Store.Channel().GetByName(teamId, "off-topic"); result.Err != nil {
......@@ -631,6 +632,8 @@ func JoinDefaultChannels(teamId string, user *model.User, channelRole string) *m
if _, err := CreatePost(fakeContext, post, false); err != nil {
l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
}
Srv.Store.User().InvalidateProfilesInChannelCache(result.Data.(*model.Channel).Id)
}
return err
......@@ -778,9 +781,9 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("name=" + channel.Name)
go func() {
InvalidateCacheForChannel(channel.Id)
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, c.TeamId, "", "", nil)
message.Add("channel_id", channel.Id)
go Publish(message)
post := &model.Post{
......@@ -917,54 +920,27 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
func getChannelStats(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["channel_id"]
var memberLimit int
if memberLimitString, ok := params["member_limit"]; !ok {
memberLimit = defaultExtraMemberLimit
} else if memberLimitInt64, err := strconv.ParseInt(memberLimitString, 10, 0); err != nil {
c.Err = model.NewLocAppError("getChannelExtraInfo", "api.channel.get_channel_extra_info.member_limit.app_error", nil, err.Error())
return
} else {
memberLimit = int(memberLimitInt64)
}
sc := Srv.Store.Channel().Get(id)
var channel *model.Channel
if cresult := <-sc; cresult.Err != nil {
c.Err = cresult.Err
if result := <-sc; result.Err != nil {
c.Err = result.Err
return
} else {
channel = cresult.Data.(*model.Channel)
channel = result.Data.(*model.Channel)
}
extraEtag := channel.ExtraEtag(memberLimit)
if HandleEtag(extraEtag, w, r) {
return
}
scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
ecm := Srv.Store.Channel().GetExtraMembers(id, memberLimit)
ccm := Srv.Store.Channel().GetMemberCount(id)
if cmresult := <-scm; cmresult.Err != nil {
c.Err = cmresult.Err
return
} else if ecmresult := <-ecm; ecmresult.Err != nil {
c.Err = ecmresult.Err
return
} else if ccmresult := <-ccm; ccmresult.Err != nil {
c.Err = ccmresult.Err
if result := <-Srv.Store.Channel().GetMemberCount(id); result.Err != nil {
c.Err = result.Err
return
} else {
//member := cmresult.Data.(model.ChannelMember)
extraMembers := ecmresult.Data.([]model.ExtraMember)
memberCount := ccmresult.Data.(int64)
memberCount := result.Data.(int64)
if channel.DeleteAt > 0 {
c.Err = model.NewLocAppError("getChannelExtraInfo", "api.channel.get_channel_extra_info.deleted.app_error", nil, "")
c.Err = model.NewLocAppError("getChannelStats", "api.channel.get_channel_extra_info.deleted.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
......@@ -973,12 +949,29 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
data := model.ChannelExtra{Id: channel.Id, Members: extraMembers, MemberCount: memberCount}
w.Header().Set(model.HEADER_ETAG_SERVER, extraEtag)
data := model.ChannelStats{ChannelId: channel.Id, MemberCount: memberCount}
w.Write([]byte(data.ToJson()))
}
}
func getChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
channelId := params["channel_id"]
userId := params["user_id"]
if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) {
return
}
if result := <-Srv.Store.Channel().GetMember(channelId, userId); result.Err != nil {
c.Err = result.Err
return
} else {
member := result.Data.(model.ChannelMember)
w.Write([]byte(member.ToJson()))
}
}
func addMember(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["channel_id"]
......@@ -1101,6 +1094,7 @@ func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel
}
InvalidateCacheForUser(userIdToRemove)
Srv.Store.User().InvalidateProfilesInChannelCache(channel.Id)
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
message.Add("user_id", userIdToRemove)
......
......@@ -10,7 +10,6 @@ import (
"time"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
)
......@@ -1106,7 +1105,7 @@ func TestDeleteChannel(t *testing.T) {
}
}
func TestGetChannelExtraInfo(t *testing.T) {
func TestGetChannelStats(t *testing.T) {
th := Setup().InitBasic()
Client := th.BasicClient
team := th.BasicTeam
......@@ -1114,115 +1113,13 @@ func TestGetChannelExtraInfo(t *testing.T) {
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
rget := Client.Must(Client.GetChannelExtraInfo(channel1.Id, -1, ""))
data := rget.Data.(*model.ChannelExtra)
if data.Id != channel1.Id {
rget := Client.Must(Client.GetChannelStats(channel1.Id, ""))
data := rget.Data.(*model.ChannelStats)
if data.ChannelId != channel1.Id {
t.Fatal("couldnt't get extra info")
} else if len(data.Members) != 1 {
t.Fatal("got incorrect members")
} else if data.MemberCount != 1 {
t.Fatal("got incorrect member count")
}
//
// Testing etag caching
//
currentEtag := rget.Etag
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil {
t.Fatal(err)
} else if cache_result.Data.(*model.ChannelExtra) != nil {
t.Log(cache_result.Data)
t.Fatal("response should be empty")
} else {
currentEtag = cache_result.Etag
}
Client2 := model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress)
user2 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Tester 2", Password: "passwd1"}
user2 = Client2.Must(Client2.CreateUser(user2, "")).Data.(*model.User)
LinkUserToTeam(user2, team)
Client2.SetTeamId(team.Id)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client2.Login(user2.Email, "passwd1")
Client2.Must(Client2.JoinChannel(channel1.Id))
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil {
t.Fatal(err)
} else if cache_result.Data.(*model.ChannelExtra) == nil {
t.Log(cache_result.Data)
t.Fatal("response should not be empty")
} else {
currentEtag = cache_result.Etag
}
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil {
t.Fatal(err)
} else if cache_result.Data.(*model.ChannelExtra) != nil {
t.Log(cache_result.Data)
t.Fatal("response should be empty")
} else {
currentEtag = cache_result.Etag
}
Client2.Must(Client2.LeaveChannel(channel1.Id))
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil {
t.Fatal(err)
} else if cache_result.Data.(*model.ChannelExtra) == nil {
t.Log(cache_result.Data)
t.Fatal("response should not be empty")
} else {
currentEtag = cache_result.Etag
}
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil {
t.Fatal(err)
} else if cache_result.Data.(*model.ChannelExtra) != nil {
t.Log(cache_result.Data)
t.Fatal("response should be empty")
} else {
currentEtag = cache_result.Etag
}
Client2.Must(Client2.JoinChannel(channel1.Id))
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, 2, currentEtag); err != nil {
t.Fatal(err)
} else if extra := cache_result.Data.(*model.ChannelExtra); extra == nil {
t.Fatal("response should not be empty")
} else if len(extra.Members) != 2 {
t.Fatal("should've returned 2 members")
} else if extra.MemberCount != 2 {
t.Fatal("should've returned member count of 2")
} else {
currentEtag = cache_result.Etag
}
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, 1, currentEtag); err != nil {
t.Fatal(err)
} else if extra := cache_result.Data.(*model.ChannelExtra); extra == nil {
t.Fatal("response should not be empty")
} else if len(extra.Members) != 1 {
t.Fatal("should've returned only 1 member")
} else if extra.MemberCount != 2 {
t.Fatal("should've returned member count of 2")
} else {
currentEtag = cache_result.Etag
}
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, 1, currentEtag); err != nil {
t.Fatal(err)
} else if cache_result.Data.(*model.ChannelExtra) != nil {
t.Log(cache_result.Data)
t.Fatal("response should be empty")
} else {
currentEtag = cache_result.Etag
}
}
func TestAddChannelMember(t *testing.T) {
......@@ -1495,3 +1392,41 @@ func TestFuzzyChannel(t *testing.T) {
}
}
}
func TestGetChannelMember(t *testing.T) {
th := Setup().InitBasic()
Client := th.BasicClient
team := th.BasicTeam
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
if result, err := Client.GetChannelMember(channel1.Id, th.BasicUser.Id); err != nil {
t.Fatal(err)
} else {
cm := result.Data.(*model.ChannelMember)
if cm.UserId != th.BasicUser.Id {
t.Fatal("user ids didn't match")
}
if cm.ChannelId != channel1.Id {
t.Fatal("channel ids didn't match")
}
}
if _, err := Client.GetChannelMember(channel1.Id, th.BasicUser2.Id); err == nil {
t.Fatal("should have failed - user not in channel")
}
if _, err := Client.GetChannelMember("junk", th.BasicUser2.Id); err == nil {
t.Fatal("should have failed - bad channel id")
}
if _, err := Client.GetChannelMember(channel1.Id, "junk"); err == nil {
t.Fatal("should have failed - bad user id")
}
if _, err := Client.GetChannelMember("junk", "junk"); err == nil {
t.Fatal("should have failed - bad channel and user id")
}
}
......@@ -68,7 +68,7 @@ func TestCliCreateUserWithTeam(t *testing.T) {
t.Fatal(err)
}
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfiles(th.SystemAdminTeam.Id, "")).Data.(map[string]*model.User)
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfilesInTeam(th.SystemAdminTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
found := false
......@@ -318,7 +318,7 @@ func TestCliJoinTeam(t *testing.T) {
t.Fatal(err)
}
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfiles(th.SystemAdminTeam.Id, "")).Data.(map[string]*model.User)
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfilesInTeam(th.SystemAdminTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
found := false
......@@ -348,7 +348,7 @@ func TestCliLeaveTeam(t *testing.T) {
t.Fatal(err)
}
profiles := th.BasicClient.Must(th.BasicClient.GetProfiles(th.BasicTeam.Id, "")).Data.(map[string]*model.User)
profiles := th.BasicClient.Must(th.BasicClient.GetProfilesInTeam(th.BasicTeam.Id, 0, 1000