[APIv4] add getChannelMembersTimezone (#9286)

* add getChannelMembersTimezone

* update per feedback review

* add delimeter to error
Этот коммит содержится в:
Carlos Tadeu Panato Junior
2018-10-13 12:35:57 +02:00
коммит произвёл GitHub
родитель e87965f39d
Коммит 908ed5555f
11 изменённых файлов: 203 добавлений и 7 удалений

Просмотреть файл

@@ -33,7 +33,7 @@ func (api *API) InitChannel() {
api.BaseRoutes.Channel.Handle("", api.ApiSessionRequired(deleteChannel)).Methods("DELETE") api.BaseRoutes.Channel.Handle("", api.ApiSessionRequired(deleteChannel)).Methods("DELETE")
api.BaseRoutes.Channel.Handle("/stats", api.ApiSessionRequired(getChannelStats)).Methods("GET") api.BaseRoutes.Channel.Handle("/stats", api.ApiSessionRequired(getChannelStats)).Methods("GET")
api.BaseRoutes.Channel.Handle("/pinned", api.ApiSessionRequired(getPinnedPosts)).Methods("GET") api.BaseRoutes.Channel.Handle("/pinned", api.ApiSessionRequired(getPinnedPosts)).Methods("GET")
api.BaseRoutes.Channel.Handle("/timezones", api.ApiSessionRequired(getChannelMembersTimezones)).Methods("GET")
api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET") api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET")
api.BaseRoutes.ChannelByName.Handle("", api.ApiSessionRequired(getChannelByName)).Methods("GET") api.BaseRoutes.ChannelByName.Handle("", api.ApiSessionRequired(getChannelByName)).Methods("GET")
@@ -821,6 +821,26 @@ func getChannelMembers(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(members.ToJson())) w.Write([]byte(members.ToJson()))
} }
func getChannelMembersTimezones(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireChannelId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToChannel(c.Session, c.Params.ChannelId, model.PERMISSION_READ_CHANNEL) {
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
return
}
membersTimezones, err := c.App.GetChannelMembersTimezones(c.Params.ChannelId)
if err != nil {
c.Err = err
return
}
w.Write([]byte(model.ArrayToJson(membersTimezones)))
}
func getChannelMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) { func getChannelMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireChannelId() c.RequireChannelId()
if c.Err != nil { if c.Err != nil {

Просмотреть файл

@@ -2335,3 +2335,52 @@ func TestUpdateChannelScheme(t *testing.T) {
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id) _, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
CheckUnauthorizedStatus(t, resp) CheckUnauthorizedStatus(t, resp)
} }
func TestGetChannelMembersTimezones(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer th.TearDown()
Client := th.Client
user := th.BasicUser
user.Timezone["useAutomaticTimezone"] = "false"
user.Timezone["manualTimezone"] = "XOXO/BLABLA"
_, resp := Client.UpdateUser(user)
CheckNoError(t, resp)
user2 := th.BasicUser2
user2.Timezone["automaticTimezone"] = "NoWhere/Island"
_, resp = th.SystemAdminClient.UpdateUser(user2)
CheckNoError(t, resp)
timezone, resp := Client.GetChannelMembersTimezones(th.BasicChannel.Id)
CheckNoError(t, resp)
if len(timezone) != 2 {
t.Fatal("should return 2 timezones")
}
//both users have same timezone
user2.Timezone["automaticTimezone"] = "XOXO/BLABLA"
_, resp = th.SystemAdminClient.UpdateUser(user2)
CheckNoError(t, resp)
timezone, resp = Client.GetChannelMembersTimezones(th.BasicChannel.Id)
CheckNoError(t, resp)
if len(timezone) != 1 {
t.Fatal("should return 1 timezone")
}
//no timezone set should return empty
user2.Timezone["automaticTimezone"] = ""
_, resp = th.SystemAdminClient.UpdateUser(user2)
CheckNoError(t, resp)
user.Timezone["manualTimezone"] = ""
_, resp = Client.UpdateUser(user)
timezone, resp = Client.GetChannelMembersTimezones(th.BasicChannel.Id)
CheckNoError(t, resp)
if len(timezone) > 0 {
t.Fatal("should return 0 timezone")
}
}

Просмотреть файл

@@ -1153,6 +1153,24 @@ func (a *App) GetChannelMembersPage(channelId string, page, perPage int) (*model
return result.Data.(*model.ChannelMembers), nil return result.Data.(*model.ChannelMembers), nil
} }
func (a *App) GetChannelMembersTimezones(channelId string) ([]string, *model.AppError) {
result := <-a.Srv.Store.Channel().GetChannelMembersTimezones(channelId)
if result.Err != nil {
return nil, result.Err
}
membersTimezones := result.Data.([]map[string]string)
var timezones []string
for _, membersTimezone := range membersTimezones {
if membersTimezone["automaticTimezone"] == "" && membersTimezone["manualTimezone"] == "" {
continue
}
timezones = append(timezones, model.GetPreferredTimezone(membersTimezone))
}
return model.RemoveDuplicateStrings(timezones), nil
}
func (a *App) GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) { func (a *App) GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) {
result := <-a.Srv.Store.Channel().GetMembersByIds(channelId, userIds) result := <-a.Srv.Store.Channel().GetMembersByIds(channelId, userIds)
if result.Err != nil { if result.Err != nil {

Просмотреть файл

@@ -4,6 +4,7 @@
package app package app
import ( import (
"strings"
"testing" "testing"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
@@ -709,3 +710,40 @@ func TestRenameChannel(t *testing.T) {
}) })
} }
} }
func TestGetChannelMembersTimezones(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer th.TearDown()
userRequestorId := ""
postRootId := ""
if _, err := th.App.AddChannelMember(th.BasicUser2.Id, th.BasicChannel, userRequestorId, postRootId, false); err != nil {
t.Fatal("Failed to add user to channel. Error: " + err.Message)
}
user := th.BasicUser
user.Timezone["useAutomaticTimezone"] = "false"
user.Timezone["manualTimezone"] = "XOXO/BLABLA"
th.App.UpdateUser(user, false)
user2 := th.BasicUser2
user2.Timezone["automaticTimezone"] = "NoWhere/Island"
th.App.UpdateUser(user2, false)
user3 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser, _ := th.App.CreateUser(&user3)
th.App.AddUserToChannel(ruser, th.BasicChannel)
ruser.Timezone["automaticTimezone"] = "NoWhere/Island"
th.App.UpdateUser(ruser, false)
user4 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser, _ = th.App.CreateUser(&user4)
th.App.AddUserToChannel(ruser, th.BasicChannel)
timezones, err := th.App.GetChannelMembersTimezones(th.BasicChannel.Id)
if err != nil {
t.Fatal("Failed to get the timezones for a channel. Error: " + err.Error())
}
assert.Equal(t, 2, len(timezones))
}

