Commit 6381d9e1 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'improve_jira_issue_closing' into 'master'

Improve JIRA issue closing

- Zendesk ticket: https://gitlab.zendesk.com/agent/tickets/11034

### Expected behavior

When a merge request has 'Fixes JIRA-1234' in the description the issue should not only be transitioned but also record a comment (regardless of workflow type in JIRA).

### Observed behavior

Depending on the configured workflow a comment may or may not be allowed while transitioning to a new state. In my testing the newest JIRA default workflow did not allow comments while transitioning to 'Done', for example. The 'classic' workflow does, though. 

This merge request splits the transition and comment in to two API calls. The advantage is that the comment will be recorded 100% of the time instead of it being dependent on the workflow configuration.

Also, JIRA returns a '204' response code when the transition is successful. I added '204' to the success conditional because we were generating an error in the current configuration even though the transition was successful.

See merge request !45
parents 7680ada3 5828de0a
...@@ -4,6 +4,7 @@ v 8.2.0 ...@@ -4,6 +4,7 @@ v 8.2.0
- When someone as marked as a required approver for a merge request, an email should be sent - When someone as marked as a required approver for a merge request, an email should be sent
- Allow configuring the Jira API path (Alex Lossent) - Allow configuring the Jira API path (Alex Lossent)
- Fix "Rebase onto master" - Fix "Rebase onto master"
- Ensure a comment is properly recorded in JIRA when a merge request is accepted
v 8.1.0 (unreleased) v 8.1.0 (unreleased)
- added an issues template (Hannes Rosenögger) - added an issues template (Hannes Rosenögger)
......
...@@ -130,7 +130,7 @@ class JiraService < IssueTrackerService ...@@ -130,7 +130,7 @@ class JiraService < IssueTrackerService
case result.code case result.code
when 201, 200 when 201, 200
Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Sucessfully connected to #{api_url}.") Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.")
true true
else else
Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}") Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}")
...@@ -161,25 +161,36 @@ class JiraService < IssueTrackerService ...@@ -161,25 +161,36 @@ class JiraService < IssueTrackerService
self.jira_issue_transition_id ||= "2" self.jira_issue_transition_id ||= "2"
end end
def close_issue(commit, issue) def close_issue(entity, issue)
url = close_issue_url(issue.iid) commit_id = if entity.is_a?(Commit)
entity.id
commit_url = build_entity_url(:commit, commit.id) elsif entity.is_a?(MergeRequest)
entity.last_commit.id
end
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
# may or may not be allowed. Split the operation in to two calls so the
# comment always works.
transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url)
end
def transition_issue(issue)
message = { message = {
update: {
comment: [{
add: {
body: "Issue solved with [#{commit.id}|#{commit_url}]."
}
}]
},
transition: { transition: {
id: jira_issue_transition_id id: jira_issue_transition_id
} }
}.to_json }
send_message(close_issue_url(issue.iid), message.to_json)
end
def add_issue_solved_comment(issue, commit_id, commit_url)
comment = {
body: "Issue solved with [#{commit_id}|#{commit_url}]."
}
send_message(url, message) send_message(comment_url(issue.iid), comment.to_json)
end end
def add_comment(data, issue_name) def add_comment(data, issue_name)
...@@ -216,8 +227,8 @@ class JiraService < IssueTrackerService ...@@ -216,8 +227,8 @@ class JiraService < IssueTrackerService
) )
message = case result.code message = case result.code
when 201, 200 when 201, 200, 204
"#{self.class.name} SUCCESS #{result.code}: Sucessfully posted to #{url}." "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}."
when 401 when 401
"#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again." "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again."
else else
......
...@@ -31,6 +31,7 @@ describe JiraService do ...@@ -31,6 +31,7 @@ describe JiraService do
describe "Execute" do describe "Execute" do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:merge_request) { create(:merge_request) }
before do before do
@jira_service = JiraService.new @jira_service = JiraService.new
...@@ -46,20 +47,22 @@ describe JiraService do ...@@ -46,20 +47,22 @@ describe JiraService do
@sample_data = Gitlab::PushDataBuilder.build_sample(project, user) @sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
# https://github.com/bblimke/webmock#request-with-basic-authentication # https://github.com/bblimke/webmock#request-with-basic-authentication
@api_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions' @api_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions'
@comment_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/comment'
WebMock.stub_request(:post, @api_url) WebMock.stub_request(:post, @api_url)
WebMock.stub_request(:post, @comment_url)
end end
it "should call JIRA API" do it "should call JIRA API" do
@jira_service.execute(sample_commit, JiraIssue.new("JIRA-123", project)) @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @api_url).with( expect(WebMock).to have_requested(:post, @comment_url).with(
body: /Issue solved with/ body: /Issue solved with/
).once ).once
end end
it "calls the api with jira_issue_transition_id" do it "calls the api with jira_issue_transition_id" do
@jira_service.jira_issue_transition_id = 'this-is-a-custom-id' @jira_service.jira_issue_transition_id = 'this-is-a-custom-id'
@jira_service.execute(sample_commit, JiraIssue.new("JIRA-123", project)) @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @api_url).with( expect(WebMock).to have_requested(:post, @api_url).with(
body: /this-is-a-custom-id/ body: /this-is-a-custom-id/
).once ).once
......
...@@ -310,14 +310,7 @@ describe GitPushService do ...@@ -310,14 +310,7 @@ describe GitPushService do
let(:message) { "this is some work.\n\ncloses JIRA-1" } let(:message) { "this is some work.\n\ncloses JIRA-1" }
it "should initiate one api call to jira server to close the issue" do it "should initiate one api call to jira server to close the issue" do
body = { transition_body = {
update: {
comment: [{
add: {
body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]."
}
}]
},
transition: { transition: {
id: '2' id: '2'
} }
...@@ -325,7 +318,18 @@ describe GitPushService do ...@@ -325,7 +318,18 @@ describe GitPushService do
service.execute(project, user, @oldrev, @newrev, @ref) service.execute(project, user, @oldrev, @newrev, @ref)
expect(WebMock).to have_requested(:post, jira_api_transition_url).with( expect(WebMock).to have_requested(:post, jira_api_transition_url).with(
body: body body: transition_body
).once
end
it "should initiate one api call to jira server to comment on the issue" do
comment_body = {
body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]."
}.to_json
service.execute(project, user, @oldrev, @newrev, @ref)
expect(WebMock).to have_requested(:post, jira_api_comment_url).with(
body: comment_body
).once ).once
end end
end end
......
...@@ -13,7 +13,7 @@ module JiraServiceHelper ...@@ -13,7 +13,7 @@ module JiraServiceHelper
end end
def jira_status_message def jira_status_message
"JiraService SUCCESS 200: Sucessfully posted to #{jira_api_comment_url}." "JiraService SUCCESS 200: Successfully posted to #{jira_api_comment_url}."
end end
def jira_issue_comments def jira_issue_comments
......
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