jira-ruby 1.1.3 → 2.3.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 (108) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +5 -3
  4. data/Gemfile +7 -1
  5. data/Guardfile +1 -1
  6. data/README.md +452 -0
  7. data/Rakefile +6 -7
  8. data/example.rb +38 -6
  9. data/http-basic-example.rb +14 -13
  10. data/jira-ruby.gemspec +13 -13
  11. data/lib/jira/base.rb +58 -53
  12. data/lib/jira/base_factory.rb +3 -6
  13. data/lib/jira/client.rb +127 -30
  14. data/lib/jira/has_many_proxy.rb +0 -1
  15. data/lib/jira/http_client.rb +54 -16
  16. data/lib/jira/http_error.rb +3 -5
  17. data/lib/jira/jwt_client.rb +67 -0
  18. data/lib/jira/oauth_client.rb +47 -17
  19. data/lib/jira/request_client.rb +16 -5
  20. data/lib/jira/resource/agile.rb +34 -9
  21. data/lib/jira/resource/applinks.rb +5 -8
  22. data/lib/jira/resource/attachment.rb +41 -3
  23. data/lib/jira/resource/board.rb +91 -0
  24. data/lib/jira/resource/board_configuration.rb +9 -0
  25. data/lib/jira/resource/comment.rb +0 -2
  26. data/lib/jira/resource/component.rb +1 -3
  27. data/lib/jira/resource/createmeta.rb +14 -22
  28. data/lib/jira/resource/field.rb +22 -22
  29. data/lib/jira/resource/filter.rb +2 -2
  30. data/lib/jira/resource/issue.rb +69 -38
  31. data/lib/jira/resource/issue_picker_suggestions.rb +24 -0
  32. data/lib/jira/resource/issue_picker_suggestions_issue.rb +10 -0
  33. data/lib/jira/resource/issuelink.rb +3 -5
  34. data/lib/jira/resource/issuelinktype.rb +0 -1
  35. data/lib/jira/resource/issuetype.rb +1 -3
  36. data/lib/jira/resource/priority.rb +1 -3
  37. data/lib/jira/resource/project.rb +8 -8
  38. data/lib/jira/resource/rapidview.rb +28 -7
  39. data/lib/jira/resource/remotelink.rb +1 -4
  40. data/lib/jira/resource/resolution.rb +2 -4
  41. data/lib/jira/resource/serverinfo.rb +1 -2
  42. data/lib/jira/resource/sprint.rb +86 -17
  43. data/lib/jira/resource/sprint_report.rb +8 -0
  44. data/lib/jira/resource/status.rb +1 -3
  45. data/lib/jira/resource/suggested_issue.rb +9 -0
  46. data/lib/jira/resource/transition.rb +2 -6
  47. data/lib/jira/resource/user.rb +12 -2
  48. data/lib/jira/resource/version.rb +1 -3
  49. data/lib/jira/resource/watcher.rb +35 -0
  50. data/lib/jira/resource/webhook.rb +3 -6
  51. data/lib/jira/resource/worklog.rb +3 -5
  52. data/lib/jira/version.rb +1 -1
  53. data/lib/jira-ruby.rb +12 -2
  54. data/lib/tasks/generate.rake +4 -4
  55. data/spec/integration/attachment_spec.rb +17 -8
  56. data/spec/integration/comment_spec.rb +31 -34
  57. data/spec/integration/component_spec.rb +21 -24
  58. data/spec/integration/field_spec.rb +15 -18
  59. data/spec/integration/issue_spec.rb +45 -46
  60. data/spec/integration/issuelinktype_spec.rb +8 -11
  61. data/spec/integration/issuetype_spec.rb +5 -7
  62. data/spec/integration/priority_spec.rb +5 -8
  63. data/spec/integration/project_spec.rb +13 -20
  64. data/spec/integration/rapidview_spec.rb +17 -10
  65. data/spec/integration/resolution_spec.rb +7 -10
  66. data/spec/integration/status_spec.rb +5 -8
  67. data/spec/integration/transition_spec.rb +17 -20
  68. data/spec/integration/user_spec.rb +24 -8
  69. data/spec/integration/version_spec.rb +21 -25
  70. data/spec/integration/watcher_spec.rb +62 -0
  71. data/spec/integration/webhook.rb +8 -17
  72. data/spec/integration/worklog_spec.rb +30 -34
  73. data/spec/jira/base_factory_spec.rb +11 -12
  74. data/spec/jira/base_spec.rb +216 -229
  75. data/spec/jira/client_spec.rb +227 -159
  76. data/spec/jira/has_many_proxy_spec.rb +11 -12
  77. data/spec/jira/http_client_spec.rb +254 -31
  78. data/spec/jira/http_error_spec.rb +7 -9
  79. data/spec/jira/jwt_uri_builder_spec.rb +59 -0
  80. data/spec/jira/oauth_client_spec.rb +110 -39
  81. data/spec/jira/request_client_spec.rb +36 -9
  82. data/spec/jira/resource/agile_spec.rb +135 -0
  83. data/spec/jira/resource/attachment_spec.rb +127 -9
  84. data/spec/jira/resource/board_spec.rb +224 -0
  85. data/spec/jira/resource/createmeta_spec.rb +39 -34
  86. data/spec/jira/resource/field_spec.rb +42 -48
  87. data/spec/jira/resource/filter_spec.rb +40 -40
  88. data/spec/jira/resource/issue_picker_suggestions_spec.rb +79 -0
  89. data/spec/jira/resource/issue_spec.rb +88 -85
  90. data/spec/jira/resource/issuelink_spec.rb +1 -1
  91. data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +18 -0
  92. data/spec/jira/resource/project_factory_spec.rb +2 -4
  93. data/spec/jira/resource/project_spec.rb +86 -33
  94. data/spec/jira/resource/sprint_spec.rb +90 -0
  95. data/spec/jira/resource/user_factory_spec.rb +6 -8
  96. data/spec/jira/resource/worklog_spec.rb +9 -11
  97. data/spec/mock_responses/board/1.json +33 -0
  98. data/spec/mock_responses/board/1_issues.json +62 -0
  99. data/spec/mock_responses/empty_issues.json +8 -0
  100. data/spec/mock_responses/issue/10002/watchers.json +13 -0
  101. data/spec/mock_responses/issue.json +1 -1
  102. data/spec/mock_responses/sprint/1_issues.json +125 -0
  103. data/spec/spec_helper.rb +8 -9
  104. data/spec/support/clients_helper.rb +4 -4
  105. data/spec/support/shared_examples/integration.rb +60 -77
  106. metadata +115 -54
  107. data/README.rdoc +0 -329
  108. /data/spec/mock_responses/{attachment → issue/10002/attachments}/10000.json +0 -0
