jira-ruby 2.2.0 → 3.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/CI.yml +28 -0
- data/.github/workflows/codeql.yml +100 -0
- data/.github/workflows/rubocop.yml +18 -0
- data/.rubocop.yml +188 -0
- data/Gemfile +11 -3
- data/Guardfile +2 -0
- data/README.md +121 -20
- data/Rakefile +4 -5
- data/jira-ruby.gemspec +11 -17
- data/lib/jira/base.rb +37 -28
- data/lib/jira/base_factory.rb +4 -1
- data/lib/jira/client.rb +65 -46
- data/lib/jira/has_many_proxy.rb +4 -2
- data/lib/jira/http_client.rb +18 -13
- data/lib/jira/http_error.rb +4 -0
- data/lib/jira/jwt_client.rb +18 -42
- data/lib/jira/oauth_client.rb +6 -3
- data/lib/jira/railtie.rb +2 -0
- data/lib/jira/request_client.rb +5 -1
- data/lib/jira/resource/agile.rb +7 -9
- data/lib/jira/resource/applinks.rb +5 -3
- data/lib/jira/resource/attachment.rb +43 -3
- data/lib/jira/resource/board.rb +5 -3
- data/lib/jira/resource/board_configuration.rb +2 -0
- data/lib/jira/resource/comment.rb +2 -0
- data/lib/jira/resource/component.rb +2 -0
- data/lib/jira/resource/createmeta.rb +3 -1
- data/lib/jira/resource/field.rb +9 -4
- data/lib/jira/resource/filter.rb +2 -0
- data/lib/jira/resource/issue.rb +35 -44
- data/lib/jira/resource/issue_picker_suggestions.rb +4 -1
- data/lib/jira/resource/issue_picker_suggestions_issue.rb +2 -0
- data/lib/jira/resource/issuelink.rb +2 -0
- data/lib/jira/resource/issuelinktype.rb +2 -0
- data/lib/jira/resource/issuetype.rb +2 -0
- data/lib/jira/resource/priority.rb +2 -0
- data/lib/jira/resource/project.rb +4 -2
- data/lib/jira/resource/rapidview.rb +5 -3
- data/lib/jira/resource/remotelink.rb +2 -0
- data/lib/jira/resource/resolution.rb +2 -0
- data/lib/jira/resource/serverinfo.rb +2 -0
- data/lib/jira/resource/sprint.rb +14 -23
- data/lib/jira/resource/status.rb +7 -1
- data/lib/jira/resource/status_category.rb +10 -0
- data/lib/jira/resource/suggested_issue.rb +2 -0
- data/lib/jira/resource/transition.rb +2 -0
- data/lib/jira/resource/user.rb +3 -1
- data/lib/jira/resource/version.rb +2 -0
- data/lib/jira/resource/watcher.rb +2 -1
- data/lib/jira/resource/webhook.rb +4 -2
- data/lib/jira/resource/worklog.rb +3 -2
- data/lib/jira/version.rb +3 -1
- data/lib/jira-ruby.rb +5 -3
- data/lib/tasks/generate.rake +4 -2
- data/spec/data/files/short.txt +1 -0
- data/spec/integration/attachment_spec.rb +3 -3
- data/spec/integration/comment_spec.rb +8 -8
- data/spec/integration/component_spec.rb +7 -7
- data/spec/integration/field_spec.rb +3 -3
- data/spec/integration/issue_spec.rb +20 -16
- data/spec/integration/issuelinktype_spec.rb +3 -3
- data/spec/integration/issuetype_spec.rb +3 -3
- data/spec/integration/priority_spec.rb +3 -3
- data/spec/integration/project_spec.rb +7 -7
- data/spec/integration/rapidview_spec.rb +9 -9
- data/spec/integration/resolution_spec.rb +3 -3
- data/spec/integration/status_category_spec.rb +20 -0
- data/spec/integration/status_spec.rb +4 -8
- data/spec/integration/transition_spec.rb +2 -2
- data/spec/integration/user_spec.rb +22 -8
- data/spec/integration/version_spec.rb +7 -7
- data/spec/integration/watcher_spec.rb +17 -18
- data/spec/integration/webhook.rb +5 -4
- data/spec/integration/worklog_spec.rb +8 -8
- data/spec/jira/base_factory_spec.rb +2 -1
- data/spec/jira/base_spec.rb +55 -41
- data/spec/jira/client_spec.rb +48 -34
- data/spec/jira/has_many_proxy_spec.rb +3 -3
- data/spec/jira/http_client_spec.rb +98 -26
- data/spec/jira/http_error_spec.rb +2 -2
- data/spec/jira/oauth_client_spec.rb +30 -8
- data/spec/jira/request_client_spec.rb +4 -4
- data/spec/jira/resource/agile_spec.rb +28 -28
- data/spec/jira/resource/attachment_spec.rb +142 -52
- data/spec/jira/resource/board_spec.rb +21 -20
- data/spec/jira/resource/createmeta_spec.rb +48 -48
- data/spec/jira/resource/field_spec.rb +30 -12
- data/spec/jira/resource/filter_spec.rb +4 -4
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +18 -18
- data/spec/jira/resource/issue_spec.rb +44 -38
- data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +3 -3
- data/spec/jira/resource/project_factory_spec.rb +3 -2
- data/spec/jira/resource/project_spec.rb +16 -16
- data/spec/jira/resource/sprint_spec.rb +70 -3
- data/spec/jira/resource/status_spec.rb +21 -0
- data/spec/jira/resource/user_factory_spec.rb +4 -4
- data/spec/jira/resource/worklog_spec.rb +3 -3
- data/spec/mock_responses/sprint/1.json +13 -0
- data/spec/mock_responses/status/1.json +8 -1
- data/spec/mock_responses/status.json +40 -5
- data/spec/mock_responses/statuscategory/1.json +7 -0
- data/spec/mock_responses/statuscategory.json +30 -0
- data/spec/mock_responses/{user_username=admin.json → user_accountId=1234567890abcdef01234567.json} +2 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/clients_helper.rb +3 -5
- data/spec/support/shared_examples/integration.rb +25 -28
- metadata +25 -257
- data/.travis.yml +0 -9
- data/example.rb +0 -232
- data/http-basic-example.rb +0 -113
- data/lib/jira/resource/sprint_report.rb +0 -8
- data/lib/jira/tasks.rb +0 -0
- data/spec/jira/jwt_uri_builder_spec.rb +0 -59
@@ -5,7 +5,7 @@ describe JIRA::Resource::Watcher do
|
|
5
5
|
let(:client) { client }
|
6
6
|
let(:site_url) { site_url }
|
7
7
|
|
8
|
-
let(:target) {
|
8
|
+
let(:target) { described_class.new(client, attrs: { 'id' => '99999' }, issue_id: '10002') }
|
9
9
|
|
10
10
|
let(:belongs_to) do
|
11
11
|
JIRA::Resource::Issue.new(client, attrs: {
|
@@ -19,44 +19,43 @@ describe JIRA::Resource::Watcher do
|
|
19
19
|
let(:expected_attributes) do
|
20
20
|
{
|
21
21
|
'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/watchers',
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
isWatching: false,
|
23
|
+
watchCount: 1,
|
24
|
+
watchers: [
|
25
25
|
{
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
self: 'http://www.example.com/jira/rest/api/2/user?username=admin',
|
27
|
+
name: 'admin',
|
28
|
+
displayName: 'admin',
|
29
|
+
active: false
|
30
30
|
}
|
31
31
|
]
|
32
32
|
}
|
33
33
|
end
|
34
34
|
|
35
35
|
describe 'watchers' do
|
36
|
-
before
|
37
|
-
stub_request(:get, site_url
|
36
|
+
before do
|
37
|
+
stub_request(:get, "#{site_url}/jira/rest/api/2/issue/10002")
|
38
38
|
.to_return(status: 200, body: get_mock_response('issue/10002.json'))
|
39
39
|
|
40
|
-
stub_request(:get, site_url
|
40
|
+
stub_request(:get, "#{site_url}/jira/rest/api/2/issue/10002/watchers")
|
41
41
|
.to_return(status: 200, body: get_mock_response('issue/10002/watchers.json'))
|
42
42
|
|
43
|
-
stub_request(:post, site_url
|
43
|
+
stub_request(:post, "#{site_url}/jira/rest/api/2/issue/10002/watchers")
|
44
44
|
.to_return(status: 204, body: nil)
|
45
45
|
end
|
46
46
|
|
47
|
-
it '
|
47
|
+
it 'returnses all the watchers' do
|
48
48
|
issue = client.Issue.find('10002')
|
49
|
-
watchers = client.Watcher.all(options = { issue:
|
49
|
+
watchers = client.Watcher.all(options = { issue: })
|
50
50
|
expect(watchers.length).to eq(1)
|
51
51
|
end
|
52
52
|
|
53
|
-
it '
|
53
|
+
it 'adds a watcher' do
|
54
54
|
issue = client.Issue.find('10002')
|
55
|
-
watcher =
|
56
|
-
user_id =
|
55
|
+
watcher = described_class.new(client, issue:)
|
56
|
+
user_id = 'tester'
|
57
57
|
watcher.save!(user_id)
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
61
60
|
end
|
62
61
|
end
|
data/spec/integration/webhook.rb
CHANGED
@@ -8,14 +8,15 @@ describe JIRA::Resource::Webhook do
|
|
8
8
|
let(:key) { '2' }
|
9
9
|
|
10
10
|
let(:expected_attributes) do
|
11
|
-
{ 'name' => 'from API', 'url' => 'http://localhost:3000/webhooks/1', 'excludeBody' => false,
|
11
|
+
{ 'name' => 'from API', 'url' => 'http://localhost:3000/webhooks/1', 'excludeBody' => false,
|
12
|
+
'filters' => { 'issue-related-events-section' => '' }, 'events' => [], 'enabled' => true, 'self' => 'http://localhost:2990/jira/rest/webhooks/1.0/webhook/2', 'lastUpdatedUser' => 'admin', 'lastUpdatedDisplayName' => 'admin', 'lastUpdated' => 1_453_306_520_188 }
|
12
13
|
end
|
13
14
|
|
14
15
|
let(:expected_collection_length) { 1 }
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
it_behaves_like 'a resource'
|
18
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
19
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
19
20
|
|
20
21
|
it 'returns a collection of components' do
|
21
22
|
stub_request(:get, site_url + described_class.singular_path(client, key))
|
@@ -7,7 +7,7 @@ describe JIRA::Resource::Worklog do
|
|
7
7
|
|
8
8
|
let(:key) { '10000' }
|
9
9
|
|
10
|
-
let(:target) {
|
10
|
+
let(:target) { described_class.new(client, attrs: { 'id' => '99999' }, issue_id: '54321') }
|
11
11
|
|
12
12
|
let(:expected_collection_length) { 3 }
|
13
13
|
|
@@ -22,7 +22,7 @@ describe JIRA::Resource::Worklog do
|
|
22
22
|
let(:expected_attributes) do
|
23
23
|
{
|
24
24
|
'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10000',
|
25
|
-
'id'
|
25
|
+
'id' => key,
|
26
26
|
'comment' => 'Some epic work.'
|
27
27
|
}
|
28
28
|
end
|
@@ -41,11 +41,11 @@ describe JIRA::Resource::Worklog do
|
|
41
41
|
{ 'id' => '10001', 'timeSpent' => '4d' }
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
it_behaves_like 'a resource'
|
45
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
46
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
47
|
+
it_behaves_like 'a resource with a DELETE endpoint'
|
48
|
+
it_behaves_like 'a resource with a POST endpoint'
|
49
|
+
it_behaves_like 'a resource with a PUT endpoint'
|
50
50
|
end
|
51
51
|
end
|
@@ -4,9 +4,10 @@ describe JIRA::BaseFactory do
|
|
4
4
|
class JIRA::Resource::FooFactory < JIRA::BaseFactory; end
|
5
5
|
class JIRA::Resource::Foo; end
|
6
6
|
|
7
|
-
let(:client) { double }
|
8
7
|
subject { JIRA::Resource::FooFactory.new(client) }
|
9
8
|
|
9
|
+
let(:client) { double }
|
10
|
+
|
10
11
|
it 'initializes correctly' do
|
11
12
|
expect(subject.class).to eq(JIRA::Resource::FooFactory)
|
12
13
|
expect(subject.client).to eq(client)
|
data/spec/jira/base_spec.rb
CHANGED
@@ -32,11 +32,11 @@ describe JIRA::Base do
|
|
32
32
|
attribute_key: 'irregularlyNamedThings'
|
33
33
|
end
|
34
34
|
|
35
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs:) }
|
36
|
+
|
35
37
|
let(:client) { double('client') }
|
36
38
|
let(:attrs) { {} }
|
37
39
|
|
38
|
-
subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) }
|
39
|
-
|
40
40
|
let(:decorated) { JIRADelegation.new(subject) }
|
41
41
|
|
42
42
|
describe '#respond_to?' do
|
@@ -44,6 +44,7 @@ describe JIRA::Base do
|
|
44
44
|
it 'responds to client' do
|
45
45
|
expect(decorated.respond_to?(:client)).to eq(true)
|
46
46
|
end
|
47
|
+
|
47
48
|
it 'does not raise an error' do
|
48
49
|
expect do
|
49
50
|
decorated.respond_to?(:client)
|
@@ -125,7 +126,7 @@ describe JIRA::Base do
|
|
125
126
|
end
|
126
127
|
|
127
128
|
describe 'collection_path' do
|
128
|
-
before
|
129
|
+
before do
|
129
130
|
expect(client).to receive(:options).and_return(rest_base_path: '/deadbeef/bar')
|
130
131
|
end
|
131
132
|
|
@@ -147,8 +148,9 @@ describe JIRA::Base do
|
|
147
148
|
end
|
148
149
|
|
149
150
|
describe 'dynamic instance methods' do
|
151
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs:) }
|
152
|
+
|
150
153
|
let(:attrs) { { 'foo' => 'bar', 'flum' => 'goo', 'object_id' => 'dummy' } }
|
151
|
-
subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) }
|
152
154
|
|
153
155
|
it 'responds to each of the top level attribute names' do
|
154
156
|
expect(subject).to respond_to(:foo)
|
@@ -169,7 +171,7 @@ describe JIRA::Base do
|
|
169
171
|
subject { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '98765' }) }
|
170
172
|
|
171
173
|
describe 'not cached' do
|
172
|
-
before
|
174
|
+
before do
|
173
175
|
response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}')
|
174
176
|
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response)
|
175
177
|
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
@@ -222,17 +224,17 @@ describe JIRA::Base do
|
|
222
224
|
end
|
223
225
|
|
224
226
|
describe 'save' do
|
225
|
-
let(:response) { double }
|
226
|
-
|
227
227
|
subject { JIRA::Resource::Deadbeef.new(client) }
|
228
228
|
|
229
|
-
|
229
|
+
let(:response) { double }
|
230
|
+
|
231
|
+
before do
|
230
232
|
expect(subject).to receive(:url).and_return('/foo/bar')
|
231
233
|
end
|
232
234
|
|
233
235
|
it 'POSTs a new record' do
|
234
236
|
response = instance_double('Response', body: '{"id":"123"}')
|
235
|
-
allow(subject).to receive(:new_record?)
|
237
|
+
allow(subject).to receive(:new_record?).and_return(true)
|
236
238
|
expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
237
239
|
expect(subject.save('foo' => 'bar')).to be_truthy
|
238
240
|
expect(subject.id).to eq('123')
|
@@ -241,7 +243,7 @@ describe JIRA::Base do
|
|
241
243
|
|
242
244
|
it 'PUTs an existing record' do
|
243
245
|
response = instance_double('Response', body: nil)
|
244
|
-
allow(subject).to receive(:new_record?)
|
246
|
+
allow(subject).to receive(:new_record?).and_return(false)
|
245
247
|
expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
246
248
|
expect(subject.save('foo' => 'bar')).to be_truthy
|
247
249
|
expect(subject.expanded).to be_falsey
|
@@ -255,14 +257,17 @@ describe JIRA::Base do
|
|
255
257
|
expect(subject.foo).to eq('bar' => 'baz', 'fum' => 'dum')
|
256
258
|
end
|
257
259
|
|
258
|
-
it 'returns false when an invalid field is set' do
|
260
|
+
it 'returns false when an invalid field is set' do
|
261
|
+
# The JIRA REST API apparently ignores fields that you aren't allowed to set manually
|
259
262
|
response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400)
|
260
|
-
allow(subject).to receive(:new_record?)
|
261
|
-
expect(client).to receive(:put).with('/foo/bar',
|
263
|
+
allow(subject).to receive(:new_record?).and_return(false)
|
264
|
+
expect(client).to receive(:put).with('/foo/bar',
|
265
|
+
'{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response))
|
262
266
|
expect(subject.save('invalid_field' => 'foobar')).to be_falsey
|
263
267
|
end
|
264
268
|
|
265
|
-
it 'returns false with exception details when non json response body (unauthorized)' do
|
269
|
+
it 'returns false with exception details when non json response body (unauthorized)' do
|
270
|
+
# Unauthorized requests return a non-json body. This makes sure we can handle non-json bodies on HTTPError
|
266
271
|
response = double('Response', body: 'totally invalid json', code: 401, message: 'Unauthorized')
|
267
272
|
expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_raise(JIRA::HTTPError.new(response))
|
268
273
|
expect(subject.save('foo' => 'bar')).to be_falsey
|
@@ -272,17 +277,17 @@ describe JIRA::Base do
|
|
272
277
|
end
|
273
278
|
|
274
279
|
describe 'save!' do
|
275
|
-
let(:response) { double }
|
276
|
-
|
277
280
|
subject { JIRA::Resource::Deadbeef.new(client) }
|
278
281
|
|
279
|
-
|
282
|
+
let(:response) { double }
|
283
|
+
|
284
|
+
before do
|
280
285
|
expect(subject).to receive(:url).and_return('/foo/bar')
|
281
286
|
end
|
282
287
|
|
283
288
|
it 'POSTs a new record' do
|
284
289
|
response = instance_double('Response', body: '{"id":"123"}')
|
285
|
-
allow(subject).to receive(:new_record?)
|
290
|
+
allow(subject).to receive(:new_record?).and_return(true)
|
286
291
|
expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
287
292
|
expect(subject.save!('foo' => 'bar')).to be_truthy
|
288
293
|
expect(subject.id).to eq('123')
|
@@ -291,7 +296,7 @@ describe JIRA::Base do
|
|
291
296
|
|
292
297
|
it 'PUTs an existing record' do
|
293
298
|
response = instance_double('Response', body: nil)
|
294
|
-
allow(subject).to receive(:new_record?)
|
299
|
+
allow(subject).to receive(:new_record?).and_return(false)
|
295
300
|
expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
296
301
|
expect(subject.save!('foo' => 'bar')).to be_truthy
|
297
302
|
expect(subject.expanded).to be_falsey
|
@@ -299,9 +304,10 @@ describe JIRA::Base do
|
|
299
304
|
|
300
305
|
it 'throws an exception when an invalid field is set' do
|
301
306
|
response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400)
|
302
|
-
allow(subject).to receive(:new_record?)
|
303
|
-
expect(client).to receive(:put).with('/foo/bar',
|
304
|
-
|
307
|
+
allow(subject).to receive(:new_record?).and_return(false)
|
308
|
+
expect(client).to receive(:put).with('/foo/bar',
|
309
|
+
'{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response))
|
310
|
+
expect { subject.save!('invalid_field' => 'foobar') }.to raise_error(JIRA::HTTPError)
|
305
311
|
end
|
306
312
|
end
|
307
313
|
|
@@ -320,9 +326,9 @@ describe JIRA::Base do
|
|
320
326
|
end
|
321
327
|
|
322
328
|
describe 'delete' do
|
323
|
-
before
|
329
|
+
before do
|
324
330
|
expect(client).to receive(:delete).with('/foo/bar')
|
325
|
-
allow(subject).to receive(:url)
|
331
|
+
allow(subject).to receive(:url).and_return('/foo/bar')
|
326
332
|
end
|
327
333
|
|
328
334
|
it 'flags itself as deleted' do
|
@@ -360,8 +366,8 @@ describe JIRA::Base do
|
|
360
366
|
end
|
361
367
|
|
362
368
|
describe 'url' do
|
363
|
-
before
|
364
|
-
allow(client).to receive(:options)
|
369
|
+
before do
|
370
|
+
allow(client).to receive(:options).and_return({ rest_base_path: '/foo/bar' })
|
365
371
|
end
|
366
372
|
|
367
373
|
it 'returns self as the URL if set' do
|
@@ -370,13 +376,13 @@ describe JIRA::Base do
|
|
370
376
|
end
|
371
377
|
|
372
378
|
it 'returns path as the URL if set and site options is specified' do
|
373
|
-
allow(client).to receive(:options)
|
379
|
+
allow(client).to receive(:options).and_return({ site: 'http://foo' })
|
374
380
|
attrs['self'] = 'http://foo/bar'
|
375
381
|
expect(subject.url).to eq('/bar')
|
376
382
|
end
|
377
383
|
|
378
384
|
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)
|
385
|
+
allow(client).to receive(:options).and_return({ site: 'http://foo/' })
|
380
386
|
attrs['self'] = 'http://foo/bar'
|
381
387
|
expect(subject.url).to eq('/bar')
|
382
388
|
end
|
@@ -428,7 +434,7 @@ describe JIRA::Base do
|
|
428
434
|
|
429
435
|
h = { 'key' => subject }
|
430
436
|
h_attrs = { 'key' => subject.attrs }
|
431
|
-
expect(h.to_json).to eq(h_attrs.to_json)
|
437
|
+
expect(h['key'].to_json).to eq(h_attrs['key'].to_json)
|
432
438
|
end
|
433
439
|
|
434
440
|
describe 'extract attrs from response' do
|
@@ -487,7 +493,9 @@ describe JIRA::Base do
|
|
487
493
|
end
|
488
494
|
|
489
495
|
it 'allows the has_many attributes to be nested inside another attribute' do
|
490
|
-
subject = JIRA::Resource::HasManyExample.new(client,
|
496
|
+
subject = JIRA::Resource::HasManyExample.new(client,
|
497
|
+
attrs: { 'nested' => { 'brunchmuffins' => [{ 'id' => '123' },
|
498
|
+
{ 'id' => '456' }] } })
|
491
499
|
expect(subject.brunchmuffins.length).to eq(2)
|
492
500
|
subject.brunchmuffins.each do |brunchmuffin|
|
493
501
|
expect(brunchmuffin.class).to eq(JIRA::Resource::Deadbeef)
|
@@ -495,9 +503,12 @@ describe JIRA::Base do
|
|
495
503
|
end
|
496
504
|
|
497
505
|
it 'allows it to be deeply nested' do
|
498
|
-
subject = JIRA::Resource::HasManyExample.new(
|
499
|
-
|
500
|
-
|
506
|
+
subject = JIRA::Resource::HasManyExample.new(
|
507
|
+
client,
|
508
|
+
attrs: {
|
509
|
+
'nested' => { 'breakfastscone' => { 'breakfastscones' => [{ 'id' => '123' }, { 'id' => '456' }] } }
|
510
|
+
}
|
511
|
+
)
|
501
512
|
expect(subject.breakfastscones.length).to eq(2)
|
502
513
|
subject.breakfastscones.each do |breakfastscone|
|
503
514
|
expect(breakfastscone.class).to eq(JIRA::Resource::Deadbeef)
|
@@ -512,7 +523,9 @@ describe JIRA::Base do
|
|
512
523
|
end
|
513
524
|
|
514
525
|
it 'allows the attribute key to be specified' do
|
515
|
-
subject = JIRA::Resource::HasManyExample.new(client,
|
526
|
+
subject = JIRA::Resource::HasManyExample.new(client,
|
527
|
+
attrs: { 'irregularlyNamedThings' => [{ 'id' => '123' },
|
528
|
+
{ 'id' => '456' }] })
|
516
529
|
expect(subject.irregularly_named_things.length).to eq(2)
|
517
530
|
subject.irregularly_named_things.each do |thing|
|
518
531
|
expect(thing.class).to eq(JIRA::Resource::Deadbeef)
|
@@ -545,7 +558,8 @@ describe JIRA::Base do
|
|
545
558
|
end
|
546
559
|
|
547
560
|
it 'allows the has_one attributes to be nested inside another attribute' do
|
548
|
-
subject = JIRA::Resource::HasOneExample.new(client,
|
561
|
+
subject = JIRA::Resource::HasOneExample.new(client,
|
562
|
+
attrs: { 'nested' => { 'brunchmuffin' => { 'id' => '123' } } })
|
549
563
|
expect(subject.brunchmuffin.class).to eq(JIRA::Resource::Deadbeef)
|
550
564
|
expect(subject.brunchmuffin.id).to eq('123')
|
551
565
|
end
|
@@ -570,27 +584,27 @@ describe JIRA::Base do
|
|
570
584
|
belongs_to :deadbeef
|
571
585
|
end
|
572
586
|
|
573
|
-
|
587
|
+
subject { JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef:) }
|
574
588
|
|
575
|
-
|
589
|
+
let(:deadbeef) { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '999' }) }
|
576
590
|
|
577
591
|
it 'sets up an accessor for the belongs to relationship' do
|
578
592
|
expect(subject.deadbeef).to eq(deadbeef)
|
579
593
|
end
|
580
594
|
|
581
595
|
it 'raises an exception when initialized without a belongs_to instance' do
|
582
|
-
expect
|
596
|
+
expect do
|
583
597
|
JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' })
|
584
|
-
|
598
|
+
end.to raise_exception(ArgumentError, 'Required option :deadbeef missing')
|
585
599
|
end
|
586
600
|
|
587
601
|
it 'returns the right url' do
|
588
|
-
allow(client).to receive(:options)
|
602
|
+
allow(client).to receive(:options).and_return({ rest_base_path: '/foo' })
|
589
603
|
expect(subject.url).to eq('/foo/deadbeef/999/belongstoexample/123')
|
590
604
|
end
|
591
605
|
|
592
606
|
it 'can be initialized with an instance or a key value' do
|
593
|
-
allow(client).to receive(:options)
|
607
|
+
allow(client).to receive(:options).and_return({ rest_base_path: '/foo' })
|
594
608
|
subject = JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef_id: '987')
|
595
609
|
expect(subject.url).to eq('/foo/deadbeef/987/belongstoexample/123')
|
596
610
|
end
|
data/spec/jira/client_spec.rb
CHANGED
@@ -28,7 +28,7 @@ RSpec.shared_examples 'Client Common Tests' do
|
|
28
28
|
expect(subject).to receive(:merge_default_headers).exactly(3).times.with({})
|
29
29
|
|
30
30
|
# response for merging headers for http methods with body
|
31
|
-
expect(subject).to receive(:merge_default_headers).
|
31
|
+
expect(subject).to receive(:merge_default_headers).twice.with(content_type_header)
|
32
32
|
|
33
33
|
%i[delete get head].each { |method| subject.send(method, '/path', {}) }
|
34
34
|
%i[post put].each { |method| subject.send(method, '/path', '', content_type_header) }
|
@@ -62,13 +62,20 @@ RSpec.shared_examples 'Client Common Tests' do
|
|
62
62
|
|
63
63
|
describe 'SSL client options' do
|
64
64
|
context 'without certificate and key' do
|
65
|
-
let(:options) { { use_client_cert: true } }
|
66
65
|
subject { JIRA::Client.new(options) }
|
67
66
|
|
67
|
+
let(:options) { { use_client_cert: true } }
|
68
|
+
|
68
69
|
it 'raises an ArgumentError' do
|
69
|
-
expect
|
70
|
+
expect do
|
71
|
+
subject
|
72
|
+
end.to raise_exception(ArgumentError,
|
73
|
+
'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true')
|
70
74
|
options[:ssl_client_cert] = '<cert></cert>'
|
71
|
-
expect
|
75
|
+
expect do
|
76
|
+
subject
|
77
|
+
end.to raise_exception(ArgumentError,
|
78
|
+
'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true')
|
72
79
|
end
|
73
80
|
end
|
74
81
|
end
|
@@ -77,11 +84,13 @@ end
|
|
77
84
|
RSpec.shared_examples 'HttpClient tests' do
|
78
85
|
it 'makes a valid request' do
|
79
86
|
%i[delete get head].each do |method|
|
80
|
-
expect(subject.request_client).to receive(:make_request).with(method, '/path', nil,
|
87
|
+
expect(subject.request_client).to receive(:make_request).with(method, '/path', nil,
|
88
|
+
headers).and_return(successful_response)
|
81
89
|
subject.send(method, '/path', headers)
|
82
90
|
end
|
83
91
|
%i[post put].each do |method|
|
84
|
-
expect(subject.request_client).to receive(:make_request).with(method, '/path', '',
|
92
|
+
expect(subject.request_client).to receive(:make_request).with(method, '/path', '',
|
93
|
+
merged_headers).and_return(successful_response)
|
85
94
|
subject.send(method, '/path', '', headers)
|
86
95
|
end
|
87
96
|
end
|
@@ -106,11 +115,13 @@ RSpec.shared_examples 'OAuth Common Tests' do
|
|
106
115
|
describe 'that call a oauth client' do
|
107
116
|
specify 'which makes a request' do
|
108
117
|
%i[delete get head].each do |method|
|
109
|
-
expect(subject.request_client).to receive(:make_request).with(method, '/path', nil,
|
118
|
+
expect(subject.request_client).to receive(:make_request).with(method, '/path', nil,
|
119
|
+
headers).and_return(successful_response)
|
110
120
|
subject.send(method, '/path', {})
|
111
121
|
end
|
112
122
|
%i[post put].each do |method|
|
113
|
-
expect(subject.request_client).to receive(:make_request).with(method, '/path', '',
|
123
|
+
expect(subject.request_client).to receive(:make_request).with(method, '/path', '',
|
124
|
+
merged_headers).and_return(successful_response)
|
114
125
|
subject.send(method, '/path', '', {})
|
115
126
|
end
|
116
127
|
end
|
@@ -130,20 +141,22 @@ describe JIRA::Client do
|
|
130
141
|
|
131
142
|
context 'behaviour that applies to all client classes irrespective of authentication method' do
|
132
143
|
it 'allows the overriding of some options' do
|
133
|
-
client =
|
144
|
+
client = described_class.new(consumer_key: 'foo', consumer_secret: 'bar', site: 'http://foo.com/')
|
134
145
|
expect(client.options[:site]).to eq('http://foo.com/')
|
135
146
|
expect(JIRA::Client::DEFAULT_OPTIONS[:site]).not_to eq('http://foo.com/')
|
136
147
|
end
|
137
148
|
end
|
138
149
|
|
139
150
|
context 'with basic http authentication' do
|
140
|
-
subject {
|
151
|
+
subject { described_class.new(username: 'foo', password: 'bar', auth_type: :basic) }
|
141
152
|
|
142
|
-
before
|
143
|
-
stub_request(:get, 'https://
|
153
|
+
before do
|
154
|
+
stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project')
|
155
|
+
.with(headers: { 'Authorization' => "Basic #{Base64.strict_encode64('foo:bar').chomp}" })
|
144
156
|
.to_return(status: 200, body: '[]', headers: {})
|
145
157
|
|
146
|
-
stub_request(:get, 'https://
|
158
|
+
stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project')
|
159
|
+
.with(headers: { 'Authorization' => "Basic #{Base64.strict_encode64('foo:badpassword').chomp}" })
|
147
160
|
.to_return(status: 401, headers: {})
|
148
161
|
end
|
149
162
|
|
@@ -157,33 +170,33 @@ describe JIRA::Client do
|
|
157
170
|
expect(subject.options[:password]).to eq('bar')
|
158
171
|
end
|
159
172
|
|
160
|
-
it 'fails with wrong user name and password' do
|
161
|
-
bad_login = JIRA::Client.new(username: 'foo', password: 'badpassword', auth_type: :basic)
|
162
|
-
expect(bad_login.authenticated?).to be_falsey
|
163
|
-
expect { bad_login.Project.all }.to raise_error JIRA::HTTPError
|
164
|
-
end
|
165
|
-
|
166
173
|
it 'only returns a true for #authenticated? once we have requested some data' do
|
167
|
-
expect(subject.authenticated?).to
|
174
|
+
expect(subject.authenticated?).to be_nil
|
168
175
|
expect(subject.Project.all).to be_empty
|
169
176
|
expect(subject.authenticated?).to be_truthy
|
170
177
|
end
|
178
|
+
|
179
|
+
it 'fails with wrong user name and password' do
|
180
|
+
bad_login = described_class.new(username: 'foo', password: 'badpassword', auth_type: :basic)
|
181
|
+
expect(bad_login.authenticated?).to be_falsey
|
182
|
+
expect { bad_login.Project.all }.to raise_error JIRA::HTTPError
|
183
|
+
end
|
171
184
|
end
|
172
185
|
|
173
186
|
context 'with cookie authentication' do
|
174
|
-
subject {
|
187
|
+
subject { described_class.new(username: 'foo', password: 'bar', auth_type: :cookie) }
|
175
188
|
|
176
189
|
let(:session_cookie) { '6E3487971234567896704A9EB4AE501F' }
|
177
190
|
let(:session_body) do
|
178
191
|
{
|
179
|
-
|
180
|
-
|
192
|
+
session: { 'name' => 'JSESSIONID', 'value' => session_cookie },
|
193
|
+
loginInfo: { 'failedLoginCount' => 1, 'loginCount' => 2,
|
181
194
|
'lastFailedLoginTime' => (DateTime.now - 2).iso8601,
|
182
195
|
'previousLoginTime' => (DateTime.now - 5).iso8601 }
|
183
196
|
}
|
184
197
|
end
|
185
198
|
|
186
|
-
before
|
199
|
+
before do
|
187
200
|
# General case of API call with no authentication, or wrong authentication
|
188
201
|
stub_request(:post, 'https://localhost:2990/jira/rest/auth/1/session')
|
189
202
|
.to_return(status: 401, headers: {})
|
@@ -210,7 +223,7 @@ describe JIRA::Client do
|
|
210
223
|
end
|
211
224
|
|
212
225
|
it 'does not authenticate with an incorrect username and password' do
|
213
|
-
bad_client =
|
226
|
+
bad_client = described_class.new(username: 'foo', password: 'bad_password', auth_type: :cookie)
|
214
227
|
expect(bad_client).not_to be_authenticated
|
215
228
|
end
|
216
229
|
|
@@ -222,7 +235,7 @@ describe JIRA::Client do
|
|
222
235
|
|
223
236
|
context 'with jwt authentication' do
|
224
237
|
subject do
|
225
|
-
|
238
|
+
described_class.new(
|
226
239
|
issuer: 'foo',
|
227
240
|
base_url: 'https://host.tld',
|
228
241
|
shared_secret: 'shared_secret_key',
|
@@ -230,10 +243,10 @@ describe JIRA::Client do
|
|
230
243
|
)
|
231
244
|
end
|
232
245
|
|
233
|
-
before
|
246
|
+
before do
|
234
247
|
stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project')
|
235
|
-
|
236
|
-
|
248
|
+
.with(headers: { 'Authorization' => /JWT .+/ })
|
249
|
+
.to_return(status: 200, body: '[]', headers: {})
|
237
250
|
end
|
238
251
|
|
239
252
|
include_examples 'Client Common Tests'
|
@@ -248,8 +261,8 @@ describe JIRA::Client do
|
|
248
261
|
context 'with a incorrect jwt key' do
|
249
262
|
before do
|
250
263
|
stub_request(:get, 'https://localhost:2990/jira/rest/api/2/project')
|
251
|
-
|
252
|
-
|
264
|
+
.with(headers: { 'Authorization' => /JWT .+/ })
|
265
|
+
.to_return(status: 401, body: '[]', headers: {})
|
253
266
|
end
|
254
267
|
|
255
268
|
it 'is not authenticated' do
|
@@ -269,20 +282,21 @@ describe JIRA::Client do
|
|
269
282
|
end
|
270
283
|
|
271
284
|
context 'oauth authentication' do
|
272
|
-
subject {
|
285
|
+
subject { described_class.new(consumer_key: 'foo', consumer_secret: 'bar') }
|
273
286
|
|
274
287
|
include_examples 'OAuth Common Tests'
|
275
288
|
end
|
276
289
|
|
277
290
|
context 'with oauth_2legged' do
|
278
|
-
subject {
|
291
|
+
subject { described_class.new(consumer_key: 'foo', consumer_secret: 'bar', auth_type: :oauth_2legged) }
|
279
292
|
|
280
293
|
include_examples 'OAuth Common Tests'
|
281
294
|
end
|
282
295
|
|
283
296
|
context 'with unknown options' do
|
297
|
+
subject { described_class.new(options) }
|
298
|
+
|
284
299
|
let(:options) { { 'username' => 'foo', 'password' => 'bar', auth_type: :basic } }
|
285
|
-
subject { JIRA::Client.new(options) }
|
286
300
|
|
287
301
|
it 'raises an ArgumentError' do
|
288
302
|
expect { subject }.to raise_exception(ArgumentError, 'Unknown option(s) given: ["username", "password"]')
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe JIRA::HasManyProxy do
|
4
4
|
class Foo; end
|
5
5
|
|
6
|
-
subject {
|
6
|
+
subject { described_class.new(parent, Foo, collection) }
|
7
7
|
|
8
8
|
let(:parent) { double('parent') }
|
9
9
|
let(:collection) { double('collection') }
|
@@ -25,7 +25,7 @@ describe JIRA::HasManyProxy do
|
|
25
25
|
foo = double('foo')
|
26
26
|
allow(parent).to receive(:client).and_return(client)
|
27
27
|
allow(parent).to receive(:to_sym).and_return(:parent)
|
28
|
-
expect(Foo).to receive(:new).with(client, attrs: { 'foo' => 'bar' }, parent:
|
28
|
+
expect(Foo).to receive(:new).with(client, attrs: { 'foo' => 'bar' }, parent:).and_return(foo)
|
29
29
|
expect(collection).to receive(:<<).with(foo)
|
30
30
|
expect(subject.build('foo' => 'bar')).to eq(foo)
|
31
31
|
end
|
@@ -35,7 +35,7 @@ describe JIRA::HasManyProxy do
|
|
35
35
|
client = double('client')
|
36
36
|
allow(parent).to receive(:client).and_return(client)
|
37
37
|
allow(parent).to receive(:to_sym).and_return(:parent)
|
38
|
-
expect(Foo).to receive(:all).with(client, parent:
|
38
|
+
expect(Foo).to receive(:all).with(client, parent:).and_return(foo)
|
39
39
|
expect(subject.all).to eq(foo)
|
40
40
|
end
|
41
41
|
|