From cab34dfbb2192e23dad9abc8091b00544496632c Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 3 Feb 2015 17:35:19 -0800 Subject: [PATCH] When JIRA issue is mentioned, add a reference comment to the issue. --- app/models/note.rb | 4 +- .../project_services/issue_tracker_service.rb | 4 + app/models/project_services/jira_service.rb | 91 ++++++++++++++++--- app/services/notes/create_service.rb | 2 +- app/views/projects/services/_form.html.haml | 2 +- 5 files changed, 86 insertions(+), 17 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 0b988cc3e0..343c6344cf 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -169,7 +169,9 @@ class Note < ActiveRecord::Base # eg. mentioning a commit in MR comments which exists inside a MR # should not create "mentioned in" note. def cross_reference_disallowed?(noteable, mentioner) - if mentioner.kind_of?(MergeRequest) + if noteable.is_a?(ExternalIssue) + true + elsif mentioner.kind_of?(MergeRequest) mentioner.commits.map(&:id).include? noteable.id end end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index b19c02bab4..d0205f69b5 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -36,6 +36,10 @@ class IssueTrackerService < Service # implement inside child end + def create_cross_reference_note + # implement inside child + end + def issue_url(iid) self.issues_url.gsub(':id', iid.to_s) end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index dd2c4faa73..cdb13529e2 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -14,6 +14,7 @@ class JiraService < IssueTrackerService include HTTParty + include Rails.application.routes.url_helpers prop_accessor :username, :password, :api_version, :jira_issue_transition_id, :title, :description, :project_url, :issues_url, :new_issue_url @@ -49,13 +50,29 @@ class JiraService < IssueTrackerService ) end - def execute(push, issue = nil) close_issue(push, issue) if issue end - def create_cross_reference_note - # TODO implement + def create_cross_reference_note(mentioned, noteable, author, project) + issue_name = mentioned.id + + data = { + user: { + name: author.name, + url: resource_url(user_path(author)), + }, + project: { + name: project.path_with_namespace, + url: resource_url(project_path(project)) + }, + entity: { + name: noteable.class.name.underscore.humanize.downcase, + url: resource_url(polymorphic_url([project, noteable], routing_type: :path)) + } + } + + add_comment(data, issue_name) end private @@ -84,30 +101,72 @@ class JiraService < IssueTrackerService 'transition' => { 'id' => jira_issue_transition_id } - } + }.to_json - json_body = message.to_json - Rails.logger.info("#{self.class.name}: sending POST with body #{json_body} to #{url}") + send_message(url, message) + end - JiraService.post( - url, - body: json_body, - headers: { - 'Content-Type' => 'application/json', - 'Authorization' => "Basic #{auth}" - } - ) + def add_comment(data, issue_name) + url = add_comment_url(issue_name) + user_name = data[:user][:name] + user_url = data[:user][:url] + entity_name = data[:entity][:name] + entity_url = data[:entity][:url] + entity_iid = data[:entity][:iid] + project_name = data[:project][:name] + project_url = data[:project][:url] + + message = { + body: "[#{user_name}|#{user_url}] mentioned #{issue_name} in #{entity_name} of [#{project_name}|#{entity_url}]." + }.to_json + + send_message(url, message) end def close_issue_url(issue_name) "#{server_url}/rest/api/#{self.api_version}/issue/#{issue_name}/transitions" end + def add_comment_url(issue_name) + "#{server_url}/rest/api/#{self.api_version}/issue/#{issue_name}/comment" + end + def auth require 'base64' Base64.urlsafe_encode64("#{self.username}:#{self.password}") end + def send_message(url, message) + begin + result = JiraService.post( + url, + body: message, + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => "Basic #{auth}" + } + ) + rescue URI::InvalidURIError => e + result = e.message + end + + message = if result.is_a?(String) + "#{self.class.name} ERROR: #{result}. Hostname: #{url}." + else + case result.code + when 201 + "#{self.class.name} SUCCESS 201: Sucessfully posted to #{url}." + when 401 + "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again." + else + "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}" + end + end + + Rails.logger.info(message) + message + end + def server_url server = URI(project_url) default_ports = [80, 443].include?(server.port) @@ -115,4 +174,8 @@ class JiraService < IssueTrackerService server_url.concat(":#{server.port}") unless default_ports return server_url end + + def resource_url(resource) + "#{Settings.gitlab['url'].chomp("/")}#{resource}" + end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index f924d7efeb..a003699092 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -16,7 +16,7 @@ module Notes # issue, merge request, or commit. note.references.each do |mentioned| if mentioned.is_a?(ExternalIssue) - note.project.issues_tracker.create_cross_reference_note + note.project.issues_tracker.create_cross_reference_note(mentioned, note.noteable, note.author, note.project) else Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project) end diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index e7c62c593b..3dbb70c70d 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -46,7 +46,7 @@ - elsif type == 'select' = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } - elsif type == 'password' - = f.password_field name, value: value, class: 'form-control' + = f.password_field name, placeholder: value, class: 'form-control' .form-actions = f.submit 'Save', class: 'btn btn-save'