jira-ruby 2.3.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 +94 -18
  12. data/Rakefile +3 -4
  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 +64 -46
  17. data/lib/jira/has_many_proxy.rb +4 -2
  18. data/lib/jira/http_client.rb +17 -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 +94 -27
  84. data/spec/jira/http_error_spec.rb +2 -2
  85. data/spec/jira/oauth_client_spec.rb +8 -6
  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 +17 -17
  94. data/spec/jira/resource/issue_spec.rb +43 -37
  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
data/lib/jira/base.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/string'
2
4
  require 'active_support/inflector'
3
5
  require 'set'
@@ -81,8 +83,8 @@ module JIRA
81
83
  if options[relation]
82
84
  instance_variable_set("@#{relation}", options[relation])
83
85
  instance_variable_set("@#{relation}_id", options[relation].key_value)
84
- elsif options["#{relation}_id".to_sym]
85
- instance_variable_set("@#{relation}_id", options["#{relation}_id".to_sym])
86
+ elsif options[:"#{relation}_id"]
87
+ instance_variable_set("@#{relation}_id", options[:"#{relation}_id"])
86
88
  else
87
89
  raise ArgumentError, "Required option #{relation.inspect} missing" unless options[relation]
88
90
  end
@@ -96,7 +98,7 @@ module JIRA
96
98
  json = parse_json(response.body)
97
99
  json = json[endpoint_name.pluralize] if collection_attributes_are_nested
98
100
  json.map do |attrs|
99
- new(client, { attrs: attrs }.merge(options))
101
+ new(client, { attrs: }.merge(options))
100
102
  end
101
103
  end
102
104
 
@@ -111,7 +113,7 @@ module JIRA
111
113
  # Builds a new instance of the resource with the given attributes.
112
114
  # These attributes will be posted to the JIRA Api if save is called.
113
115
  def self.build(client, attrs)
114
- new(client, attrs: attrs)
116
+ new(client, attrs:)
115
117
  end
116
118
 
117
119
  # Returns the name of this resource for use in URL components.
@@ -141,7 +143,7 @@ module JIRA
141
143
  # JIRA::Resource::Comment.singular_path('456','/issue/123/')
142
144
  # # => /jira/rest/api/2/issue/123/comment/456
143
145
  def self.singular_path(client, key, prefix = '/')
144
- collection_path(client, prefix) + '/' + key
146
+ "#{collection_path(client, prefix)}/#{key}"
145
147
  end
146
148
 
147
149
  # Returns the attribute name of the attribute used for find.
@@ -193,10 +195,11 @@ module JIRA
193
195
  # # => Looks for {"foo":{"bar":{"baz":{"child":{}}}}}
194
196
  def self.has_one(resource, options = {})
195
197
  attribute_key = options[:attribute_key] || resource.to_s
196
- child_class = options[:class] || ('JIRA::Resource::' + resource.to_s.classify).constantize
198
+ child_class = options[:class] || "JIRA::Resource::#{resource.to_s.classify}".constantize
197
199
  define_method(resource) do
198
200
  attribute = maybe_nested_attribute(attribute_key, options[:nested_under])
199
201
  return nil unless attribute
202
+
200
203
  child_class.new(client, attrs: attribute)
201
204
  end
202
205
  end
@@ -244,7 +247,7 @@ module JIRA
244
247
  # # => Looks for {"foo":{"bar":{"baz":{"children":{}}}}}
245
248
  def self.has_many(collection, options = {})
246
249
  attribute_key = options[:attribute_key] || collection.to_s
247
- child_class = options[:class] || ('JIRA::Resource::' + collection.to_s.classify).constantize
250
+ child_class = options[:class] || "JIRA::Resource::#{collection.to_s.classify}".constantize
248
251
  self_class_basename = name.split('::').last.downcase.to_sym
249
252
  define_method(collection) do
250
253
  child_class_options = { self_class_basename => self }
@@ -322,7 +325,7 @@ module JIRA
322
325
  # issue it returns '/issue'
323
326
  def path_component
324
327
  path_component = "/#{self.class.endpoint_name}"
325
- path_component += '/' + key_value if key_value
328
+ path_component += "/#{key_value}" if key_value
326
329
  path_component
327
330
  end
328
331
 
