Commit 1ca21356 authored by Arturo Herrero's avatar Arturo Herrero

Jira Issue detail serializer and entity

This retrieve a Jira issue using the Jira API V2 and returns the JSON to
be consumed by the frontend.
parent 2e50e7cf
......@@ -158,8 +158,8 @@ class JiraService < IssueTrackerService
# support any events.
end
def find_issue(issue_key)
jira_request { client.Issue.find(issue_key) }
def find_issue(issue_key, options = {})
jira_request { client.Issue.find(issue_key, options) }
end
def close_issue(entity, external_issue)
......
......@@ -56,27 +56,8 @@ module Projects
end
def issue_json
{
title_html: '<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
description_html: '<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
created_at: 2.hours.ago,
author: {
id: 2,
username: 'justin_ho',
name: 'Justin Ho',
web_url: 'http://127.0.0.1:3000/root',
avatar_url: 'http://127.0.0.1:3000/uploads/-/system/user/avatar/1/avatar.png?width=90'
},
labels: [
{
title: 'In Progress',
description: 'Work that is still in progress',
color: '#EBECF0',
text_color: '#283856'
}
],
state: 'opened'
}
::Integrations::Jira::IssueDetailSerializer.new
.represent(project.jira_service.find_issue(params[:id], { expand: 'renderedFields' }), project: project)
end
def finder
......
# frozen_string_literal: true
module Integrations
module Jira
class IssueDetailEntity < ::Integrations::Jira::IssueEntity
expose :description_html do |jira_issue|
Banzai::Pipeline::GfmPipeline.call(jira_issue.renderedFields['description'], project: nil)[:output].to_html
end
expose :state do |jira_issue|
jira_issue.resolutiondate ? 'closed' : 'opened'
end
end
end
end
# frozen_string_literal: true
module Integrations
module Jira
class IssueDetailSerializer < BaseSerializer
entity ::Integrations::Jira::IssueDetailEntity
end
end
end
......@@ -33,7 +33,6 @@ module Integrations
name: name,
color: '#EBECF0',
text_color: '#283856'
}
end
end
......@@ -41,7 +40,8 @@ module Integrations
expose :author do |jira_issue|
{
name: jira_issue.reporter.displayName,
web_url: author_web_url(jira_issue)
web_url: author_web_url(jira_issue),
avatar_url: jira_issue.reporter.avatarUrls['48x48']
}
end
......
......@@ -187,6 +187,8 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do
end
context 'when `jira_issues_show_integration` feature is enabled' do
let(:jira_issue) { {} }
before do
stub_feature_flags(jira_issues_show_integration: true)
end
......@@ -199,15 +201,15 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do
end
it 'returns JSON response' do
get :show, params: { namespace_id: project.namespace, project_id: project, id: 1, format: :json }
expect_next_found_instance_of(JiraService) do |service|
expect(service).to receive(:find_issue).with('1', { expand: 'renderedFields' }).and_return(jira_issue)
end
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to include(
'title_html',
'description_html',
'author',
'labels'
)
expect_next_instance_of(Integrations::Jira::IssueDetailSerializer) do |serializer|
expect(serializer).to receive(:represent).with(jira_issue, project: project)
end
get :show, params: { namespace_id: project.namespace, project_id: project, id: 1, format: :json }
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::Jira::IssueDetailEntity do
let(:project) { build(:project) }
let(:jira_client) { double(options: { site: 'http://jira.com/' }) }
let(:reporter) do
double(
'displayName' => 'reporter',
'avatarUrls' => { '48x48' => 'http://reporter.avatar' },
'name' => double # Default to Jira Server issue response, Jira Cloud replaces name with accountId,
)
end
let(:jira_issue) do
double(
renderedFields: { 'description' => '<p>Description</p>' },
summary: 'Title',
created: '2020-06-25T15:39:30.000+0000',
updated: '2020-06-26T15:38:32.000+0000',
resolutiondate: '2020-06-27T13:23:51.000+0000',
labels: ['backend'],
reporter: reporter,
assignee: double('displayName' => 'assignee'),
project: double(key: 'GL'),
key: 'GL-5',
client: jira_client,
status: double(name: 'To Do')
)
end
subject { described_class.new(jira_issue, project: project).as_json }
it 'returns the Jira issues attributes' do
expect(subject).to include(
project_id: project.id,
title: 'Title',
description_html: "<p dir=\"auto\">Description</p>",
created_at: '2020-06-25T15:39:30.000+0000'.to_datetime.utc,
updated_at: '2020-06-26T15:38:32.000+0000'.to_datetime.utc,
closed_at: '2020-06-27T13:23:51.000+0000'.to_datetime.utc,
status: 'To Do',
state: 'closed',
labels: [
{
name: 'backend',
color: '#EBECF0',
text_color: '#283856'
}
],
author: hash_including(
name: 'reporter',
avatar_url: 'http://reporter.avatar'
),
assignees: [
{ name: 'assignee' }
],
web_url: 'http://jira.com/browse/GL-5',
references: { relative: 'GL-5' },
external_tracker: 'jira'
)
end
context 'with Jira Server configuration' do
before do
allow(reporter).to receive(:name).and_return('reporter@reporter.com')
end
it 'returns the Jira Server profile URL' do
expect(subject[:author]).to include(web_url: 'http://jira.com/secure/ViewProfile.jspa?name=reporter@reporter.com')
end
context 'and context_path' do
let(:jira_client) { double(options: { site: 'http://jira.com/', context_path: '/jira-sub-path' }) }
it 'returns URLs including context path' do
expect(subject[:author]).to include(web_url: 'http://jira.com/jira-sub-path/secure/ViewProfile.jspa?name=reporter@reporter.com')
expect(subject[:web_url]).to eq('http://jira.com/jira-sub-path/browse/GL-5')
end
end
end
context 'with Jira Cloud configuration' do
before do
allow(reporter).to receive(:accountId).and_return('12345')
end
it 'returns the Jira Cloud profile URL' do
expect(subject[:author]).to include(web_url: 'http://jira.com/people/12345')
end
end
context 'without assignee' do
before do
allow(jira_issue).to receive(:assignee).and_return(nil)
end
it 'returns an empty array' do
expect(subject).to include(assignees: [])
end
end
context 'without labels' do
before do
allow(jira_issue).to receive(:labels).and_return([])
end
it 'returns an empty array' do
expect(subject).to include(labels: [])
end
end
context 'without resolution date' do
before do
allow(jira_issue).to receive(:resolutiondate).and_return(nil)
end
it "returns 'Open' state" do
expect(subject).to include(state: 'opened')
end
end
end
......@@ -8,6 +8,7 @@ RSpec.describe Integrations::Jira::IssueEntity do
let(:reporter) do
double(
'displayName' => 'reporter',
'avatarUrls' => { '48x48' => 'http://reporter.avatar' },
'name' => double # Default to Jira Server issue response, Jira Cloud replaces name with accountId
)
end
......@@ -47,7 +48,10 @@ RSpec.describe Integrations::Jira::IssueEntity do
text_color: '#283856'
}
],
author: hash_including(name: 'reporter'),
author: hash_including(
name: 'reporter',
avatar_url: 'http://reporter.avatar'
),
assignees: [
{ name: 'assignee' }
],
......
......@@ -456,6 +456,16 @@ RSpec.describe JiraService do
expect(WebMock).to have_requested(:get, issue_url)
end
context 'with options' do
let(:issue_url) { "#{url}/rest/api/2/issue/#{issue_key}?expand=renderedFields" }
it 'call the Jira API with the options to get the issue' do
jira_service.find_issue(issue_key, { expand: 'renderedFields' })
expect(WebMock).to have_requested(:get, issue_url)
end
end
end
describe '#close_issue' do
......
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