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.
Files changed (138) 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 +30 -0
  6. data/.github/workflows/codeql.yml +96 -0
  7. data/.github/workflows/rubocop.yml +18 -0
  8. data/.gitignore +3 -1
  9. data/.rubocop.yml +123 -0
  10. data/.yardopts +4 -0
  11. data/Gemfile +11 -3
  12. data/Guardfile +2 -0
  13. data/README.md +94 -19
  14. data/Rakefile +3 -4
  15. data/jira-ruby.gemspec +12 -17
  16. data/lib/jira/atlassian/jwt.rb +76 -0
  17. data/lib/jira/base.rb +37 -36
  18. data/lib/jira/base_factory.rb +4 -1
  19. data/lib/jira/client.rb +132 -51
  20. data/lib/jira/has_many_proxy.rb +32 -28
  21. data/lib/jira/http_client.rb +80 -13
  22. data/lib/jira/http_error.rb +4 -0
  23. data/lib/jira/jwt_client.rb +19 -43
  24. data/lib/jira/oauth_client.rb +68 -3
  25. data/lib/jira/railtie.rb +2 -0
  26. data/lib/jira/request_client.rb +31 -2
  27. data/lib/jira/resource/agile.rb +7 -9
  28. data/lib/jira/resource/applinks.rb +5 -3
  29. data/lib/jira/resource/attachment.rb +144 -4
  30. data/lib/jira/resource/board.rb +5 -3
  31. data/lib/jira/resource/board_configuration.rb +2 -0
  32. data/lib/jira/resource/comment.rb +14 -0
  33. data/lib/jira/resource/component.rb +2 -0
  34. data/lib/jira/resource/createmeta.rb +7 -5
  35. data/lib/jira/resource/field.rb +13 -12
  36. data/lib/jira/resource/filter.rb +2 -0
  37. data/lib/jira/resource/issue.rb +148 -54
  38. data/lib/jira/resource/issue_picker_suggestions.rb +4 -1
  39. data/lib/jira/resource/issue_picker_suggestions_issue.rb +2 -0
  40. data/lib/jira/resource/issuelink.rb +6 -3
  41. data/lib/jira/resource/issuelinktype.rb +2 -0
  42. data/lib/jira/resource/issuetype.rb +2 -0
  43. data/lib/jira/resource/priority.rb +2 -0
  44. data/lib/jira/resource/project.rb +4 -2
  45. data/lib/jira/resource/properties.rb +56 -0
  46. data/lib/jira/resource/rapidview.rb +5 -3
  47. data/lib/jira/resource/remotelink.rb +2 -0
  48. data/lib/jira/resource/resolution.rb +2 -0
  49. data/lib/jira/resource/serverinfo.rb +2 -0
  50. data/lib/jira/resource/sprint.rb +14 -23
  51. data/lib/jira/resource/status.rb +7 -1
  52. data/lib/jira/resource/status_category.rb +10 -0
  53. data/lib/jira/resource/suggested_issue.rb +2 -0
  54. data/lib/jira/resource/transition.rb +2 -0
  55. data/lib/jira/resource/user.rb +3 -1
  56. data/lib/jira/resource/version.rb +2 -0
  57. data/lib/jira/resource/watcher.rb +3 -2
  58. data/lib/jira/resource/webhook.rb +9 -3
  59. data/lib/jira/resource/worklog.rb +15 -2
  60. data/lib/jira/version.rb +3 -1
  61. data/lib/jira-ruby.rb +6 -3
  62. data/lib/tasks/generate.rake +3 -1
  63. data/spec/data/files/jwt-signed-urls.json +317 -0
  64. data/spec/data/files/short.txt +1 -0
  65. data/spec/integration/attachment_spec.rb +3 -3
  66. data/spec/integration/comment_spec.rb +8 -8
  67. data/spec/integration/component_spec.rb +7 -7
  68. data/spec/integration/field_spec.rb +3 -3
  69. data/spec/integration/issue_spec.rb +21 -18
  70. data/spec/integration/issuelinktype_spec.rb +3 -3
  71. data/spec/integration/issuetype_spec.rb +3 -3
  72. data/spec/integration/priority_spec.rb +3 -3
  73. data/spec/integration/project_spec.rb +8 -8
  74. data/spec/integration/properties_spec.rb +45 -0
  75. data/spec/integration/rapidview_spec.rb +10 -10
  76. data/spec/integration/resolution_spec.rb +3 -3
  77. data/spec/integration/status_category_spec.rb +20 -0
  78. data/spec/integration/status_spec.rb +4 -8
  79. data/spec/integration/transition_spec.rb +5 -4
  80. data/spec/integration/user_spec.rb +34 -11
  81. data/spec/integration/version_spec.rb +7 -7
  82. data/spec/integration/watcher_spec.rb +21 -18
  83. data/spec/integration/webhook_spec.rb +35 -0
  84. data/spec/integration/worklog_spec.rb +8 -8
  85. data/spec/jira/atlassian/jwt_spec.rb +60 -0
  86. data/spec/jira/base_factory_spec.rb +13 -3
  87. data/spec/jira/base_spec.rb +135 -98
  88. data/spec/jira/client_spec.rb +72 -56
  89. data/spec/jira/has_many_proxy_spec.rb +3 -3
  90. data/spec/jira/http_client_spec.rb +94 -27
  91. data/spec/jira/http_error_spec.rb +2 -2
  92. data/spec/jira/oauth_client_spec.rb +14 -8
  93. data/spec/jira/request_client_spec.rb +4 -4
  94. data/spec/jira/resource/agile_spec.rb +32 -32
  95. data/spec/jira/resource/attachment_spec.rb +170 -57
  96. data/spec/jira/resource/board_spec.rb +24 -23
  97. data/spec/jira/resource/createmeta_spec.rb +48 -48
  98. data/spec/jira/resource/field_spec.rb +44 -27
  99. data/spec/jira/resource/filter_spec.rb +7 -6
  100. data/spec/jira/resource/issue_picker_suggestions_spec.rb +17 -17
  101. data/spec/jira/resource/issue_spec.rb +159 -93
  102. data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +3 -3
  103. data/spec/jira/resource/project_factory_spec.rb +3 -2
  104. data/spec/jira/resource/project_spec.rb +16 -16
  105. data/spec/jira/resource/sprint_spec.rb +88 -9
  106. data/spec/jira/resource/status_spec.rb +21 -0
  107. data/spec/jira/resource/user_factory_spec.rb +5 -5
  108. data/spec/jira/resource/worklog_spec.rb +4 -4
  109. data/spec/mock_responses/board/1_issues.json +2 -1
  110. data/spec/mock_responses/issue/10002/properties/foo.json +4 -0
  111. data/spec/mock_responses/issue/10002/properties/xyz.json +4 -0
  112. data/spec/mock_responses/issue/10002/properties/xyz.put.json +4 -0
  113. data/spec/mock_responses/issue/10002/properties.json +12 -0
  114. data/spec/mock_responses/issue.json +1 -0
  115. data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.full.json +2 -1
  116. data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json +2 -1
  117. data/spec/mock_responses/sprint/1.json +13 -0
  118. data/spec/mock_responses/status/1.json +8 -1
  119. data/spec/mock_responses/status.json +40 -5
  120. data/spec/mock_responses/statuscategory/1.json +7 -0
  121. data/spec/mock_responses/statuscategory.json +30 -0
  122. data/spec/mock_responses/{user_username=admin.json → user_accountId=1234567890abcdef01234567.json} +2 -1
  123. data/spec/spec_helper.rb +1 -0
  124. data/spec/support/clients_helper.rb +5 -7
  125. data/spec/support/mock_client.rb +9 -0
  126. data/spec/support/mock_response.rb +8 -0
  127. data/spec/support/shared_examples/integration.rb +51 -58
  128. metadata +45 -257
  129. data/.travis.yml +0 -9
  130. data/example.rb +0 -232
  131. data/http-basic-example.rb +0 -113
  132. data/lib/jira/resource/sprint_report.rb +0 -8
  133. data/lib/jira/tasks.rb +0 -0
  134. data/spec/integration/webhook.rb +0 -25
  135. data/spec/jira/jwt_uri_builder_spec.rb +0 -59
  136. data/spec/mock_responses/jira/rest/webhooks/1.0/webhook.json +0 -11
  137. data/spec/mock_responses/webhook/webhook.json +0 -11
  138. /data/spec/mock_responses/{jira/rest/webhooks/1.0/webhook → webhook}/2.json +0 -0
