jira-ruby 2.3.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 +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