jira-ruby 2.3.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.
- 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 +28 -0
- data/.github/workflows/codeql.yml +100 -0
- data/.github/workflows/rubocop.yml +18 -0
- data/.rubocop.yml +188 -0
- data/Gemfile +11 -3
- data/Guardfile +2 -0
- data/README.md +94 -18
- data/Rakefile +3 -4
- data/jira-ruby.gemspec +11 -17
- data/lib/jira/base.rb +37 -28
- data/lib/jira/base_factory.rb +4 -1
- data/lib/jira/client.rb +64 -46
- data/lib/jira/has_many_proxy.rb +4 -2
- data/lib/jira/http_client.rb +17 -13
- data/lib/jira/http_error.rb +4 -0
- data/lib/jira/jwt_client.rb +18 -42
- data/lib/jira/oauth_client.rb +6 -3
- data/lib/jira/railtie.rb +2 -0
- data/lib/jira/request_client.rb +5 -1
- data/lib/jira/resource/agile.rb +7 -9
- data/lib/jira/resource/applinks.rb +5 -3
- data/lib/jira/resource/attachment.rb +43 -3
- data/lib/jira/resource/board.rb +5 -3
- data/lib/jira/resource/board_configuration.rb +2 -0
- data/lib/jira/resource/comment.rb +2 -0
- data/lib/jira/resource/component.rb +2 -0
- data/lib/jira/resource/createmeta.rb +3 -1
- data/lib/jira/resource/field.rb +9 -4
- data/lib/jira/resource/filter.rb +2 -0
- data/lib/jira/resource/issue.rb +35 -44
- 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 +2 -0
- 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/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 +2 -1
- data/lib/jira/resource/webhook.rb +4 -2
- data/lib/jira/resource/worklog.rb +3 -2
- data/lib/jira/version.rb +3 -1
- data/lib/jira-ruby.rb +5 -3
- data/lib/tasks/generate.rake +4 -2
- 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 +20 -16
- 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 +7 -7
- data/spec/integration/rapidview_spec.rb +9 -9
- 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 +2 -2
- data/spec/integration/user_spec.rb +22 -8
- data/spec/integration/version_spec.rb +7 -7
- data/spec/integration/watcher_spec.rb +17 -18
- data/spec/integration/webhook.rb +5 -4
- data/spec/integration/worklog_spec.rb +8 -8
- data/spec/jira/base_factory_spec.rb +2 -1
- data/spec/jira/base_spec.rb +55 -41
- data/spec/jira/client_spec.rb +48 -34
- 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 +8 -6
- data/spec/jira/request_client_spec.rb +4 -4
- data/spec/jira/resource/agile_spec.rb +28 -28
- data/spec/jira/resource/attachment_spec.rb +142 -52
- data/spec/jira/resource/board_spec.rb +21 -20
- data/spec/jira/resource/createmeta_spec.rb +48 -48
- data/spec/jira/resource/field_spec.rb +30 -12
- data/spec/jira/resource/filter_spec.rb +4 -4
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +17 -17
- data/spec/jira/resource/issue_spec.rb +43 -37
- 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 +70 -3
- data/spec/jira/resource/status_spec.rb +21 -0
- data/spec/jira/resource/user_factory_spec.rb +4 -4
- data/spec/jira/resource/worklog_spec.rb +3 -3
- 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 +3 -5
- data/spec/support/shared_examples/integration.rb +25 -28
- metadata +25 -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/jira/jwt_uri_builder_spec.rb +0 -59
data/lib/jira/base.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support/core_ext/string'
|
|
2
4
|
require 'active_support/inflector'
|
|
3
5
|
require 'set'
|
|
@@ -81,8 +83,8 @@ module JIRA
|
|
|
81
83
|
if options[relation]
|
|
82
84
|
instance_variable_set("@#{relation}", options[relation])
|
|
83
85
|
instance_variable_set("@#{relation}_id", options[relation].key_value)
|
|
84
|
-
elsif options["#{relation}_id"
|
|
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 }
|
|
@@ -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 +=
|
|
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 =>
|
|
366
|
+
rescue JIRA::HTTPError => e
|
|
363
367
|
begin
|
|
364
|
-
set_attrs_from_response(
|
|
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' =>
|
|
368
|
-
'code' =>
|
|
369
|
-
'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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
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, :get_backlog_issues,
|
|
41
|
+
delegate_to_target_class :all, :find, :collection_path, :singular_path, :jql, :get_backlog_issues,
|
|
42
|
+
:get_board_issues, :get_sprints, :get_sprint_issues, :get_projects, :get_projects_full
|
|
40
43
|
|
|
41
44
|
# This method needs special handling as it has a default argument value
|
|
42
45
|
def build(attrs = {})
|
data/lib/jira/client.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'json'
|
|
2
4
|
require 'forwardable'
|
|
3
5
|
require 'ostruct'
|
|
@@ -34,6 +36,7 @@ module JIRA
|
|
|
34
36
|
# :default_headers => {},
|
|
35
37
|
# :use_client_cert => false,
|
|
36
38
|
# :read_timeout => nil,
|
|
39
|
+
# :max_retries => nil,
|
|
37
40
|
# :http_debug => false,
|
|
38
41
|
# :shared_secret => nil,
|
|
39
42
|
# :cert_path => nil,
|
|
@@ -57,43 +60,45 @@ module JIRA
|
|
|
57
60
|
# The configuration options for this client instance
|
|
58
61
|
attr_reader :options
|
|
59
62
|
|
|
60
|
-
def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token,
|
|
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
|
-
|
|
63
|
+
def_delegators :@request_client, :init_access_token, :set_access_token, :set_request_token, :request_token,
|
|
64
|
+
:access_token, :authenticated?
|
|
65
|
+
|
|
66
|
+
DEFINED_OPTIONS = %i[
|
|
67
|
+
site
|
|
68
|
+
context_path
|
|
69
|
+
signature_method
|
|
70
|
+
request_token_path
|
|
71
|
+
authorize_path
|
|
72
|
+
access_token_path
|
|
73
|
+
private_key
|
|
74
|
+
private_key_file
|
|
75
|
+
rest_base_path
|
|
76
|
+
consumer_key
|
|
77
|
+
consumer_secret
|
|
78
|
+
ssl_verify_mode
|
|
79
|
+
ssl_version
|
|
80
|
+
use_ssl
|
|
81
|
+
username
|
|
82
|
+
password
|
|
83
|
+
auth_type
|
|
84
|
+
proxy_address
|
|
85
|
+
proxy_port
|
|
86
|
+
proxy_username
|
|
87
|
+
proxy_password
|
|
88
|
+
use_cookies
|
|
89
|
+
additional_cookies
|
|
90
|
+
default_headers
|
|
91
|
+
use_client_cert
|
|
92
|
+
read_timeout
|
|
93
|
+
max_retries
|
|
94
|
+
http_debug
|
|
95
|
+
issuer
|
|
96
|
+
base_url
|
|
97
|
+
shared_secret
|
|
98
|
+
cert_path
|
|
99
|
+
key_path
|
|
100
|
+
ssl_client_cert
|
|
101
|
+
ssl_client_key
|
|
97
102
|
].freeze
|
|
98
103
|
|
|
99
104
|
DEFAULT_OPTIONS = {
|
|
@@ -117,11 +122,20 @@ module JIRA
|
|
|
117
122
|
raise ArgumentError, "Unknown option(s) given: #{unknown_options}" unless unknown_options.empty?
|
|
118
123
|
|
|
119
124
|
if options[:use_client_cert]
|
|
120
|
-
|
|
125
|
+
if @options[:cert_path]
|
|
126
|
+
@options[:ssl_client_cert] =
|
|
127
|
+
OpenSSL::X509::Certificate.new(File.read(@options[:cert_path]))
|
|
128
|
+
end
|
|
121
129
|
@options[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(@options[:key_path])) if @options[:key_path]
|
|
122
130
|
|
|
123
|
-
|
|
124
|
-
|
|
131
|
+
unless @options[:ssl_client_cert]
|
|
132
|
+
raise ArgumentError,
|
|
133
|
+
'Options: :cert_path or :ssl_client_cert must be set when :use_client_cert is true'
|
|
134
|
+
end
|
|
135
|
+
unless @options[:ssl_client_key]
|
|
136
|
+
raise ArgumentError,
|
|
137
|
+
'Options: :key_path or :ssl_client_key must be set when :use_client_cert is true'
|
|
138
|
+
end
|
|
125
139
|
end
|
|
126
140
|
|
|
127
141
|
case options[:auth_type]
|
|
@@ -133,7 +147,11 @@ module JIRA
|
|
|
133
147
|
when :basic
|
|
134
148
|
@request_client = HttpClient.new(@options)
|
|
135
149
|
when :cookie
|
|
136
|
-
|
|
150
|
+
if @options.key?(:use_cookies) && !@options[:use_cookies]
|
|
151
|
+
raise ArgumentError,
|
|
152
|
+
'Options: :use_cookies must be true for :cookie authorization type'
|
|
153
|
+
end
|
|
154
|
+
|
|
137
155
|
@options[:use_cookies] = true
|
|
138
156
|
@request_client = HttpClient.new(@options)
|
|
139
157
|
@request_client.make_cookie_auth_request
|
|
@@ -182,6 +200,10 @@ module JIRA
|
|
|
182
200
|
JIRA::Resource::StatusFactory.new(self)
|
|
183
201
|
end
|
|
184
202
|
|
|
203
|
+
def StatusCategory # :nodoc:
|
|
204
|
+
JIRA::Resource::StatusCategoryFactory.new(self)
|
|
205
|
+
end
|
|
206
|
+
|
|
185
207
|
def Resolution # :nodoc:
|
|
186
208
|
JIRA::Resource::ResolutionFactory.new(self)
|
|
187
209
|
end
|
|
@@ -226,10 +248,6 @@ module JIRA
|
|
|
226
248
|
JIRA::Resource::SprintFactory.new(self)
|
|
227
249
|
end
|
|
228
250
|
|
|
229
|
-
def SprintReport
|
|
230
|
-
JIRA::Resource::SprintReportFactory.new(self)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
251
|
def ServerInfo
|
|
234
252
|
JIRA::Resource::ServerInfoFactory.new(self)
|
|
235
253
|
end
|
|
@@ -291,7 +309,7 @@ module JIRA
|
|
|
291
309
|
|
|
292
310
|
def post_multipart(path, file, headers = {})
|
|
293
311
|
puts "post multipart: #{path} - [#{file}]" if @http_debug
|
|
294
|
-
@request_client.request_multipart(path, file, headers)
|
|
312
|
+
@request_client.request_multipart(path, file, merge_default_headers(headers))
|
|
295
313
|
end
|
|
296
314
|
|
|
297
315
|
def put(path, body = '', headers = {})
|
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
|
|
@@ -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,
|
|
40
|
-
collection.send(method_name,
|
|
41
|
+
def method_missing(method_name, ...)
|
|
42
|
+
collection.send(method_name, ...)
|
|
41
43
|
end
|
|
42
44
|
end
|
data/lib/jira/http_client.rb
CHANGED
|
@@ -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]
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
http_conn =
|
|
51
|
+
if @options[:proxy_address]
|
|
52
|
+
Net::HTTP.new(uri.host, uri.port, @options[:proxy_address], @options[:proxy_port] || 80,
|
|
53
|
+
@options[:proxy_username], @options[:proxy_password])
|
|
54
|
+
else
|
|
55
|
+
Net::HTTP.new(uri.host, uri.port)
|
|
56
|
+
end
|
|
54
57
|
http_conn.use_ssl = @options[:use_ssl]
|
|
55
58
|
if @options[:use_client_cert]
|
|
56
59
|
http_conn.cert = @options[:ssl_client_cert]
|
|
@@ -59,6 +62,7 @@ module JIRA
|
|
|
59
62
|
http_conn.verify_mode = @options[:ssl_verify_mode]
|
|
60
63
|
http_conn.ssl_version = @options[:ssl_version] if @options[:ssl_version]
|
|
61
64
|
http_conn.read_timeout = @options[:read_timeout]
|
|
65
|
+
http_conn.max_retries = @options[:max_retries] if @options[:max_retries]
|
|
62
66
|
http_conn.ca_file = @options[:ca_file] if @options[:ca_file]
|
|
63
67
|
http_conn
|
|
64
68
|
end
|
|
@@ -94,13 +98,13 @@ module JIRA
|
|
|
94
98
|
|
|
95
99
|
def store_cookies(response)
|
|
96
100
|
cookies = response.get_fields('set-cookie')
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
end
|
|
101
|
+
return unless cookies
|
|
102
|
+
cookies.each do |cookie|
|
|
103
|
+
data = CGI::Cookie.parse(cookie)
|
|
104
|
+
data.delete('Path')
|
|
105
|
+
@cookies.merge!(data)
|
|
103
106
|
end
|
|
107
|
+
|
|
104
108
|
end
|
|
105
109
|
|
|
106
110
|
def add_cookies(request)
|
data/lib/jira/http_error.rb
CHANGED
data/lib/jira/jwt_client.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
23
|
+
attr_reader :http_method
|
|
39
24
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
50
|
-
end
|
|
28
|
+
{ 'Authorization' => "JWT #{jwt}" }
|
|
51
29
|
end
|
|
52
30
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
data/lib/jira/oauth_client.rb
CHANGED
|
@@ -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 = {},
|
|
49
|
-
@request_token ||= get_request_token(options,
|
|
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
|
|
88
|
+
"#{uri.query}&#{oauth_params_str}"
|
|
86
89
|
end
|
|
87
90
|
url = uri.to_s
|
|
88
91
|
end
|
data/lib/jira/railtie.rb
CHANGED
data/lib/jira/request_client.rb
CHANGED
|
@@ -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
|
data/lib/jira/resource/agile.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'cgi'
|
|
2
4
|
|
|
3
5
|
module JIRA
|
|
@@ -25,7 +27,8 @@ module JIRA
|
|
|
25
27
|
response = client.get(path_base(client) + "/board/#{board_id}/issue?#{hash_to_query_string(options)}")
|
|
26
28
|
json = parse_json(response.body)
|
|
27
29
|
# To get Issue objects with the same structure as for Issue.all
|
|
28
|
-
return {} if json['issues'].
|
|
30
|
+
return {} if json['issues'].empty?
|
|
31
|
+
|
|
29
32
|
issue_ids = json['issues'].map do |issue|
|
|
30
33
|
issue['id']
|
|
31
34
|
end
|
|
@@ -58,22 +61,17 @@ module JIRA
|
|
|
58
61
|
parse_json(response.body)
|
|
59
62
|
end
|
|
60
63
|
|
|
61
|
-
# def self.find(client, key, options = {})
|
|
62
|
-
# options[:maxResults] ||= 100
|
|
63
|
-
# fields = options[:fields].join(',') unless options[:fields].nil?
|
|
64
|
-
# response = client.get("/rest/api/latest/search?jql=sprint=#{key}&fields=#{fields}&maxResults=#{options[:maxResults]}")
|
|
65
|
-
# parse_json(response.body)
|
|
66
|
-
# end
|
|
67
|
-
|
|
68
64
|
private
|
|
69
65
|
|
|
70
66
|
def self.path_base(client)
|
|
71
|
-
client.options[:context_path]
|
|
67
|
+
"#{client.options[:context_path]}/rest/agile/1.0"
|
|
72
68
|
end
|
|
73
69
|
|
|
74
70
|
def path_base(client)
|
|
75
71
|
self.class.path_base(client)
|
|
76
72
|
end
|
|
73
|
+
|
|
74
|
+
private_class_method :path_base
|
|
77
75
|
end
|
|
78
76
|
end
|
|
79
77
|
end
|