@@ -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
- instance_double('Client', options: { rest_base_path: '/jira/rest/api/2' })
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' },
10
- 'updateAuthor' => { 'foo' => 'bar' }
11
- })
8
+ described_class.new(client, issue_id: '99999', attrs: {
9
+ 'author' => { 'foo' => 'bar' },
10
+ 'updateAuthor' => { 'foo' => 'bar' }
11
+ })
12
12
  end
13
13
 
14
14
  it 'has the correct relationships' do
@@ -3,6 +3,7 @@
3
3
  "startAt": 0,
4
4
  "maxResults": 1000,
5
5
  "total": 9,
6
+ "isLast": true,
6
7
  "issues": [
7
8
  {
8
9
  "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
@@ -59,4 +60,4 @@
59
60
  "key": "SBT-19"
60
61
  }
61
62
  ]
62
- }
63
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "key": "foo",
3
+ "value": "bar"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "key": "xyz",
3
+ "value": "supercalifragilistic"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "key": "xyz",
3
+ "value": "expialidocious"
4
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "keys": [
3
+ {
4
+ "key": "xyz",
5
+ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/properties/xyz"
6
+ },
7
+ {
8
+ "key": "foo",
9
+ "self": "http://localhost:2990/jira/rest/api/2/issue/10002/properties/foo"
10
+ }
11
+ ]
12
+ }
@@ -3,6 +3,7 @@
3
3
  "startAt": 0,
