Commit 63695d43 authored by Rémy Coutable's avatar Rémy Coutable

Expose `last_activity_on` in the User API

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent 67149e58
...@@ -74,6 +74,7 @@ GET /users ...@@ -74,6 +74,7 @@ GET /users
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
"current_sign_in_at": "2012-06-02T06:36:55Z", "current_sign_in_at": "2012-06-02T06:36:55Z",
...@@ -106,6 +107,7 @@ GET /users ...@@ -106,6 +107,7 @@ GET /users
"organization": "", "organization": "",
"last_sign_in_at": null, "last_sign_in_at": null,
"confirmed_at": "2012-05-30T16:53:06.148Z", "confirmed_at": "2012-05-30T16:53:06.148Z",
"last_activity_on": "2012-05-23",
"color_scheme_id": 3, "color_scheme_id": 3,
"projects_limit": 100, "projects_limit": 100,
"current_sign_in_at": "2014-03-19T17:54:13Z", "current_sign_in_at": "2014-03-19T17:54:13Z",
...@@ -198,6 +200,7 @@ Parameters: ...@@ -198,6 +200,7 @@ Parameters:
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
"current_sign_in_at": "2012-06-02T06:36:55Z", "current_sign_in_at": "2012-06-02T06:36:55Z",
...@@ -322,6 +325,7 @@ GET /user ...@@ -322,6 +325,7 @@ GET /user
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
"current_sign_in_at": "2012-06-02T06:36:55Z", "current_sign_in_at": "2012-06-02T06:36:55Z",
...@@ -367,6 +371,7 @@ GET /user ...@@ -367,6 +371,7 @@ GET /user
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
"current_sign_in_at": "2012-06-02T06:36:55Z", "current_sign_in_at": "2012-06-02T06:36:55Z",
...@@ -999,16 +1004,9 @@ The activities that update the timestamp are: ...@@ -999,16 +1004,9 @@ The activities that update the timestamp are:
- Git HTTP/SSH activities (such as clone, push) - Git HTTP/SSH activities (such as clone, push)
- User logging in into GitLab - User logging in into GitLab
The data is stored in Redis and it depends on it for being recorded and displayed
over time. This means that we will lose the data if Redis gets flushed, or a custom
TTL is reached.
By default, it shows the activity for all users in the last 6 months, but this can be By default, it shows the activity for all users in the last 6 months, but this can be
amended by using the `from` parameter. amended by using the `from` parameter.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
``` ```
GET /user/activities GET /user/activities
``` ```
...@@ -1029,15 +1027,20 @@ Example response: ...@@ -1029,15 +1027,20 @@ Example response:
[ [
{ {
"username": "user1", "username": "user1",
"last_activity_at": "2015-12-14 01:00:00" "last_activity_on": "2015-12-14",
"last_activity_at": "2015-12-14"
}, },
{ {
"username": "user2", "username": "user2",
"last_activity_at": "2015-12-15 01:00:00" "last_activity_on": "2015-12-15",
"last_activity_at": "2015-12-15"
}, },
{ {
"username": "user3", "username": "user3",
"last_activity_at": "2015-12-16 01:00:00" "last_activity_on": "2015-12-16",
"last_activity_at": "2015-12-16"
} }
] ]
``` ```
Please note that `last_activity_at` is deprecated, please use `last_activity_on`.
...@@ -20,7 +20,8 @@ module API ...@@ -20,7 +20,8 @@ module API
class UserActivity < Grape::Entity class UserActivity < Grape::Entity
expose :username expose :username
expose :last_activity_at expose :last_activity_on
expose :last_activity_on, as: :last_activity_at # Back-compat
end end
class Identity < Grape::Entity class Identity < Grape::Entity
...@@ -30,6 +31,7 @@ module API ...@@ -30,6 +31,7 @@ module API
class UserPublic < User class UserPublic < User
expose :last_sign_in_at expose :last_sign_in_at
expose :confirmed_at expose :confirmed_at
expose :last_activity_on
expose :email expose :email
expose :color_scheme_id, :projects_limit, :current_sign_in_at expose :color_scheme_id, :projects_limit, :current_sign_in_at
expose :identities, using: Entities::Identity expose :identities, using: Entities::Identity
......
...@@ -537,19 +537,17 @@ module API ...@@ -537,19 +537,17 @@ module API
desc 'Get a list of user activities' desc 'Get a list of user activities'
params do params do
optional :from, type: String, desc: 'Date string in the format YEAR-MONTH-DAY' optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY'
use :pagination use :pagination
end end
get ":activities" do get "activities" do
authenticated_as_admin! authenticated_as_admin!
activity_set = Gitlab::UserActivities::ActivitySet.new(from: params[:from], activities = User.
page: params[:page], where(User.arel_table[:last_activity_on].gteq(params[:from])).
per_page: params[:per_page]) reorder(last_activity_on: :asc)
add_pagination_headers(activity_set) present paginate(activities), with: Entities::UserActivity
present activity_set.activities, with: Entities::UserActivity
end end
end end
end end
......
...@@ -1171,72 +1171,44 @@ describe API::Users, api: true do ...@@ -1171,72 +1171,44 @@ describe API::Users, api: true do
end end
context "user activities", :redis do context "user activities", :redis do
let!(:old_active_user) { create(:user, last_activity_on: Time.utc(2000, 1, 1)) }
let!(:newly_active_user) { create(:user, last_activity_on: 2.days.ago.midday) }
context 'last activity as normal user' do context 'last activity as normal user' do
it 'has no permission' do it 'has no permission' do
user.record_activity
get api("/user/activities", user) get api("/user/activities", user)
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
end end
context 'last activity as admin' do context 'as admin' do
it 'returns the last activity' do it 'returns the activities from the last 6 months' do
allow(Time).to receive(:now).and_return(Time.new(2000, 1, 1))
user.record_activity
get api("/user/activities", admin) get api("/user/activities", admin)
activity = json_response.last
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(activity['username']).to eq(user.username) expect(json_response.size).to eq(1)
expect(activity['last_activity_at']).to eq('2000-01-01 00:00:00')
end
end
context 'last activities paginated', :redis do
let(:activity) { json_response.first }
let(:old_date) { 2.months.ago.to_date }
before do
5.times do |num|
Timecop.freeze(old_date + num)
create(:user, username: num.to_s).record_activity
end
end
after do
Timecop.return
end
it 'returns 3 activities' do activity = json_response.last
get api("/user/activities?page=1&per_page=3", admin)
expect(json_response.count).to eq(3) expect(activity['username']).to eq(newly_active_user.username)
expect(activity['last_activity_on']).to eq(2.days.ago.to_date.to_s)
expect(activity['last_activity_at']).to eq(2.days.ago.to_date.to_s)
end end
it 'contains the first activities' do context 'passing a :from parameter' do
get api("/user/activities?page=1&per_page=3", admin) it 'returns the activities from the given date' do
get api("/user/activities?from=2000-1-1", admin)
expect(json_response.map { |activity| activity['username'] }).to eq(%w[0 1 2]) expect(response).to include_pagination_headers
end expect(json_response.size).to eq(2)
it 'contains the last activities' do activity = json_response.first
get api("/user/activities?page=2&per_page=3", admin)
expect(json_response.map { |activity| activity['username'] }).to eq(%w[3 4]) expect(activity['username']).to eq(old_active_user.username)
expect(activity['last_activity_on']).to eq(Time.utc(2000, 1, 1).to_date.to_s)
expect(activity['last_activity_at']).to eq(Time.utc(2000, 1, 1).to_date.to_s)
end end
it 'contains activities created after user 3 was created' do
from = (old_date + 3).to_s("%Y-%m-%d")
get api("/user/activities?page=1&per_page=5&from=#{from}", admin)
expect(json_response.map { |activity| activity['username'] }).to eq(%w[3 4])
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment