jira-ruby 2.3.0 → 3.2.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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/CI.yml +30 -0
- data/.github/workflows/codeql.yml +96 -0
- data/.github/workflows/rubocop.yml +18 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +123 -0
- data/.yardopts +4 -0
- data/Gemfile +11 -3
- data/Guardfile +2 -0
- data/README.md +94 -19
- data/Rakefile +3 -4
- data/jira-ruby.gemspec +12 -17
- data/lib/jira/atlassian/jwt.rb +76 -0
- data/lib/jira/base.rb +37 -36
- data/lib/jira/base_factory.rb +4 -1
- data/lib/jira/client.rb +132 -51
- data/lib/jira/has_many_proxy.rb +32 -28
- data/lib/jira/http_client.rb +80 -13
- data/lib/jira/http_error.rb +4 -0
- data/lib/jira/jwt_client.rb +19 -43
- data/lib/jira/oauth_client.rb +68 -3
- data/lib/jira/railtie.rb +2 -0
- data/lib/jira/request_client.rb +31 -2
- data/lib/jira/resource/agile.rb +7 -9
- data/lib/jira/resource/applinks.rb +5 -3
- data/lib/jira/resource/attachment.rb +144 -4
- data/lib/jira/resource/board.rb +5 -3
- data/lib/jira/resource/board_configuration.rb +2 -0
- data/lib/jira/resource/comment.rb +14 -0
- data/lib/jira/resource/component.rb +2 -0
- data/lib/jira/resource/createmeta.rb +7 -5
- data/lib/jira/resource/field.rb +13 -12
- data/lib/jira/resource/filter.rb +2 -0
- data/lib/jira/resource/issue.rb +148 -54
- data/lib/jira/resource/issue_picker_suggestions.rb +4 -1
- data/lib/jira/resource/issue_picker_suggestions_issue.rb +2 -0
- data/lib/jira/resource/issuelink.rb +6 -3
- data/lib/jira/resource/issuelinktype.rb +2 -0
- data/lib/jira/resource/issuetype.rb +2 -0
- data/lib/jira/resource/priority.rb +2 -0
- data/lib/jira/resource/project.rb +4 -2
- data/lib/jira/resource/properties.rb +56 -0
- data/lib/jira/resource/rapidview.rb +5 -3
- data/lib/jira/resource/remotelink.rb +2 -0
- data/lib/jira/resource/resolution.rb +2 -0
- data/lib/jira/resource/serverinfo.rb +2 -0
- data/lib/jira/resource/sprint.rb +14 -23
- data/lib/jira/resource/status.rb +7 -1
- data/lib/jira/resource/status_category.rb +10 -0
- data/lib/jira/resource/suggested_issue.rb +2 -0
- data/lib/jira/resource/transition.rb +2 -0
- data/lib/jira/resource/user.rb +3 -1
- data/lib/jira/resource/version.rb +2 -0
- data/lib/jira/resource/watcher.rb +3 -2
- data/lib/jira/resource/webhook.rb +9 -3
- data/lib/jira/resource/worklog.rb +15 -2
- data/lib/jira/version.rb +3 -1
- data/lib/jira-ruby.rb +6 -3
- data/lib/tasks/generate.rake +3 -1
- data/spec/data/files/jwt-signed-urls.json +317 -0
- data/spec/data/files/short.txt +1 -0
- data/spec/integration/attachment_spec.rb +3 -3
- data/spec/integration/comment_spec.rb +8 -8
- data/spec/integration/component_spec.rb +7 -7
- data/spec/integration/field_spec.rb +3 -3
- data/spec/integration/issue_spec.rb +21 -18
- data/spec/integration/issuelinktype_spec.rb +3 -3
- data/spec/integration/issuetype_spec.rb +3 -3
- data/spec/integration/priority_spec.rb +3 -3
- data/spec/integration/project_spec.rb +8 -8
- data/spec/integration/properties_spec.rb +45 -0
- data/spec/integration/rapidview_spec.rb +10 -10
- data/spec/integration/resolution_spec.rb +3 -3
- data/spec/integration/status_category_spec.rb +20 -0
- data/spec/integration/status_spec.rb +4 -8
- data/spec/integration/transition_spec.rb +5 -4
- data/spec/integration/user_spec.rb +34 -11
- data/spec/integration/version_spec.rb +7 -7
- data/spec/integration/watcher_spec.rb +21 -18
- data/spec/integration/webhook_spec.rb +35 -0
- data/spec/integration/worklog_spec.rb +8 -8
- data/spec/jira/atlassian/jwt_spec.rb +60 -0
- data/spec/jira/base_factory_spec.rb +13 -3
- data/spec/jira/base_spec.rb +135 -98
- data/spec/jira/client_spec.rb +72 -56
- data/spec/jira/has_many_proxy_spec.rb +3 -3
- data/spec/jira/http_client_spec.rb +94 -27
- data/spec/jira/http_error_spec.rb +2 -2
- data/spec/jira/oauth_client_spec.rb +14 -8
- data/spec/jira/request_client_spec.rb +4 -4
- data/spec/jira/resource/agile_spec.rb +32 -32
- data/spec/jira/resource/attachment_spec.rb +170 -57
- data/spec/jira/resource/board_spec.rb +24 -23
- data/spec/jira/resource/createmeta_spec.rb +48 -48
- data/spec/jira/resource/field_spec.rb +44 -27
- data/spec/jira/resource/filter_spec.rb +7 -6
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +17 -17
- data/spec/jira/resource/issue_spec.rb +159 -93
- data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +3 -3
- data/spec/jira/resource/project_factory_spec.rb +3 -2
- data/spec/jira/resource/project_spec.rb +16 -16
- data/spec/jira/resource/sprint_spec.rb +88 -9
- data/spec/jira/resource/status_spec.rb +21 -0
- data/spec/jira/resource/user_factory_spec.rb +5 -5
- data/spec/jira/resource/worklog_spec.rb +4 -4
- data/spec/mock_responses/board/1_issues.json +2 -1
- data/spec/mock_responses/issue/10002/properties/foo.json +4 -0
- data/spec/mock_responses/issue/10002/properties/xyz.json +4 -0
- data/spec/mock_responses/issue/10002/properties/xyz.put.json +4 -0
- data/spec/mock_responses/issue/10002/properties.json +12 -0
- data/spec/mock_responses/issue.json +1 -0
- data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.full.json +2 -1
- data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json +2 -1
- data/spec/mock_responses/sprint/1.json +13 -0
- data/spec/mock_responses/status/1.json +8 -1
- data/spec/mock_responses/status.json +40 -5
- data/spec/mock_responses/statuscategory/1.json +7 -0
- data/spec/mock_responses/statuscategory.json +30 -0
- data/spec/mock_responses/{user_username=admin.json → user_accountId=1234567890abcdef01234567.json} +2 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/clients_helper.rb +5 -7
- data/spec/support/mock_client.rb +9 -0
- data/spec/support/mock_response.rb +8 -0
- data/spec/support/shared_examples/integration.rb +51 -58
- metadata +45 -257
- data/.travis.yml +0 -9
- data/example.rb +0 -232
- data/http-basic-example.rb +0 -113
- data/lib/jira/resource/sprint_report.rb +0 -8
- data/lib/jira/tasks.rb +0 -0
- data/spec/integration/webhook.rb +0 -25
- data/spec/jira/jwt_uri_builder_spec.rb +0 -59
- data/spec/mock_responses/jira/rest/webhooks/1.0/webhook.json +0 -11
- data/spec/mock_responses/webhook/webhook.json +0 -11
- /data/spec/mock_responses/{jira/rest/webhooks/1.0/webhook → webhook}/2.json +0 -0
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"
|
|
85
|
-
instance_variable_set("@#{relation}_id", options["#{relation}_id"
|
|
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:
|
|
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:
|
|
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)
|
|
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] ||
|
|
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] ||
|
|
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 }
|
|
@@ -288,11 +291,7 @@ module JIRA
|
|
|
288
291
|
# and returns true when found, otherwise proxies the
|
|
289
292
|
# call to the superclass.
|
|
290
293
|
def respond_to?(method_name, _include_all = false)
|
|
291
|
-
|
|
292
|
-
true
|
|
293
|
-
else
|
|
294
|
-
super(method_name)
|
|
295
|
-
end
|
|
294
|
+
attrs.key?(method_name.to_s) || super(method_name)
|
|
296
295
|
end
|
|
297
296
|
|
|
298
297
|
# Overrides method_missing to check the attribute hash
|
|
@@ -322,7 +321,7 @@ module JIRA
|
|
|
322
321
|
# issue it returns '/issue'
|
|
323
322
|
def path_component
|
|
324
323
|
path_component = "/#{self.class.endpoint_name}"
|
|
325
|
-
path_component +=
|
|
324
|
+
path_component += "/#{key_value}" if key_value
|
|
326
325
|
path_component
|
|
327
326
|
end
|
|
328
327
|
|
|
@@ -331,6 +330,7 @@ module JIRA
|
|
|
331
330
|
# is not set
|
|
332
331
|
def fetch(reload = false, query_params = {})
|
|
333
332
|
return if expanded? && !reload
|
|
333
|
+
|
|
334
334
|
response = client.get(url_with_query_params(url, query_params))
|
|
335
335
|
set_attrs_from_response(response)
|
|
336
336
|
@expanded = true
|
|
@@ -359,14 +359,14 @@ module JIRA
|
|
|
359
359
|
def save(attrs, path = url)
|
|
360
360
|
begin
|
|
361
361
|
save_status = save!(attrs, path)
|
|
362
|
-
rescue JIRA::HTTPError =>
|
|
362
|
+
rescue JIRA::HTTPError => e
|
|
363
363
|
begin
|
|
364
|
-
set_attrs_from_response(
|
|
365
|
-
rescue JSON::ParserError
|
|
364
|
+
set_attrs_from_response(e.response) # Merge error status generated by JIRA REST API
|
|
365
|
+
rescue JSON::ParserError
|
|
366
366
|
set_attrs('exception' => {
|
|
367
|
-
'class' =>
|
|
368
|
-
'code' =>
|
|
369
|
-
'message' =>
|
|
367
|
+
'class' => e.response.class.name,
|
|
368
|
+
'code' => e.response.code,
|
|
369
|
+
'message' => e.response.message
|
|
370
370
|
})
|
|
371
371
|
end
|
|
372
372
|
# raise exception
|
|
@@ -378,10 +378,10 @@ module JIRA
|
|
|
378
378
|
# Sets the attributes hash from a HTTPResponse object from JIRA if it is
|
|
379
379
|
# not nil or is not a json response.
|
|
380
380
|
def set_attrs_from_response(response)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
return if response.body.nil? || (response.body.length < 2)
|
|
382
|
+
|
|
383
|
+
json = self.class.parse_json(response.body)
|
|
384
|
+
set_attrs(json)
|
|
385
385
|
end
|
|
386
386
|
|
|
387
387
|
# Set the current attributes from a hash. If clobber is true, any existing
|
|
@@ -419,7 +419,7 @@ module JIRA
|
|
|
419
419
|
prefix = '/'
|
|
420
420
|
unless self.class.belongs_to_relationships.empty?
|
|
421
421
|
prefix = self.class.belongs_to_relationships.inject(prefix) do |prefix_so_far, relationship|
|
|
422
|
-
prefix_so_far
|
|
422
|
+
"#{prefix_so_far}#{relationship}/#{send("#{relationship}_id")}/"
|
|
423
423
|
end
|
|
424
424
|
end
|
|
425
425
|
if @attrs['self']
|
|
@@ -434,7 +434,8 @@ module JIRA
|
|
|
434
434
|
end
|
|
435
435
|
|
|
436
436
|
# 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
|
|
437
|
+
# Looks like this issue is actual only in case if you use atlassian sdk your app pathis not root
|
|
438
|
+
# (like /jira in example below)
|
|
438
439
|
# issue.save() for existing resource.
|
|
439
440
|
# As a result we got error 400 from JIRA API:
|
|
440
441
|
# [07/Jun/2015:15:32:19 +0400] "PUT jira/rest/api/2/issue/10111 HTTP/1.1" 400 -
|
|
@@ -443,6 +444,7 @@ module JIRA
|
|
|
443
444
|
def patched_url
|
|
444
445
|
result = url
|
|
445
446
|
return result if result.start_with?('/', 'http')
|
|
447
|
+
|
|
446
448
|
"/#{result}"
|
|
447
449
|
end
|
|
448
450
|
|
|
@@ -476,15 +478,18 @@ module JIRA
|
|
|
476
478
|
|
|
477
479
|
def self.maybe_nested_attribute(attributes, attribute_name, nested_under = nil)
|
|
478
480
|
return attributes[attribute_name] if nested_under.nil?
|
|
481
|
+
|
|
479
482
|
if nested_under.instance_of? Array
|
|
480
483
|
final = nested_under.inject(attributes) do |parent, key|
|
|
481
484
|
break if parent.nil?
|
|
485
|
+
|
|
482
486
|
parent[key]
|
|
483
487
|
end
|
|
484
488
|
return nil if final.nil?
|
|
489
|
+
|
|
485
490
|
final[attribute_name]
|
|
486
491
|
else
|
|
487
|
-
|
|
492
|
+
attributes[nested_under][attribute_name]
|
|
488
493
|
end
|
|
489
494
|
end
|
|
490
495
|
|
|
@@ -493,10 +498,10 @@ module JIRA
|
|
|
493
498
|
end
|
|
494
499
|
|
|
495
500
|
def self.url_with_query_params(url, query_params)
|
|
496
|
-
if
|
|
497
|
-
"#{url}?#{hash_to_query_string query_params}"
|
|
498
|
-
else
|
|
501
|
+
if query_params.empty?
|
|
499
502
|
url
|
|
503
|
+
else
|
|
504
|
+
"#{url}?#{hash_to_query_string query_params}"
|
|
500
505
|
end
|
|
501
506
|
end
|
|
502
507
|
|
|
@@ -506,20 +511,16 @@ module JIRA
|
|
|
506
511
|
|
|
507
512
|
def self.hash_to_query_string(query_params)
|
|
508
513
|
query_params.map do |k, v|
|
|
509
|
-
CGI.escape(k.to_s)
|
|
514
|
+
"#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
|
|
510
515
|
end.join('&')
|
|
511
516
|
end
|
|
512
517
|
|
|
513
518
|
def self.query_params_for_single_fetch(options)
|
|
514
|
-
|
|
515
|
-
QUERY_PARAMS_FOR_SINGLE_FETCH.include? k
|
|
516
|
-
end]
|
|
519
|
+
options.slice(*QUERY_PARAMS_FOR_SINGLE_FETCH)
|
|
517
520
|
end
|
|
518
521
|
|
|
519
522
|
def self.query_params_for_search(options)
|
|
520
|
-
|
|
521
|
-
QUERY_PARAMS_FOR_SEARCH.include? k
|
|
522
|
-
end]
|
|
523
|
+
options.slice(*QUERY_PARAMS_FOR_SEARCH)
|
|
523
524
|
end
|
|
524
525
|
end
|
|
525
526
|
end
|
data/lib/jira/base_factory.rb
CHANGED
|
@@ -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, :
|
|
41
|
+
delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql, :jql_paged, :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,6 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'json'
|
|
2
4
|
require 'forwardable'
|
|
3
|
-
require 'ostruct'
|
|
4
5
|
|
|
5
6
|
module JIRA
|
|
6
7
|
# This class is the main access point for all JIRA::Resource instances.
|
|
@@ -34,6 +35,7 @@ module JIRA
|
|
|
34
35
|
# :default_headers => {},
|
|
35
36
|
# :use_client_cert => false,
|
|
36
37
|
# :read_timeout => nil,
|
|
38
|
+
# :max_retries => nil,
|
|
37
39
|
# :http_debug => false,
|
|
38
40
|
# :shared_secret => nil,
|
|
39
41
|
# :cert_path => nil,
|
|
@@ -52,48 +54,50 @@ module JIRA
|
|
|
52
54
|
#
|
|
53
55
|
# The authenticated client instance returned by the respective client type
|
|
54
56
|
# (Oauth, Basic)
|
|
55
|
-
attr_accessor :consumer, :request_client, :http_debug, :
|
|
57
|
+
attr_accessor :consumer, :request_client, :http_debug, :field_map_cache
|
|
56
58
|
|
|
57
59
|
# The configuration options for this client instance
|
|
58
60
|
attr_reader :options
|
|
59
61
|
|
|
60
|
-
def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
62
|
+
def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token,
|
|
63
|
+
:access_token, :authenticated?
|
|
64
|
+
|
|
65
|
+
DEFINED_OPTIONS = %i[
|
|
66
|
+
site
|
|
67
|
+
context_path
|
|
68
|
+
signature_method
|
|
69
|
+
request_token_path
|
|
70
|
+
authorize_path
|
|
71
|
+
access_token_path
|
|
72
|
+
private_key
|
|
73
|
+
private_key_file
|
|
74
|
+
rest_base_path
|
|
75
|
+
consumer_key
|
|
76
|
+
consumer_secret
|
|
77
|
+
ssl_verify_mode
|
|
78
|
+
ssl_version
|
|
79
|
+
use_ssl
|
|
80
|
+
username
|
|
81
|
+
password
|
|
82
|
+
auth_type
|
|
83
|
+
proxy_address
|
|
84
|
+
proxy_port
|
|
85
|
+
proxy_username
|
|
86
|
+
proxy_password
|
|
87
|
+
use_cookies
|
|
88
|
+
additional_cookies
|
|
89
|
+
default_headers
|
|
90
|
+
use_client_cert
|
|
91
|
+
read_timeout
|
|
92
|
+
max_retries
|
|
93
|
+
http_debug
|
|
94
|
+
issuer
|
|
95
|
+
base_url
|
|
96
|
+
shared_secret
|
|
97
|
+
cert_path
|
|
98
|
+
key_path
|
|
99
|
+
ssl_client_cert
|
|
100
|
+
ssl_client_key
|
|
97
101
|
].freeze
|
|
98
102
|
|
|
99
103
|
DEFAULT_OPTIONS = {
|
|
@@ -108,6 +112,23 @@ module JIRA
|
|
|
108
112
|
default_headers: {}
|
|
109
113
|
}.freeze
|
|
110
114
|
|
|
115
|
+
# Creates a new JIRA::Client instance
|
|
116
|
+
# @param [Hash] options The configuration options for the client
|
|
117
|
+
# @option options [Symbol] :auth_type The authentication type to use, :basic, :oauth, :oauth_2legged, or :cookie.
|
|
118
|
+
# @option options [String] :site The base URL for the JIRA instance
|
|
119
|
+
# @option options [Boolean] :use_ssl Whether to use HTTPS for requests
|
|
120
|
+
# @option options [Integer] :ssl_verify_mode The SSL verification mode OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE
|
|
121
|
+
# @option options [Hash] :default_headers Additional headers to send with each request
|
|
122
|
+
# @option options [String] :cert_path The path to the SSL certificate file to verify the server with
|
|
123
|
+
# @option options [String] :use_client_cert Whether to use a client certificate for authentication
|
|
124
|
+
# @option options [String] :ssl_client_cert The client certificate to use
|
|
125
|
+
# @option options [String] :ssl_client_key The client certificate key to use
|
|
126
|
+
# @option options [String] :proxy_address The address of the proxy server to use
|
|
127
|
+
# @option options [Integer] :proxy_port The port of the proxy server to use
|
|
128
|
+
# @option options [String] :proxy_username The username for the proxy server
|
|
129
|
+
# @option options [String] :proxy_password The password for the proxy server
|
|
130
|
+
# @return [JIRA::Client] The client instance
|
|
131
|
+
# @raise [ArgumentError] If an unknown option is given
|
|
111
132
|
def initialize(options = {})
|
|
112
133
|
options = DEFAULT_OPTIONS.merge(options)
|
|
113
134
|
@options = options
|
|
@@ -117,11 +138,20 @@ module JIRA
|
|
|
117
138
|
raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
|
|
118
139
|
|
|
119
140
|
if options[:use_client_cert]
|
|
120
|
-
|
|
141
|
+
if @options[:cert_path]
|
|
142
|
+
@options[:ssl_client_cert] =
|
|
143
|
+
OpenSSL::X509::Certificate.new(File.read(@options[:cert_path]))
|
|
144
|
+
end
|
|
121
145
|
@options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path]
|
|
122
146
|
|
|
123
|
-
|
|
124
|
-
|
|
147
|
+
unless @options[:ssl_client_cert]
|
|
148
|
+
raise ArgumentError,
|
|
149
|
+
'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true'
|
|
150
|
+
end
|
|
151
|
+
unless @options[:ssl_client_key]
|
|
152
|
+
raise ArgumentError,
|
|
153
|
+
'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true'
|
|
154
|
+
end
|
|
125
155
|
end
|
|
126
156
|
|
|
127
157
|
case options[:auth_type]
|
|
@@ -133,7 +163,11 @@ module JIRA
|
|
|
133
163
|
when :basic
|
|
134
164
|
@request_client = HttpClient.new(@options)
|
|
135
165
|
when :cookie
|
|
136
|
-
|
|
166
|
+
if @options.key?(:use_cookies) && !@options[:use_cookies]
|
|
167
|
+
raise ArgumentError,
|
|
168
|
+
'Options: :use_cookies must be true for :cookie authorization type'
|
|
169
|
+
end
|
|
170
|
+
|
|
137
171
|
@options[:use_cookies] = true
|
|
138
172
|
@request_client = HttpClient.new(@options)
|
|
139
173
|
@request_client.make_cookie_auth_request
|
|
@@ -146,8 +180,6 @@ module JIRA
|
|
|
146
180
|
@http_debug = @options[:http_debug]
|
|
147
181
|
|
|
148
182
|
@options.freeze
|
|
149
|
-
|
|
150
|
-
@cache = OpenStruct.new
|
|
151
183
|
end
|
|
152
184
|
|
|
153
185
|
def Project # :nodoc:
|
|
@@ -182,6 +214,10 @@ module JIRA
|
|
|
182
214
|
JIRA::Resource::StatusFactory.new(self)
|
|
183
215
|
end
|
|
184
216
|
|
|
217
|
+
def StatusCategory # :nodoc:
|
|
218
|
+
JIRA::Resource::StatusCategoryFactory.new(self)
|
|
219
|
+
end
|
|
220
|
+
|
|
185
221
|
def Resolution # :nodoc:
|
|
186
222
|
JIRA::Resource::ResolutionFactory.new(self)
|
|
187
223
|
end
|
|
@@ -226,10 +262,6 @@ module JIRA
|
|
|
226
262
|
JIRA::Resource::SprintFactory.new(self)
|
|
227
263
|
end
|
|
228
264
|
|
|
229
|
-
def SprintReport
|
|
230
|
-
JIRA::Resource::SprintReportFactory.new(self)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
265
|
def ServerInfo
|
|
234
266
|
JIRA::Resource::ServerInfoFactory.new(self)
|
|
235
267
|
end
|
|
@@ -270,30 +302,70 @@ module JIRA
|
|
|
270
302
|
JIRA::Resource::AgileFactory.new(self)
|
|
271
303
|
end
|
|
272
304
|
|
|
305
|
+
def Properties
|
|
306
|
+
JIRA::Resource::PropertiesFactory.new(self)
|
|
307
|
+
end
|
|
308
|
+
|
|
273
309
|
# HTTP methods without a body
|
|
310
|
+
|
|
311
|
+
# Make an HTTP DELETE request
|
|
312
|
+
# @param [String] path The path to request
|
|
313
|
+
# @param [Hash] headers The headers to send with the request
|
|
314
|
+
# @return [Net::HTTPResponse] The response object
|
|
315
|
+
# @raise [JIRA::HTTPError] If the response is not an HTTP success code
|
|
274
316
|
def delete(path, headers = {})
|
|
275
317
|
request(:delete, path, nil, merge_default_headers(headers))
|
|
276
318
|
end
|
|
277
319
|
|
|
320
|
+
# Make an HTTP GET request
|
|
321
|
+
# @param [String] path The path to request
|
|
322
|
+
# @param [Hash] headers The headers to send with the request
|
|
323
|
+
# @return [Net::HTTPResponse] The response object
|
|
324
|
+
# @raise [JIRA::HTTPError] If the response is not an HTTP success code
|
|
278
325
|
def get(path, headers = {})
|
|
279
326
|
request(:get, path, nil, merge_default_headers(headers))
|
|
280
327
|
end
|
|
281
328
|
|
|
329
|
+
# Make an HTTP HEAD request
|
|
330
|
+
# @param [String] path The path to request
|
|
331
|
+
# @param [Hash] headers The headers to send with the request
|
|
332
|
+
# @return [Net::HTTPResponse] The response object
|
|
333
|
+
# @raise [JIRA::HTTPError] If the response is not an HTTP success code
|
|
282
334
|
def head(path, headers = {})
|
|
283
335
|
request(:head, path, nil, merge_default_headers(headers))
|
|
284
336
|
end
|
|
285
337
|
|
|
286
338
|
# HTTP methods with a body
|
|
339
|
+
|
|
340
|
+
# Make an HTTP POST request
|
|
341
|
+
# @param [String] path The path to request
|
|
342
|
+
# @param [String] body The body of the request
|
|
343
|
+
# @param [Hash] headers The headers to send with the request
|
|
344
|
+
# @return [Net::HTTPResponse] The response object
|
|
287
345
|
def post(path, body = '', headers = {})
|
|
288
346
|
headers = { 'Content-Type' => 'application/json' }.merge(headers)
|
|
289
347
|
request(:post, path, body, merge_default_headers(headers))
|
|
290
348
|
end
|
|
291
349
|
|
|
350
|
+
# Make an HTTP POST request with a file upload
|
|
351
|
+
# @param [String] path The path to request
|
|
352
|
+
# @param [String] file The file to upload
|
|
353
|
+
# @param [Hash] headers The headers to send with the request
|
|
354
|
+
# @return [Net::HTTPResponse] The response object
|
|
355
|
+
# @raise [JIRA::HTTPError] If the response is not an HTTP success code
|
|
292
356
|
def post_multipart(path, file, headers = {})
|
|
293
357
|
puts "post multipart: #{path} - [#{file}]" if @http_debug
|
|
294
|
-
@request_client.request_multipart(path, file, headers)
|
|
358
|
+
res = @request_client.request_multipart(path, file, merge_default_headers(headers))
|
|
359
|
+
puts "response: #{res}" if @http_debug
|
|
360
|
+
res
|
|
295
361
|
end
|
|
296
362
|
|
|
363
|
+
# Make an HTTP PUT request
|
|
364
|
+
# @param [String] path The path to request
|
|
365
|
+
# @param [String] body The body of the request
|
|
366
|
+
# @param [Hash] headers The headers to send with the request
|
|
367
|
+
# @return [Net::HTTPResponse] The response object
|
|
368
|
+
# @raise [JIRA::HTTPError] If the response is not an HTTP success code
|
|
297
369
|
def put(path, body = '', headers = {})
|
|
298
370
|
headers = { 'Content-Type' => 'application/json' }.merge(headers)
|
|
299
371
|
request(:put, path, body, merge_default_headers(headers))
|
|
@@ -301,11 +373,20 @@ module JIRA
|
|
|
301
373
|
|
|
302
374
|
# Sends the specified HTTP request to the REST API through the
|
|
303
375
|
# appropriate method (oauth, basic).
|
|
376
|
+
# @param [Symbol] http_method The HTTP method to use
|
|
377
|
+
# @param [String] path The path to request
|
|
378
|
+
# @param [String] body The body of the request
|
|
379
|
+
# @param [Hash] headers The headers to send with the request
|
|
380
|
+
# @return [Net::HTTPResponse] The response object
|
|
381
|
+
# @raise [JIRA::HTTPError] If the response is not an HTTP success code
|
|
304
382
|
def request(http_method, path, body = '', headers = {})
|
|
305
383
|
puts "#{http_method}: #{path} - [#{body}]" if @http_debug
|
|
306
|
-
@request_client.request(http_method, path, body, headers)
|
|
384
|
+
res = @request_client.request(http_method, path, body, headers)
|
|
385
|
+
puts "response: #{res}" if @http_debug
|
|
386
|
+
res
|
|
307
387
|
end
|
|
308
388
|
|
|
389
|
+
# @private
|
|
309
390
|
# Stops sensitive client information from being displayed in logs
|
|
310
391
|
def inspect
|
|
311
392
|
"#<JIRA::Client:#{object_id}>"
|
data/lib/jira/has_many_proxy.rb
CHANGED
|
@@ -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
|
|
@@ -6,37 +8,39 @@
|
|
|
6
8
|
#
|
|
7
9
|
# In practice, instances of this class behave exactly like an Array.
|
|
8
10
|
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
module JIRA
|
|
12
|
+
class HasManyProxy
|
|
13
|
+
attr_reader :target_class, :parent
|
|
14
|
+
attr_accessor :collection
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def initialize(parent, target_class, collection = [])
|
|
17
|
+
@parent = parent
|
|
18
|
+
@target_class = target_class
|
|
19
|
+
@collection = collection
|
|
20
|
+
end
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
# Builds an instance of this class with the correct parent.
|
|
23
|
+
# For example, issue.comments.build(attrs) will initialize a
|
|
24
|
+
# comment as follows:
|
|
25
|
+
#
|
|
26
|
+
# JIRA::Resource::Comment.new(issue.client,
|
|
27
|
+
# :attrs => attrs,
|
|
28
|
+
# :issue => issue)
|
|
29
|
+
def build(attrs = {})
|
|
30
|
+
resource = target_class.new(parent.client, :attrs => attrs, parent.to_sym => parent)
|
|
31
|
+
collection << resource
|
|
32
|
+
resource
|
|
33
|
+
end
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
# Forces an HTTP request to fetch all instances of the target class that
|
|
36
|
+
# are associated with the parent
|
|
37
|
+
def all
|
|
38
|
+
target_class.all(parent.client, parent.to_sym => parent)
|
|
39
|
+
end
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
# Delegate any missing methods to the collection that this proxy wraps
|
|
42
|
+
def method_missing(method_name, ...)
|
|
43
|
+
collection.send(method_name, ...)
|
|
44
|
+
end
|
|
41
45
|
end
|
|
42
46
|
end
|