4
4
  "maxResults": 1000,
5
5
  "total": 11,
6
+ "isLast": true,
6
7
  "issues": [
7
8
  {
8
9
  "expand": "editmeta,renderedFields,transitions,changelog",
@@ -2,6 +2,7 @@
2
2
  "expand": "schema,names",
3
3
  "startAt": 0,
4
4
  "maxResults": 50,
5
+ "isLast": true,
5
6
  "total": 2,
6
7
  "issues": [
7
8
  {
@@ -273,4 +274,4 @@
273
274
  }
274
275
  }
275
276
  ]
276
- }
277
+ }
@@ -101,6 +101,7 @@
101
101
  },
102
102
  "canManageSprints": true,
103
103
  "maxIssuesExceeded": false,
104
+ "isLast": true,
104
105
  "queryResultLimit": 2147483647,
105
106
  "versionData": {
106
107
  "versionsPerProject": {
@@ -108,4 +109,4 @@
108
109
  },
109
110
  "canCreateVersion": true
110
111
  }
111
- }
112
+ }
@@ -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,16 +1,14 @@
1
1
  module ClientsHelper
2
- def with_each_client
2
+ def with_each_client(opts = {}, &)
3
3
  clients = {}
4
4
 
5
- oauth_client = JIRA::Client.new(consumer_key: 'foo', consumer_secret: 'bar')
5
+ oauth_client = JIRA::Client.new({ consumer_key: 'foo', consumer_secret: 'bar' }.merge(opts))
6
6
  oauth_client.set_access_token('abc', '123')
7
7
  clients['http://localhost:2990'] = oauth_client
8
8
 
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
9
+ basic_client = JIRA::Client.new({ username: 'foo', password: 'bar', auth_type: :basic, use_ssl: false }.merge(opts))
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(&)
15
13
  end
16
14
  end
@@ -0,0 +1,9 @@
1
+ class Client
2
+ attr_reader :options
3
+
4
+ def initialize(options = {})
5
+ @options = options
6
+ end
7
+
8
+ def get(url) end
9
+ end
@@ -0,0 +1,8 @@
1
+ class Response
2
+ attr_reader :body, :status
3
+
4
+ def initialize(body, status = nil)
5
+ @body = body
6
+ @status = status
7
+ end
8
+ end
@@ -1,22 +1,20 @@
1
1
  require 'cgi'
2
2
 
