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
data/jira-ruby.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $LOAD_PATH.push File.expand_path('lib', __dir__)
2
4
  require 'jira/version'
3
5
 
@@ -9,27 +11,19 @@ Gem::Specification.new do |s|
9
11
  s.summary = 'Ruby Gem for use with the Atlassian JIRA REST API'
10
12
  s.description = 'API for JIRA'
11
13
  s.licenses = ['MIT']
12
- s.metadata = { 'source_code_uri' => 'https://github.com/sumoheavy/jira-ruby' }
14
+ s.metadata = {
15
+ 'source_code_uri' => 'https://github.com/sumoheavy/jira-ruby',
16
+ 'rubygems_mfa_required' => 'true'
17
+ }
13
18
 
14
- s.required_ruby_version = '>= 1.9.3'
19
+ s.required_ruby_version = '>= 3.1.0'
15
20
 
16
21
  s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
22
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
23
  s.require_paths = ['lib']
20
24
 
21
- # Runtime Dependencies
22
- s.add_runtime_dependency 'activesupport'
23
- s.add_runtime_dependency 'atlassian-jwt'
24
- s.add_runtime_dependency 'multipart-post'
25
- s.add_runtime_dependency 'oauth', '~> 0.5', '>= 0.5.0'
26
-
27
- # Development Dependencies
28
- s.add_development_dependency 'guard', '~> 2.13', '>= 2.13.0'
29
- s.add_development_dependency 'guard-rspec', '~> 4.6', '>= 4.6.5'
30
- s.add_development_dependency 'pry', '~> 0.10', '>= 0.10.3'
31
- s.add_development_dependency 'railties'
32
- s.add_development_dependency 'rake', '~> 10.3', '>= 10.3.2'
33
- s.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
34
- s.add_development_dependency 'webmock', '~> 1.18', '>= 1.18.0'
25
+ s.add_dependency 'activesupport'
26
+ s.add_dependency 'atlassian-jwt'
27
+ s.add_dependency 'multipart-post'
28
+ s.add_dependency 'oauth', '~> 1.0'
35
29
  end
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,12 +36,14 @@ 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,
40
43
  # :key_path => nil,
41
44
  # :ssl_client_cert => nil,
42
45
  # :ssl_client_key => nil
46
+ # :ca_file => nil
43
47
  #
44
48
  # See the JIRA::Base class methods for all of the available methods on these accessor
45
49
  # objects.
@@ -56,43 +60,45 @@ module JIRA
56
60
  # The configuration options for this client instance
57
61
  attr_reader :options
58
62
 
59
- def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token, :authenticated?
60
-
61
- DEFINED_OPTIONS = [
62
- :site,
63
- :context_path,
64
- :signature_method,
65
- :request_token_path,
66
- :authorize_path,
67
- :access_token_path,
68
- :private_key,
69
- :private_key_file,
70
- :rest_base_path,
71
- :consumer_key,
72
- :consumer_secret,
73
- :ssl_verify_mode,
74
- :ssl_version,
75
- :use_ssl,
76
- :username,
77
- :password,
78
- :auth_type,
79
- :proxy_address,
80
- :proxy_port,
81
- :proxy_username,
82
- :proxy_password,
83
- :use_cookies,
84
- :additional_cookies,
85
- :default_headers,
86
- :use_client_cert,
87
- :read_timeout,
88
- :http_debug,
89
- :issuer,
90
- :base_url,
91
- :shared_secret,
92
- :cert_path,
93
- :key_path,
94
- :ssl_client_cert,
95
- :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
96
102
  ].freeze
97
103
 
98
104
  DEFAULT_OPTIONS = {
@@ -116,11 +122,20 @@ module JIRA
116
122
  raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
117
123
 
118
124
  if options[:use_client_cert]
119
- @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
120
129
  @options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path]
121
130
 
122
- raise ArgumentError, 'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true' unless @options[:ssl_client_cert]
123
- 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
124
139
  end
125
140
 
126
141
  case options[:auth_type]
@@ -132,7 +147,11 @@ module JIRA
132
147
  when :basic
133
148
  @request_client = HttpClient.new(@options)
134
149
  when :cookie
135
- 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
+
136
155
  @options[:use_cookies] = true
137
156
  @request_client = HttpClient.new(@options)
138
157
  @request_client.make_cookie_auth_request
@@ -181,6 +200,10 @@ module JIRA
181
200
  JIRA::Resource::StatusFactory.new(self)
182
201
  end
183
202
 
203
+ def StatusCategory # :nodoc:
204
+ JIRA::Resource::StatusCategoryFactory.new(self)
205
+ end
206
+
184
207
  def Resolution # :nodoc:
185
208
  JIRA::Resource::ResolutionFactory.new(self)
186
209
  end
@@ -225,10 +248,6 @@ module JIRA
225
248
  JIRA::Resource::SprintFactory.new(self)
226
249
  end
227
250
 
228
- def SprintReport
229
- JIRA::Resource::SprintReportFactory.new(self)
230
- end
231
-
232
251
  def ServerInfo
233
252
  JIRA::Resource::ServerInfoFactory.new(self)
234
253
  end
@@ -290,7 +309,7 @@ module JIRA
290
309
 
291
310
  def post_multipart(path, file, headers = {})
292
311
  puts "post multipart: #{path} - [#{file}]" if @http_debug
293
- @request_client.request_multipart(path, file, headers)
312
+ @request_client.request_multipart(path, file, merge_default_headers(headers))
294
313
  end
295
314
 
296
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,8 @@ 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]
66
+ http_conn.ca_file = @options[:ca_file] if @options[:ca_file]
62
67
  http_conn
63
68
  end
64
69
 
@@ -93,13 +98,13 @@ module JIRA
93
98
 
94
99
  def store_cookies(response)
95
100
  cookies = response.get_fields('set-cookie')
96
- if cookies
97
- cookies.each do |cookie|
98
- data = CGI::Cookie.parse(cookie)
99
- data.delete('Path')
100
- @cookies.merge!(data)
101
- end
101
+ return unless cookies
102
+ cookies.each do |cookie|
103
+ data = CGI::Cookie.parse(cookie)
104
+ data.delete('Path')
105
+ @cookies.merge!(data)
102
106
  end
107
+
103
108
  end
104
109
 
105
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