data/jira-ruby.gemspec CHANGED
@@ -1,35 +1,35 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path('../lib', __FILE__)
1
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
3
2
  require 'jira/version'
4
3
 
5
4
  Gem::Specification.new do |s|
6
5
  s.name = 'jira-ruby'
7
6
  s.version = JIRA::VERSION
8
- s.authors = ['SUMO Heavy Industries']
7
+ s.authors = ['SUMO Heavy Industries', 'test IO']
9
8
  s.homepage = 'http://www.sumoheavy.com'
10
- s.summary = %q{Ruby Gem for use with the Atlassian JIRA REST API}
11
- s.description = %q{API for JIRA}
9
+ s.summary = 'Ruby Gem for use with the Atlassian JIRA REST API'
10
+ s.description = 'API for JIRA'
12
11
  s.licenses = ['MIT']
12
+ s.metadata = { 'source_code_uri' => 'https://github.com/sumoheavy/jira-ruby' }
13
13
 
14
14
  s.required_ruby_version = '>= 1.9.3'
15
15
 
16
- s.rubyforge_project = 'jira-ruby'
17
-
18
16
  s.files = `git ls-files`.split("\n")
19
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
21
19
  s.require_paths = ['lib']
22
20
 
23
21
  # Runtime Dependencies
24
- s.add_runtime_dependency 'oauth', '~> 0.5', '>= 0.5.0'
25
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
26
 