@@ -331,6 +334,7 @@ module JIRA
331
334
  # is not set
332
335
  def fetch(reload = false, query_params = {})
333
336
  return if expanded? && !reload
337
+
334
338
  response = client.get(url_with_query_params(url, query_params))
335
339
  set_attrs_from_response(response)
336
340
  @expanded = true
@@ -359,14 +363,14 @@ module JIRA
359
363
  def save(attrs, path = url)
360
364
  begin
361
365
  save_status = save!(attrs, path)
362
- rescue JIRA::HTTPError => exception
366
+ rescue JIRA::HTTPError => e
363
367
  begin
364
- set_attrs_from_response(exception.response) # Merge error status generated by JIRA REST API
368
+ set_attrs_from_response(e.response) # Merge error status generated by JIRA REST API
365
369
  rescue JSON::ParserError => parse_exception
366
370
  set_attrs('exception' => {
367
- 'class' => exception.response.class.name,
368
- 'code' => exception.response.code,
369
- 'message' => exception.response.message
371
+ 'class' => e.response.class.name,
372
+ 'code' => e.response.code,
373
+ 'message' => e.response.message
370
374
  })
371
375
  end
372
376
  # raise exception
@@ -378,10 +382,10 @@ module JIRA
378
382
  # Sets the attributes hash from a HTTPResponse object from JIRA if it is
379
383
  # not nil or is not a json response.
380
384
  def set_attrs_from_response(response)
381
- unless response.body.nil? || (response.body.length < 2)
382
- json = self.class.parse_json(response.body)
383
- set_attrs(json)
384
- end
385
+ return if response.body.nil? || (response.body.length < 2)
386
+ json = self.class.parse_json(response.body)
387
+ set_attrs(json)
388
+
385
389
  end
386
390
 
387
391
  # Set the current attributes from a hash. If clobber is true, any existing
@@ -419,7 +423,7 @@ module JIRA
419
423
  prefix = '/'
420
424
  unless self.class.belongs_to_relationships.empty?
421
425
  prefix = self.class.belongs_to_relationships.inject(prefix) do |prefix_so_far, relationship|
422
- prefix_so_far.to_s + relationship.to_s + '/' + send("#{relationship}_id").to_s + '/'
426
+ "#{prefix_so_far}#{relationship}/#{send("#{relationship}_id")}/"
423
427
  end
424
428
  end
425
429
  if @attrs['self']
@@ -434,7 +438,8 @@ module JIRA
434
438
  end
435
439
 
436
440
  # This method fixes issue that there is no / prefix in url. It is happened when we call for instance
437
- # Looks like this issue is actual only in case if you use atlassian sdk your app path is not root (like /jira in example below)
441
+ # Looks like this issue is actual only in case if you use atlassian sdk your app pathis not root
442
+ # (like /jira in example below)
438
443
  # issue.save() for existing resource.
439
444
  # As a result we got error 400 from JIRA API:
440
445
  # [07/Jun/2015:15:32:19 +0400] "PUT jira/rest/api/2/issue/10111 HTTP/1.1" 400 -
@@ -443,6 +448,7 @@ module JIRA
443
448
  def patched_url
444
449
  result = url
445
450
  return result if result.start_with?('/', 'http')
451
+
446
452
  "/#{result}"
447
453
  end
448
454
 
@@ -476,15 +482,18 @@ module JIRA
476
482
 
477
483
  def self.maybe_nested_attribute(attributes, attribute_name, nested_under = nil)
478
484
  return attributes[attribute_name] if nested_under.nil?
485
+
479
486
  if nested_under.instance_of? Array
480
487
  final = nested_under.inject(attributes) do |parent, key|
481
488
  break if parent.nil?
489
+
482
490
  parent[key]
483
491
  end
484
492
  return nil if final.nil?
493
+
485
494
  final[attribute_name]
486
495
  else
487
- return attributes[nested_under][attribute_name]
496
+ attributes[nested_under][attribute_name]
488
497
  end
489
498
  end
490
499
 
@@ -493,10 +502,10 @@ module JIRA
493
502
  end
494
503
 
495
504
  def self.url_with_query_params(url, query_params)
496
- if !query_params.empty?
497
- "#{url}?#{hash_to_query_string query_params}"
498
- else
505
+ if query_params.empty?
499
506
  url
