jira-ruby 1.6.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +3 -2
- data/README.md +40 -11
- data/Rakefile +2 -2
- data/example.rb +8 -0
- data/jira-ruby.gemspec +1 -2
- data/lib/jira/base.rb +1 -1
- data/lib/jira/client.rb +85 -12
- data/lib/jira/http_client.rb +29 -13
- data/lib/jira/http_error.rb +1 -1
- data/lib/jira/jwt_client.rb +67 -0
- data/lib/jira/oauth_client.rb +18 -6
- data/lib/jira/request_client.rb +15 -3
- data/lib/jira/resource/attachment.rb +19 -14
- data/lib/jira/resource/board.rb +8 -1
- data/lib/jira/resource/board_configuration.rb +9 -0
- data/lib/jira/resource/issue_picker_suggestions.rb +24 -0
- data/lib/jira/resource/issue_picker_suggestions_issue.rb +10 -0
- data/lib/jira/resource/sprint.rb +12 -8
- data/lib/jira/resource/suggested_issue.rb +9 -0
- data/lib/jira/resource/user.rb +1 -1
- data/lib/jira/resource/watcher.rb +7 -0
- data/lib/jira/version.rb +1 -1
- data/lib/jira-ruby.rb +6 -0
- data/spec/integration/user_spec.rb +3 -3
- data/spec/integration/watcher_spec.rb +15 -6
- data/spec/jira/base_spec.rb +12 -0
- data/spec/jira/client_spec.rb +70 -0
- data/spec/jira/http_client_spec.rb +137 -8
- data/spec/jira/jwt_uri_builder_spec.rb +59 -0
- data/spec/jira/oauth_client_spec.rb +47 -10
- data/spec/jira/request_client_spec.rb +37 -10
- data/spec/jira/resource/attachment_spec.rb +79 -22
- data/spec/jira/resource/board_spec.rb +50 -1
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +79 -0
- data/spec/jira/resource/issue_spec.rb +1 -1
- data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +18 -0
- data/spec/jira/resource/sprint_spec.rb +23 -11
- metadata +32 -8
@@ -19,27 +19,32 @@ module JIRA
|
|
19
19
|
parse_json(response.body)
|
20
20
|
end
|
21
21
|
|
22
|
-
def save!(attrs)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
request = Net::HTTP::Post::Multipart.new url, data, headers
|
27
|
-
request.basic_auth(client.request_client.options[:username],
|
28
|
-
client.request_client.options[:password])
|
22
|
+
def save!(attrs, path = url)
|
23
|
+
file = attrs['file'] || attrs[:file] # Keep supporting 'file' parameter as a string for backward compatibility
|
24
|
+
mime_type = attrs[:mimeType] || 'application/binary'
|
29
25
|
|
30
|
-
|
26
|
+
headers = { 'X-Atlassian-Token' => 'nocheck' }
|
27
|
+
data = { 'file' => UploadIO.new(file, mime_type, file) }
|
31
28
|
|
32
|
-
|
33
|
-
unless response.body.nil? || response.body.length < 2
|
34
|
-
json = self.class.parse_json(response.body)
|
35
|
-
attachment = json[0]
|
29
|
+
response = client.post_multipart(path, data , headers)
|
36
30
|
|
37
|
-
|
38
|
-
end
|
31
|
+
set_attributes(attrs, response)
|
39
32
|
|
40
33
|
@expanded = false
|
41
34
|
true
|
42
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_attributes(attributes, response)
|
40
|
+
set_attrs(attributes, false)
|
41
|
+
return if response.body.nil? || response.body.length < 2
|
42
|
+
|
43
|
+
json = self.class.parse_json(response.body)
|
44
|
+
attachment = json[0]
|
45
|
+
|
46
|
+
set_attrs(attachment)
|
47
|
+
end
|
43
48
|
end
|
44
49
|
end
|
45
50
|
end
|
data/lib/jira/resource/board.rb
CHANGED
@@ -31,7 +31,7 @@ module JIRA
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def issues(params = {})
|
34
|
-
path = path_base(client) + "/board/#{id}/issue
|
34
|
+
path = path_base(client) + "/board/#{id}/issue"
|
35
35
|
response = client.get(url_with_query_params(path, params))
|
36
36
|
json = self.class.parse_json(response.body)
|
37
37
|
results = json['issues']
|
@@ -46,6 +46,13 @@ module JIRA
|
|
46
46
|
results.map { |issue| client.Issue.build(issue) }
|
47
47
|
end
|
48
48
|
|
49
|
+
def configuration(params = {})
|
50
|
+
path = path_base(client) + "/board/#{id}/configuration"
|
51
|
+
response = client.get(url_with_query_params(path, params))
|
52
|
+
json = self.class.parse_json(response.body)
|
53
|
+
client.BoardConfiguration.build(json)
|
54
|
+
end
|
55
|
+
|
49
56
|
# options
|
50
57
|
# - state ~ future, active, closed, you can define multiple states separated by commas, e.g. state=active,closed
|
51
58
|
# - maxResults ~ default: 50 (JIRA API), 1000 (this library)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module JIRA
|
2
|
+
module Resource
|
3
|
+
class IssuePickerSuggestionsFactory < JIRA::BaseFactory # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class IssuePickerSuggestions < JIRA::Base
|
7
|
+
has_many :sections, class: JIRA::Resource::IssuePickerSuggestionsIssue
|
8
|
+
|
9
|
+
def self.all(client, query = '', options = { current_jql: nil, current_issue_key: nil, current_project_id: nil, show_sub_tasks: nil, show_sub_tasks_parent: nil })
|
10
|
+
url = client.options[:rest_base_path] + "/issue/picker?query=#{CGI.escape(query)}"
|
11
|
+
|
12
|
+
url << "¤tJQL=#{CGI.escape(options[:current_jql])}" if options[:current_jql]
|
13
|
+
url << "¤tIssueKey=#{CGI.escape(options[:current_issue_key])}" if options[:current_issue_key]
|
14
|
+
url << "¤tProjectId=#{CGI.escape(options[:current_project_id])}" if options[:current_project_id]
|
15
|
+
url << "&showSubTasks=#{options[:show_sub_tasks]}" if options[:show_sub_tasks]
|
16
|
+
url << "&showSubTaskParent=#{options[:show_sub_task_parent]}" if options[:show_sub_task_parent]
|
17
|
+
|
18
|
+
response = client.get(url)
|
19
|
+
json = parse_json(response.body)
|
20
|
+
client.IssuePickerSuggestions.build(json)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/jira/resource/sprint.rb
CHANGED
@@ -5,7 +5,7 @@ module JIRA
|
|
5
5
|
|
6
6
|
class Sprint < JIRA::Base
|
7
7
|
def self.find(client, key)
|
8
|
-
response = client.get(
|
8
|
+
response = client.get(agile_path(client, key))
|
9
9
|
json = parse_json(response.body)
|
10
10
|
client.Sprint.build(json)
|
11
11
|
end
|
@@ -19,7 +19,7 @@ module JIRA
|
|
19
19
|
|
20
20
|
def add_issue(issue)
|
21
21
|
request_body = { issues: [issue.id] }.to_json
|
22
|
-
response = client.post(
|
22
|
+
response = client.post("#{agile_path}/issue", request_body)
|
23
23
|
true
|
24
24
|
end
|
25
25
|
|
@@ -47,8 +47,8 @@ module JIRA
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def get_sprint_details
|
50
|
-
search_url =
|
51
|
-
|
50
|
+
search_url =
|
51
|
+
"#{client.options[:site]}#{client.options[:client_path]}/rest/greenhopper/1.0/rapid/charts/sprintreport?rapidViewId=#{rapidview_id}&sprintId=#{id}"
|
52
52
|
begin
|
53
53
|
response = client.get(search_url)
|
54
54
|
rescue StandardError
|
@@ -76,12 +76,12 @@ module JIRA
|
|
76
76
|
|
77
77
|
def save(attrs = {}, _path = nil)
|
78
78
|
attrs = @attrs if attrs.empty?
|
79
|
-
super(attrs,
|
79
|
+
super(attrs, agile_path)
|
80
80
|
end
|
81
81
|
|
82
82
|
def save!(attrs = {}, _path = nil)
|
83
83
|
attrs = @attrs if attrs.empty?
|
84
|
-
super(attrs,
|
84
|
+
super(attrs, agile_path)
|
85
85
|
end
|
86
86
|
|
87
87
|
# WORK IN PROGRESS
|
@@ -93,8 +93,12 @@ module JIRA
|
|
93
93
|
|
94
94
|
private
|
95
95
|
|
96
|
-
def
|
97
|
-
|
96
|
+
def agile_path
|
97
|
+
self.class.agile_path(client, id)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.agile_path(client, key)
|
101
|
+
"#{client.options[:context_path]}/rest/agile/1.0/sprint/#{key}"
|
98
102
|
end
|
99
103
|
end
|
100
104
|
end
|
data/lib/jira/resource/user.rb
CHANGED
@@ -18,7 +18,7 @@ module JIRA
|
|
18
18
|
|
19
19
|
# Cannot retrieve more than 1,000 users through the api, please see: https://jira.atlassian.com/browse/JRASERVER-65089
|
20
20
|
def self.all(client)
|
21
|
-
response = client.get("/rest/api/2/
|
21
|
+
response = client.get("/rest/api/2/users/search?username=_&maxResults=#{MAX_RESULTS}")
|
22
22
|
all_users = JSON.parse(response.body)
|
23
23
|
|
24
24
|
all_users.flatten.uniq.map do |user|
|
data/lib/jira/version.rb
CHANGED
data/lib/jira-ruby.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
$LOAD_PATH << __dir__
|
2
2
|
|
3
|
+
require 'active_support'
|
3
4
|
require 'active_support/inflector'
|
4
5
|
ActiveSupport::Inflector.inflections do |inflector|
|
5
6
|
inflector.singular /status$/, 'status'
|
@@ -25,6 +26,9 @@ require 'jira/resource/worklog'
|
|
25
26
|
require 'jira/resource/applinks'
|
26
27
|
require 'jira/resource/issuelinktype'
|
27
28
|
require 'jira/resource/issuelink'
|
29
|
+
require 'jira/resource/suggested_issue'
|
30
|
+
require 'jira/resource/issue_picker_suggestions_issue'
|
31
|
+
require 'jira/resource/issue_picker_suggestions'
|
28
32
|
require 'jira/resource/remotelink'
|
29
33
|
require 'jira/resource/sprint'
|
30
34
|
require 'jira/resource/sprint_report'
|
@@ -38,10 +42,12 @@ require 'jira/resource/createmeta'
|
|
38
42
|
require 'jira/resource/webhook'
|
39
43
|
require 'jira/resource/agile'
|
40
44
|
require 'jira/resource/board'
|
45
|
+
require 'jira/resource/board_configuration'
|
41
46
|
|
42
47
|
require 'jira/request_client'
|
43
48
|
require 'jira/oauth_client'
|
44
49
|
require 'jira/http_client'
|
50
|
+
require 'jira/jwt_client'
|
45
51
|
require 'jira/client'
|
46
52
|
|
47
53
|
require 'jira/railtie' if defined?(Rails)
|
@@ -21,18 +21,18 @@ describe JIRA::Resource::User do
|
|
21
21
|
describe '#all' do
|
22
22
|
let(:client) do
|
23
23
|
client = double(options: { rest_base_path: '/jira/rest/api/2' })
|
24
|
-
allow(client).to receive(:get).with('/rest/api/2/
|
24
|
+
allow(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000').and_return(JIRA::Resource::UserFactory.new(client))
|
25
25
|
client
|
26
26
|
end
|
27
27
|
|
28
28
|
before do
|
29
29
|
allow(client).to receive(:get)
|
30
|
-
.with('/rest/api/2/
|
30
|
+
.with('/rest/api/2/users/search?username=_&maxResults=1000') { OpenStruct.new(body: '["User1"]') }
|
31
31
|
allow(client).to receive_message_chain(:User, :build).with('users') { [] }
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'gets users with maxResults of 1000' do
|
35
|
-
expect(client).to receive(:get).with('/rest/api/2/
|
35
|
+
expect(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000')
|
36
36
|
expect(client).to receive_message_chain(:User, :build).with('User1')
|
37
37
|
JIRA::Resource::User.all(client)
|
38
38
|
end
|
@@ -33,21 +33,30 @@ describe JIRA::Resource::Watcher do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
describe 'watchers' do
|
36
|
-
|
37
|
-
stub_request(:get,
|
38
|
-
site_url + '/jira/rest/api/2/issue/10002')
|
36
|
+
before(:each) do
|
37
|
+
stub_request(:get, site_url + '/jira/rest/api/2/issue/10002')
|
39
38
|
.to_return(status: 200, body: get_mock_response('issue/10002.json'))
|
40
39
|
|
41
|
-
stub_request(:get,
|
42
|
-
site_url + '/jira/rest/api/2/issue/10002/watchers')
|
40
|
+
stub_request(:get, site_url + '/jira/rest/api/2/issue/10002/watchers')
|
43
41
|
.to_return(status: 200, body: get_mock_response('issue/10002/watchers.json'))
|
44
42
|
|
43
|
+
stub_request(:post, site_url + '/jira/rest/api/2/issue/10002/watchers')
|
44
|
+
.to_return(status: 204, body: nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should returns all the watchers' do
|
45
48
|
issue = client.Issue.find('10002')
|
46
49
|
watchers = client.Watcher.all(options = { issue: issue })
|
47
50
|
expect(watchers.length).to eq(1)
|
48
51
|
end
|
52
|
+
|
53
|
+
it 'should add a watcher' do
|
54
|
+
issue = client.Issue.find('10002')
|
55
|
+
watcher = JIRA::Resource::Watcher.new(client, issue: issue)
|
56
|
+
user_id = "tester"
|
57
|
+
watcher.save!(user_id)
|
58
|
+
end
|
49
59
|
end
|
50
60
|
|
51
|
-
it_should_behave_like 'a resource'
|
52
61
|
end
|
53
62
|
end
|
data/spec/jira/base_spec.rb
CHANGED
@@ -369,6 +369,18 @@ describe JIRA::Base do
|
|
369
369
|
expect(subject.url).to eq('http://foo/bar')
|
370
370
|
end
|
371
371
|
|
372
|
+
it 'returns path as the URL if set and site options is specified' do
|
373
|
+
allow(client).to receive(:options) { { site: 'http://foo' } }
|
374
|
+
attrs['self'] = 'http://foo/bar'
|
375
|
+
expect(subject.url).to eq('/bar')
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'returns path as the URL if set and site options is specified and ends with a slash' do
|
379
|
+
allow(client).to receive(:options) { { site: 'http://foo/' } }
|
380
|
+
attrs['self'] = 'http://foo/bar'
|
381
|
+
expect(subject.url).to eq('/bar')
|
382
|
+
end
|
383
|
+
|
372
384
|
it 'generates the URL from id if self not set' do
|
373
385
|
attrs['self'] = nil
|
374
386
|
attrs['id'] = '98765'
|
data/spec/jira/client_spec.rb
CHANGED
@@ -59,6 +59,19 @@ RSpec.shared_examples 'Client Common Tests' do
|
|
59
59
|
expect(subject.Project.find('123')).to eq(find_result)
|
60
60
|
end
|
61
61
|
end
|
62
|
+
|
63
|
+
describe 'SSL client options' do
|
64
|
+
context 'without certificate and key' do
|
65
|
+
let(:options) { { use_client_cert: true } }
|
66
|
+
subject { JIRA::Client.new(options) }
|
67
|
+
|
68
|
+
it 'raises an ArgumentError' do
|
69
|
+
expect { subject }.to raise_exception(ArgumentError, 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true')
|
70
|
+
options[:ssl_client_cert] = '<cert></cert>'
|
71
|
+
expect { subject }.to raise_exception(ArgumentError, 'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
62
75
|
end
|
63
76
|
|
64
77
|
RSpec.shared_examples 'HttpClient tests' do
|
@@ -207,6 +220,54 @@ describe JIRA::Client do
|
|
207
220
|
end
|
208
221
|
end
|
209
222
|
|
223
|
+
context 'with jwt authentication' do
|
224
|
+
subject do
|
225
|
+
JIRA::Client.new(
|
226
|
+
issuer: 'foo',
|
227
|
+
base_url: 'https://host.tld',
|
228
|
+
shared_secret: 'shared_secret_key',
|
229
|
+
auth_type: :jwt
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
before(:each) do
|
234
|
+
stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project')
|
235
|
+
.with(query: hash_including(:jwt))
|
236
|
+
.to_return(status: 200, body: '[]', headers: {})
|
237
|
+
end
|
238
|
+
|
239
|
+
include_examples 'Client Common Tests'
|
240
|
+
include_examples 'HttpClient tests'
|
241
|
+
|
242
|
+
specify { expect(subject.request_client).to be_a JIRA::JwtClient }
|
243
|
+
|
244
|
+
it 'sets the username and password' do
|
245
|
+
expect(subject.options[:shared_secret]).to eq('shared_secret_key')
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'with a incorrect jwt key' do
|
249
|
+
before do
|
250
|
+
stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project')
|
251
|
+
.with(query: hash_including(:jwt))
|
252
|
+
.to_return(status: 401, body: '[]', headers: {})
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'is not authenticated' do
|
256
|
+
expect(subject.authenticated?).to be_falsey
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'raises a JIRA::HTTPError when trying to fetch projects' do
|
260
|
+
expect { subject.Project.all }.to raise_error JIRA::HTTPError
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'only returns a true for #authenticated? once we have requested some data' do
|
265
|
+
expect(subject.authenticated?).to be_falsey
|
266
|
+
expect(subject.Project.all).to be_empty
|
267
|
+
expect(subject.authenticated?).to be_truthy
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
210
271
|
context 'oauth authentication' do
|
211
272
|
subject { JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar') }
|
212
273
|
|
@@ -218,4 +279,13 @@ describe JIRA::Client do
|
|
218
279
|
|
219
280
|
include_examples 'OAuth Common Tests'
|
220
281
|
end
|
282
|
+
|
283
|
+
context 'with unknown options' do
|
284
|
+
let(:options) { { 'username' => 'foo', 'password' => 'bar', auth_type: :basic } }
|
285
|
+
subject { JIRA::Client.new(options) }
|
286
|
+
|
287
|
+
it 'raises an ArgumentError' do
|
288
|
+
expect { subject }.to raise_exception(ArgumentError, 'Unknown option(s) given: ["username", "password"]')
|
289
|
+
end
|
290
|
+
end
|
221
291
|
end
|
@@ -2,12 +2,22 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe JIRA::HttpClient do
|
4
4
|
let(:basic_client) do
|
5
|
-
options = JIRA::Client::DEFAULT_OPTIONS
|
5
|
+
options = JIRA::Client::DEFAULT_OPTIONS
|
6
|
+
.merge(JIRA::HttpClient::DEFAULT_OPTIONS)
|
7
|
+
.merge(basic_auth_credentials)
|
6
8
|
JIRA::HttpClient.new(options)
|
7
9
|
end
|
8
10
|
|
9
11
|
let(:basic_cookie_client) do
|
10
|
-
options = JIRA::Client::DEFAULT_OPTIONS
|
12
|
+
options = JIRA::Client::DEFAULT_OPTIONS
|
13
|
+
.merge(JIRA::HttpClient::DEFAULT_OPTIONS)
|
14
|
+
.merge(use_cookies: true)
|
15
|
+
.merge(basic_auth_credentials)
|
16
|
+
JIRA::HttpClient.new(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:custom_ssl_version_client) do
|
20
|
+
options = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::HttpClient::DEFAULT_OPTIONS).merge(ssl_version: :TLSv1_2)
|
11
21
|
JIRA::HttpClient.new(options)
|
12
22
|
end
|
13
23
|
|
@@ -20,10 +30,13 @@ describe JIRA::HttpClient do
|
|
20
30
|
end
|
21
31
|
|
22
32
|
let(:basic_cookie_client_with_additional_cookies) do
|
23
|
-
options = JIRA::Client::DEFAULT_OPTIONS
|
24
|
-
|
25
|
-
|
26
|
-
|
33
|
+
options = JIRA::Client::DEFAULT_OPTIONS
|
34
|
+
.merge(JIRA::HttpClient::DEFAULT_OPTIONS)
|
35
|
+
.merge(
|
36
|
+
use_cookies: true,
|
37
|
+
additional_cookies: ['sessionToken=abc123', 'internal=true']
|
38
|
+
)
|
39
|
+
.merge(basic_auth_credentials)
|
27
40
|
JIRA::HttpClient.new(options)
|
28
41
|
end
|
29
42
|
|
@@ -36,6 +49,26 @@ describe JIRA::HttpClient do
|
|
36
49
|
JIRA::HttpClient.new(options)
|
37
50
|
end
|
38
51
|
|
52
|
+
let(:basic_client_with_no_auth_credentials) do
|
53
|
+
options = JIRA::Client::DEFAULT_OPTIONS
|
54
|
+
.merge(JIRA::HttpClient::DEFAULT_OPTIONS)
|
55
|
+
JIRA::HttpClient.new(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:basic_auth_credentials) do
|
59
|
+
{ username: 'donaldduck', password: 'supersecret' }
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:proxy_client) do
|
63
|
+
options = JIRA::Client::DEFAULT_OPTIONS.merge(JIRA::HttpClient::DEFAULT_OPTIONS).merge(
|
64
|
+
proxy_address: 'proxyAddress',
|
65
|
+
proxy_port: 42,
|
66
|
+
proxy_username: 'proxyUsername',
|
67
|
+
proxy_password: 'proxyPassword'
|
68
|
+
)
|
69
|
+
JIRA::HttpClient.new(options)
|
70
|
+
end
|
71
|
+
|
39
72
|
let(:response) do
|
40
73
|
response = double('response')
|
41
74
|
allow(response).to receive(:kind_of?).with(Net::HTTPSuccess).and_return(true)
|
@@ -159,6 +192,19 @@ describe JIRA::HttpClient do
|
|
159
192
|
basic_client.make_request(:get, 'http://mydomain.com/foo', body, headers)
|
160
193
|
end
|
161
194
|
|
195
|
+
it 'does not try to use basic auth if the credentials are not set' do
|
196
|
+
body = nil
|
197
|
+
headers = double
|
198
|
+
basic_auth_http_conn = double
|
199
|
+
http_request = double
|
200
|
+
expect(Net::HTTP::Get).to receive(:new).with('/foo', headers).and_return(http_request)
|
201
|
+
|
202
|
+
expect(basic_auth_http_conn).to receive(:request).with(http_request).and_return(response)
|
203
|
+
expect(http_request).not_to receive(:basic_auth)
|
204
|
+
allow(basic_client_with_no_auth_credentials).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn)
|
205
|
+
basic_client_with_no_auth_credentials.make_request(:get, '/foo', body, headers)
|
206
|
+
end
|
207
|
+
|
162
208
|
it 'returns a URI' do
|
163
209
|
uri = URI.parse(basic_client.options[:site])
|
164
210
|
expect(basic_client.uri).to eq(uri)
|
@@ -178,6 +224,51 @@ describe JIRA::HttpClient do
|
|
178
224
|
expect(basic_client.http_conn(uri)).to eq(http_conn)
|
179
225
|
end
|
180
226
|
|
227
|
+
it 'sets the SSL version when one is provided' do
|
228
|
+
http_conn = double
|
229
|
+
uri = double
|
230
|
+
host = double
|
231
|
+
port = double
|
232
|
+
expect(uri).to receive(:host).and_return(host)
|
233
|
+
expect(uri).to receive(:port).and_return(port)
|
234
|
+
expect(Net::HTTP).to receive(:new).with(host, port).and_return(http_conn)
|
235
|
+
expect(http_conn).to receive(:use_ssl=).with(basic_client.options[:use_ssl]).and_return(http_conn)
|
236
|
+
expect(http_conn).to receive(:verify_mode=).with(basic_client.options[:ssl_verify_mode]).and_return(http_conn)
|
237
|
+
expect(http_conn).to receive(:ssl_version=).with(custom_ssl_version_client.options[:ssl_version]).and_return(http_conn)
|
238
|
+
expect(http_conn).to receive(:read_timeout=).with(basic_client.options[:read_timeout]).and_return(http_conn)
|
239
|
+
expect(custom_ssl_version_client.http_conn(uri)).to eq(http_conn)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'sets up a non-proxied http connection by default' do
|
243
|
+
uri = double
|
244
|
+
host = double
|
245
|
+
port = double
|
246
|
+
|
247
|
+
expect(uri).to receive(:host).and_return(host)
|
248
|
+
expect(uri).to receive(:port).and_return(port)
|
249
|
+
|
250
|
+
proxy_configuration = basic_client.http_conn(uri).class
|
251
|
+
expect(proxy_configuration.proxy_address).to be_nil
|
252
|
+
expect(proxy_configuration.proxy_port).to be_nil
|
253
|
+
expect(proxy_configuration.proxy_user).to be_nil
|
254
|
+
expect(proxy_configuration.proxy_pass).to be_nil
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'sets up a proxied http connection when using proxy options' do
|
258
|
+
uri = double
|
259
|
+
host = double
|
260
|
+
port = double
|
261
|
+
|
262
|
+
expect(uri).to receive(:host).and_return(host)
|
263
|
+
expect(uri).to receive(:port).and_return(port)
|
264
|
+
|
265
|
+
proxy_configuration = proxy_client.http_conn(uri).class
|
266
|
+
expect(proxy_configuration.proxy_address).to eq(proxy_client.options[:proxy_address])
|
267
|
+
expect(proxy_configuration.proxy_port).to eq(proxy_client.options[:proxy_port])
|
268
|
+
expect(proxy_configuration.proxy_user).to eq(proxy_client.options[:proxy_username])
|
269
|
+
expect(proxy_configuration.proxy_pass).to eq(proxy_client.options[:proxy_password])
|
270
|
+
end
|
271
|
+
|
181
272
|
it 'can use client certificates' do
|
182
273
|
http_conn = double
|
183
274
|
uri = double
|
@@ -189,11 +280,16 @@ describe JIRA::HttpClient do
|
|
189
280
|
expect(http_conn).to receive(:use_ssl=).with(basic_client.options[:use_ssl])
|
190
281
|
expect(http_conn).to receive(:verify_mode=).with(basic_client.options[:ssl_verify_mode])
|
191
282
|
expect(http_conn).to receive(:read_timeout=).with(basic_client.options[:read_timeout])
|
192
|
-
expect(http_conn).to receive(:cert=).with(basic_client_cert_client.options[:
|
193
|
-
expect(http_conn).to receive(:key=).with(basic_client_cert_client.options[:
|
283
|
+
expect(http_conn).to receive(:cert=).with(basic_client_cert_client.options[:ssl_client_cert])
|
284
|
+
expect(http_conn).to receive(:key=).with(basic_client_cert_client.options[:ssl_client_key])
|
194
285
|
expect(basic_client_cert_client.http_conn(uri)).to eq(http_conn)
|
195
286
|
end
|
196
287
|
|
288
|
+
it 'can use a certificate authority file' do
|
289
|
+
client = JIRA::HttpClient.new(JIRA::Client::DEFAULT_OPTIONS.merge(ca_file: '/opt/custom.ca.pem'))
|
290
|
+
expect(client.http_conn(client.uri).ca_file).to eql('/opt/custom.ca.pem')
|
291
|
+
end
|
292
|
+
|
197
293
|
it 'returns a http connection' do
|
198
294
|
http_conn = double
|
199
295
|
uri = double
|
@@ -201,4 +297,37 @@ describe JIRA::HttpClient do
|
|
201
297
|
expect(basic_client).to receive(:http_conn).and_return(http_conn)
|
202
298
|
expect(basic_client.basic_auth_http_conn).to eq(http_conn)
|
203
299
|
end
|
300
|
+
|
301
|
+
describe '#make_multipart_request' do
|
302
|
+
subject do
|
303
|
+
basic_client.make_multipart_request(path, data, headers)
|
304
|
+
end
|
305
|
+
|
306
|
+
let(:path) { '/foo' }
|
307
|
+
let(:data) { {} }
|
308
|
+
let(:headers) { { 'X-Atlassian-Token' => 'no-check' } }
|
309
|
+
let(:basic_auth_http_conn) { double }
|
310
|
+
let(:request) { double('Http Request', path: path) }
|
311
|
+
let(:response) { double('response') }
|
312
|
+
|
313
|
+
before do
|
314
|
+
allow(request).to receive(:basic_auth)
|
315
|
+
allow(Net::HTTP::Post::Multipart).to receive(:new).with(path, data, headers).and_return(request)
|
316
|
+
allow(basic_client).to receive(:basic_auth_http_conn).and_return(basic_auth_http_conn)
|
317
|
+
allow(basic_auth_http_conn).to receive(:request).with(request).and_return(response)
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'performs a basic http client request' do
|
321
|
+
expect(request).to receive(:basic_auth).with(basic_client.options[:username], basic_client.options[:password]).and_return(request)
|
322
|
+
|
323
|
+
subject
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'makes a correct HTTP request' do
|
327
|
+
expect(basic_auth_http_conn).to receive(:request).with(request).and_return(response)
|
328
|
+
expect(response).to receive(:is_a?).with(Net::HTTPOK)
|
329
|
+
|
330
|
+
subject
|
331
|
+
end
|
332
|
+
end
|
204
333
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::JwtClient::JwtUriBuilder do
|
4
|
+
subject(:url_builder) do
|
5
|
+
JIRA::JwtClient::JwtUriBuilder.new(url, http_method, shared_secret, site, issuer)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:url) { '/foo' }
|
9
|
+
let(:http_method) { :get }
|
10
|
+
let(:shared_secret) { 'shared_secret' }
|
11
|
+
let(:site) { 'http://localhost:2990' }
|
12
|
+
let(:issuer) { nil }
|
13
|
+
|
14
|
+
describe '#build' do
|
15
|
+
subject { url_builder.build }
|
16
|
+
|
17
|
+
it 'includes the jwt param' do
|
18
|
+
expect(subject).to include('?jwt=')
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when the url already contains params' do
|
22
|
+
let(:url) { '/foo?expand=projects.issuetypes.fields' }
|
23
|
+
|
24
|
+
it 'includes the jwt param' do
|
25
|
+
expect(subject).to include('&jwt=')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with a complete url' do
|
30
|
+
let(:url) { 'http://localhost:2990/rest/api/2/issue/createmeta' }
|
31
|
+
|
32
|
+
it 'includes the jwt param' do
|
33
|
+
expect(subject).to include('?jwt=')
|
34
|
+
end
|
35
|
+
|
36
|
+
it { is_expected.to start_with('/') }
|
37
|
+
|
38
|
+
it 'contains only one ?' do
|
39
|
+
expect(subject.count('?')).to eq(1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with a complete url containing a param' do
|
44
|
+
let(:url) do
|
45
|
+
'http://localhost:2990/rest/api/2/issue/createmeta?expand=projects.issuetypes.fields'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'includes the jwt param' do
|
49
|
+
expect(subject).to include('&jwt=')
|
50
|
+
end
|
51
|
+
|
52
|
+
it { is_expected.to start_with('/') }
|
53
|
+
|
54
|
+
it 'contains only one ?' do
|
55
|
+
expect(subject.count('?')).to eq(1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|