From 814212621f5f07bf8d84443644666be62674cf3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 27 Mar 2017 15:43:10 +0200 Subject: [PATCH] Expose `last_activity_on` in the User API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/api/users.md | 24 ++++++++++-------- lib/api/entities.rb | 4 ++- lib/api/users.rb | 14 +++++------ spec/requests/api/users_spec.rb | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/doc/api/users.md b/doc/api/users.md index 84dbc350e6b..a79d31d19fa 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -72,6 +72,7 @@ GET /users "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", + "last_activity_on": "2012-05-23", "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -104,6 +105,7 @@ GET /users "organization": "", "last_sign_in_at": null, "confirmed_at": "2012-05-30T16:53:06.148Z", + "last_activity_on": "2012-05-23", "color_scheme_id": 3, "projects_limit": 100, "current_sign_in_at": "2014-03-19T17:54:13Z", @@ -196,6 +198,7 @@ Parameters: "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", + "last_activity_on": "2012-05-23", "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -320,6 +323,7 @@ GET /user "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", + "last_activity_on": "2012-05-23", "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -365,6 +369,7 @@ GET /user "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", + "last_activity_on": "2012-05-23", "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -998,16 +1003,9 @@ The activities that update the timestamp are: - Git HTTP/SSH activities (such as clone, push) - 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 amended by using the `from` parameter. -This function takes pagination parameters `page` and `per_page` to restrict the list of users. - - ``` GET /user/activities ``` @@ -1028,14 +1026,20 @@ Example response: [ { "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", - "last_activity_at": "2015-12-15 01:00:00" + "last_activity_on": "2015-12-15", + "last_activity_at": "2015-12-15" }, { "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`. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 939cedc1b27..64ab6f01eb5 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -20,7 +20,8 @@ module API class UserActivity < Grape::Entity expose :username - expose :last_activity_at + expose :last_activity_on + expose :last_activity_on, as: :last_activity_at # Back-compat end class Identity < Grape::Entity @@ -30,6 +31,7 @@ module API class UserPublic < User expose :last_sign_in_at expose :confirmed_at + expose :last_activity_on expose :email expose :color_scheme_id, :projects_limit, :current_sign_in_at expose :identities, using: Entities::Identity diff --git a/lib/api/users.rb b/lib/api/users.rb index 16fa1ef6836..bcfbd9ab3c5 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -538,19 +538,17 @@ module API desc 'Get a list of user activities' 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 end - get ":activities" do + get "activities" do authenticated_as_admin! - activity_set = Gitlab::UserActivities::ActivitySet.new(from: params[:from], - page: params[:page], - per_page: params[:per_page]) + activities = User. + where(User.arel_table[:last_activity_on].gteq(params[:from])). + reorder(last_activity_on: :asc) - add_pagination_headers(activity_set) - - present activity_set.activities, with: Entities::UserActivity + present paginate(activities), with: Entities::UserActivity end end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index a4e8d8e4156..ea9b886e995 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1158,6 +1158,49 @@ describe API::Users, api: true do end end + 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 + it 'has no permission' do + get api("/user/activities", user) + + expect(response).to have_http_status(403) + end + end + + context 'as admin' do + it 'returns the activities from the last 6 months' do + get api("/user/activities", admin) + + expect(response).to include_pagination_headers + expect(json_response.size).to eq(1) + + activity = json_response.last + + 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 + + context 'passing a :from parameter' do + it 'returns the activities from the given date' do + get api("/user/activities?from=2000-1-1", admin) + + expect(response).to include_pagination_headers + expect(json_response.size).to eq(2) + + activity = json_response.first + + 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 + end + end + describe 'GET /users/:user_id/impersonation_tokens' do let!(:active_personal_access_token) { create(:personal_access_token, user: user) } let!(:revoked_personal_access_token) { create(:personal_access_token, :revoked, user: user) }