507
+ else
508
+ "#{url}?#{hash_to_query_string query_params}"
500
509
  end
501
510
  end
502
511
 
@@ -506,20 +515,20 @@ module JIRA
506
515
 
507
516
  def self.hash_to_query_string(query_params)
508
517
  query_params.map do |k, v|
509
- CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
518
+ "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
510
519
  end.join('&')
511
520
  end
512
521
 
513
522
  def self.query_params_for_single_fetch(options)
514
- Hash[options.select do |k, _v|
523
+ options.select do |k, _v|
515
524
  QUERY_PARAMS_FOR_SINGLE_FETCH.include? k
516
- end]
525
+ end.to_h
517
526
  end
518
527
 
519
528
  def self.query_params_for_search(options)
520
- Hash[options.select do |k, _v|
529
+ options.select do |k, _v|
521
530
  QUERY_PARAMS_FOR_SEARCH.include? k
522
- end]
531
+ end.to_h
523
532
  end
524
533
  end
525
534
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JIRA
2
4
  # This is the base class for all the JIRA resource factory instances.
3
5
  class BaseFactory
@@ -36,7 +38,8 @@ module JIRA
36
38
  # The principle purpose of this class is to delegate methods to the corresponding
37
39
  # non-factory class and automatically prepend the client argument to the argument
38
40
  # list.
39
- delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql, :get_backlog_issues, :get_board_issues, :get_sprints, :get_sprint_issues, :get_projects, :get_projects_full
41
+ delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql, :get_backlog_issues,
42
+ :get_board_issues, :get_sprints, :get_sprint_issues, :get_projects, :get_projects_full
40
43
 
41
44
  # This method needs special handling as it has a default argument value
42
45
  def build(attrs = {})
data/lib/jira/client.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'forwardable'
3
5
  require 'ostruct'
@@ -34,6 +36,7 @@ module JIRA
34
36
  # :default_headers => {},
35
37
  # :use_client_cert => false,
36
38
  # :read_timeout => nil,
39
+ # :max_retries => nil,
37
40
  # :http_debug => false,
38
41
  # :shared_secret => nil,
39
42
  # :cert_path => nil,
@@ -57,43 +60,45 @@ module JIRA
57
60
  # The configuration options for this client instance
58
61
  attr_reader :options
59
62
 
60
- def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token, :authenticated?
61
-
62
- DEFINED_OPTIONS = [
63
- :site,
64
- :context_path,
65
- :signature_method,
66
- :request_token_path,
67
- :authorize_path,
68
- :access_token_path,
69
- :private_key,
70
- :private_key_file,
71
- :rest_base_path,
72
- :consumer_key,
73
- :consumer_secret,
74
- :ssl_verify_mode,
75
- :ssl_version,
76
- :use_ssl,
77
- :username,
78
- :password,
79
- :auth_type,
80
- :proxy_address,
81
- :proxy_port,
82
- :proxy_username,
83
- :proxy_password,
84
- :use_cookies,
85
- :additional_cookies,
86
- :default_headers,
87
- :use_client_cert,
88
- :read_timeout,
89
- :http_debug,
90
- :issuer,
91
- :base_url,
92
- :shared_secret,
93
- :cert_path,
94
- :key_path,
95
- :ssl_client_cert,
96
- :ssl_client_key
63
+ def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token,
64
+ :access_token, :authenticated?
65
+
66
+ DEFINED_OPTIONS = %i[
67
+ site
68
+ context_path
69
+ signature_method
70
+ request_token_path
71
+ authorize_path
72
+ access_token_path
73
+ private_key
74
+ private_key_file
75
+ rest_base_path
76
+ consumer_key
77
+ consumer_secret
78
+ ssl_verify_mode
79
+ ssl_version
80
+ use_ssl
81
+ username
82
+ password
83
+ auth_type
84
+ proxy_address
85
+ proxy_port
86
+ proxy_username
87
+ proxy_password
88
+ use_cookies
89
+ additional_cookies
90
+ default_headers
91
+ use_client_cert
92
+ read_timeout
93
+ max_retries
94
+ http_debug
95
+ issuer
96
+ base_url
97
+ shared_secret
98
+ cert_path
99
+ key_path
100
+ ssl_client_cert
101
+ ssl_client_key
97
102
  ].freeze
98
103
 
99
104
  DEFAULT_OPTIONS = {
@@ -117,11 +122,20 @@ module JIRA
117
122
  raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
118
123
 
119
124
  if options[:use_client_cert]
120
- @options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(@options[:cert_path])) if @options[:cert_path]
125
+ if @options[:cert_path]
126
+ @options[:ssl_client_cert] =
127
+ OpenSSL::X509::Certificate.new(File.read(@options[:cert_path]))
128
+ end
121
129
  @options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path]
