From 3f88221c2dcb1c42cc2f5a765d2586f1755128c3 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 8 Jun 2016 09:33:41 +0200 Subject: Add endpoints for Award Emoji This only supports Issues and MergeRequests right now because of the consistency of the routes those models provide. --- lib/api/api.rb | 1 + lib/api/award_emoji.rb | 97 ++++++++++++++++++++++ lib/api/entities.rb | 8 ++ lib/api/issues.rb | 2 +- spec/requests/api/award_emoji_spec.rb | 149 ++++++++++++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 lib/api/award_emoji.rb create mode 100644 spec/requests/api/award_emoji_spec.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index 51ddd0dbfc4..7944c80cf7a 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -36,6 +36,7 @@ module API mount ::API::Session mount ::API::MergeRequests mount ::API::Notes + mount ::API::AwardEmoji mount ::API::Internal mount ::API::SystemHooks mount ::API::ProjectSnippets diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb new file mode 100644 index 00000000000..26b30d30163 --- /dev/null +++ b/lib/api/award_emoji.rb @@ -0,0 +1,97 @@ +module API + class AwardEmoji < Grape::API + before { authenticate! } + + AWARDABLES = [Issue, MergeRequest] + + resource :projects do + AWARDABLES.each do |awardable_type| + awardable_string = awardable_type.to_s.underscore.pluralize + awardable_id_string = "#{awardable_type.to_s.underscore}_id" + + # Get a list of project +awardable+ award emoji + # + # Parameters: + # id (required) - The ID of a project + # awardable_id (required) - The ID of an issue or MR + # Example Request: + # GET /projects/:id/issues/:awardable_id/award_emoji + get ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji" do + awardable = user_project.send(awardable_string.to_sym).find(params[awardable_id_string.to_sym]) + + if can?(current_user, awardable_read_ability_name(awardable), awardable) + awards = paginate(awardable.award_emoji) + present awards, with: Entities::AwardEmoji + else + not_found!("Award Emoji") + end + end + + # Get a specific award emoji + # + # Parameters: + # id (required) - The ID of a project + # awardable_id (required) - The ID of an issue or MR + # award_id (required) - The ID of the award + # Example Request: + # GET /projects/:id/issues/:awardable_id/award_emoji/:award_id + get ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji/:award_id" do + awardable = user_project.send(awardable_string.to_sym).find(params[awardable_id_string.to_sym]) + + if can?(current_user, awardable_read_ability_name(awardable), awardable) + present awardable.award_emoji.find(params[:award_id]), with: Entities::AwardEmoji + else + not_found!("Award Emoji") + end + end + + # Award a new Emoji + # + # Parameters: + # id (required) - The ID of a project + # noteable_id (required) - The ID of an issue or snippet + # name (required) - The name of a award_emoji (without colons) + # Example Request: + # POST /projects/:id/issues/:noteable_id/notes + # POST /projects/:id/snippets/:noteable_id/notes + post ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji" do + required_attributes! [:name] + + awardable = user_project.send(awardable_string.to_sym).find(params[awardable_id_string.to_sym]) + not_found!('Award Emoji') unless can?(current_user, awardable_read_ability_name(awardable), awardable) + + award = awardable.award_emoji.new(name: params[:name], user: current_user) + + if award.save + present award, with: Entities::AwardEmoji + else + not_found!("Award Emoji #{award.errors.messages}") + end + end + + # Delete a +awardables+ award emoji + # + # Parameters: + # id (required) - The ID of a project + # awardable_id (required) - The ID of an issue or MR + # award_emoji_id (required) - The ID of an award emoji + # Example Request: + # DELETE /projects/:id/issues/:noteable_id/notes/:note_id + delete ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji/:award_id" do + awardable = user_project.send(awardable_string.to_sym).find(params[awardable_id_string.to_sym]) + award = awardable.award_emoji.find(params[:award_id]) + + unauthorized! unless award.user == current_user || current_user.admin? + + award.destroy + present award, with: Entities::AwardEmoji + end + end + end + helpers do + def awardable_read_ability_name(awardable) + "read_#{awardable.class.to_s.underscore.downcase}".to_sym + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index cc29c7ef428..2e397643ed1 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -225,6 +225,14 @@ module API expose(:downvote?) { |note| false } end + class AwardEmoji < Grape::Entity + expose :id + expose :name + expose :user, using: Entities::UserBasic + expose :created_at, :updated_at + expose :awardable_id, :awardable_type + end + class MRNote < Grape::Entity expose :note expose :author, using: Entities::UserBasic diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 4c43257c48a..aa0b9ca3957 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -1,6 +1,6 @@ module API # Issues API - class Issues < Grape::API + class Issues < Grape::API before { authenticate! } helpers ::Gitlab::AkismetHelper diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb new file mode 100644 index 00000000000..49964cb51de --- /dev/null +++ b/spec/requests/api/award_emoji_spec.rb @@ -0,0 +1,149 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + let(:user) { create(:user) } + let!(:project) { create(:project) } + let(:issue) { create(:issue, project: project, author: user) } + let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) } + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) } + + before { project.team << [user, :master] } + + describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do + context 'on an issue' do + it "returns an array of award_emoji" do + get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(award_emoji.name) + end + + it "should return a 404 error when issue id not found" do + get api("/projects/#{project.id}/issues/12345/award_emoji", user) + + expect(response.status).to eq(404) + end + end + + context 'on a merge request' do + it "returns an array of award_emoji" do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(downvote.name) + end + end + + context 'when the user has no access' do + it 'returns a status code 404' do + user1 = create(:user) + + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user1) + + expect(response.status).to eq(404) + end + end + end + + describe "GET /projects/:id/awardable/:awardable_id/award_emoji/:award_id" do + context 'on an issue' do + it "returns the award emoji" do + get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user) + + expect(response.status).to eq(200) + expect(json_response['name']).to eq(award_emoji.name) + expect(json_response['awardable_id']).to eq(issue.id) + expect(json_response['awardable_type']).to eq("Issue") + end + + it "returns a 404 error if the award is not found" do + get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user) + + expect(response.status).to eq(404) + end + end + + context 'on a merge request' do + it 'returns the award emoji' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user) + + expect(response.status).to eq(200) + expect(json_response['name']).to eq(downvote.name) + expect(json_response['awardable_id']).to eq(issue.id) + expect(json_response['awardable_type']).to eq("MergeRequest") + end + end + + context 'when the user has no access' do + it 'returns a status code 404' do + user1 = create(:user) + + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user1) + + expect(response.status).to eq(404) + end + end + end + + describe "POST /projects/:id/awardable/:awardable_id/award_emoji" do + context "on an issue" do + it "creates a new award emoji" do + post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish' + + expect(response.status).to eq(201) + expect(json_response['name']).to eq('blowfish') + expect(json_response['user']['username']).to eq(user.username) + end + + it "should return a 400 bad request error if the name is not given" do + post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user) + + expect(response.status).to eq(400) + end + + it "should return a 401 unauthorized error if the user is not authenticated" do + post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji"), name: 'thumbsup' + + expect(response.status).to eq(401) + end + end + end + + describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_emoji_id' do + context 'when the awardable is an Issue' do + it 'deletes the award' do + expect do + delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user) + end.to change { issue.award_emoji.count }.from(1).to(0) + + expect(response.status).to eq(200) + end + + it 'returns a 404 error when the award emoji can not be found' do + delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user) + + expect(response.status).to eq(404) + end + end + + context 'when the awardable is a Merge Request' do + it 'deletes the award' do + expect do + delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user) + end.to change { merge_request.award_emoji.count }.from(1).to(0) + + expect(response.status).to eq(200) + end + + it 'returns a 404 error when note id not found' do + delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes/12345", user) + + expect(response.status).to eq(404) + end + end + end +end -- cgit v1.2.1