27
27
  # Development Dependencies
28
- s.add_development_dependency 'railties'
29
- s.add_development_dependency 'webmock', '~> 1.18', '>= 1.18.0'
30
- s.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
31
- s.add_development_dependency 'rake', '~> 10.3', '>= 10.3.2'
32
28
  s.add_development_dependency 'guard', '~> 2.13', '>= 2.13.0'
33
29
  s.add_development_dependency 'guard-rspec', '~> 4.6', '>= 4.6.5'
34
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'
35
35
  end
data/lib/jira/base.rb CHANGED
@@ -3,7 +3,6 @@ require 'active_support/inflector'
3
3
  require 'set'
4
4
 
5
5
  module JIRA
6
-
7
6
  # This class provides the basic object <-> REST mapping for all JIRA::Resource subclasses,
8
7
  # i.e. the Create, Retrieve, Update, Delete lifecycle methods.
9
8
  #
@@ -51,8 +50,8 @@ module JIRA
51
50
  # new_comment = issue.comments.build
52
51
  #
53
52
  class Base
54
- QUERY_PARAMS_FOR_SINGLE_FETCH = Set.new [:expand, :fields]
55
- QUERY_PARAMS_FOR_SEARCH = Set.new [:expand, :fields, :startAt, :maxResults]
53
+ QUERY_PARAMS_FOR_SINGLE_FETCH = Set.new %i[expand fields]
54
+ QUERY_PARAMS_FOR_SEARCH = Set.new %i[expand fields startAt maxResults]
56
55
 
57
56
  # A reference to the JIRA::Client used to initialize this resource.
58
57
  attr_reader :client
@@ -67,8 +66,8 @@ module JIRA
67
66
  # representation of the JSON returned from the JIRA API
68
67
  attr_accessor :attrs
69
68
 
70
- alias :expanded? :expanded
71
- alias :deleted? :deleted
69
+ alias expanded? expanded
70
+ alias deleted? deleted
72
71
 
73
72
  def initialize(client, options = {})
74
73
  @client = client
@@ -80,12 +79,12 @@ module JIRA
80
79
  # each of them must be passed in to the initializer.
81
80
  self.class.belongs_to_relationships.each do |relation|
82
81
  if options[relation]
83
- instance_variable_set("@#{relation.to_s}", options[relation])
84
- instance_variable_set("@#{relation.to_s}_id", options[relation].key_value)
82
+ instance_variable_set("@#{relation}", options[relation])
83
+ instance_variable_set("@#{relation}_id", options[relation].key_value)
85
84
  elsif options["#{relation}_id".to_sym]
86
- instance_variable_set("@#{relation.to_s}_id", options["#{relation}_id".to_sym])
85
+ instance_variable_set("@#{relation}_id", options["#{relation}_id".to_sym])
87
86
  else
88
- raise ArgumentError.new("Required option #{relation.inspect} missing") unless options[relation]
87
+ raise ArgumentError, "Required option #{relation.inspect} missing" unless options[relation]
89
88
  end
90
89
  end
91
90
  end
@@ -95,17 +94,15 @@ module JIRA
95
94
  def self.all(client, options = {})
96
95
  response = client.get(collection_path(client))
97
96
  json = parse_json(response.body)
98
- if collection_attributes_are_nested
99
- json = json[endpoint_name.pluralize]
100
- end
97
+ json = json[endpoint_name.pluralize] if collection_attributes_are_nested
101
98
  json.map do |attrs|
102
- self.new(client, {:attrs => attrs}.merge(options))
99
+ new(client, { attrs: attrs }.merge(options))
103
100
  end
104
101
  end
105
102
 
106
103
  # Finds and retrieves a resource with the given ID.
107
104
  def self.find(client, key, options = {})
108
- instance = self.new(client, options)
105
+ instance = new(client, options)
109
106
  instance.attrs[key_attribute.to_s] = key
110
107
  instance.fetch(false, query_params_for_single_fetch(options))
111
108
  instance
@@ -114,7 +111,7 @@ module JIRA
114
111
  # Builds a new instance of the resource with the given attributes.
115
112
  # These attributes will be posted to the JIRA Api if save is called.
116
113
  def self.build(client, attrs)
117
- self.new(client, :attrs => attrs)
114
+ new(client, attrs: attrs)
118
115
  end
119
116
 
120
117
  # Returns the name of this resource for use in URL components.
@@ -122,7 +119,7 @@ module JIRA
122
119
  # JIRA::Resource::Issue.endpoint_name
123
120
  # # => issue
124
121
  def self.endpoint_name
125
- self.name.split('::').last.downcase
122
+ name.split('::').last.downcase
126
123
  end
127
124
 
128
125
  # Returns the full path for a collection of this resource.
@@ -130,7 +127,7 @@ module JIRA
130
127
  # JIRA::Resource::Issue.collection_path
131
128
  # # => /jira/rest/api/2/issue
132
129
  def self.collection_path(client, prefix = '/')
133
- client.options[:rest_base_path] + prefix + self.endpoint_name
130
+ client.options[:rest_base_path] + prefix + endpoint_name
134
131
  end
135
132
 
136
133
  # Returns the singular path for the resource with the given key.
@@ -200,7 +197,7 @@ module JIRA
200
197
  define_method(resource) do
201
198
  attribute = maybe_nested_attribute(attribute_key, options[:nested_under])
202
199
  return nil unless attribute
203
- child_class.new(client, :attrs => attribute)
200
+ child_class.new(client, attrs: attribute)
204
201
  end
205
202
  end
206
203
 
@@ -248,12 +245,12 @@ module JIRA
248
245
  def self.has_many(collection, options = {})
249
246
  attribute_key = options[:attribute_key] || collection.to_s
250
247
  child_class = options[:class] || ('JIRA::Resource::' + collection.to_s.classify).constantize
251
- self_class_basename = self.name.split('::').last.downcase.to_sym
248
+ self_class_basename = name.split('::').last.downcase.to_sym
252
249
  define_method(collection) do
253
- child_class_options = {self_class_basename => self}
250
+ child_class_options = { self_class_basename => self }
254
251
  attribute = maybe_nested_attribute(attribute_key, options[:nested_under]) || []
255
252
  collection = attribute.map do |child_attributes|
256
- child_class.new(client, child_class_options.merge(:attrs => child_attributes))
253
+ child_class.new(client, child_class_options.merge(attrs: child_attributes))
257
254
  end
258
255
  HasManyProxy.new(self, child_class, collection)
259
256
  end
@@ -290,8 +287,8 @@ module JIRA
290
287
  # Checks if method_name is set in the attributes hash
291
288
  # and returns true when found, otherwise proxies the
292
289
  # call to the superclass.
293
- def respond_to?(method_name, include_all=false)
294
- if attrs.keys.include? method_name.to_s
290
+ def respond_to?(method_name, _include_all = false)
291
+ if attrs.key?(method_name.to_s)
295
292
  true
296
293
  else
297
294
  super(method_name)
@@ -301,8 +298,8 @@ module JIRA
301
298
  # Overrides method_missing to check the attribute hash
302
299
  # for resources matching method_name and proxies the call
303
300
  # to the superclass if no match is found.
304
- def method_missing(method_name, *args, &block)
305
- if attrs.keys.include? method_name.to_s
301
+ def method_missing(method_name, *_args)
302
+ if attrs.key?(method_name.to_s)
306
303
  attrs[method_name.to_s]
307
304
  else
308
305
  super(method_name)
@@ -315,7 +312,7 @@ module JIRA
315
312
  @attrs[self.class.key_attribute.to_s]
316
313
  end
317
314
 
318
- def collection_path(prefix = "/")
315
+ def collection_path(prefix = '/')
319
316
  # Just proxy this to the class method
320
317
  self.class.collection_path(client, prefix)
321
318
  end
@@ -325,9 +322,7 @@ module JIRA
325
322
  # issue it returns '/issue'
326
323
  def path_component