122
130
 
123
- raise ArgumentError, 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true' unless @options[:ssl_client_cert]
124
- raise ArgumentError, 'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true' unless @options[:ssl_client_key]
131
+ unless @options[:ssl_client_cert]
132
+ raise ArgumentError,
133
+ 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true'
134
+ end
135
+ unless @options[:ssl_client_key]
136
+ raise ArgumentError,
137
+ 'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true'
138
+ end
125
139
  end
126
140
 
127
141
  case options[:auth_type]
@@ -133,7 +147,11 @@ module JIRA
133
147
  when :basic
134
148
  @request_client = HttpClient.new(@options)
135
149
  when :cookie
136
- raise ArgumentError, 'Options: :use_cookies must be true for :cookie authorization type' if @options.key?(:use_cookies) && !@options[:use_cookies]
150
+ if @options.key?(:use_cookies) && !@options[:use_cookies]
151
+ raise ArgumentError,
152
+ 'Options: :use_cookies must be true for :cookie authorization type'
153
+ end
154
+
137
155
  @options[:use_cookies] = true
138
156
  @request_client = HttpClient.new(@options)
139
157
  @request_client.make_cookie_auth_request
@@ -182,6 +200,10 @@ module JIRA
182
200
  JIRA::Resource::StatusFactory.new(self)
183
201
  end
184
202
 
203
+ def StatusCategory # :nodoc:
204
+ JIRA::Resource::StatusCategoryFactory.new(self)
205
+ end
206
+
185
207
  def Resolution # :nodoc:
186
208
  JIRA::Resource::ResolutionFactory.new(self)
187
209
  end
@@ -226,10 +248,6 @@ module JIRA
226
248
  JIRA::Resource::SprintFactory.new(self)
227
249
  end
228
250
 
229
- def SprintReport
230
- JIRA::Resource::SprintReportFactory.new(self)
231
- end
232
-
233
251
  def ServerInfo
234
252
  JIRA::Resource::ServerInfoFactory.new(self)
235
253
  end
@@ -291,7 +309,7 @@ module JIRA
291
309
 
292
310
  def post_multipart(path, file, headers = {})
293
311
  puts "post multipart: #{path} - [#{file}]" if @http_debug
294
- @request_client.request_multipart(path, file, headers)
312
+ @request_client.request_multipart(path, file, merge_default_headers(headers))
295
313
  end
296
314
 
297
315
  def put(path, body = '', headers = {})
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Whenever a collection from a has_many relationship is accessed, an instance
3
5
  # of this class is returned. This instance wraps the Array of instances in
@@ -36,7 +38,7 @@ class JIRA::HasManyProxy
36
38
  end
37
39
 
38
40
  # Delegate any missing methods to the collection that this proxy wraps
39
- def method_missing(method_name, *args, &block)
40
- collection.send(method_name, *args, &block)
41
+ def method_missing(method_name, ...)
42
+ collection.send(method_name, ...)
41
43
  end
42
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'net/https'
3
5
  require 'cgi/cookie'
@@ -21,7 +23,7 @@ module JIRA
21
23
  body = { username: @options[:username].to_s, password: @options[:password].to_s }.to_json
22
24
  @options.delete(:username)
23
25
  @options.delete(:password)
24
- make_request(:post, @options[:context_path] + '/rest/auth/1/session', body, 'Content-Type' => 'application/json')
26
+ make_request(:post, "#{@options[:context_path]}/rest/auth/1/session", body, 'Content-Type' => 'application/json')
25
27
  end
26
28
 
27
29
  def make_request(http_method, url, body = '', headers = {})
@@ -45,12 +47,13 @@ module JIRA
45
47
  end
