Commit 29065637 authored by Phil Hughes's avatar Phil Hughes

Added award emoji to the epics REST API

For the refactor of the awards block to Vue we need access
to the public API.
This updates the award emoji API module to also include
epics and allow for other types that aren't tied to a project
to be included as well.
parent 739f9cca
---
title: Added award emoji to epics REST API
merge_request: 60410
author:
type: added
# frozen_string_literal: true
module EE
module API
module Helpers
module AwardEmoji
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
class_methods do
extend ::Gitlab::Utils::Override
override :awardables
def awardables
super.concat([
{ type: 'epic', resource: :groups, find_by: :iid, feature_category: :epics }
])
end
override :awardable_id_desc
def awardable_id_desc
"The ID of an Issue, Merge Request, Epic or Snippet"
end
end
# rubocop: disable CodeReuse/ActiveRecord
override :awardable
def awardable
super
@awardable ||= # rubocop:disable Gitlab/ModuleWithInstanceVariables
begin
if params.include?(:epic_iid)
user_group.epics.find_by!(iid: params[:epic_iid])
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::AwardEmoji do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:epic) { create(:epic, group: group, author: user) }
let_it_be(:award_emoji) { create(:award_emoji, awardable: epic, user: user) }
let_it_be(:note) { create(:note, noteable: epic) }
before do
stub_licensed_features(epics: true)
group.add_developer(user)
end
describe "GET /groups/:id/awardable/:awardable_id/award_emoji" do
context 'on an epic' do
it "returns an array of award_emoji" do
get api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji", user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(award_emoji.name)
end
it "returns a 404 error when epic id not found" do
get api("/groups/#{group.id}/epics/#{non_existing_record_iid}/award_emoji", user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'GET /groups/:id/awardable/:awardable_id/notes/:note_id/award_emoji' do
let!(:rocket) { create(:award_emoji, awardable: note, name: 'rocket') }
it 'returns an array of award emoji' do
get api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji", user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(rocket.name)
end
end
describe "GET /groups/:id/awardable/:awardable_id/award_emoji/:award_id" do
context 'on an epic' do
it "returns the award emoji" do
get api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji/#{award_emoji.id}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(award_emoji.name)
expect(json_response['awardable_id']).to eq(epic.id)
expect(json_response['awardable_type']).to eq("Epic")
end
it "returns a 404 error if the award is not found" do
get api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji/#{non_existing_record_id}", user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'GET /groups/:id/awardable/:awardable_id/notes/:note_id/award_emoji/:award_id' do
let!(:rocket) { create(:award_emoji, awardable: note, name: 'rocket') }
it 'returns an award emoji' do
get api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji/#{rocket.id}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to be_an Array
expect(json_response['name']).to eq(rocket.name)
end
end
describe "POST /groups/:id/awardable/:awardable_id/award_emoji" do
context "on an epic" do
it "creates a new award emoji" do
post api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji", user), params: { name: 'blowfish' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq('blowfish')
expect(json_response['user']['username']).to eq(user.username)
end
it "returns a 400 bad request error if the name is not given" do
post api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji", user)
expect(response).to have_gitlab_http_status(:bad_request)
end
it "returns a 401 unauthorized error if the user is not authenticated" do
post api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji"), params: { name: 'thumbsup' }
expect(response).to have_gitlab_http_status(:unauthorized)
end
it "normalizes +1 as thumbsup award" do
post api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji", user), params: { name: '+1' }
expect(epic.award_emoji.last.name).to eq("thumbsup")
end
context 'when the emoji already has been awarded' do
it 'returns a 404 status code' do
post api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji", user), params: { name: 'thumbsup' }
post api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji", user), params: { name: 'thumbsup' }
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response["message"]).to match("has already been taken")
end
end
end
end
describe "POST /groups/:id/awardable/:awardable_id/notes/:note_id/award_emoji" do
let(:note2) { create(:note, noteable: epic, author: user) }
it 'creates a new award emoji' do
expect do
post api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji", user), params: { name: 'rocket' }
end.to change { note.award_emoji.count }.from(0).to(1)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['user']['username']).to eq(user.username)
end
it 'marks Todos on the Noteable as done' do
todo = create(:todo, target: note2.noteable, group: group, user: user)
post api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji", user), params: { name: 'rocket' }
expect(todo.reload).to be_done
end
it "normalizes +1 as thumbsup award" do
post api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji", user), params: { name: '+1' }
expect(note.award_emoji.last.name).to eq("thumbsup")
end
context 'when the emoji already has been awarded' do
it 'returns a 404 status code' do
post api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji", user), params: { name: 'rocket' }
post api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji", user), params: { name: 'rocket' }
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response["message"]).to match("has already been taken")
end
end
end
describe 'DELETE /groups/:id/awardable/:awardable_id/award_emoji/:award_id' do
context 'when the awardable is an Epic' do
it 'deletes the award' do
expect do
delete api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji/#{award_emoji.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { epic.award_emoji.count }.from(1).to(0)
end
it 'returns a 404 error when the award emoji can not be found' do
delete api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji/#{non_existing_record_id}", user)
expect(response).to have_gitlab_http_status(:not_found)
end
it_behaves_like '412 response' do
let(:request) { api("/groups/#{group.id}/epics/#{epic.iid}/award_emoji/#{award_emoji.id}", user) }
end
end
end
describe 'DELETE /groups/:id/awardable/:awardable_id/award_emoji/:award_emoji_id' do
let!(:rocket) { create(:award_emoji, awardable: note, name: 'rocket', user: user) }
it 'deletes the award' do
expect do
delete api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji/#{rocket.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { note.award_emoji.count }.from(1).to(0)
end
it_behaves_like '412 response' do
let(:request) { api("/groups/#{group.id}/epics/#{epic.iid}/notes/#{note.id}/award_emoji/#{rocket.id}", user) }
end
end
end
......@@ -4,23 +4,18 @@ module API
class AwardEmoji < ::API::Base
include PaginationParams
helpers ::API::Helpers::AwardEmoji
before { authenticate! }
AWARDABLES = [
{ type: 'issue', find_by: :iid, feature_category: :issue_tracking },
{ type: 'merge_request', find_by: :iid, feature_category: :code_review },
{ type: 'snippet', find_by: :id, feature_category: :snippets }
].freeze
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
AWARDABLES.each do |awardable_params|
Helpers::AwardEmoji.awardables.each do |awardable_params|
resource awardable_params[:resource], requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
awardable_string = awardable_params[:type].pluralize
awardable_id_string = "#{awardable_params[:type]}_#{awardable_params[:find_by]}"
params do
requires :"#{awardable_id_string}", type: Integer, desc: "The ID of an Issue, Merge Request or Snippet"
requires :id, type: String, desc: "The ID of a #{awardable_params[:resource] == :projects ? 'project' : 'group'}"
requires :"#{awardable_id_string}", type: Integer, desc: Helpers::AwardEmoji.awardable_id_desc
end
[
......@@ -104,25 +99,6 @@ module API
awardable.user_can_award?(current_user)
end
# rubocop: disable CodeReuse/ActiveRecord
def awardable
@awardable ||=
begin
if params.include?(:note_id)
note_id = params.delete(:note_id)
awardable.notes.find(note_id)
elsif params.include?(:issue_iid)
user_project.issues.find_by!(iid: params[:issue_iid])
elsif params.include?(:merge_request_iid)
user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
else
user_project.snippets.find(params[:snippet_id])
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def read_ability(awardable)
case awardable
when Note
......
# frozen_string_literal: true
module API
module Helpers
module AwardEmoji
def self.awardables
[
{ type: 'issue', resource: :projects, find_by: :iid, feature_category: :issue_tracking },
{ type: 'merge_request', resource: :projects, find_by: :iid, feature_category: :code_review },
{ type: 'snippet', resource: :projects, find_by: :id, feature_category: :snippets }
]
end
def self.awardable_id_desc
"The ID of an Issue, Merge Request or Snippet"
end
# rubocop: disable CodeReuse/ActiveRecord
def awardable
@awardable ||=
begin
if params.include?(:note_id)
note_id = params.delete(:note_id)
awardable.notes.find(note_id)
elsif params.include?(:issue_iid)
user_project.issues.find_by!(iid: params[:issue_iid])
elsif params.include?(:merge_request_iid)
user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
elsif params.include?(:snippet_id)
user_project.snippets.find(params[:snippet_id])
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
API::Helpers::AwardEmoji.prepend_if_ee('EE::API::Helpers::AwardEmoji')
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