3
- def get_mock_from_path(method, options = {})
4
- prefix = if defined? belongs_to
5
- belongs_to.path_component + '/'
6
- else
7
- ''
8
- end
9
-
10
- url = if options[:url]
11
- options[:url]
12
- elsif options[:key]
13
- described_class.singular_path(client, options[:key], prefix)
14
- else
15
- described_class.collection_path(client, prefix)
16
- end
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
3
+ def build_url(options = {})
4
+ prefix = defined?(belongs_to) ? "#{belongs_to.path_component}/" : '/'
5
+ path = if options.key?(:key)
6
+ described_class.singular_path(client, options[:key], prefix)
7
+ else
8
+ described_class.collection_path(client, prefix)
9
+ end
10
+ site_url + path
11
+ end
12
+
13
+ def get_mock_from_url(method, url, options = {})
14
+ # Remove site_url and rest api portion of the url
15
+ file_path = url.sub(site_url + client.options[:rest_base_path], '')
16
+ file_path = "#{file_path}.#{options[:suffix]}" if options[:suffix]
17
+ file_path = "#{file_path}.#{method}" unless method == :get
20
18
  value_if_not_found = options.key?(:value_if_not_found) ? options[:value_if_not_found] : false
21
19
  get_mock_response("#{file_path}.json", value_if_not_found)
22
20
  end
@@ -33,7 +31,7 @@ end
33
31
 
34
32
  def prefix
35
33
  prefix = '/'
36
- prefix = belongs_to.path_component + '/' if defined? belongs_to
34
+ prefix = "#{belongs_to.path_component}/" if defined? belongs_to
37
35
  prefix
38
36
  end
39
37
 
@@ -47,24 +45,24 @@ end
47
45
 
48
46
  shared_examples 'a resource' do
49
47
  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
48
+ subject = if defined? target
49
+ target
50
+ else
51
+ client.send(class_basename).build(described_class.key_attribute.to_s => '99999')
52
+ end
55
53
  stub_request(:put, site_url + subject.url)
56
54
  .to_return(status: 405, body: '<html><body>Some HTML</body></html>')
57
55
  expect(subject.save('foo' => 'bar')).to be_falsey
58
- expect(lambda do
56
+ expect do
59
57
  expect(subject.save!('foo' => 'bar')).to be_falsey
60
- end).to raise_error(JIRA::HTTPError)
58
+ end.to raise_error(JIRA::HTTPError)
61
59
  end
62
60
  end
63
61
 
64
62
  shared_examples 'a resource with a collection GET endpoint' do
65
- it 'should get the collection' do
66
- stub_request(:get, site_url + described_class.collection_path(client))
67
- .to_return(status: 200, body: get_mock_from_path(:get))
63
+ it 'gets the collection' do
64
+ req_url = build_url
65
+ stub_request(:get, req_url).to_return(status: 200, body: get_mock_from_url(:get, req_url))
68
66
  collection = build_receiver.all
69
67
 
70
68
  expect(collection.length).to eq(expected_collection_length)
@@ -73,14 +71,9 @@ shared_examples 'a resource with a collection GET endpoint' do
73
71
  end
74
72
 
75
73
  shared_examples 'a resource with JQL inputs and a collection GET endpoint' do
76
- it 'should get the collection' do
77
- stub_request(
78
- :get,
79
- site_url +
80
- client.options[:rest_base_path] +
81
- '/search?jql=' +
82
- CGI.escape(jql_query_string)
83
- ).to_return(status: 200, body: get_mock_response('issue.json'))
74
+ it 'gets the collection' do
75
+ req_url = "#{site_url}#{client.options[:rest_base_path]}/search/jql?jql=#{CGI.escape(jql_query_string)}"
76
+ stub_request(:get, req_url).to_return(status: 200, body: get_mock_response('issue.json'))
84
77
 
85
78
  collection = build_receiver.jql(jql_query_string)
86
79
 
@@ -93,8 +86,8 @@ shared_examples 'a resource with a singular GET endpoint' do
93
86
  it 'GETs a single resource' do
94
87
  # E.g., for JIRA::Resource::Project, we need to call
95
88
  # client.Project.find()
96
- 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))
89
+ req_url = build_url(key:)
90
+ stub_request(:get, req_url).to_return(status: 200, body: get_mock_from_url(:get, req_url))
98
91
  subject = client.send(class_basename).find(key, options)
99
92
 
100
93
  expect(subject).to have_attributes(expected_attributes)
@@ -103,8 +96,8 @@ shared_examples 'a resource with a singular GET endpoint' do
103
96
  it 'builds and fetches a single resource' do
