diff options
author | enahum <nahumhbl@gmail.com> | 2016-09-06 15:48:43 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-06 15:48:43 -0300 |
commit | 35b816b92217237de922c928a6623b5ac426655a (patch) | |
tree | adf7e7a96bd4b50aae0b295f94f8501643e7fc38 | |
parent | 9a9ae3dcd1c248a5d9e6f986ea967fa8bbf06f19 (diff) | |
download | chat-35b816b92217237de922c928a6623b5ac426655a.tar.gz chat-35b816b92217237de922c928a6623b5ac426655a.tar.bz2 chat-35b816b92217237de922c928a6623b5ac426655a.zip |
PLT-3921 Fix System Console Recent Active Users (#3856)
* PLT-3921 System Console Recent Active Users
-rw-r--r-- | api/admin.go | 35 | ||||
-rw-r--r-- | api/admin_test.go | 15 | ||||
-rw-r--r-- | i18n/en.json | 4 | ||||
-rw-r--r-- | model/client.go | 11 | ||||
-rw-r--r-- | model/user.go | 1 | ||||
-rw-r--r-- | store/sql_status_store.go | 22 | ||||
-rw-r--r-- | store/store.go | 1 | ||||
-rw-r--r-- | webapp/client/client.jsx | 9 | ||||
-rw-r--r-- | webapp/utils/async_client.jsx | 2 |
9 files changed, 99 insertions, 1 deletions
diff --git a/api/admin.go b/api/admin.go index d48c8d379..573a22c6b 100644 --- a/api/admin.go +++ b/api/admin.go @@ -48,6 +48,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") } func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { @@ -754,3 +755,37 @@ func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.StringInterfaceToJson(status))) } + +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 { + 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 + } + + w.Write([]byte(model.UserMapToJson(profiles))) + } + +} diff --git a/api/admin_test.go b/api/admin_test.go index 3d8a95676..7f3c584d8 100644 --- a/api/admin_test.go +++ b/api/admin_test.go @@ -527,3 +527,18 @@ func TestAdminLdapSyncNow(t *testing.T) { t.Fatal("Returned Failure") } } + +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") + } +} diff --git a/i18n/en.json b/i18n/en.json index 920e444ab..b52836bdf 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -4284,6 +4284,10 @@ "translation": "Encountered an error retrieving all the online/away statuses" }, { + "id": "store.sql_status.get_team_statuses.app_error", + "translation": "Encountered an error retrieving all statuses from the team members" + }, + { "id": "store.sql_status.get_total_active_users_count.app_error", "translation": "We could not count the active users" }, diff --git a/model/client.go b/model/client.go index 2c3fb5aca..e54f61347 100644 --- a/model/client.go +++ b/model/client.go @@ -820,6 +820,17 @@ func (c *Client) GetClusterStatus() ([]*ClusterInfo, *AppError) { } } +// GetRecentlyActiveUsers returns a map of users including lastActivityAt using user id as the key +func (c *Client) GetRecentlyActiveUsers(teamId string) (*Result, *AppError) { + if r, err := c.DoApiGet("/admin/recently_active_users/"+teamId, "", ""); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil + } +} + func (c *Client) GetAllAudits() (*Result, *AppError) { if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil { return nil, err diff --git a/model/user.go b/model/user.go index 434ce2732..680bc48c9 100644 --- a/model/user.go +++ b/model/user.go @@ -48,6 +48,7 @@ type User struct { Locale string `json:"locale"` MfaActive bool `json:"mfa_active,omitempty"` MfaSecret string `json:"mfa_secret,omitempty"` + LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"` } // IsValid validates the user and returns an error if it isn't configured diff --git a/store/sql_status_store.go b/store/sql_status_store.go index 9f7441796..0915ced48 100644 --- a/store/sql_status_store.go +++ b/store/sql_status_store.go @@ -129,6 +129,28 @@ func (s SqlStatusStore) GetOnline() StoreChannel { return storeChannel } +func (s SqlStatusStore) GetAllFromTeam(teamId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var statuses []*model.Status + if _, err := s.GetReplica().Select(&statuses, + `SELECT s.* FROM Status AS s INNER JOIN + TeamMembers AS tm ON tm.TeamId=:TeamId AND s.UserId=tm.UserId`, map[string]interface{}{"TeamId": teamId}); err != nil { + result.Err = model.NewLocAppError("SqlStatusStore.GetAllFromTeam", "store.sql_status.get_team_statuses.app_error", nil, err.Error()) + } else { + result.Data = statuses + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (s SqlStatusStore) ResetAll() StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/store.go b/store/store.go index d3b908106..cf08a6c64 100644 --- a/store/store.go +++ b/store/store.go @@ -275,6 +275,7 @@ type StatusStore interface { Get(userId string) StoreChannel GetOnlineAway() StoreChannel GetOnline() StoreChannel + GetAllFromTeam(teamId string) StoreChannel ResetAll() StoreChannel GetTotalActiveUsersCount() StoreChannel UpdateLastActivityAt(userId string, lastActivityAt int64) StoreChannel diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx index 80e2cfe3e..6aabee080 100644 --- a/webapp/client/client.jsx +++ b/webapp/client/client.jsx @@ -958,6 +958,15 @@ export default class Client { end(this.handleResponse.bind(this, 'getAudits', success, error)); } + getRecentlyActiveUsers(id, success, error) { + request. + get(`${this.getAdminRoute()}/recently_active_users/${id}`). + set(this.defaultHeaders). + type('application/json'). + accept('application/json'). + end(this.handleResponse.bind(this, 'getRecentlyActiveUsers', success, error)); + } + getDirectProfiles(success, error) { request. get(`${this.getUsersRoute()}/direct_profiles`). diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx index 585e4b1c0..22b223bc5 100644 --- a/webapp/utils/async_client.jsx +++ b/webapp/utils/async_client.jsx @@ -1138,7 +1138,7 @@ export function getRecentAndNewUsersAnalytics(teamId) { callTracker[callName] = utils.getTimestamp(); - Client.getProfilesForTeam( + Client.getRecentlyActiveUsers( teamId, (users) => { const stats = {}; |