jira-ruby 2.2.0 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.github/dependabot.yml +6 -0
  5. data/.github/workflows/CI.yml +28 -0
  6. data/.github/workflows/codeql.yml +100 -0
  7. data/.github/workflows/rubocop.yml +18 -0
  8. data/.rubocop.yml +188 -0
  9. data/Gemfile +11 -3
  10. data/Guardfile +2 -0
  11. data/README.md +121 -20
  12. data/Rakefile +4 -5
  13. data/jira-ruby.gemspec +11 -17
  14. data/lib/jira/base.rb +37 -28
  15. data/lib/jira/base_factory.rb +4 -1
  16. data/lib/jira/client.rb +65 -46
  17. data/lib/jira/has_many_proxy.rb +4 -2
  18. data/lib/jira/http_client.rb +18 -13
  19. data/lib/jira/http_error.rb +4 -0
  20. data/lib/jira/jwt_client.rb +18 -42
  21. data/lib/jira/oauth_client.rb +6 -3
  22. data/lib/jira/railtie.rb +2 -0
  23. data/lib/jira/request_client.rb +5 -1
  24. data/lib/jira/resource/agile.rb +7 -9
  25. data/lib/jira/resource/applinks.rb +5 -3
  26. data/lib/jira/resource/attachment.rb +43 -3
  27. data/lib/jira/resource/board.rb +5 -3
  28. data/lib/jira/resource/board_configuration.rb +2 -0
  29. data/lib/jira/resource/comment.rb +2 -0
  30. data/lib/jira/resource/component.rb +2 -0
  31. data/lib/jira/resource/createmeta.rb +3 -1
  32. data/lib/jira/resource/field.rb +9 -4
  33. data/lib/jira/resource/filter.rb +2 -0
  34. data/lib/jira/resource/issue.rb +35 -44
  35. data/lib/jira/resource/issue_picker_suggestions.rb +4 -1
  36. data/lib/jira/resource/issue_picker_suggestions_issue.rb +2 -0
  37. data/lib/jira/resource/issuelink.rb +2 -0
  38. data/lib/jira/resource/issuelinktype.rb +2 -0
  39. data/lib/jira/resource/issuetype.rb +2 -0
  40. data/lib/jira/resource/priority.rb +2 -0
  41. data/lib/jira/resource/project.rb +4 -2
  42. data/lib/jira/resource/rapidview.rb +5 -3
  43. data/lib/jira/resource/remotelink.rb +2 -0
  44. data/lib/jira/resource/resolution.rb +2 -0
  45. data/lib/jira/resource/serverinfo.rb +2 -0
  46. data/lib/jira/resource/sprint.rb +14 -23
  47. data/lib/jira/resource/status.rb +7 -1
  48. data/lib/jira/resource/status_category.rb +10 -0
  49. data/lib/jira/resource/suggested_issue.rb +2 -0
  50. data/lib/jira/resource/transition.rb +2 -0
  51. data/lib/jira/resource/user.rb +3 -1
  52. data/lib/jira/resource/version.rb +2 -0
  53. data/lib/jira/resource/watcher.rb +2 -1
  54. data/lib/jira/resource/webhook.rb +4 -2
  55. data/lib/jira/resource/worklog.rb +3 -2
  56. data/lib/jira/version.rb +3 -1
  57. data/lib/jira-ruby.rb +5 -3
  58. data/lib/tasks/generate.rake +4 -2
  59. data/spec/data/files/short.txt +1 -0
  60. data/spec/integration/attachment_spec.rb +3 -3
  61. data/spec/integration/comment_spec.rb +8 -8
  62. data/spec/integration/component_spec.rb +7 -7
  63. data/spec/integration/field_spec.rb +3 -3
  64. data/spec/integration/issue_spec.rb +20 -16
  65. data/spec/integration/issuelinktype_spec.rb +3 -3
  66. data/spec/integration/issuetype_spec.rb +3 -3
  67. data/spec/integration/priority_spec.rb +3 -3
  68. data/spec/integration/project_spec.rb +7 -7
  69. data/spec/integration/rapidview_spec.rb +9 -9
  70. data/spec/integration/resolution_spec.rb +3 -3
  71. data/spec/integration/status_category_spec.rb +20 -0
  72. data/spec/integration/status_spec.rb +4 -8
  73. data/spec/integration/transition_spec.rb +2 -2
  74. data/spec/integration/user_spec.rb +22 -8
  75. data/spec/integration/version_spec.rb +7 -7
  76. data/spec/integration/watcher_spec.rb +17 -18
  77. data/spec/integration/webhook.rb +5 -4
  78. data/spec/integration/worklog_spec.rb +8 -8
  79. data/spec/jira/base_factory_spec.rb +2 -1
  80. data/spec/jira/base_spec.rb +55 -41
  81. data/spec/jira/client_spec.rb +48 -34
  82. data/spec/jira/has_many_proxy_spec.rb +3 -3
  83. data/spec/jira/http_client_spec.rb +98 -26
  84. data/spec/jira/http_error_spec.rb +2 -2
  85. data/spec/jira/oauth_client_spec.rb +30 -8
  86. data/spec/jira/request_client_spec.rb +4 -4
  87. data/spec/jira/resource/agile_spec.rb +28 -28
  88. data/spec/jira/resource/attachment_spec.rb +142 -52
  89. data/spec/jira/resource/board_spec.rb +21 -20
  90. data/spec/jira/resource/createmeta_spec.rb +48 -48
  91. data/spec/jira/resource/field_spec.rb +30 -12
  92. data/spec/jira/resource/filter_spec.rb +4 -4
  93. data/spec/jira/resource/issue_picker_suggestions_spec.rb +18 -18
  94. data/spec/jira/resource/issue_spec.rb +44 -38
  95. data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +3 -3
  96. data/spec/jira/resource/project_factory_spec.rb +3 -2
  97. data/spec/jira/resource/project_spec.rb +16 -16
  98. data/spec/jira/resource/sprint_spec.rb +70 -3
  99. data/spec/jira/resource/status_spec.rb +21 -0
  100. data/spec/jira/resource/user_factory_spec.rb +4 -4
  101. data/spec/jira/resource/worklog_spec.rb +3 -3
  102. data/spec/mock_responses/sprint/1.json +13 -0
  103. data/spec/mock_responses/status/1.json +8 -1
  104. data/spec/mock_responses/status.json +40 -5
  105. data/spec/mock_responses/statuscategory/1.json +7 -0
  106. data/spec/mock_responses/statuscategory.json +30 -0
  107. data/spec/mock_responses/{user_username=admin.json → user_accountId=1234567890abcdef01234567.json} +2 -1
  108. data/spec/spec_helper.rb +1 -0
  109. data/spec/support/clients_helper.rb +3 -5
  110. data/spec/support/shared_examples/integration.rb +25 -28
  111. metadata +25 -257
  112. data/.travis.yml +0 -9
  113. data/example.rb +0 -232
  114. data/http-basic-example.rb +0 -113
  115. data/lib/jira/resource/sprint_report.rb +0 -8
  116. data/lib/jira/tasks.rb +0 -0
  117. data/spec/jira/jwt_uri_builder_spec.rb +0 -59
