Merge branch 'user-events-api' into 'master'
API: New /users/:id/events endpoint ## What does this MR do? If add a new `/users/:id/events` endpoint to retrieve a user's contribution events. The events returned are filtered so that only the events for projects that the current user can see are returned (similarly to what we do at the controller level). ## Why was this MR needed? Because it's a nice feature to calculate leaderboards, for instance for #17815. ## What are the relevant issue numbers? Closes #20866. See merge request !6771
This commit is contained in:
commit
7c07c07d7a
|
@ -73,6 +73,7 @@ v 8.13.0 (unreleased)
|
||||||
- Add disabled delete button to protected branches (ClemMakesApps)
|
- Add disabled delete button to protected branches (ClemMakesApps)
|
||||||
- Add broadcast messages and alerts below sub-nav
|
- Add broadcast messages and alerts below sub-nav
|
||||||
- Better empty state for Groups view
|
- Better empty state for Groups view
|
||||||
|
- API: New /users/:id/events endpoint
|
||||||
- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
|
- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
|
||||||
- Replace bootstrap caret with fontawesome caret (ClemMakesApps)
|
- Replace bootstrap caret with fontawesome caret (ClemMakesApps)
|
||||||
- Fix unnecessary escaping of reserved HTML characters in milestone title. !6533
|
- Fix unnecessary escaping of reserved HTML characters in milestone title. !6533
|
||||||
|
|
|
@ -436,7 +436,7 @@ Parameters:
|
||||||
### Get project events
|
### Get project events
|
||||||
|
|
||||||
Get the events for the specified project.
|
Get the events for the specified project.
|
||||||
Sorted from newest to latest
|
Sorted from newest to oldest
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /projects/:id/events
|
GET /projects/:id/events
|
||||||
|
|
146
doc/api/users.md
146
doc/api/users.md
|
@ -627,3 +627,149 @@ Parameters:
|
||||||
|
|
||||||
Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
|
Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
|
||||||
`403 Forbidden` when trying to unblock a user blocked by LDAP synchronization.
|
`403 Forbidden` when trying to unblock a user blocked by LDAP synchronization.
|
||||||
|
|
||||||
|
### Get user contribution events
|
||||||
|
|
||||||
|
Get the contribution events for the specified user, sorted from newest to oldest.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /users/:id/events
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer | yes | The ID of the user |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/user/:id/events
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": null,
|
||||||
|
"project_id": 15,
|
||||||
|
"action_name": "closed",
|
||||||
|
"target_id": 830,
|
||||||
|
"target_type": "Issue",
|
||||||
|
"author_id": 1,
|
||||||
|
"data": null,
|
||||||
|
"target_title": "Public project search field",
|
||||||
|
"author": {
|
||||||
|
"name": "Dmitriy Zaporozhets",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
|
||||||
|
"web_url": "http://localhost:3000/u/root"
|
||||||
|
},
|
||||||
|
"author_username": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": null,
|
||||||
|
"project_id": 15,
|
||||||
|
"action_name": "opened",
|
||||||
|
"target_id": null,
|
||||||
|
"target_type": null,
|
||||||
|
"author_id": 1,
|
||||||
|
"author": {
|
||||||
|
"name": "Dmitriy Zaporozhets",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
|
||||||
|
"web_url": "http://localhost:3000/u/root"
|
||||||
|
},
|
||||||
|
"author_username": "john",
|
||||||
|
"data": {
|
||||||
|
"before": "50d4420237a9de7be1304607147aec22e4a14af7",
|
||||||
|
"after": "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"user_id": 1,
|
||||||
|
"user_name": "Dmitriy Zaporozhets",
|
||||||
|
"repository": {
|
||||||
|
"name": "gitlabhq",
|
||||||
|
"url": "git@dev.gitlab.org:gitlab/gitlabhq.git",
|
||||||
|
"description": "GitLab: self hosted Git management software. \r\nDistributed under the MIT License.",
|
||||||
|
"homepage": "https://dev.gitlab.org/gitlab/gitlabhq"
|
||||||
|
},
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"id": "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
|
||||||
|
"message": "Add simple search to projects in public area",
|
||||||
|
"timestamp": "2013-05-13T18:18:08+00:00",
|
||||||
|
"url": "https://dev.gitlab.org/gitlab/gitlabhq/commit/c5feabde2d8cd023215af4d2ceeb7a64839fc428",
|
||||||
|
"author": {
|
||||||
|
"name": "Dmitriy Zaporozhets",
|
||||||
|
"email": "dmitriy.zaporozhets@gmail.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_commits_count": 1
|
||||||
|
},
|
||||||
|
"target_title": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": null,
|
||||||
|
"project_id": 15,
|
||||||
|
"action_name": "closed",
|
||||||
|
"target_id": 840,
|
||||||
|
"target_type": "Issue",
|
||||||
|
"author_id": 1,
|
||||||
|
"data": null,
|
||||||
|
"target_title": "Finish & merge Code search PR",
|
||||||
|
"author": {
|
||||||
|
"name": "Dmitriy Zaporozhets",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
|
||||||
|
"web_url": "http://localhost:3000/u/root"
|
||||||
|
},
|
||||||
|
"author_username": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": null,
|
||||||
|
"project_id": 15,
|
||||||
|
"action_name": "commented on",
|
||||||
|
"target_id": 1312,
|
||||||
|
"target_type": "Note",
|
||||||
|
"author_id": 1,
|
||||||
|
"data": null,
|
||||||
|
"target_title": null,
|
||||||
|
"created_at": "2015-12-04T10:33:58.089Z",
|
||||||
|
"note": {
|
||||||
|
"id": 1312,
|
||||||
|
"body": "What an awesome day!",
|
||||||
|
"attachment": null,
|
||||||
|
"author": {
|
||||||
|
"name": "Dmitriy Zaporozhets",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
|
||||||
|
"web_url": "http://localhost:3000/u/root"
|
||||||
|
},
|
||||||
|
"created_at": "2015-12-04T10:33:56.698Z",
|
||||||
|
"system": false,
|
||||||
|
"upvote": false,
|
||||||
|
"downvote": false,
|
||||||
|
"noteable_id": 377,
|
||||||
|
"noteable_type": "Issue"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Dmitriy Zaporozhets",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
|
||||||
|
"web_url": "http://localhost:3000/u/root"
|
||||||
|
},
|
||||||
|
"author_username": "root"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
|
@ -321,6 +321,26 @@ module API
|
||||||
user.activate
|
user.activate
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc 'Get contribution events of a specified user' do
|
||||||
|
detail 'This feature was introduced in GitLab 8.13.'
|
||||||
|
success Entities::Event
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :id, type: String, desc: 'The user ID'
|
||||||
|
end
|
||||||
|
get ':id/events' do
|
||||||
|
user = User.find_by(id: declared(params).id)
|
||||||
|
not_found!('User') unless user
|
||||||
|
|
||||||
|
events = user.recent_events.
|
||||||
|
merge(ProjectsFinder.new.execute(current_user)).
|
||||||
|
references(:project).
|
||||||
|
with_associations.
|
||||||
|
page(params[:page])
|
||||||
|
|
||||||
|
present paginate(events), with: Entities::Event
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resource :user do
|
resource :user do
|
||||||
|
|
|
@ -913,4 +913,58 @@ describe API::API, api: true do
|
||||||
expect(response).to have_http_status(404)
|
expect(response).to have_http_status(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /user/:id/events' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:project) { create(:empty_project) }
|
||||||
|
let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_user(user, :developer)
|
||||||
|
EventCreateService.new.leave_note(note, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "as a user than cannot see the event's project" do
|
||||||
|
it 'returns no events' do
|
||||||
|
other_user = create(:user)
|
||||||
|
|
||||||
|
get api("/users/#{user.id}/events", other_user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(json_response).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "as a user than can see the event's project" do
|
||||||
|
it_behaves_like 'a paginated resources' do
|
||||||
|
let(:request) { get api("/users/#{user.id}/events", user) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'joined event' do
|
||||||
|
it 'returns the "joined" event' do
|
||||||
|
get api("/users/#{user.id}/events", user)
|
||||||
|
|
||||||
|
comment_event = json_response.find { |e| e['action_name'] == 'commented on' }
|
||||||
|
|
||||||
|
expect(comment_event['project_id'].to_i).to eq(project.id)
|
||||||
|
expect(comment_event['author_username']).to eq(user.username)
|
||||||
|
expect(comment_event['note']['id']).to eq(note.id)
|
||||||
|
expect(comment_event['note']['body']).to eq('What an awesome day!')
|
||||||
|
|
||||||
|
joined_event = json_response.find { |e| e['action_name'] == 'joined' }
|
||||||
|
|
||||||
|
expect(joined_event['project_id'].to_i).to eq(project.id)
|
||||||
|
expect(joined_event['author_username']).to eq(user.username)
|
||||||
|
expect(joined_event['author']['name']).to eq(user.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a 404 error if not found' do
|
||||||
|
get api('/users/42/events', user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
expect(json_response['message']).to eq('404 User Not Found')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue