diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index eb2ff0428f6..8ab0401d288 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -1,6 +1,6 @@ # == Mentionable concern # -# Contains functionality related to objects that can mention Users, Issues, MergeRequests, or Commits by +# Contains functionality related to objects that can mention Users, Issues, MergeRequests, Commits or Snippets by # GFM references. # # Used by Issue, Note, MergeRequest, and Commit. diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2caf6179ef8..aeded715893 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -128,15 +128,9 @@ class JiraService < IssueTrackerService return unless jira_issue.present? - project = self.project - noteable_name = noteable.model_name.singular - noteable_id = if noteable.is_a?(Commit) - noteable.id - else - noteable.iid - end - - entity_url = build_entity_url(noteable_name.to_sym, noteable_id) + noteable_id = noteable.respond_to?(:iid) ? noteable.iid : noteable.id + noteable_type = noteable_name(noteable) + entity_url = build_entity_url(noteable_type, noteable_id) data = { user: { @@ -144,11 +138,11 @@ class JiraService < IssueTrackerService url: resource_url(user_path(author)), }, project: { - name: project.path_with_namespace, - url: resource_url(namespace_project_path(project.namespace, project)) + name: self.project.path_with_namespace, + url: resource_url(namespace_project_path(project.namespace, self.project)) }, entity: { - name: noteable_name.humanize.downcase, + name: noteable_type.humanize.downcase, url: entity_url, title: noteable.title } @@ -285,18 +279,26 @@ class JiraService < IssueTrackerService "#{Settings.gitlab.base_url.chomp("/")}#{resource}" end - def build_entity_url(entity_name, entity_id) + def build_entity_url(noteable_type, entity_id) polymorphic_url( [ self.project.namespace.becomes(Namespace), self.project, - entity_name + noteable_type.to_sym ], id: entity_id, host: Settings.gitlab.base_url ) end + def noteable_name(noteable) + name = noteable.model_name.singular + + # ProjectSnippet inherits from Snippet class so it causes + # routing error building the URL. + name == "project_snippet" ? "snippet" : name + end + # Handle errors when doing JIRA API calls def jira_request yield diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 2373b445009..8ff4e7ae718 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -6,6 +6,7 @@ class Snippet < ActiveRecord::Base include Referable include Sortable include Awardable + include Mentionable cache_markdown_field :title, pipeline: :single_line cache_markdown_field :content diff --git a/changelogs/unreleased/issue_24303.yml b/changelogs/unreleased/issue_24303.yml new file mode 100644 index 00000000000..1f007712732 --- /dev/null +++ b/changelogs/unreleased/issue_24303.yml @@ -0,0 +1,4 @@ +--- +title: Fix JIRA references for project snippets +merge_request: +author: diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 150e21574a1..2a5709c6322 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -543,7 +543,10 @@ describe SystemNoteService, services: true do let(:comment_url) { jira_api_comment_url(jira_issue.id) } let(:success_message) { "JiraService SUCCESS: Successfully posted to http://jira.example.net." } - before { stub_jira_urls(jira_issue.id) } + before do + stub_jira_urls(jira_issue.id) + jira_service_settings + end noteable_types = ["merge_requests", "commit"] @@ -569,16 +572,16 @@ describe SystemNoteService, services: true do end end - context 'in JIRA issue tracker' do - before { jira_service_settings } + describe "new reference" do + context 'for commits' do + it "creates comment" do + result = described_class.cross_reference(jira_issue, commit, author) - describe "new reference" do - subject { described_class.cross_reference(jira_issue, commit, author) } - - it { is_expected.to eq(success_message) } + expect(result).to eq(success_message) + end it "creates remote link" do - subject + described_class.cross_reference(jira_issue, commit, author) expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with( body: hash_including( @@ -593,18 +596,18 @@ describe SystemNoteService, services: true do ).once end end - end - context 'in commit' do - context 'in JIRA issue tracker' do - before { jira_service_settings } + context 'for issues' do + let(:issue) { create(:issue, project: project) } - subject { described_class.cross_reference(jira_issue, issue, author) } + it "creates comment" do + result = described_class.cross_reference(jira_issue, issue, author) - it { is_expected.to eq(success_message) } + expect(result).to eq(success_message) + end it "creates remote link" do - subject + described_class.cross_reference(jira_issue, issue, author) expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with( body: hash_including( @@ -619,6 +622,32 @@ describe SystemNoteService, services: true do ).once end end + + context 'for snippets' do + let(:snippet) { create(:snippet, project: project) } + + it "creates comment" do + result = described_class.cross_reference(jira_issue, snippet, author) + + expect(result).to eq(success_message) + end + + it "creates remote link" do + described_class.cross_reference(jira_issue, snippet, author) + + expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with( + body: hash_including( + GlobalID: "GitLab", + object: { + url: namespace_project_snippet_url(project.namespace, project, snippet), + title: "GitLab: Mentioned on snippet - #{snippet.title}", + icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, + status: { resolved: false } + } + ) + ).once + end + end end describe "existing reference" do @@ -627,9 +656,11 @@ describe SystemNoteService, services: true do allow_any_instance_of(JIRA::Resource::Issue).to receive(:comments).and_return([OpenStruct.new(body: message)]) end - subject { described_class.cross_reference(jira_issue, commit, author) } + it "does not return success message" do + result = described_class.cross_reference(jira_issue, commit, author) - it { is_expected.not_to eq(success_message) } + expect(result).not_to eq(success_message) + end it 'does not try to create comment and remote link' do subject