PLT-3921 Fix System Console Recent Active Users (#3856)

* PLT-3921 System Console Recent Active Users
Этот коммит содержится в:
enahum
2016-09-06 15:48:43 -03:00
коммит произвёл GitHub
родитель 9a9ae3dcd1
Коммит 35b816b922
9 изменённых файлов: 99 добавлений и 1 удалений

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

@@ -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)))
}
}

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

@@ -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")
}
}

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

@@ -4283,6 +4283,10 @@
"id": "store.sql_status.get_online_away.app_error",
"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"

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

@@ -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

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

@@ -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

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

@@ -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)

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

@@ -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

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

@@ -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`).

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

@@ -1138,7 +1138,7 @@ export function getRecentAndNewUsersAnalytics(teamId) {
callTracker[callName] = utils.getTimestamp();
Client.getProfilesForTeam(
Client.getRecentlyActiveUsers(
teamId,
(users) => {
const stats = {};