104
97
  # E.g., for JIRA::Resource::Project, we need to call
105
98
  # client.Project.build('key' => 'ABC123')
106
- 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))
99
+ req_url = build_url(key:)
100
+ stub_request(:get, req_url).to_return(status: 200, body: get_mock_from_url(:get, req_url))
108
101
 
109
102
  subject = build_receiver.build(described_class.key_attribute.to_s => key)
110
103
  subject.fetch
@@ -113,11 +106,11 @@ shared_examples 'a resource with a singular GET endpoint' do
113
106
  end
114
107
 
115
108
  it 'handles a 404' do
116
- 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
109
+ stub_request(:get, build_url(key: '99999'))
110
+ .to_return(status: 404, body: "{\"errorMessages\":[\"#{class_basename} Does Not Exist\"],\"errors\": {}}")
111
+ expect do
119
112
  client.send(class_basename).find('99999', options)
120
- end).to raise_exception(JIRA::HTTPError)
113
+ end.to raise_exception(JIRA::HTTPError)
121
114
  end
122
115
  end
123
116
 
@@ -125,8 +118,8 @@ shared_examples 'a resource with a DELETE endpoint' do
125
118
  it 'deletes a resource' do
126
119
  # E.g., for JIRA::Resource::Project, we need to call
127
120
  # client.Project.delete()
128
- stub_request(:delete, site_url + described_class.singular_path(client, key, prefix))
129
- .to_return(status: 204, body: nil)
121
+ req_url = build_url(key:)
122
+ stub_request(:delete, req_url).to_return(status: 204, body: nil)
130
123
 
131
124
  subject = build_receiver.build(described_class.key_attribute.to_s => key)
132
125
  expect(subject.delete).to be_truthy
@@ -135,8 +128,8 @@ end
135
128
 
136
129
  shared_examples 'a resource with a POST endpoint' do
137
130
  it 'saves a new resource' do
138
- stub_request(:post, site_url + described_class.collection_path(client, prefix))
139
- .to_return(status: 201, body: get_mock_from_path(:post))
131
+ req_url = build_url
132
+ stub_request(:post, req_url).to_return(status: 201, body: get_mock_from_url(:post, req_url))
140
133
  subject = build_receiver.build
141
134
  expect(subject.save(attributes_for_post)).to be_truthy
142
135
  expected_attributes_from_post.each do |method_name, value|
@@ -147,10 +140,10 @@ end
147
140
 
148
141
  shared_examples 'a resource with a PUT endpoint' do
149
142
  it 'saves an existing component' do
150
- 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))
152
- 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))
143
+ req_url = build_url(key:)
144
+ stub_request(:get, req_url).to_return(status: 200, body: get_mock_from_url(:get, req_url))
145
+ stub_request(:put, req_url)
146
+ .to_return(status: 200, body: get_mock_from_url(:put, req_url, value_if_not_found: nil))
154
147
  subject = build_receiver.build(described_class.key_attribute.to_s => key)
155
148
  subject.fetch
156
149
  expect(subject.save(attributes_for_put)).to be_truthy
@@ -162,16 +155,16 @@ end
162
155
 
163
156
  shared_examples 'a resource with a PUT endpoint that rejects invalid fields' do
164
157
  it 'fails to save with an invalid field' do
165
- stub_request(:get, site_url + described_class.singular_path(client, key))
166
- .to_return(status: 200, body: get_mock_from_path(:get, key: key))
167
- 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'))
158
+ req_url = build_url(key:)
159
+ stub_request(:get, req_url).to_return(status: 200, body: get_mock_from_url(:get, req_url))
160
+ stub_request(:put, req_url)
161
+ .to_return(status: 400, body: get_mock_from_url(:put, req_url, suffix: 'invalid'))
169
162
  subject = client.send(class_basename).build(described_class.key_attribute.to_s => key)
170
163
  subject.fetch
171
164
 
172
165
  expect(subject.save('fields' => { 'invalid' => 'field' })).to be_falsey
173
- expect(lambda do
166
+ expect do
174
167
  subject.save!('fields' => { 'invalid' => 'field' })
175
- end).to raise_error(JIRA::HTTPError)
168
+ end.to raise_error(JIRA::HTTPError)
176
169
  end
177
170
  end