327
324
  path_component = "/#{self.class.endpoint_name}"
328
- if key_value
329
- path_component += '/' + key_value
330
- end
325
+ path_component += '/' + key_value if key_value
331
326
  path_component
332
327
  end
333
328
 
@@ -346,9 +341,10 @@ module JIRA
346
341
  #
347
342
  # Accepts an attributes hash of the values to be saved. Will throw a
348
343
  # JIRA::HTTPError if the request fails (response is not HTTP 2xx).
349
- def save!(attrs)
344
+ def save!(attrs, path = nil)
345
+ path ||= new_record? ? url : patched_url
350
346
  http_method = new_record? ? :post : :put
351
- response = client.send(http_method, new_record? ? url : patched_url, attrs.to_json)
347
+ response = client.send(http_method, path, attrs.to_json)
352
348
  set_attrs(attrs, false)
353
349
  set_attrs_from_response(response)
354
350
  @expanded = false
@@ -360,21 +356,20 @@ module JIRA
360
356
  #
361
357
  # Accepts an attributes hash of the values to be saved. Will return false
362
358
  # if the request fails.
363
- def save(attrs)
359
+ def save(attrs, path = url)
364
360
  begin
365
- save_status = save!(attrs)
361
+ save_status = save!(attrs, path)
366
362
  rescue JIRA::HTTPError => exception
367
- puts ">>>>>>>>> Exception response: #{exception.response.body}"
368
363
  begin
369
364
  set_attrs_from_response(exception.response) # Merge error status generated by JIRA REST API
370
365
  rescue JSON::ParserError => parse_exception
371
- set_attrs("exception" => {
372
- "class" => exception.response.class.name,
373
- "code" => exception.response.code,
374
- "message" => exception.response.message
375
- }
376
- )
366
+ set_attrs('exception' => {
367
+ 'class' => exception.response.class.name,
368
+ 'code' => exception.response.code,
369
+ 'message' => exception.response.message
370
+ })
377
371
  end
372
+ # raise exception
378
373
  save_status = false
379
374
  end
380
375
  save_status
@@ -383,7 +378,7 @@ module JIRA
383
378
  # Sets the attributes hash from a HTTPResponse object from JIRA if it is
384
379
  # not nil or is not a json response.
385
380
  def set_attrs_from_response(response)
386
- unless response.body.nil? or response.body.length < 2
381
+ unless response.body.nil? || (response.body.length < 2)
387
382
  json = self.class.parse_json(response.body)
388
383
  set_attrs(json)
389
384
  end
@@ -393,7 +388,7 @@ module JIRA
393
388
  # hash values will be clobbered by the new hash, otherwise the hash will
394
389
  # be deeply merged into attrs. The target paramater is for internal use only
395
390
  # and should not be used.
396
- def set_attrs(hash, clobber=true, target = nil)
391
+ def set_attrs(hash, clobber = true, target = nil)
397
392
  target ||= @attrs
398
393
  if clobber
399
394
  target.merge!(hash)
@@ -424,12 +419,12 @@ module JIRA
424
419
  prefix = '/'
425
420
  unless self.class.belongs_to_relationships.empty?
426
421
  prefix = self.class.belongs_to_relationships.inject(prefix) do |prefix_so_far, relationship|
427
- prefix_so_far.to_s + relationship.to_s + "/" + self.send("#{relationship.to_s}_id").to_s + '/'
422
+ prefix_so_far.to_s + relationship.to_s + '/' + send("#{relationship}_id").to_s + '/'
428
423
  end
429
424
  end
430
425
  if @attrs['self']
431
- the_url = @attrs['self'].sub(@client.options[:site],'')
432
- the_url = "/#{the_url}" if (the_url =~ /^\//).nil?
426
+ the_url = @attrs['self']
427
+ the_url = the_url.sub(@client.options[:site].chomp('/'), '') if @client.options[:site]
433
428
  the_url
434
429
  elsif key_value
435
430
  self.class.singular_path(client, key_value.to_s, prefix)
@@ -447,7 +442,7 @@ module JIRA
447
442
  # [07/Jun/2015:15:17:18 +0400] "PUT /jira/rest/api/2/issue/10111 HTTP/1.1" 204 -
448
443
  def patched_url
449
444
  result = url
450
- return result if result.start_with?('/')
445
+ return result if result.start_with?('/', 'http')
451
446
  "/#{result}"
452
447
  end
453
448
 
@@ -494,25 +489,35 @@ module JIRA
494
489
  end
495
490
 
496
491
  def url_with_query_params(url, query_params)
497
- uri = URI.parse(url)
498
- uri.query = uri.query.nil? ? "#{hash_to_query_string query_params}" : "#{uri.query}&#{hash_to_query_string query_params}" unless query_params.empty?
499
- uri.to_s
492
+ self.class.url_with_query_params(url, query_params)
493
+ end
494
+
495
+ def self.url_with_query_params(url, query_params)
496
+ if !query_params.empty?
497
+ "#{url}?#{hash_to_query_string query_params}"
498
+ else
499
+ url
500
+ end
500
501
  end
501
502
 
502
503
  def hash_to_query_string(query_params)
503
- query_params.map do |k,v|
504
- CGI.escape(k.to_s) + "=" + CGI.escape(v.to_s)
504
+ self.class.hash_to_query_string(query_params)
505
+ end
506
+
507
+ def self.hash_to_query_string(query_params)
508
+ query_params.map do |k, v|
509
+ CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
505
510
  end.join('&')
506
511
  end
507
512
 
508
513
  def self.query_params_for_single_fetch(options)
509
- Hash[options.select do |k,v|
514
+ Hash[options.select do |k, _v|
510
515
  QUERY_PARAMS_FOR_SINGLE_FETCH.include? k
511
516
  end]
512
517
  end
513
518
 
514
519
  def self.query_params_for_search(options)
515
- Hash[options.select do |k,v|
520
+ Hash[options.select do |k, _v|
516
521
  QUERY_PARAMS_FOR_SEARCH.include? k
517
522
  end]
518
523
  end
@@ -1,15 +1,13 @@
1
1
  module JIRA
2
-
3
2
  # This is the base class for all the JIRA resource factory instances.
4
3
  class BaseFactory
5
-
6
4
  attr_reader :client
7
5
 
8
6
  def initialize(client)
9
7
  @client = client
10
8
  end
11
9
 
12
- # Return the name of the class which this factory generates, i.e.
10
+ # Return the name of the class which this factory generates, i.e.
13
11
  # JIRA::Resource::FooFactory creates JIRA::Resource::Foo instances.
14
12
  def target_class
15
13
  # Need to do a little bit of work here as Module.const_get doesn't work
@@ -38,12 +36,11 @@ module JIRA
38
36
  # The principle purpose of this class is to delegate methods to the corresponding
39
37
  # non-factory class and automatically prepend the client argument to the argument
40
38
  # list.
41
- delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql, :get_backlog_issues, :get_sprints, :get_sprint_issues
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
42
40
 
43
41
  # This method needs special handling as it has a default argument value
44
- def build(attrs={})
42
+ def build(attrs = {})
45
43
  target_class.build(@client, attrs)
46
44
  end
47
-
48
45
  end
49
46
  end
data/lib/jira/client.rb CHANGED
@@ -3,11 +3,10 @@ require 'forwardable'
3
3
  require 'ostruct'
4
4
 
5
5
  module JIRA
6
-
7
6
  # This class is the main access point for all JIRA::Resource instances.
8
7
  #
9
8
  # The client must be initialized with an options hash containing
10
- # configuration options. The available options are:
9
+ # configuration options. The available options are:
11
10
  #
12
11
  # :site => 'http://localhost:2990',
13
12
  # :context_path => '/jira',
@@ -15,23 +14,38 @@ module JIRA
15
14
  # :request_token_path => "/plugins/servlet/oauth/request-token",
16
15
  # :authorize_path => "/plugins/servlet/oauth/authorize",
17
16
  # :access_token_path => "/plugins/servlet/oauth/access-token",
17
+ # :private_key => nil,
18
18
  # :private_key_file => "rsakey.pem",
19
19
  # :rest_base_path => "/rest/api/2",
20
20
  # :consumer_key => nil,
21
21
  # :consumer_secret => nil,
22
22
  # :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
23
+ # :ssl_version => nil,
23
24
  # :use_ssl => true,
24
25
  # :username => nil,
25
26
  # :password => nil,
26
- # :auth_type => :oauth
27
- # :proxy_address => nil
28
- # :proxy_port => nil
27
+ # :auth_type => :oauth,
28
+ # :proxy_address => nil,
29
+ # :proxy_port => nil,
30
+ # :proxy_username => nil,
31
+ # :proxy_password => nil,
32
+ # :use_cookies => nil,
33
+ # :additional_cookies => nil,
34
+ # :default_headers => {},
35
+ # :use_client_cert => false,
36
+ # :read_timeout => nil,
37
+ # :http_debug => false,
38
+ # :shared_secret => nil,
39
+ # :cert_path => nil,
40
+ # :key_path => nil,
41
+ # :ssl_client_cert => nil,
42
+ # :ssl_client_key => nil
43
+ # :ca_file => nil
29
44
  #
30
45
  # See the JIRA::Base class methods for all of the available methods on these accessor
31
46
  # objects.
32
47
 
33
48
  class Client
34
-
35
49
  extend Forwardable
36
50
 
37
51
  # The OAuth::Consumer instance returned by the OauthClient
@@ -43,27 +57,79 @@ module JIRA
43
57
  # The configuration options for this client instance
44
58
  attr_reader :options
45
59
 
46
- def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token, :access_token
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
97
+ ].freeze
47
98
 
48
99
  DEFAULT_OPTIONS = {
49
- :site => 'http://localhost:2990',
50
- :context_path => '/jira',
51
- :rest_base_path => "/rest/api/2",
52
- :ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER,
53
- :use_ssl => true,
54
- :auth_type => :oauth,
55
- :http_debug => false
56
- }
57
-
58
- def initialize(options={})
100
+ site: 'http://localhost:2990',
101
+ context_path: '/jira',
102
+ rest_base_path: '/rest/api/2',
103
+ ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER,
104
+ use_ssl: true,
105
+ use_client_cert: false,
106
+ auth_type: :oauth,
107
+ http_debug: false,
108
+ default_headers: {}
109
+ }.freeze
110
+
111
+ def initialize(options = {})
59
112
  options = DEFAULT_OPTIONS.merge(options)
60
113
  @options = options
61
114
  @options[:rest_base_path] = @options[:context_path] + @options[:rest_base_path]
62
115
 
116
+ unknown_options = options.keys.reject { |o| DEFINED_OPTIONS.include?(o) }
117
+ raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
118
+
119
+ if options[:use_client_cert]
120
+ @options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(@options[:cert_path])) if @options[:cert_path]
121
+ @options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path]
122
+
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]
125
+ end
126
+
63
127
  case options[:auth_type]
64
- when :oauth
128
+ when :oauth, :oauth_2legged
65
129
  @request_client = OauthClient.new(@options)
66
130
  @consumer = @request_client.consumer
131
+ when :jwt
132
+ @request_client = JwtClient.new(@options)
67
133
  when :basic
68
134
  @request_client = HttpClient.new(@options)
69
135
  when :cookie
@@ -71,8 +137,10 @@ module JIRA
71
137
  @options[:use_cookies] = true
72
138
  @request_client = HttpClient.new(@options)
73
139
  @request_client.make_cookie_auth_request
140
+ @options.delete(:username)
141
+ @options.delete(:password)
74
142
  else
75
- raise ArgumentError, 'Options: ":auth_type" must be ":oauth", ":cookie" or ":basic"'
143
+ raise ArgumentError, 'Options: ":auth_type" must be ":oauth",":oauth_2legged", ":cookie" or ":basic"'
76
144
  end
77
145
 
78
146
  @http_debug = @options[:http_debug]
@@ -142,10 +210,26 @@ module JIRA
142
210
  JIRA::Resource::FieldFactory.new(self)
143
211
  end
144
212
 
213
+ def Board
214
+ JIRA::Resource::BoardFactory.new(self)
215
+ end
216
+
217
+ def BoardConfiguration
218
+ JIRA::Resource::BoardConfigurationFactory.new(self)
219
+ end
220
+
145
221
  def RapidView
