jira-ruby 2.3.0 → 3.2.0
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 +30 -0
- data/.github/workflows/codeql.yml +96 -0
- data/.github/workflows/rubocop.yml +18 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +123 -0
- data/.yardopts +4 -0
- data/Gemfile +11 -3
- data/Guardfile +2 -0
- data/README.md +94 -19
- data/Rakefile +3 -4
- data/jira-ruby.gemspec +12 -17
- data/lib/jira/atlassian/jwt.rb +76 -0
- data/lib/jira/base.rb +37 -36
- data/lib/jira/base_factory.rb +4 -1
- data/lib/jira/client.rb +132 -51
- data/lib/jira/has_many_proxy.rb +32 -28
- data/lib/jira/http_client.rb +80 -13
- data/lib/jira/http_error.rb +4 -0
- data/lib/jira/jwt_client.rb +19 -43
- data/lib/jira/oauth_client.rb +68 -3
- data/lib/jira/railtie.rb +2 -0
- data/lib/jira/request_client.rb +31 -2
- data/lib/jira/resource/agile.rb +7 -9
- data/lib/jira/resource/applinks.rb +5 -3
- data/lib/jira/resource/attachment.rb +144 -4
- data/lib/jira/resource/board.rb +5 -3
- data/lib/jira/resource/board_configuration.rb +2 -0
- data/lib/jira/resource/comment.rb +14 -0
- data/lib/jira/resource/component.rb +2 -0
- data/lib/jira/resource/createmeta.rb +7 -5
- data/lib/jira/resource/field.rb +13 -12
- data/lib/jira/resource/filter.rb +2 -0
- data/lib/jira/resource/issue.rb +148 -54
- 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 +6 -3
- 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/properties.rb +56 -0
- 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 +3 -2
- data/lib/jira/resource/webhook.rb +9 -3
- data/lib/jira/resource/worklog.rb +15 -2
- data/lib/jira/version.rb +3 -1
- data/lib/jira-ruby.rb +6 -3
- data/lib/tasks/generate.rake +3 -1
- data/spec/data/files/jwt-signed-urls.json +317 -0
- 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 +21 -18
- 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 +8 -8
- data/spec/integration/properties_spec.rb +45 -0
- data/spec/integration/rapidview_spec.rb +10 -10
- 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 +5 -4
- data/spec/integration/user_spec.rb +34 -11
- data/spec/integration/version_spec.rb +7 -7
- data/spec/integration/watcher_spec.rb +21 -18
- data/spec/integration/webhook_spec.rb +35 -0
- data/spec/integration/worklog_spec.rb +8 -8
- data/spec/jira/atlassian/jwt_spec.rb +60 -0
- data/spec/jira/base_factory_spec.rb +13 -3
- data/spec/jira/base_spec.rb +135 -98
- data/spec/jira/client_spec.rb +72 -56
- data/spec/jira/has_many_proxy_spec.rb +3 -3
- data/spec/jira/http_client_spec.rb +94 -27
- data/spec/jira/http_error_spec.rb +2 -2
- data/spec/jira/oauth_client_spec.rb +14 -8
- data/spec/jira/request_client_spec.rb +4 -4
- data/spec/jira/resource/agile_spec.rb +32 -32
- data/spec/jira/resource/attachment_spec.rb +170 -57
- data/spec/jira/resource/board_spec.rb +24 -23
- data/spec/jira/resource/createmeta_spec.rb +48 -48
- data/spec/jira/resource/field_spec.rb +44 -27
- data/spec/jira/resource/filter_spec.rb +7 -6
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +17 -17
- data/spec/jira/resource/issue_spec.rb +159 -93
- 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 +88 -9
- data/spec/jira/resource/status_spec.rb +21 -0
- data/spec/jira/resource/user_factory_spec.rb +5 -5
- data/spec/jira/resource/worklog_spec.rb +4 -4
- data/spec/mock_responses/board/1_issues.json +2 -1
- data/spec/mock_responses/issue/10002/properties/foo.json +4 -0
- data/spec/mock_responses/issue/10002/properties/xyz.json +4 -0
- data/spec/mock_responses/issue/10002/properties/xyz.put.json +4 -0
- data/spec/mock_responses/issue/10002/properties.json +12 -0
- data/spec/mock_responses/issue.json +1 -0
- data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.full.json +2 -1
- data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json +2 -1
- 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 +5 -7
- data/spec/support/mock_client.rb +9 -0
- data/spec/support/mock_response.rb +8 -0
- data/spec/support/shared_examples/integration.rb +51 -58
- metadata +45 -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/integration/webhook.rb +0 -25
- data/spec/jira/jwt_uri_builder_spec.rb +0 -59
- data/spec/mock_responses/jira/rest/webhooks/1.0/webhook.json +0 -11
- data/spec/mock_responses/webhook/webhook.json +0 -11
- /data/spec/mock_responses/{jira/rest/webhooks/1.0/webhook → webhook}/2.json +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe JIRA::Resource::Properties do
|
|
4
|
+
with_each_client do |site_url, client|
|
|
5
|
+
context 'when accessing singular resource' do
|
|
6
|
+
let(:client) { client }
|
|
7
|
+
let(:site_url) { site_url }
|
|
8
|
+
let(:key) { 'xyz' }
|
|
9
|
+
let(:target) { described_class.new(client, attrs: { 'key' => 'xyz' }, issue_id: '10002') }
|
|
10
|
+
let(:belongs_to) { JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002' }) }
|
|
11
|
+
let(:expected_attributes) { { 'key' => key, 'value' => 'supercalifragilistic' } }
|
|
12
|
+
let(:attributes_for_put) { { 'value' => 'expialidocious' } }
|
|
13
|
+
let(:expected_attributes_from_put) { { 'key' => key, 'value' => 'expialidocious' } }
|
|
14
|
+
|
|
15
|
+
it_behaves_like 'a resource'
|
|
16
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
17
|
+
it_behaves_like 'a resource with a DELETE endpoint'
|
|
18
|
+
it_behaves_like 'a resource with a PUT endpoint'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context 'when accessing collection' do
|
|
22
|
+
let(:client) { client }
|
|
23
|
+
let(:site_url) { site_url }
|
|
24
|
+
let(:key) { 'xyz' }
|
|
25
|
+
let(:belongs_to) { JIRA::Resource::Issue.new(client, attrs: { 'id' => '10002' }) }
|
|
26
|
+
let(:expected_collection_length) { 2 }
|
|
27
|
+
let(:expected_attributes) { { 'key' => key, 'value' => 'supercalifragilistic' } }
|
|
28
|
+
|
|
29
|
+
before do
|
|
30
|
+
## Since properties collections do subsequent queries on each individual properties records,
|
|
31
|
+
## we need to additionally define stub requests for each of the individual records
|
|
32
|
+
additional_targets = [
|
|
33
|
+
described_class.new(client, attrs: { 'key' => 'foo' }, issue_id: '10002'),
|
|
34
|
+
described_class.new(client, attrs: { 'key' => 'xyz' }, issue_id: '10002')
|
|
35
|
+
]
|
|
36
|
+
additional_targets.each do |target|
|
|
37
|
+
req_url = site_url + target.url
|
|
38
|
+
stub_request(:get, req_url).to_return(status: 200, body: get_mock_from_url(:get, req_url))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -18,7 +18,7 @@ describe JIRA::Resource::RapidView do
|
|
|
18
18
|
}
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
it_behaves_like 'a resource'
|
|
22
22
|
# TODO@Anton: Add json file
|
|
23
23
|
# it_should_behave_like 'a resource with a singular GET endpoint'
|
|
24
24
|
|
|
@@ -26,19 +26,19 @@ describe JIRA::Resource::RapidView do
|
|
|
26
26
|
let(:client) { client }
|
|
27
27
|
let(:site_url) { site_url }
|
|
28
28
|
|
|
29
|
-
before
|
|
30
|
-
stub_request(:get, site_url
|
|
29
|
+
before do
|
|
30
|
+
stub_request(:get, "#{site_url}/jira/rest/greenhopper/1.0/rapidview")
|
|
31
31
|
.to_return(status: 200, body: get_mock_response('rapidview.json'))
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
describe 'issues' do
|
|
37
|
-
it '
|
|
38
|
+
it 'returns all the issues' do
|
|
38
39
|
stub_request(
|
|
39
40
|
:get,
|
|
40
|
-
site_url
|
|
41
|
-
'/jira/rest/greenhopper/1.0/xboard/plan/backlog/data?rapidViewId=1'
|
|
41
|
+
"#{site_url}/jira/rest/greenhopper/1.0/xboard/plan/backlog/data?rapidViewId=1"
|
|
42
42
|
).to_return(
|
|
43
43
|
status: 200,
|
|
44
44
|
body: get_mock_response('rapidview/SAMPLEPROJECT.issues.json')
|
|
@@ -46,7 +46,7 @@ describe JIRA::Resource::RapidView do
|
|
|
46
46
|
|
|
47
47
|
stub_request(
|
|
48
48
|
:get,
|
|
49
|
-
site_url
|
|
49
|
+
"#{site_url}/jira/rest/api/2/search/jql?jql=id IN(10001, 10000)"
|
|
50
50
|
).to_return(
|
|
51
51
|
status: 200,
|
|
52
52
|
body: get_mock_response('rapidview/SAMPLEPROJECT.issues.full.json')
|
|
@@ -54,7 +54,7 @@ describe JIRA::Resource::RapidView do
|
|
|
54
54
|
|
|
55
55
|
stub_request(
|
|
56
56
|
:get,
|
|
57
|
-
site_url
|
|
57
|
+
"#{site_url}/jira/rest/api/2/search/jql?jql=id IN(10000, 10001) AND sprint IS NOT EMPTY"
|
|
58
58
|
).to_return(
|
|
59
59
|
status: 200,
|
|
60
60
|
body: get_mock_response('rapidview/SAMPLEPROJECT.issues.full.json')
|
|
@@ -66,7 +66,7 @@ describe JIRA::Resource::RapidView do
|
|
|
66
66
|
|
|
67
67
|
issues.each do |issue|
|
|
68
68
|
expect(issue.class).to eq(JIRA::Resource::Issue)
|
|
69
|
-
expect(issue
|
|
69
|
+
expect(issue).not_to be_expanded
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
end
|
|
@@ -19,8 +19,8 @@ describe JIRA::Resource::Resolution do
|
|
|
19
19
|
|
|
20
20
|
let(:expected_collection_length) { 2 }
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
it_behaves_like 'a resource'
|
|
23
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
|
24
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe JIRA::Resource::StatusCategory do
|
|
4
|
+
with_each_client do |site_url, client|
|
|
5
|
+
let(:client) { client }
|
|
6
|
+
let(:site_url) { site_url }
|
|
7
|
+
|
|
8
|
+
let(:key) { 1 }
|
|
9
|
+
|
|
10
|
+
let(:expected_attributes) do
|
|
11
|
+
JSON.parse(File.read('spec/mock_responses/statuscategory/1.json'))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:expected_collection_length) { 4 }
|
|
15
|
+
|
|
16
|
+
it_behaves_like 'a resource'
|
|
17
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
|
18
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -8,17 +8,13 @@ describe JIRA::Resource::Status do
|
|
|
8
8
|
let(:key) { '1' }
|
|
9
9
|
|
|
10
10
|
let(:expected_attributes) do
|
|
11
|
-
|
|
12
|
-
'self' => 'http://localhost:2990/jira/rest/api/2/status/1',
|
|
13
|
-
'id' => key,
|
|
14
|
-
'name' => 'Open'
|
|
15
|
-
}
|
|
11
|
+
JSON.parse(File.read('spec/mock_responses/status/1.json'))
|
|
16
12
|
end
|
|
17
13
|
|
|
18
14
|
let(:expected_collection_length) { 5 }
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
it_behaves_like 'a resource'
|
|
17
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
|
18
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
23
19
|
end
|
|
24
20
|
end
|
|
@@ -7,7 +7,7 @@ describe JIRA::Resource::Transition 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: '10014') }
|
|
11
11
|
|
|
12
12
|
let(:belongs_to) do
|
|
13
13
|
JIRA::Resource::Issue.new(client, attrs: {
|
|
@@ -34,13 +34,14 @@ describe JIRA::Resource::Transition do
|
|
|
34
34
|
}
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
it_behaves_like 'a resource'
|
|
38
38
|
|
|
39
39
|
describe 'POST endpoint' do
|
|
40
40
|
it 'saves a new resource' do
|
|
41
|
-
|
|
41
|
+
req_url = build_url
|
|
42
|
+
stub_request(:post, req_url)
|
|
42
43
|
.with(body: attributes_for_post.to_json)
|
|
43
|
-
.to_return(status: 200, body:
|
|
44
|
+
.to_return(status: 200, body: get_mock_from_url(:post, req_url))
|
|
44
45
|
subject = build_receiver.build
|
|
45
46
|
expect(subject.save(attributes_for_post)).to be_truthy
|
|
46
47
|
end
|
|
@@ -5,36 +5,59 @@ describe JIRA::Resource::User do
|
|
|
5
5
|
let(:client) { client }
|
|
6
6
|
let(:site_url) { site_url }
|
|
7
7
|
|
|
8
|
-
let(:key) { '
|
|
8
|
+
let(:key) { '1234567890abcdef01234567' }
|
|
9
9
|
|
|
10
10
|
let(:expected_attributes) do
|
|
11
11
|
{
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
12
|
+
'id' => '1234567890abcdef01234567',
|
|
13
|
+
'self' => 'http://localhost:2990/jira/rest/api/2/user?accountId=1234567890abcdef01234567',
|
|
14
|
+
'name' => 'admin',
|
|
15
|
+
'emailAddress' => 'admin@example.com',
|
|
16
|
+
'avatarUrls' => {
|
|
17
|
+
'16x16' => 'http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122',
|
|
18
|
+
'48x48' => 'http://localhost:2990/jira/secure/useravatar?avatarId=10122'
|
|
19
|
+
},
|
|
20
|
+
'displayName' => 'admin',
|
|
21
|
+
'active' => true,
|
|
22
|
+
'timeZone' => 'Pacific/Auckland',
|
|
23
|
+
'groups' => {
|
|
24
|
+
'size' => 3,
|
|
25
|
+
'items' => []
|
|
26
|
+
},
|
|
27
|
+
'expand' => 'groups'
|
|
15
28
|
}
|
|
16
29
|
end
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
it_behaves_like 'a resource'
|
|
32
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
20
33
|
|
|
21
34
|
describe '#all' do
|
|
22
35
|
let(:client) do
|
|
23
36
|
client = double(options: { rest_base_path: '/jira/rest/api/2' })
|
|
24
|
-
allow(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000')
|
|
37
|
+
allow(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000')
|
|
38
|
+
.and_return(JIRA::Resource::UserFactory.new(client))
|
|
25
39
|
client
|
|
26
40
|
end
|
|
27
41
|
|
|
28
42
|
before do
|
|
43
|
+
user_factory = double('UserFactory')
|
|
44
|
+
|
|
29
45
|
allow(client).to receive(:get)
|
|
30
|
-
.with('/rest/api/2/users/search?username=_&maxResults=1000')
|
|
31
|
-
|
|
46
|
+
.with('/rest/api/2/users/search?username=_&maxResults=1000')
|
|
47
|
+
.and_return(double(body: '["User1"]'))
|
|
48
|
+
allow(client).to receive(:User).and_return(user_factory)
|
|
49
|
+
allow(user_factory).to receive(:build).with('users').and_return([])
|
|
32
50
|
end
|
|
33
51
|
|
|
34
52
|
it 'gets users with maxResults of 1000' do
|
|
53
|
+
user_factory = double('UserFactory')
|
|
54
|
+
|
|
35
55
|
expect(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000')
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
.and_return(double(body: '["User1"]'))
|
|
57
|
+
expect(client).to receive(:User).and_return(user_factory)
|
|
58
|
+
expect(user_factory).to receive(:build).with('User1')
|
|
59
|
+
|
|
60
|
+
described_class.all(client)
|
|
38
61
|
end
|
|
39
62
|
end
|
|
40
63
|
end
|
|
@@ -10,7 +10,7 @@ describe JIRA::Resource::Version do
|
|
|
10
10
|
let(:expected_attributes) do
|
|
11
11
|
{
|
|
12
12
|
'self' => 'http://localhost:2990/jira/rest/api/2/version/10000',
|
|
13
|
-
'id'
|
|
13
|
+
'id' => key,
|
|
14
14
|
'description' => 'Initial version'
|
|
15
15
|
}
|
|
16
16
|
end
|
|
@@ -29,11 +29,11 @@ describe JIRA::Resource::Version do
|
|
|
29
29
|
{ 'id' => '10000', 'name' => '2.0.0' }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
it_behaves_like 'a resource'
|
|
33
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
34
|
+
it_behaves_like 'a resource with a DELETE endpoint'
|
|
35
|
+
it_behaves_like 'a resource with a POST endpoint'
|
|
36
|
+
it_behaves_like 'a resource with a PUT endpoint'
|
|
37
|
+
it_behaves_like 'a resource with a PUT endpoint that rejects invalid fields'
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -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,47 @@ 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(
|
|
49
|
+
watchers = client.Watcher.all({ 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: issue)
|
|
56
|
+
user_id = 'tester'
|
|
57
|
+
|
|
57
58
|
watcher.save!(user_id)
|
|
59
|
+
|
|
60
|
+
expect(WebMock).to have_requested(:post, "#{site_url}/jira/rest/api/2/issue/10002/watchers")
|
|
61
|
+
.with(body: '"tester"')
|
|
58
62
|
end
|
|
59
63
|
end
|
|
60
|
-
|
|
61
64
|
end
|
|
62
65
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe JIRA::Resource::Webhook do
|
|
4
|
+
## This endpoint uses a different base path, so override this client's rest_base_path option
|
|
5
|
+
## so this test can still use the SharedExampleGroups
|
|
6
|
+
with_each_client(rest_base_path: described_class.const_get(:REST_BASE_PATH)) do |site_url, client|
|
|
7
|
+
let(:client) { client }
|
|
8
|
+
let(:site_url) { site_url }
|
|
9
|
+
|
|
10
|
+
let(:key) { '2' }
|
|
11
|
+
|
|
12
|
+
let(:expected_attributes) do
|
|
13
|
+
{ 'name' => 'from API', 'url' => 'http://localhost:3000/webhooks/1', 'excludeBody' => false,
|
|
14
|
+
'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 }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:expected_collection_length) { 1 }
|
|
18
|
+
|
|
19
|
+
it_behaves_like 'a resource'
|
|
20
|
+
it_behaves_like 'a resource with a collection GET endpoint'
|
|
21
|
+
it_behaves_like 'a resource with a singular GET endpoint'
|
|
22
|
+
|
|
23
|
+
it 'returns a collection of components' do
|
|
24
|
+
stub_request(:get, site_url + described_class.singular_path(client, key))
|
|
25
|
+
.to_return(status: 200, body: get_mock_response('webhook/2.json'))
|
|
26
|
+
|
|
27
|
+
webhook = client.Webhook.find(key)
|
|
28
|
+
|
|
29
|
+
expect(webhook).to be_a described_class
|
|
30
|
+
expect(webhook.name).to eq 'from API'
|
|
31
|
+
expect(webhook.url).to eq '/jira/rest/webhooks/1.0/webhook/2'
|
|
32
|
+
expect(webhook.enabled).to be true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# rubocop:disable RSpec/LeakyLocalVariable
|
|
4
|
+
describe JIRA::Atlassian::Jwt do
|
|
5
|
+
let(:jwt_opts) do
|
|
6
|
+
{
|
|
7
|
+
algorithm: 'HS256',
|
|
8
|
+
leeway: (3600 * 24 * 365 * 10) # 10 years of leeway -- the JWT gem verifies the token expiry time
|
|
9
|
+
}
|
|
10
|
+
end
|
|
11
|
+
let(:base_url) { '' }
|
|
12
|
+
|
|
13
|
+
it 'generates claims' do
|
|
14
|
+
url = 'https://example.atlassian.com/jira/projects'
|
|
15
|
+
issuer = 'com.atlassian.test'
|
|
16
|
+
|
|
17
|
+
now = Time.now.to_i
|
|
18
|
+
qsh = Digest::SHA256.hexdigest(
|
|
19
|
+
described_class.create_canonical_request(url, 'get', base_url)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
expected_claim = {
|
|
23
|
+
iss: 'com.atlassian.test',
|
|
24
|
+
iat: now,
|
|
25
|
+
exp: now + 60,
|
|
26
|
+
qsh: qsh
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
claim = described_class.build_claims(issuer, url, 'get', base_url, now, now + 60)
|
|
30
|
+
expect(claim).to eq expected_claim
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Offical Atlassian signed URL test data
|
|
34
|
+
json_tests = File.read(File.expand_path('../../data/files/jwt-signed-urls.json', File.dirname(__FILE__)))
|
|
35
|
+
|
|
36
|
+
test_data = JSON.parse(json_tests)
|
|
37
|
+
shared_secret = test_data['secret']
|
|
38
|
+
|
|
39
|
+
test_data['tests'].each do |test|
|
|
40
|
+
signed_url = test['signedUrl']
|
|
41
|
+
signed_uri = URI.parse(signed_url)
|
|
42
|
+
token = CGI.parse(signed_uri.query)['jwt'].first
|
|
43
|
+
|
|
44
|
+
it "#{test['name']} - Canonical URL" do
|
|
45
|
+
canonical_uri = described_class.create_canonical_request(signed_url, 'GET', base_url)
|
|
46
|
+
|
|
47
|
+
# Remote the jwt query param from the signed URL to get the original
|
|
48
|
+
expect(canonical_uri).to eq test['canonicalUrl']
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "#{test['name']} - QSH match" do
|
|
52
|
+
expected_qsh = Digest::SHA256.hexdigest(described_class.create_canonical_request(signed_url, 'GET', base_url))
|
|
53
|
+
|
|
54
|
+
decoded_token = JWT.decode(token, shared_secret, true, jwt_opts).first
|
|
55
|
+
|
|
56
|
+
expect(expected_qsh).to eq decoded_token['qsh']
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
# rubocop:enable RSpec/LeakyLocalVariable
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe JIRA::BaseFactory do
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
module JIRA
|
|
5
|
+
module Resource
|
|
6
|
+
class FooFactory < JIRA::BaseFactory; end
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module JIRA
|
|
11
|
+
module Resource
|
|
12
|
+
class Foo; end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
6
15
|
|
|
7
|
-
let(:client) { double }
|
|
8
16
|
subject { JIRA::Resource::FooFactory.new(client) }
|
|
9
17
|
|
|
18
|
+
let(:client) { double }
|
|
19
|
+
|
|
10
20
|
it 'initializes correctly' do
|
|
11
21
|
expect(subject.class).to eq(JIRA::Resource::FooFactory)
|
|
12
22
|
expect(subject.client).to eq(client)
|