Commit a4e4042b authored by Aishwarya Subramanian's avatar Aishwarya Subramanian

Moved jira issue details to MergeRequestPollCachedWidgetEntity

Since the Jira issue <> MR details contains only 2
attributes, and only used in MR widget curently,
the data could be presented in MergeRequestPollCachedWidgetEntity
instead of creating a new api at this point.
parent cbeb875c
......@@ -211,6 +211,8 @@ module EE
delegate :pipeline_configuration_full_path, to: :compliance_management_framework, allow_nil: true
alias_attribute :compliance_pipeline_configuration_full_path, :pipeline_configuration_full_path
delegate :prevent_merge_without_jira_issue, to: :project_setting
validates :repository_size_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true }
validates :max_pages_size,
......
......@@ -56,6 +56,10 @@ module EE
project_security_discover_path(project) if show_discover_project_security?(project)
end
def issue_keys
Atlassian::JiraIssueKeyExtractor.new(merge_request.title, merge_request.description).issue_keys
end
private
def expose_mr_approval_path?
......
......@@ -16,6 +16,16 @@ module EE
expose :missing_security_scan_types do |merge_request|
presenter(merge_request).missing_security_scan_types
end
expose :jira_associations, if: -> (mr) { mr.project.jira_issue_association_required_to_merge_enabled? } do
expose :enforced do |merge_request|
presenter(merge_request).project.prevent_merge_without_jira_issue
end
expose :issue_keys do |merge_request|
presenter(merge_request).issue_keys
end
end
end
end
end
# frozen_string_literal: true
module API
module Entities
class MergeRequestJiraIssue < Grape::Entity
expose :issue_keys, if: -> (merge_request, _) { merge_request.project&.project_setting&.prevent_merge_without_jira_issue } do |merge_request|
Atlassian::JiraIssueKeyExtractor.new(merge_request.title, merge_request.description).issue_keys
end
end
end
end
# frozen_string_literal: true
module API
class MergeRequestJiraIssue < ::API::Base
before { authenticate_non_get! }
before { check_jira_issue_feature_available! }
feature_category :source_code_management
helpers do
def check_jira_issue_feature_available!
not_found! unless user_project.jira_issue_association_required_to_merge_enabled?
end
def present_jira_issue(merge_request)
present merge_request, with: ::API::Entities::MergeRequestJiraIssue, current_user: current_user
end
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
segment ':id/merge_requests/:merge_request_iid' do
# Get the Jira Issue association for a merge request
#
# Parameters:
# id (required) - The ID of a project
# merge_request_iid (required) - IID of MR
# Examples:
# GET /projects/:id/merge_requests/:merge_request_iid/jira_issue
desc 'Get Jira Issue association for merge request'
params do
requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_iid, types: [Integer, String], desc: 'The IID of a merge request'
end
get 'jira_issue' do
forbidden! unless can?(current_user, :read_merge_request, user_project)
merge_request = find_merge_request_with_access(declared(params)[:merge_request_iid])
present_jira_issue(merge_request)
end
end
end
end
end
......@@ -39,7 +39,6 @@ module EE
mount ::API::VulnerabilityIssueLinks
mount ::API::VulnerabilityExports
mount ::API::MergeRequestApprovalRules
mount ::API::MergeRequestJiraIssue
mount ::API::ProjectAliases
mount ::API::Dependencies
mount ::API::VisualReviewDiscussions
......
......@@ -21,6 +21,8 @@ RSpec.describe Project do
it { is_expected.to delegate_method(:pipeline_configuration_full_path).to(:compliance_management_framework) }
it { is_expected.to delegate_method(:prevent_merge_without_jira_issue).to(:project_setting) }
it { is_expected.to belong_to(:deleting_user) }
it { is_expected.to have_one(:import_state).class_name('ProjectImportState') }
......
......@@ -156,4 +156,28 @@ RSpec.describe MergeRequestPresenter do
end
end
end
describe '#issue_keys' do
let(:presenter) { described_class.new(merge_request, current_user: user) }
subject { presenter.issue_keys }
context 'when Jira issue is provided in MR title / description' do
let(:issue_key) { 'SIGNUP-1234' }
before do
merge_request.update!(title: "Fixes sign up issue #{issue_key}", description: "Related to #{issue_key}")
end
it { is_expected.to contain_exactly(issue_key) }
end
context 'when Jira issue is NOT provided in MR title / description' do
before do
merge_request.update!(title: "Fixes sign up issue", description: "Prevent spam sign ups by adding a rate limiter")
end
it { is_expected.to be_empty }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::MergeRequestJiraIssue do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository, creator: user) }
let_it_be_with_reload(:merge_request) { create(:merge_request, author: user, source_project: project, target_project: project) }
describe 'GET /projects/:id/merge_requests/:merge_request_iid/jira_issue' do
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/jira_issue" }
subject { get api(url, current_user) }
context 'when feature is available' do
before do
stub_licensed_features(jira_issue_association_enforcement: true)
stub_feature_flags(jira_issue_association_on_merge_request: true)
end
context 'user does not have access' do
let_it_be(:unauthorized_user) { create(:user) }
let(:current_user) { unauthorized_user }
before do
project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
end
it 'responds with forbidden' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'user has access' do
let(:current_user) { user }
context 'when jira issue is not required for merge' do
before do
project.create_project_setting(prevent_merge_without_jira_issue: false)
end
it 'does not include attribute issue_keys' do
subject
expect(json_response).not_to have_key(:issue_keys)
end
end
context 'when jira issue is required for merge' do
before do
project.create_project_setting(prevent_merge_without_jira_issue: true)
end
it 'responds with status :ok' do
subject
expect(response).to have_gitlab_http_status(:ok)
end
context 'when Jira issue is not provided in MR title/description' do
it 'responds with empty issue key' do
subject
expect(json_response["issue_keys"]).to be_empty
end
end
context 'when Jira issue is provided in MR title' do
before do
merge_request.update!(title: 'Fix PRODUCT-1234')
end
it 'responds with the issue key in MR title' do
subject
expect(json_response["issue_keys"]).to eq(["PRODUCT-1234"])
end
end
context 'when Jira issue is provided in MR description' do
before do
merge_request.update!(description: 'Jira issue associated: FEATURE-1234')
end
it 'responds with the issue key in MR description' do
subject
expect(json_response["issue_keys"]).to eq(["FEATURE-1234"])
end
end
end
end
end
context 'when feature is not available' do
using RSpec::Parameterized::TableSyntax
let(:current_user) { user }
shared_examples 'not found' do
specify do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
where(:licensed, :feature_flag) do
false | true
true | false
false | false
end
with_them do
before do
stub_licensed_features(jira_issue_association_enforcement: licensed)
stub_feature_flags(jira_issue_association_on_merge_request: feature_flag)
end
it_behaves_like 'not found'
end
end
end
end
......@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe MergeRequestPollCachedWidgetEntity do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:resource) { create(:merge_request, source_project: project, target_project: project) }
let_it_be_with_reload(:resource) { create(:merge_request, source_project: project, target_project: project) }
let_it_be(:user) { create(:user) }
let(:request) { double('request', current_user: user, project: project) }
......@@ -22,4 +22,86 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
it 'includes missing security scan types' do
is_expected.to include(:missing_security_scan_types)
end
context 'jira_associations' do
context 'when feature is available' do
before do
stub_licensed_features(jira_issue_association_enforcement: true)
stub_feature_flags(jira_issue_association_on_merge_request: true)
end
it { is_expected.to include(:jira_associations) }
shared_examples 'contains the issue key specified in MR title / description' do
context 'when Jira issue is provided in MR title' do
let(:issue_key) { 'SIGNUP-1234' }
before do
resource.update!(title: "Fixes sign up issue #{issue_key}")
end
it { expect(subject[:jira_associations][:issue_keys]).to contain_exactly(issue_key) }
end
context 'when Jira issue is provided in MR description' do
let(:issue_key) { 'SECURITY-1234' }
before do
resource.update!(description: "Related to #{issue_key}")
end
it { expect(subject[:jira_associations][:issue_keys]).to contain_exactly(issue_key) }
end
end
shared_examples 'when issue key is NOT specified in MR title / description' do
before do
resource.update!(title: "Fixes sign up issue", description: "Prevent spam sign ups by adding a rate limiter")
end
it { expect(subject[:jira_associations][:issue_keys]).to be_empty }
end
context 'when jira issue is required for merge' do
before do
project.create_project_setting(prevent_merge_without_jira_issue: true)
end
it { expect(subject[:jira_associations][:enforced]).to be_truthy }
it_behaves_like 'contains the issue key specified in MR title / description'
it_behaves_like 'when issue key is NOT specified in MR title / description'
end
context 'when jira issue is NOT required for merge' do
before do
project.create_project_setting(prevent_merge_without_jira_issue: false)
end
it { expect(subject[:jira_associations][:enforced]).to be_falsey }
it_behaves_like 'contains the issue key specified in MR title / description'
it_behaves_like 'when issue key is NOT specified in MR title / description'
end
end
context 'when feature is NOT available' do
using RSpec::Parameterized::TableSyntax
where(:licensed, :feature_flag) do
false | true
true | false
false | false
end
with_them do
before do
stub_licensed_features(jira_issue_association_enforcement: licensed)
stub_feature_flags(jira_issue_association_on_merge_request: feature_flag)
end
it { is_expected.not_to include(:jira_associations) }
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