146
222
  JIRA::Resource::RapidViewFactory.new(self)
147
223
  end
148
224
 
225
+ def Sprint
226
+ JIRA::Resource::SprintFactory.new(self)
227
+ end
228
+
229
+ def SprintReport
230
+ JIRA::Resource::SprintReportFactory.new(self)
231
+ end
232
+
149
233
  def ServerInfo
150
234
  JIRA::Resource::ServerInfoFactory.new(self)
151
235
  end
@@ -158,6 +242,10 @@ module JIRA
158
242
  JIRA::Resource::ApplicationLinkFactory.new(self)
159
243
  end
160
244
 
245
+ def Watcher
246
+ JIRA::Resource::WatcherFactory.new(self)
247
+ end
248
+
161
249
  def Webhook
162
250
  JIRA::Resource::WebhookFactory.new(self)
163
251
  end
@@ -170,12 +258,12 @@ module JIRA
170
258
  JIRA::Resource::IssuelinktypeFactory.new(self)
171
259
  end
172
260
 
173
- def Remotelink
174
- JIRA::Resource::RemotelinkFactory.new(self)
261
+ def IssuePickerSuggestions
262
+ JIRA::Resource::IssuePickerSuggestionsFactory.new(self)
175
263
  end
176
264
 
177
- def Sprint
178
- JIRA::Resource::SprintFactory.new(self)
265
+ def Remotelink
266
+ JIRA::Resource::RemotelinkFactory.new(self)
179
267
  end
180
268
 
181
269
  def Agile
@@ -197,27 +285,36 @@ module JIRA
197
285
 
198
286
  # HTTP methods with a body
199
287
  def post(path, body = '', headers = {})
200
- headers = {'Content-Type' => 'application/json'}.merge(headers)
288
+ headers = { 'Content-Type' => 'application/json' }.merge(headers)
201
289
  request(:post, path, body, merge_default_headers(headers))
202
290
  end
203
291
 
292
+ def post_multipart(path, file, headers = {})
293
+ puts "post multipart: #{path} - [#{file}]" if @http_debug
294
+ @request_client.request_multipart(path, file, headers)
295
+ end
296
+
204
297
  def put(path, body = '', headers = {})
205
- headers = {'Content-Type' => 'application/json'}.merge(headers)
298
+ headers = { 'Content-Type' => 'application/json' }.merge(headers)
206
299
  request(:put, path, body, merge_default_headers(headers))
207
300
  end
208
301
 
209
302
  # Sends the specified HTTP request to the REST API through the
210
303
  # appropriate method (oauth, basic).
211
- def request(http_method, path, body = '', headers={})
304
+ def request(http_method, path, body = '', headers = {})
212
305
  puts "#{http_method}: #{path} - [#{body}]" if @http_debug
213
306
  @request_client.request(http_method, path, body, headers)
214
307
  end
215
308
 
216
- protected
309
+ # Stops sensitive client information from being displayed in logs
310
+ def inspect
311
+ "#<JIRA::Client:#{object_id}>"
312
+ end
217
313
 
218
- def merge_default_headers(headers)
219
- {'Accept' => 'application/json'}.merge(headers)
220
- end
314
+ protected
221
315
 
316
+ def merge_default_headers(headers)
317
+ { 'Accept' => 'application/json' }.merge(@options[:default_headers]).merge(headers)
318
+ end
222
319
  end
223
320
  end
@@ -7,7 +7,6 @@
7
7
  # In practice, instances of this class behave exactly like an Array.
8
8
  #
9
9
  class JIRA::HasManyProxy
10
-
11
10
  attr_reader :target_class, :parent
12
11
  attr_accessor :collection
13
12