@@ -1,22 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe JIRA::Resource::UserFactory do
4
+ subject { described_class.new(client) }
5
+
4
6
  let(:client) do
5
7
  instance_double('Client', options: { rest_base_path: '/jira/rest/api/2' })
6
8
  end
7
9
 
8
- subject { JIRA::Resource::UserFactory.new(client) }
9
-
10
10
  describe '#myself' do
11
11
  let(:response) do
12
12
  instance_double(
13
- 'Response', body: get_mock_response('user_username=admin.json')
13
+ 'Response', body: get_mock_response('user_accountId=1234567890abcdef01234567.json')
14
14
  )
15
15
  end
16
16
 
17
17
  let(:user) { subject.myself }
18
18
 
19
- before(:each) do
19
+ before do
20
20
  allow(client).to receive(:get).with(
21
21
  '/jira/rest/api/2/myself'
22
22
  ).and_return(response)
@@ -5,10 +5,10 @@ describe JIRA::Resource::Worklog do
5
5
 
6
6
  describe 'relationships' do
7
7
  subject do
8
- JIRA::Resource::Worklog.new(client, issue_id: '99999', attrs: {
9
- 'author' => { 'foo' => 'bar' },
8
+ described_class.new(client, issue_id: '99999', attrs: {
9
+ 'author' => { 'foo' => 'bar' },
10
10
  'updateAuthor' => { 'foo' => 'bar' }
11
- })
11
+ })
12
12
  end
13
13
 
14
14
  it 'has the correct relationships' do
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": 1,
3
+ "self": "https://localhost:2990/jira/rest/agile/1.0/sprint/1",
4
+ "state": "closed",
5
+ "name": "SP Sprint 1",
6
+ "startDate": "2024-01-01T03:20:00.000Z",
7
+ "endDate": "2024-01-15T03:20:00.000Z",
8
+ "completeDate": "2024-01-16T03:48:00.000Z",
9
+ "createdDate": "2024-01-01T00:00:00.000Z",
10
+ "originBoardId": 1,
11
+ "goal": "",
12
+ "rapidview_id": 1
13
+ }
@@ -3,5 +3,12 @@
3
3
  "description": "The issue is open and ready for the assignee to start work on it.",