46
48
 
47
49
  def http_conn(uri)
48
- if @options[:proxy_address]
49
- http_class = Net::HTTP::Proxy(@options[:proxy_address], @options[:proxy_port] || 80, @options[:proxy_username], @options[:proxy_password])
50
- else
51
- http_class = Net::HTTP
52
- end
53
- http_conn = http_class.new(uri.host, uri.port)
50
+ http_conn =
51
+ if @options[:proxy_address]
52
+ Net::HTTP.new(uri.host, uri.port, @options[:proxy_address], @options[:proxy_port] || 80,
53
+ @options[:proxy_username], @options[:proxy_password])
54
+ else
55
+ Net::HTTP.new(uri.host, uri.port)
56
+ end
54
57
  http_conn.use_ssl = @options[:use_ssl]
55
58
  if @options[:use_client_cert]
56
59
  http_conn.cert = @options[:ssl_client_cert]
@@ -59,6 +62,7 @@ module JIRA
59
62
  http_conn.verify_mode = @options[:ssl_verify_mode]
60
63
  http_conn.ssl_version = @options[:ssl_version] if @options[:ssl_version]
61
64
  http_conn.read_timeout = @options[:read_timeout]
65
+ http_conn.max_retries = @options[:max_retries] if @options[:max_retries]
62
66
  http_conn.ca_file = @options[:ca_file] if @options[:ca_file]
63
67
  http_conn
64
68
  end
@@ -94,13 +98,13 @@ module JIRA
94
98
 
95
99
  def store_cookies(response)
96
100
  cookies = response.get_fields('set-cookie')
97
- if cookies
98
- cookies.each do |cookie|
99
- data = CGI::Cookie.parse(cookie)
100
- data.delete('Path')
101
- @cookies.merge!(data)
102
- end
101
+ return unless cookies
102
+ cookies.each do |cookie|
103
+ data = CGI::Cookie.parse(cookie)
104
+ data.delete('Path')
105
+ @cookies.merge!(data)
103
106
  end
107
+
104
108
  end
105
109
 
106
110
  def add_cookies(request)
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
4
+ require 'active_support/core_ext/object'
5
+
2
6
  module JIRA
3
7
  class HTTPError < StandardError
4
8
  extend Forwardable
@@ -1,67 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'atlassian/jwt'
2
4
 
3
5
  module JIRA
4
6
  class JwtClient < HttpClient
5
7
  def make_request(http_method, url, body = '', headers = {})
6
8
  @http_method = http_method
9
+ jwt_header = build_jwt_header(url)
7
10
 
8
- super(http_method, url, body, headers)
11
+ super(http_method, url, body, headers.merge(jwt_header))
9
12
  end
10
13
 
11
14
  def make_multipart_request(url, data, headers = {})
12
15
  @http_method = :post
16
+ jwt_header = build_jwt_header(url)
13
17
 
14
- super(url, data, headers)
18
+ super(url, data, headers.merge(jwt_header))
15
19
  end
16
20
 
17
- class JwtUriBuilder
18
- attr_reader :request_url, :http_method, :shared_secret, :site, :issuer
19
-
20
- def initialize(request_url, http_method, shared_secret, site, issuer)
21
- @request_url = request_url
22
- @http_method = http_method
23
- @shared_secret = shared_secret
24
- @site = site
25
- @issuer = issuer
26
- end
27
-
28
- def build
29
- uri = URI.parse(request_url)
30
- new_query = URI.decode_www_form(String(uri.query)) << ['jwt', jwt_header]
31
- uri.query = URI.encode_www_form(new_query)
32
-
33
- return uri.to_s unless uri.is_a?(URI::HTTP)
34
-
35
- uri.request_uri
36
- end
21
+ private
37
22
 
38
- private
23
+ attr_reader :http_method
39
24
 
40
- def jwt_header
41
- claim = Atlassian::Jwt.build_claims \
42
- issuer,
43
- request_url,
44
- http_method.to_s,
45
- site,
46
- (Time.now - 60).to_i,
47
- (Time.now + 86_400).to_i
25
+ def build_jwt_header(url)
26
+ jwt = build_jwt(url)
48
27
 
