Commit 8a1b340f authored by Robert Schilling's avatar Robert Schilling

Remove deprecated upvotes and downvotes from the notes API

parent 8c5d328c
---
title: 'API: Remove deprecated fields Notes#upvotes and Notes#downvotes'
merge_request: 1275
author: Robert Schilling
...@@ -34,8 +34,6 @@ Parameters: ...@@ -34,8 +34,6 @@ Parameters:
"created_at": "2013-10-02T09:22:45Z", "created_at": "2013-10-02T09:22:45Z",
"updated_at": "2013-10-02T10:22:45Z", "updated_at": "2013-10-02T10:22:45Z",
"system": true, "system": true,
"upvote": false,
"downvote": false,
"noteable_id": 377, "noteable_id": 377,
"noteable_type": "Issue" "noteable_type": "Issue"
}, },
...@@ -54,8 +52,6 @@ Parameters: ...@@ -54,8 +52,6 @@ Parameters:
"created_at": "2013-10-02T09:56:03Z", "created_at": "2013-10-02T09:56:03Z",
"updated_at": "2013-10-02T09:56:03Z", "updated_at": "2013-10-02T09:56:03Z",
"system": true, "system": true,
"upvote": false,
"downvote": false,
"noteable_id": 121, "noteable_id": 121,
"noteable_type": "Issue" "noteable_type": "Issue"
} }
...@@ -147,9 +143,7 @@ Example Response: ...@@ -147,9 +143,7 @@ Example Response:
"created_at": "2016-04-05T22:10:44.164Z", "created_at": "2016-04-05T22:10:44.164Z",
"system": false, "system": false,
"noteable_id": 11, "noteable_id": 11,
"noteable_type": "Issue", "noteable_type": "Issue"
"upvote": false,
"downvote": false
} }
``` ```
...@@ -271,9 +265,7 @@ Example Response: ...@@ -271,9 +265,7 @@ Example Response:
"created_at": "2016-04-06T16:51:53.239Z", "created_at": "2016-04-06T16:51:53.239Z",
"system": false, "system": false,
"noteable_id": 52, "noteable_id": 52,
"noteable_type": "Snippet", "noteable_type": "Snippet"
"upvote": false,
"downvote": false
} }
``` ```
...@@ -322,8 +314,6 @@ Parameters: ...@@ -322,8 +314,6 @@ Parameters:
"created_at": "2013-10-02T08:57:14Z", "created_at": "2013-10-02T08:57:14Z",
"updated_at": "2013-10-02T08:57:14Z", "updated_at": "2013-10-02T08:57:14Z",
"system": false, "system": false,
"upvote": false,
"downvote": false,
"noteable_id": 2, "noteable_id": 2,
"noteable_type": "MergeRequest" "noteable_type": "MergeRequest"
} }
...@@ -400,8 +390,6 @@ Example Response: ...@@ -400,8 +390,6 @@ Example Response:
"created_at": "2016-04-05T22:11:59.923Z", "created_at": "2016-04-05T22:11:59.923Z",
"system": false, "system": false,
"noteable_id": 7, "noteable_id": 7,
"noteable_type": "MergeRequest", "noteable_type": "MergeRequest"
"upvote": false,
"downvote": false
} }
``` ```
...@@ -408,8 +408,6 @@ Parameters: ...@@ -408,8 +408,6 @@ Parameters:
}, },
"created_at": "2015-12-04T10:33:56.698Z", "created_at": "2015-12-04T10:33:56.698Z",
"system": false, "system": false,
"upvote": false,
"downvote": false,
"noteable_id": 377, "noteable_id": 377,
"noteable_type": "Issue" "noteable_type": "Issue"
}, },
......
...@@ -814,8 +814,6 @@ Example response: ...@@ -814,8 +814,6 @@ Example response:
}, },
"created_at": "2015-12-04T10:33:56.698Z", "created_at": "2015-12-04T10:33:56.698Z",
"system": false, "system": false,
"upvote": false,
"downvote": false,
"noteable_id": 377, "noteable_id": 377,
"noteable_type": "Issue" "noteable_type": "Issue"
}, },
...@@ -843,13 +841,13 @@ The activities that update the timestamp are: ...@@ -843,13 +841,13 @@ 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 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 over time. This means that we will lose the data if Redis gets flushed, or a custom
TTL is reached. 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. This function takes pagination parameters `page` and `per_page` to restrict the list of users.
......
...@@ -30,3 +30,4 @@ changes are in V4: ...@@ -30,3 +30,4 @@ changes are in V4:
- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` - Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)`
- Make subscription API more RESTful. Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource. - Make subscription API more RESTful. Use `post ":id/#{type}/:subscribable_id/subscribe"` to subscribe and `post ":id/#{type}/:subscribable_id/unsubscribe"` to unsubscribe from a resource.
- Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) - Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR)
- Notes do not return deprecated field `upvote` and `downvote` [!1275](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1275)
...@@ -13,6 +13,7 @@ module API ...@@ -13,6 +13,7 @@ module API
mount ::API::V3::Members mount ::API::V3::Members
mount ::API::V3::MergeRequestDiffs mount ::API::V3::MergeRequestDiffs
mount ::API::V3::MergeRequests mount ::API::V3::MergeRequests
mount ::API::V3::Notes
mount ::API::V3::ProjectHooks mount ::API::V3::ProjectHooks
mount ::API::V3::Projects mount ::API::V3::Projects
mount ::API::V3::ProjectSnippets mount ::API::V3::ProjectSnippets
......
...@@ -388,9 +388,6 @@ module API ...@@ -388,9 +388,6 @@ module API
expose :created_at, :updated_at expose :created_at, :updated_at
expose :system?, as: :system expose :system?, as: :system
expose :noteable_id, :noteable_type expose :noteable_id, :noteable_type
# upvote? and downvote? are deprecated, always return false
expose(:upvote?) { |note| false }
expose(:downvote?) { |note| false }
end end
class AwardEmoji < Grape::Entity class AwardEmoji < Grape::Entity
......
...@@ -11,6 +11,40 @@ module API ...@@ -11,6 +11,40 @@ module API
Gitlab::UrlBuilder.build(snippet) Gitlab::UrlBuilder.build(snippet)
end end
end end
class Note < Grape::Entity
expose :id
expose :note, as: :body
expose :attachment_identifier, as: :attachment
expose :author, using: ::API::Entities::UserBasic
expose :created_at, :updated_at
expose :system?, as: :system
expose :noteable_id, :noteable_type
# upvote? and downvote? are deprecated, always return false
expose(:upvote?) { |note| false }
expose(:downvote?) { |note| false }
end
class Event < Grape::Entity
expose :title, :project_id, :action_name
expose :target_id, :target_type, :author_id
expose :data, :target_title
expose :created_at
expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
expose :author, using: ::API::Entities::UserBasic, if: ->(event, options) { event.author }
expose :author_username do |event, options|
event.author&.username
end
end
class AwardEmoji < Grape::Entity
expose :id
expose :name
expose :user, using: ::API::Entities::UserBasic
expose :created_at, :updated_at
expose :awardable_id, :awardable_type
end
end end
end end
end end
module API
module V3
class Notes < Grape::API
include PaginationParams
before { authenticate! }
NOTEABLE_TYPES = [Issue, MergeRequest, Snippet]
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
NOTEABLE_TYPES.each do |noteable_type|
noteables_str = noteable_type.to_s.underscore.pluralize
desc 'Get a list of project +noteable+ notes' do
success ::API::V3::Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
use :pagination
end
get ":id/#{noteables_str}/:noteable_id/notes" do
noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
if can?(current_user, noteable_read_ability_name(noteable), noteable)
# We exclude notes that are cross-references and that cannot be viewed
# by the current user. By doing this exclusion at this level and not
# at the DB query level (which we cannot in that case), the current
# page can have less elements than :per_page even if
# there's more than one page.
notes =
# paginate() only works with a relation. This could lead to a
# mismatch between the pagination headers info and the actual notes
# array returned, but this is really a edge-case.
paginate(noteable.notes).
reject { |n| n.cross_reference_not_visible_for?(current_user) }
present notes, with: ::API::V3::Entities::Note
else
not_found!("Notes")
end
end
desc 'Get a single +noteable+ note' do
success ::API::V3::Entities::Note
end
params do
requires :note_id, type: Integer, desc: 'The ID of a note'
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
end
get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
note = noteable.notes.find(params[:note_id])
can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user)
if can_read_note
present note, with: ::API::V3::Entities::Note
else
not_found!("Note")
end
end
desc 'Create a new +noteable+ note' do
success ::API::V3::Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :body, type: String, desc: 'The content of a note'
optional :created_at, type: String, desc: 'The creation date of the note'
end
post ":id/#{noteables_str}/:noteable_id/notes" do
opts = {
note: params[:body],
noteable_type: noteables_str.classify,
noteable_id: params[:noteable_id]
}
noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
if can?(current_user, noteable_read_ability_name(noteable), noteable)
if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
opts[:created_at] = params[:created_at]
end
note = ::Notes::CreateService.new(user_project, current_user, opts).execute
if note.valid?
present note, with: ::API::V3::Entities::const_get(note.class.name)
else
not_found!("Note #{note.errors.messages}")
end
else
not_found!("Note")
end
end
desc 'Update an existing +noteable+ note' do
success ::API::V3::Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :note_id, type: Integer, desc: 'The ID of a note'
requires :body, type: String, desc: 'The content of a note'
end
put ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
note = user_project.notes.find(params[:note_id])
authorize! :admin_note, note
opts = {
note: params[:body]
}
note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note)
if note.valid?
present note, with: ::API::V3::Entities::Note
else
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
desc 'Delete a +noteable+ note' do
success ::API::V3::Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :note_id, type: Integer, desc: 'The ID of a note'
end
delete ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
note = user_project.notes.find(params[:note_id])
authorize! :admin_note, note
::Notes::DestroyService.new(user_project, current_user).execute(note)
present note, with: ::API::V3::Entities::Note
end
end
end
helpers do
def noteable_read_ability_name(noteable)
"read_#{noteable.class.to_s.underscore}".to_sym
end
end
end
end
end
...@@ -236,13 +236,13 @@ module API ...@@ -236,13 +236,13 @@ module API
end end
desc 'Get events for a single project' do desc 'Get events for a single project' do
success ::API::Entities::Event success ::API::V3::Entities::Event
end end
params do params do
use :pagination use :pagination
end end
get ":id/events" do get ":id/events" do
present paginate(user_project.events.recent), with: ::API::Entities::Event present paginate(user_project.events.recent), with: ::API::V3::Entities::Event
end end
desc 'Fork new project for the current user or provided namespace.' do desc 'Fork new project for the current user or provided namespace.' do
......
...@@ -71,6 +71,27 @@ module API ...@@ -71,6 +71,27 @@ module API
user.activate user.activate
end end
end end
desc 'Get the contribution events of a specified user' do
detail 'This feature was introduced in GitLab 8.13.'
success ::API::V3::Entities::Event
end
params do
requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end
get ':id/events' do
user = User.find_by(id: params[:id])
not_found!('User') unless user
events = user.events.
merge(ProjectsFinder.new.execute(current_user)).
references(:project).
with_associations.
recent
present paginate(events), with: ::API::V3::Entities::Event
end
end end
resource :user do resource :user do
......
require 'spec_helper'
describe API::V3::Notes, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:empty_project, :public, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) }
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
create(:empty_project, namespace: private_user.namespace).
tap { |p| p.team << [private_user, :master] }
end
let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:empty_project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let!(:cross_reference_note) do
create :note,
noteable: ext_issue, project: ext_proj,
note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
system: true
end
before { project.team << [user, :reporter] }
describe "GET /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
it "returns an array of issue notes" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(issue_note.note)
expect(json_response.first['upvote']).to be_falsey
expect(json_response.first['downvote']).to be_falsey
end
it "returns a 404 error when issue id not found" do
get v3_api("/projects/#{project.id}/issues/12345/notes", user)
expect(response).to have_http_status(404)
end
context "and current user cannot view the notes" do
it "returns an empty array" do
get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response).to be_empty
end
context "and issue is confidential" do
before { ext_issue.update_attributes(confidential: true) }
it "returns 404" do
get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
expect(response).to have_http_status(404)
end
end
context "and current user can view the note" do
it "returns an empty array" do
get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(cross_reference_note.note)
end
end
end
end
context "when noteable is a Snippet" do
it "returns an array of snippet notes" do
get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(snippet_note.note)
end
it "returns a 404 error when snippet id not found" do
get v3_api("/projects/#{project.id}/snippets/42/notes", user)
expect(response).to have_http_status(404)
end
it "returns 404 when not authorized" do
get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user)
expect(response).to have_http_status(404)
end
end
context "when noteable is a Merge Request" do
it "returns an array of merge_requests notes" do
get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(merge_request_note.note)
end
it "returns a 404 error if merge request id not found" do
get v3_api("/projects/#{project.id}/merge_requests/4444/notes", user)
expect(response).to have_http_status(404)
end
it "returns 404 when not authorized" do
get v3_api("/projects/#{project.id}/merge_requests/4444/notes", private_user)
expect(response).to have_http_status(404)
end
end
end
describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do
context "when noteable is an Issue" do
it "returns an issue note by id" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user)
expect(response).to have_http_status(200)
expect(json_response['body']).to eq(issue_note.note)
end
it "returns a 404 error if issue note not found" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
expect(response).to have_http_status(404)
end
context "and current user cannot view the note" do
it "returns a 404 error" do
get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user)
expect(response).to have_http_status(404)
end
context "when issue is confidential" do
before { issue.update_attributes(confidential: true) }
it "returns 404" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user)
expect(response).to have_http_status(404)
end
end
context "and current user can view the note" do
it "returns an issue note by id" do
get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user)
expect(response).to have_http_status(200)
expect(json_response['body']).to eq(cross_reference_note.note)
end
end
end
end
context "when noteable is a Snippet" do
it "returns a snippet note by id" do
get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user)
expect(response).to have_http_status(200)
expect(json_response['body']).to eq(snippet_note.note)
end
it "returns a 404 error if snippet note not found" do
get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user)
expect(response).to have_http_status(404)
end
end
end
describe "POST /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
it "creates a new issue note" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
expect(response).to have_http_status(201)
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
end
it "returns a 400 bad request error if body not given" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
expect(response).to have_http_status(400)
end
it "returns a 401 unauthorized error if user not authenticated" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
expect(response).to have_http_status(401)
end
context 'when an admin or owner makes the request' do
it 'accepts the creation date to be set' do
creation_time = 2.weeks.ago
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
body: 'hi!', created_at: creation_time
expect(response).to have_http_status(201)
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
end
end
context 'when the user is posting an award emoji on an issue created by someone else' do
let(:issue2) { create(:issue, project: project) }
it 'returns an award emoji' do
post v3_api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
expect(json_response['awardable_id']).to eq issue2.id
end
end
context 'when the user is posting an award emoji on his/her own issue' do
it 'creates a new issue note' do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:'
expect(response).to have_http_status(201)
expect(json_response['body']).to eq(':+1:')
end
end
end
context "when noteable is a Snippet" do
it "creates a new snippet note" do
post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!'
expect(response).to have_http_status(201)
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
end
it "returns a 400 bad request error if body not given" do
post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
expect(response).to have_http_status(400)
end
it "returns a 401 unauthorized error if user not authenticated" do
post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!'
expect(response).to have_http_status(401)
end
end
context 'when user does not have access to read the noteable' do
it 'responds with 404' do
project = create(:empty_project, :private) { |p| p.add_guest(user) }
issue = create(:issue, :confidential, project: project)
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
body: 'Foo'
expect(response).to have_http_status(404)
end
end
context 'when user does not have access to create noteable' do
let(:private_issue) { create(:issue, project: create(:empty_project, :private)) }
##
# We are posting to project user has access to, but we use issue id
# from a different project, see #15577
#
before do
post v3_api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user),
body: 'Hi!'
end
it 'responds with resource not found error' do
expect(response.status).to eq 404
end
it 'does not create new note' do
expect(private_issue.notes.reload).to be_empty
end
end
end
describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
it "creates an activity event when an issue note is created" do
expect(Event).to receive(:create)
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
end
end
describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do
context 'when noteable is an Issue' do
it 'returns modified note' do
put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\
"notes/#{issue_note.id}", user), body: 'Hello!'
expect(response).to have_http_status(200)
expect(json_response['body']).to eq('Hello!')
end
it 'returns a 404 error when note id not found' do
put v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user),
body: 'Hello!'
expect(response).to have_http_status(404)
end
it 'returns a 400 bad request error if body not given' do
put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\
"notes/#{issue_note.id}", user)
expect(response).to have_http_status(400)
end
end
context 'when noteable is a Snippet' do
it 'returns modified note' do
put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/#{snippet_note.id}", user), body: 'Hello!'
expect(response).to have_http_status(200)
expect(json_response['body']).to eq('Hello!')
end
it 'returns a 404 error when note id not found' do
put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/12345", user), body: "Hello!"
expect(response).to have_http_status(404)
end
end
context 'when noteable is a Merge Request' do
it 'returns modified note' do
put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
"notes/#{merge_request_note.id}", user), body: 'Hello!'
expect(response).to have_http_status(200)
expect(json_response['body']).to eq('Hello!')
end
it 'returns a 404 error when note id not found' do
put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
"notes/12345", user), body: "Hello!"
expect(response).to have_http_status(404)
end
end
end
describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do
context 'when noteable is an Issue' do
it 'deletes a note' do
delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\
"notes/#{issue_note.id}", user)
expect(response).to have_http_status(200)
# Check if note is really deleted
delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\
"notes/#{issue_note.id}", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 error when note id not found' do
delete v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
expect(response).to have_http_status(404)
end
end
context 'when noteable is a Snippet' do
it 'deletes a note' do
delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/#{snippet_note.id}", user)
expect(response).to have_http_status(200)
# Check if note is really deleted
delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/#{snippet_note.id}", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 error when note id not found' do
delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/12345", user)
expect(response).to have_http_status(404)
end
end
context 'when noteable is a Merge Request' do
it 'deletes a note' do
delete v3_api("/projects/#{project.id}/merge_requests/"\
"#{merge_request.id}/notes/#{merge_request_note.id}", user)
expect(response).to have_http_status(200)
# Check if note is really deleted
delete v3_api("/projects/#{project.id}/merge_requests/"\
"#{merge_request.id}/notes/#{merge_request_note.id}", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 error when note id not found' do
delete v3_api("/projects/#{project.id}/merge_requests/"\
"#{merge_request.id}/notes/12345", user)
expect(response).to have_http_status(404)
end
end
end
end
...@@ -186,4 +186,81 @@ describe API::V3::Users, api: true do ...@@ -186,4 +186,81 @@ describe API::V3::Users, api: true do
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
describe 'GET /users/: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
context 'joined event' do
it 'returns the "joined" event' do
get v3_api("/users/#{user.id}/events", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
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
context 'when there are multiple events from different projects' do
let(:second_note) { create(:note_on_issue, project: create(:empty_project)) }
let(:third_note) { create(:note_on_issue, project: project) }
before do
second_note.project.add_user(user, :developer)
[second_note, third_note].each do |note|
EventCreateService.new.leave_note(note, user)
end
end
it 'returns events in the correct order (from newest to oldest)' do
get v3_api("/users/#{user.id}/events", user)
comment_events = json_response.select { |e| e['action_name'] == 'commented on' }
expect(comment_events[0]['target_id']).to eq(third_note.id)
expect(comment_events[1]['target_id']).to eq(second_note.id)
expect(comment_events[2]['target_id']).to eq(note.id)
end
end
end
it 'returns a 404 error if not found' do
get v3_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
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