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.
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