4
4
  "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif",
5
5
  "name": "Open",
6
- "id": "1"
6
+ "id": "1",
7
+ "statusCategory": {
8
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/2",
9
+ "id": 2,
10
+ "key": "new",
11
+ "colorName": "blue-gray",
12
+ "name": "To Do"
13
+ }
7
14
  }
@@ -4,34 +4,69 @@
4
4
  "description": "The issue is open and ready for the assignee to start work on it.",
5
5
  "iconUrl": "http://localhost:2990/jira/images/icons/status_open.gif",
6
6
  "name": "Open",
7
- "id": "1"
7
+ "id": "1",
8
+ "statusCategory": {
9
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/2",
10
+ "id": 2,
11
+ "key": "new",
12
+ "colorName": "blue-gray",
13
+ "name": "To Do"
14
+ }
8
15
  },
9
16
  {
10
17
  "self": "http://localhost:2990/jira/rest/api/2/status/3",
11
18
  "description": "This issue is being actively worked on at the moment by the assignee.",
12
19
  "iconUrl": "http://localhost:2990/jira/images/icons/status_inprogress.gif",
13
20
  "name": "In Progress",
14
- "id": "3"
21
+ "id": "3",
22
+ "statusCategory": {
23
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/4",
24
+ "id": 4,
25
+ "key": "indeterminate",
26
+ "colorName": "yellow",
27
+ "name": "In Progress"
28
+ }
15
29
  },
16
30
  {
17
31
  "self": "http://localhost:2990/jira/rest/api/2/status/4",
18
32
  "description": "This issue was once resolved, but the resolution was deemed incorrect. From here issues are either marked assigned or resolved.",
19
33
  "iconUrl": "http://localhost:2990/jira/images/icons/status_reopened.gif",
20
34
  "name": "Reopened",
21
- "id": "4"
35
+ "id": "4",
36
+ "statusCategory": {
37
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/2",
38
+ "id": 2,
39
+ "key": "new",
40
+ "colorName": "blue-gray",
41
+ "name": "To Do"
42
+ }
22
43
  },
23
44
  {
24
45
  "self": "http://localhost:2990/jira/rest/api/2/status/5",
25
46
  "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.",
26
47
  "iconUrl": "http://localhost:2990/jira/images/icons/status_resolved.gif",
27
48
  "name": "Resolved",
28
- "id": "5"
49
+ "id": "5",
50
+ "statusCategory": {
51
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/3",
52
+ "id": 3,
53
+ "key": "done",
54
+ "colorName": "green",
55
+ "name": "Done"
56
+ }
29
57
  },
30
58
  {
31
59
  "self": "http://localhost:2990/jira/rest/api/2/status/6",
32
60
  "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.",
33
61
  "iconUrl": "http://localhost:2990/jira/images/icons/status_closed.gif",
34
62
  "name": "Closed",
35
- "id": "6"
63
+ "id": "6",
64
+ "statusCategory": {
65
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/3",
66
+ "id": 3,
67
+ "key": "done",
68
+ "colorName": "green",
69
+ "name": "Done"
70
+ }
36
71
  }
37
72
  ]