49
- JWT.encode claim, shared_secret
50
- end
28
+ { 'Authorization' => "JWT #{jwt}" }
51
29
  end
52
30
 
53
- private
54
-
55
- attr_reader :http_method
56
-
57
- def request_path(url)
58
- JwtUriBuilder.new(
31
+ def build_jwt(url)
32
+ claim = Atlassian::Jwt.build_claims \
33
+ @options[:issuer],
59
34
  url,
60
35
  http_method.to_s,
61
- @options[:shared_secret],
62
36
  @options[:site],
63
- @options[:issuer]
64
- ).build
37
+ (Time.now - 60).to_i,
38
+ (Time.now + 86_400).to_i
39
+
40
+ JWT.encode claim, @options[:shared_secret]
65
41
  end
66
42
  end
67
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oauth'
2
4
  require 'json'
3
5
  require 'forwardable'
@@ -45,8 +47,8 @@ module JIRA
45
47
 
46
48
  # Returns the current request token if it is set, else it creates
47
49
  # and sets a new token.
48
- def request_token(options = {}, *arguments, &block)
49
- @request_token ||= get_request_token(options, *arguments, &block)
50
+ def request_token(options = {}, ...)
51
+ @request_token ||= get_request_token(options, ...)
50
52
  end
51
53
 
52
54
  # Sets the request token from a given token and secret.
@@ -71,6 +73,7 @@ module JIRA
71
73
  # JIRA::Client::UninitializedAccessTokenError exception if it is not set.
72
74
  def access_token
73
75
  raise UninitializedAccessTokenError unless @access_token
76
+
74
77
  @access_token
75
78
  end
76
79
 
@@ -82,7 +85,7 @@ module JIRA
82
85
  uri.query = if uri.query.to_s == ''
83
86
  oauth_params_str
84
87
  else
85
- uri.query + '&' + oauth_params_str
88
+ "#{uri.query}&#{oauth_params_str}"
86
89
  end
87
90
  url = uri.to_s
88
91
  end
data/lib/jira/railtie.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'jira-ruby'
2
4
  require 'rails'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oauth'
2
4
  require 'json'
3
5
  require 'net/https'
@@ -11,12 +13,14 @@ module JIRA
11
13
  def request(*args)
12
14
  response = make_request(*args)
13
15
  raise HTTPError, response unless response.is_a?(Net::HTTPSuccess)
16
+
14
17
  response
15
18
  end
16
19
 
17
20
  def request_multipart(*args)
18
21
  response = make_multipart_request(*args)
19
22
  raise HTTPError, response unless response.is_a?(Net::HTTPSuccess)
23
+
20
24
  response
21
25
  end
22
26
 
@@ -28,4 +32,4 @@ module JIRA
28
32
  raise NotImplementedError
29
33
  end
30
34
  end
31
- end
35
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cgi'
2
4
 
3
5
  module JIRA
@@ -25,7 +27,8 @@ module JIRA
25
27
  response = client.get(path_base(client) + "/board/#{board_id}/issue?#{hash_to_query_string(options)}")
26
28
  json = parse_json(response.body)
27
29
  # To get Issue objects with the same structure as for Issue.all
28
- return {} if json['issues'].size.zero?
30
+ return {} if json['issues'].empty?
31
+
29
32
  issue_ids = json['issues'].map do |issue|
30
33
  issue['id']
31
34
  end
@@ -58,22 +61,17 @@ module JIRA
58
61
  parse_json(response.body)
59
62
  end
60
63
 
61
- # def self.find(client, key, options = {})
62
- # options[:maxResults] ||= 100
63
- # fields = options[:fields].join(',') unless options[:fields].nil?
64
- # response = client.get("/rest/api/latest/search?jql=sprint=#{key}&fields=#{fields}&maxResults=#{options[:maxResults]}")
65
- # parse_json(response.body)
66
- # end
67
-
68
64
  private
69
65
 
70
66
  def self.path_base(client)
71
- client.options[:context_path] + '/rest/agile/1.0'
67
+ "#{client.options[:context_path]}/rest/agile/1.0"
72
68
  end
73
69
 
74
70
  def path_base(client)
75
71
  self.class.path_base(client)
76
72
  end
73
+
74
+ private_class_method :path_base
77
75
  end
78
76
  end
79
77
  end