Просмотреть файл

@@ -4986,6 +4986,10 @@
"id": "store.sql_channel.get_members.app_error", "id": "store.sql_channel.get_members.app_error",
"translation": "Unable to get the channel members" "translation": "Unable to get the channel members"
}, },
{
"id": "store.sql_channel.get_timezone.app_error",
"translation": "We couldn't get the channel members timezones"
},
{ {
"id": "store.sql_channel.get_members_by_ids.app_error", "id": "store.sql_channel.get_members_by_ids.app_error",
"translation": "Unable to get the channel members" "translation": "Unable to get the channel members"

Просмотреть файл

@@ -1746,6 +1746,17 @@ func (c *Client4) GetChannelStats(channelId string, etag string) (*ChannelStats,
} }
} }
// GetChannelMembersTimezones gets a list of timezones for a channel.
func (c *Client4) GetChannelMembersTimezones(channelId string) ([]string, *Response) {
r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/timezones", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
// GetPinnedPosts gets a list of pinned posts. // GetPinnedPosts gets a list of pinned posts.
func (c *Client4) GetPinnedPosts(channelId string, etag string) (*PostList, *Response) { func (c *Client4) GetPinnedPosts(channelId string, etag string) (*PostList, *Response) {
if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", etag); err != nil { if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", etag); err != nil {

Просмотреть файл

@@ -499,11 +499,7 @@ func (u *User) IsSAMLUser() bool {
} }
func (u *User) GetPreferredTimezone() string { func (u *User) GetPreferredTimezone() string {
if u.Timezone["useAutomaticTimezone"] == "true" { return GetPreferredTimezone(u.Timezone)
return u.Timezone["automaticTimezone"]
}
return u.Timezone["manualTimezone"]
} }
// UserFromJson will decode the input and return a User // UserFromJson will decode the input and return a User

Просмотреть файл

@@ -564,3 +564,26 @@ func IsDomainName(s string) bool {
return ok return ok
} }
func RemoveDuplicateStrings(in []string) []string {
out := []string{}
seen := make(map[string]bool, len(in))
for _, item := range in {
if !seen[item] {
out = append(out, item)
seen[item] = true
}
}
return out
}
func GetPreferredTimezone(timezone StringMap) string {
if timezone["useAutomaticTimezone"] == "true" {
return timezone["automaticTimezone"]
}
return timezone["manualTimezone"]
}

Просмотреть файл

@@ -1197,7 +1197,7 @@ func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) store.S
var dbMembers channelMemberWithSchemeRolesList var dbMembers channelMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset}) _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
if err != nil { if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+","+err.Error(), http.StatusInternalServerError)
return return
} }
@@ -1205,6 +1205,27 @@ func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) store.S
}) })
} }
func (s SqlChannelStore) GetChannelMembersTimezones(channelId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var dbMembersTimezone []map[string]string
_, err := s.GetReplica().Select(&dbMembersTimezone, `
SELECT
Users.Timezone
FROM
ChannelMembers
LEFT JOIN
Users ON ChannelMembers.UserId = Id
WHERE ChannelId = :ChannelId
`, map[string]interface{}{
"ChannelId": channelId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetChannelMembersTimezones", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+","+err.Error(), http.StatusInternalServerError)
return
}
result.Data = dbMembersTimezone
})
}
func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel { func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var dbMember channelMemberWithSchemeRoles var dbMember channelMemberWithSchemeRoles

Просмотреть файл

@@ -147,6 +147,7 @@ type ChannelStore interface {
UpdateMember(member *model.ChannelMember) StoreChannel UpdateMember(member *model.ChannelMember) StoreChannel
GetMembers(channelId string, offset, limit int) StoreChannel GetMembers(channelId string, offset, limit int) StoreChannel
GetMember(channelId string, userId string) StoreChannel GetMember(channelId string, userId string) StoreChannel
GetChannelMembersTimezones(channelId string) StoreChannel
GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) StoreChannel GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) StoreChannel
InvalidateAllChannelMembersForUser(userId string) InvalidateAllChannelMembersForUser(userId string)
IsUserInChannelUseCache(userId string, channelId string) bool IsUserInChannelUseCache(userId string, channelId string) bool

Просмотреть файл

@@ -442,6 +442,21 @@ func (_m *ChannelStore) GetMember(channelId string, userId string) store.StoreCh
return r0 return r0
} }
func (_m *ChannelStore) GetChannelMembersTimezones(channelId string) store.StoreChannel {
ret := _m.Called(channelId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(channelId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetMemberCount provides a mock function with given fields: channelId, allowFromCache // GetMemberCount provides a mock function with given fields: channelId, allowFromCache
func (_m *ChannelStore) GetMemberCount(channelId string, allowFromCache bool) store.StoreChannel { func (_m *ChannelStore) GetMemberCount(channelId string, allowFromCache bool) store.StoreChannel {
ret := _m.Called(channelId, allowFromCache) ret := _m.Called(channelId, allowFromCache)