jira-ruby 1.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +3 -0
- data/.travis.yml +5 -3
- data/Gemfile +7 -1
- data/Guardfile +1 -1
- data/README.md +452 -0
- data/Rakefile +6 -7
- data/example.rb +23 -1
- data/http-basic-example.rb +13 -12
- data/jira-ruby.gemspec +13 -13
- data/lib/jira/base.rb +53 -52
- data/lib/jira/base_factory.rb +3 -6
- data/lib/jira/client.rb +127 -30
- data/lib/jira/has_many_proxy.rb +0 -1
- data/lib/jira/http_client.rb +54 -16
- data/lib/jira/http_error.rb +3 -5
- data/lib/jira/jwt_client.rb +67 -0
- data/lib/jira/oauth_client.rb +47 -17
- data/lib/jira/request_client.rb +16 -5
- data/lib/jira/resource/agile.rb +34 -9
- data/lib/jira/resource/applinks.rb +5 -8
- data/lib/jira/resource/attachment.rb +41 -3
- data/lib/jira/resource/board.rb +91 -0
- data/lib/jira/resource/board_configuration.rb +9 -0
- data/lib/jira/resource/comment.rb +0 -2
- data/lib/jira/resource/component.rb +1 -3
- data/lib/jira/resource/createmeta.rb +12 -14
- data/lib/jira/resource/field.rb +22 -22
- data/lib/jira/resource/filter.rb +2 -2
- data/lib/jira/resource/issue.rb +69 -38
- data/lib/jira/resource/issue_picker_suggestions.rb +24 -0
- data/lib/jira/resource/issue_picker_suggestions_issue.rb +10 -0
- data/lib/jira/resource/issuelink.rb +3 -5
- data/lib/jira/resource/issuelinktype.rb +0 -1
- data/lib/jira/resource/issuetype.rb +1 -3
- data/lib/jira/resource/priority.rb +1 -3
- data/lib/jira/resource/project.rb +5 -7
- data/lib/jira/resource/rapidview.rb +28 -7
- data/lib/jira/resource/remotelink.rb +1 -4
- data/lib/jira/resource/resolution.rb +2 -4
- data/lib/jira/resource/serverinfo.rb +1 -2
- data/lib/jira/resource/sprint.rb +86 -17
- data/lib/jira/resource/sprint_report.rb +8 -0
- data/lib/jira/resource/status.rb +1 -3
- data/lib/jira/resource/suggested_issue.rb +9 -0
- data/lib/jira/resource/transition.rb +2 -6
- data/lib/jira/resource/user.rb +12 -2
- data/lib/jira/resource/version.rb +1 -3
- data/lib/jira/resource/watcher.rb +35 -0
- data/lib/jira/resource/webhook.rb +3 -6
- data/lib/jira/resource/worklog.rb +3 -5
- data/lib/jira/version.rb +1 -1
- data/lib/jira-ruby.rb +12 -2
- data/lib/tasks/generate.rake +4 -4
- data/spec/integration/attachment_spec.rb +17 -8
- data/spec/integration/comment_spec.rb +31 -34
- data/spec/integration/component_spec.rb +21 -24
- data/spec/integration/field_spec.rb +15 -18
- data/spec/integration/issue_spec.rb +45 -46
- data/spec/integration/issuelinktype_spec.rb +8 -11
- data/spec/integration/issuetype_spec.rb +5 -7
- data/spec/integration/priority_spec.rb +5 -8
- data/spec/integration/project_spec.rb +13 -20
- data/spec/integration/rapidview_spec.rb +17 -10
- data/spec/integration/resolution_spec.rb +7 -10
- data/spec/integration/status_spec.rb +5 -8
- data/spec/integration/transition_spec.rb +17 -20
- data/spec/integration/user_spec.rb +24 -8
- data/spec/integration/version_spec.rb +21 -25
- data/spec/integration/watcher_spec.rb +62 -0
- data/spec/integration/webhook.rb +8 -17
- data/spec/integration/worklog_spec.rb +30 -34
- data/spec/jira/base_factory_spec.rb +11 -12
- data/spec/jira/base_spec.rb +216 -229
- data/spec/jira/client_spec.rb +227 -159
- data/spec/jira/has_many_proxy_spec.rb +11 -12
- data/spec/jira/http_client_spec.rb +254 -31
- data/spec/jira/http_error_spec.rb +7 -9
- data/spec/jira/jwt_uri_builder_spec.rb +59 -0
- data/spec/jira/oauth_client_spec.rb +110 -39
- data/spec/jira/request_client_spec.rb +36 -9
- data/spec/jira/resource/agile_spec.rb +135 -0
- data/spec/jira/resource/attachment_spec.rb +127 -9
- data/spec/jira/resource/board_spec.rb +224 -0
- data/spec/jira/resource/createmeta_spec.rb +29 -32
- data/spec/jira/resource/field_spec.rb +42 -48
- data/spec/jira/resource/filter_spec.rb +40 -40
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +79 -0
- data/spec/jira/resource/issue_spec.rb +88 -85
- data/spec/jira/resource/issuelink_spec.rb +1 -1
- data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +18 -0
- data/spec/jira/resource/project_factory_spec.rb +2 -4
- data/spec/jira/resource/project_spec.rb +33 -33
- data/spec/jira/resource/sprint_spec.rb +90 -0
- data/spec/jira/resource/user_factory_spec.rb +6 -8
- data/spec/jira/resource/worklog_spec.rb +9 -11
- data/spec/mock_responses/board/1.json +33 -0
- data/spec/mock_responses/board/1_issues.json +62 -0
- data/spec/mock_responses/empty_issues.json +8 -0
- data/spec/mock_responses/issue/10002/watchers.json +13 -0
- data/spec/mock_responses/issue.json +1 -1
- data/spec/mock_responses/sprint/1_issues.json +125 -0
- data/spec/spec_helper.rb +8 -9
- data/spec/support/clients_helper.rb +4 -4
- data/spec/support/shared_examples/integration.rb +60 -77
- metadata +115 -55
- data/.ruby-version +0 -1
- data/README.rdoc +0 -333
- /data/spec/mock_responses/{attachment → issue/10002/attachments}/10000.json +0 -0
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 [
|
55
|
-
QUERY_PARAMS_FOR_SEARCH = Set.new [
|
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
|
71
|
-
alias
|
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
|
84
|
-
instance_variable_set("@#{relation
|
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
|
85
|
+
instance_variable_set("@#{relation}_id", options["#{relation}_id".to_sym])
|
87
86
|
else
|
88
|
-
raise ArgumentError
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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 +
|
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, :
|
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 =
|
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(:
|
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,
|
294
|
-
if attrs.
|
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, *
|
305
|
-
if attrs.
|
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,
|
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(
|
372
|
-
|
373
|
-
|
374
|
-
|
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?
|
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 +
|
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']
|
432
|
-
the_url =
|
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,9 +489,15 @@ module JIRA
|
|
494
489
|
end
|
495
490
|
|
496
491
|
def url_with_query_params(url, query_params)
|
497
|
-
|
498
|
-
|
499
|
-
|
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)
|
@@ -504,19 +505,19 @@ module JIRA
|
|
504
505
|
end
|
505
506
|
|
506
507
|
def self.hash_to_query_string(query_params)
|
507
|
-
query_params.map do |k,v|
|
508
|
+
query_params.map do |k, v|
|
508
509
|
CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
|
509
510
|
end.join('&')
|
510
511
|
end
|
511
512
|
|
512
513
|
def self.query_params_for_single_fetch(options)
|
513
|
-
Hash[options.select do |k,
|
514
|
+
Hash[options.select do |k, _v|
|
514
515
|
QUERY_PARAMS_FOR_SINGLE_FETCH.include? k
|
515
516
|
end]
|
516
517
|
end
|
517
518
|
|
518
519
|
def self.query_params_for_search(options)
|
519
|
-
Hash[options.select do |k,
|
520
|
+
Hash[options.select do |k, _v|
|
520
521
|
QUERY_PARAMS_FOR_SEARCH.include? k
|
521
522
|
end]
|
522
523
|
end
|
data/lib/jira/base_factory.rb
CHANGED
@@ -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.
|
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
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
174
|
-
JIRA::Resource::
|
261
|
+
def IssuePickerSuggestions
|
262
|
+
JIRA::Resource::IssuePickerSuggestionsFactory.new(self)
|
175
263
|
end
|
176
264
|
|
177
|
-
def
|
178
|
-
JIRA::Resource::
|
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
|
-
|
309
|
+
# Stops sensitive client information from being displayed in logs
|
310
|
+
def inspect
|
311
|
+
"#<JIRA::Client:#{object_id}>"
|
312
|
+
end
|
217
313
|
|
218
|
-
|
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
|