@@ -0,0 +1,7 @@
1
+ {
2
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/1",
3
+ "id": 1,
4
+ "key": "undefined",
5
+ "colorName": "medium-gray",
6
+ "name": "No Category"
7
+ }
@@ -0,0 +1,30 @@
1
+ [
2
+ {
3
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/1",
4
+ "id": 1,
5
+ "key": "undefined",
6
+ "colorName": "medium-gray",
7
+ "name": "No Category"
8
+ },
9
+ {
10
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/2",
11
+ "id": 2,
12
+ "key": "new",
13
+ "colorName": "blue-gray",
14
+ "name": "To Do"
15
+ },
16
+ {
17
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/4",
18
+ "id": 4,
19
+ "key": "indeterminate",
20
+ "colorName": "yellow",
21
+ "name": "In Progress"
22
+ },
23
+ {
24
+ "self": "http://localhost:2990/jira/rest/api/2/statuscategory/3",
25
+ "id": 3,
26
+ "key": "done",
27
+ "colorName": "green",
28
+ "name": "Done"
29
+ }
30
+ ]
@@ -1,5 +1,6 @@
1
1
  {
2
- "self": "http://localhost:2990/jira/rest/api/2/user?username=admin",
2
+ "id": "1234567890abcdef01234567",
3
+ "self": "http://localhost:2990/jira/rest/api/2/user?accountId=1234567890abcdef01234567",
3
4
  "name": "admin",
4
5
  "emailAddress": "admin@example.com",
5
6
  "avatarUrls": {
data/spec/spec_helper.rb CHANGED
@@ -17,5 +17,6 @@ def get_mock_response(file, value_if_file_not_found = false)
17
17
  File.read(File.join(File.dirname(__FILE__), 'mock_responses/', file))
18
18
  rescue Errno::ENOENT => e
19
19
  raise e if value_if_file_not_found == false
20
+
20
21
  value_if_file_not_found
21
22
  end
@@ -1,5 +1,5 @@
1
1
  module ClientsHelper
2
- def with_each_client
2
+ def with_each_client(&block)
3
3
  clients = {}
4
4
 
5
5
  oauth_client = JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar')
@@ -7,10 +7,8 @@ module ClientsHelper
7
7
  clients['http://localhost:2990'] = oauth_client
8
8
 
9
9
  basic_client = JIRA::Client.new(username: 'foo', password: 'bar', auth_type: :basic, use_ssl: false)
10
- clients['http://foo:bar@localhost:2990'] = basic_client
10
+ clients['http://localhost:2990'] = basic_client
11
11
 
12
- clients.each do |site_url, client|
13
- yield site_url, client
14
- end
12
+ clients.each(&block)
15
13
  end
16
14
  end
@@ -2,7 +2,7 @@ require 'cgi'
2
2
 
3
3
  def get_mock_from_path(method, options = {})
4
4
  prefix = if defined? belongs_to
5
- belongs_to.path_component + '/'
5
+ "#{belongs_to.path_component}/"
6
6
  else
7
7
  ''
8
8
  end
@@ -15,8 +15,8 @@ def get_mock_from_path(method, options = {})
15
15
  described_class.collection_path(client, prefix)
16
16
  end
17
17
  file_path = url.sub(client.options[:rest_base_path], '')
18
- file_path = file_path + '.' + options[:suffix] if options[:suffix]
19
- file_path = file_path + '.' + method.to_s unless method == :get
18
+ file_path = "#{file_path}.#{options[:suffix]}" if options[:suffix]
19
+ file_path = "#{file_path}.#{method}" unless method == :get
20
20
  value_if_not_found = options.key?(:value_if_not_found) ? options[:value_if_not_found] : false
21
21
  get_mock_response("#{file_path}.json", value_if_not_found)
22
22
  end
@@ -33,7 +33,7 @@ end
33
33
 
34
34
  def prefix
35
35
  prefix = '/'
36
- prefix = belongs_to.path_component + '/' if defined? belongs_to
36
+ prefix = "#{belongs_to.path_component}/" if defined? belongs_to
37
37
  prefix
38
38
  end
39
39
 
@@ -47,22 +47,22 @@ end
47
47
 
48
48
  shared_examples 'a resource' do
49
49
  it 'gracefully handles non-json responses' do
50
- if defined? target
51
- subject = target
52
- else
53
- subject = client.send(class_basename).build(described_class.key_attribute.to_s => '99999')
54
- end
50
+ subject = if defined? target
51
+ target
52
+ else
53
+ client.send(class_basename).build(described_class.key_attribute.to_s => '99999')
54
+ end
55
55
  stub_request(:put, site_url + subject.url)
56
56
  .to_return(status: 405, body: '<html><body>Some HTML</body></html>')
57
57
  expect(subject.save('foo' => 'bar')).to be_falsey
58
- expect(lambda do
58
+ expect do
59
59
  expect(subject.save!('foo' => 'bar')).to be_falsey
60
- end).to raise_error(JIRA::HTTPError)
60
+ end.to raise_error(JIRA::HTTPError)
61
61
  end
62
62
  end
63
63
 
64
64
  shared_examples 'a resource with a collection GET endpoint' do
65
- it 'should get the collection' do
65
+ it 'gets the collection' do
66
66
  stub_request(:get, site_url + described_class.collection_path(client))
67
67
  .to_return(status: 200, body: get_mock_from_path(:get))
68
68
  collection = build_receiver.all
@@ -73,13 +73,10 @@ shared_examples 'a resource with a collection GET endpoint' do
73
73
  end
74
74
 
75
75
  shared_examples 'a resource with JQL inputs and a collection GET endpoint' do
76
- it 'should get the collection' do
76
+ it 'gets the collection' do
77
77
  stub_request(
78
78
  :get,
79
- site_url +
80
- client.options[:rest_base_path] +
81
- '/search?jql=' +
82
- CGI.escape(jql_query_string)
79
+ "#{site_url}#{client.options[:rest_base_path]}/search?jql=#{CGI.escape(jql_query_string)}"
83
80
  ).to_return(status: 200, body: get_mock_response('issue.json'))
84
81
 
85
82
  collection = build_receiver.jql(jql_query_string)
@@ -94,7 +91,7 @@ shared_examples 'a resource with a singular GET endpoint' do
94
91
  # E.g., for JIRA::Resource::Project, we need to call
95
92
  # client.Project.find()
96
93
  stub_request(:get, site_url + described_class.singular_path(client, key, prefix))
97
- .to_return(status: 200, body: get_mock_from_path(:get, key: key))
94
+ .to_return(status: 200, body: get_mock_from_path(:get, key:))
98
95
  subject = client.send(class_basename).find(key, options)
99
96
 
100
97
  expect(subject).to have_attributes(expected_attributes)
@@ -104,7 +101,7 @@ shared_examples 'a resource with a singular GET endpoint' do
104
101
  # E.g., for JIRA::Resource::Project, we need to call
105
102
  # client.Project.build('key' => 'ABC123')
106
103
  stub_request(:get, site_url + described_class.singular_path(client, key, prefix))
107
- .to_return(status: 200, body: get_mock_from_path(:get, key: key))
104
+ .to_return(status: 200, body: get_mock_from_path(:get, key:))
108
105
 
109
106
  subject = build_receiver.build(described_class.key_attribute.to_s => key)
110
107
  subject.fetch
@@ -114,10 +111,10 @@ shared_examples 'a resource with a singular GET endpoint' do
114
111
 
115
112
  it 'handles a 404' do
116
113
  stub_request(:get, site_url + described_class.singular_path(client, '99999', prefix))
117
- .to_return(status: 404, body: '{"errorMessages":["' + class_basename + ' Does Not Exist"],"errors": {}}')
118
- expect(lambda do
114
+ .to_return(status: 404, body: "{\"errorMessages\":[\"#{class_basename} Does Not Exist\"],\"errors\": {}}")
115
+ expect do
119
116
  client.send(class_basename).find('99999', options)
120
- end).to raise_exception(JIRA::HTTPError)
117
+ end.to raise_exception(JIRA::HTTPError)
121
118
  end
122
119
  end
123
120
 
@@ -148,9 +145,9 @@ end
148
145
  shared_examples 'a resource with a PUT endpoint' do
149
146
  it 'saves an existing component' do
150
147
  stub_request(:get, site_url + described_class.singular_path(client, key, prefix))
151
- .to_return(status: 200, body: get_mock_from_path(:get, key: key))
148
+ .to_return(status: 200, body: get_mock_from_path(:get, key:))
152
149
  stub_request(:put, site_url + described_class.singular_path(client, key, prefix))
153
- .to_return(status: 200, body: get_mock_from_path(:put, key: key, value_if_not_found: nil))
150
+ .to_return(status: 200, body: get_mock_from_path(:put, key:, value_if_not_found: nil))
154
151
  subject = build_receiver.build(described_class.key_attribute.to_s => key)
155
152
  subject.fetch
156
153
  expect(subject.save(attributes_for_put)).to be_truthy
@@ -163,15 +160,15 @@ end
163
160
  shared_examples 'a resource with a PUT endpoint that rejects invalid fields' do
164
161
  it 'fails to save with an invalid field' do
165
162
  stub_request(:get, site_url + described_class.singular_path(client, key))
166
- .to_return(status: 200, body: get_mock_from_path(:get, key: key))
163
+ .to_return(status: 200, body: get_mock_from_path(:get, key:))
167
164
  stub_request(:put, site_url + described_class.singular_path(client, key))
168
- .to_return(status: 400, body: get_mock_from_path(:put, key: key, suffix: 'invalid'))
165
+ .to_return(status: 400, body: get_mock_from_path(:put, key:, suffix: 'invalid'))
169
166
  subject = client.send(class_basename).build(described_class.key_attribute.to_s => key)
170
167
  subject.fetch
171
168
 
172
169
  expect(subject.save('fields' => { 'invalid' => 'field' })).to be_falsey
173
- expect(lambda do
170
+ expect do
174
171
  subject.save!('fields' => { 'invalid' => 'field' })
175
- end).to raise_error(JIRA::HTTPError)
172
+ end.to raise_error(JIRA::HTTPError)
176
173
  end
177
174
  end