jira-script 1.0.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.
- data/lib/jira-script.rb +95 -0
- data/lib/jira-script/request.rb +201 -0
- metadata +48 -0
data/lib/jira-script.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'jira-script/request'
|
2
|
+
require 'jira-script/request_exception'
|
3
|
+
|
4
|
+
module Jira
|
5
|
+
|
6
|
+
# Jira class wrapper that basically runs the script
|
7
|
+
class Dispatcher
|
8
|
+
attr_accessor :config, :request_queue
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
self.config = {
|
12
|
+
api_path: 'rest/api/2',
|
13
|
+
default_issue_type: 'Story',
|
14
|
+
default_subtask_type: 'Technical task',
|
15
|
+
verbosity: 1
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# config related methods
|
20
|
+
def user(u)
|
21
|
+
config[:user] = u
|
22
|
+
end
|
23
|
+
|
24
|
+
def password(p)
|
25
|
+
config[:password] = p
|
26
|
+
end
|
27
|
+
|
28
|
+
def host(h)
|
29
|
+
config[:host] = h
|
30
|
+
end
|
31
|
+
|
32
|
+
def api_path(api)
|
33
|
+
config[:api_path] = api
|
34
|
+
end
|
35
|
+
|
36
|
+
def project(p)
|
37
|
+
config[:project] = p
|
38
|
+
end
|
39
|
+
|
40
|
+
def quite(on)
|
41
|
+
self.config[:verbosity] = 0 if on
|
42
|
+
end
|
43
|
+
|
44
|
+
def verbosity(v)
|
45
|
+
self.config[:verbosity] = v
|
46
|
+
end
|
47
|
+
|
48
|
+
# build request config
|
49
|
+
def _get_request_config(res, type)
|
50
|
+
{
|
51
|
+
user: config[:user],
|
52
|
+
password: config[:password],
|
53
|
+
host: config[:host],
|
54
|
+
api_path: config[:api_path],
|
55
|
+
project: config[:project],
|
56
|
+
http_request_type: type,
|
57
|
+
resource: res,
|
58
|
+
default_issue_type: config[:default_issue_type],
|
59
|
+
default_subtask_type: config[:default_subtask_type],
|
60
|
+
verbosity: config[:verbosity]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# update command
|
65
|
+
def update(key, &block)
|
66
|
+
raise RequestException, "No update definition provided for issue #{key}" unless block_given?
|
67
|
+
request_config = _get_request_config('issue/' + key, :put)
|
68
|
+
request = Request.new(:update, request_config)
|
69
|
+
request.key = key
|
70
|
+
request.instance_eval(&block)
|
71
|
+
request.run
|
72
|
+
end
|
73
|
+
|
74
|
+
def create(summary, &block)
|
75
|
+
# create new request
|
76
|
+
request_config = _get_request_config('issue', :post)
|
77
|
+
request = Request.new(:create, request_config)
|
78
|
+
request.summary summary
|
79
|
+
request.project config[:project]
|
80
|
+
|
81
|
+
request.instance_eval(&block) if block_given?
|
82
|
+
|
83
|
+
# run request
|
84
|
+
request.run
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.run(&block)
|
89
|
+
Dispatcher.new.instance_eval(&block)
|
90
|
+
rescue RequestException => e
|
91
|
+
p "ERROR: #{e.message}"
|
92
|
+
puts e.backtrace
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'typhoeus'
|
3
|
+
require 'jira-script/request_exception'
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
|
7
|
+
# Generic Jira request
|
8
|
+
class Request
|
9
|
+
# request config - host, api resource, etc
|
10
|
+
attr_accessor :config
|
11
|
+
|
12
|
+
# request data to send to server
|
13
|
+
attr_accessor :json_data
|
14
|
+
|
15
|
+
# request fields that were set
|
16
|
+
attr_accessor :fields
|
17
|
+
|
18
|
+
# mapping between our dsl keywords and Jira's json fields
|
19
|
+
attr_accessor :data_map
|
20
|
+
|
21
|
+
# request parent - used when creating subtasks
|
22
|
+
attr_accessor :request_parent
|
23
|
+
|
24
|
+
# request response
|
25
|
+
attr_accessor :response
|
26
|
+
|
27
|
+
# request children
|
28
|
+
attr_accessor :children
|
29
|
+
|
30
|
+
# request type (create, update)
|
31
|
+
attr_accessor :request_type
|
32
|
+
|
33
|
+
# issue key
|
34
|
+
attr_accessor :key
|
35
|
+
|
36
|
+
def initialize(type, config = {})
|
37
|
+
self.request_type = type
|
38
|
+
self.config = config
|
39
|
+
self.fields = {}
|
40
|
+
self.request_parent = nil
|
41
|
+
self.json_data = {}
|
42
|
+
self.children = []
|
43
|
+
self.key = nil
|
44
|
+
init_data_map
|
45
|
+
end
|
46
|
+
|
47
|
+
def init_data_map
|
48
|
+
self.data_map = {
|
49
|
+
project: 'fields/project/key',
|
50
|
+
parent: 'fields/parent/key',
|
51
|
+
summary: 'fields/summary',
|
52
|
+
description: 'fields/description',
|
53
|
+
type: 'fields/issuetype/name',
|
54
|
+
assignee: 'fields/assignee/name',
|
55
|
+
estimate: 'fields/timetracking/originalEstimate',
|
56
|
+
remaining: 'fields/timetracking/remainingEstimate',
|
57
|
+
components: 'fields/components',
|
58
|
+
labels: 'fields/labels'
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_json
|
63
|
+
JSON.generate(json_data)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
json_data
|
68
|
+
end
|
69
|
+
|
70
|
+
def _set_default_type
|
71
|
+
# set default type if empty
|
72
|
+
return if fields.key?(:type)
|
73
|
+
if fields.key?(:parent)
|
74
|
+
type config[:default_subtask_type]
|
75
|
+
else
|
76
|
+
type config[:default_issue_type]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def _set_xpath(h, xpath, value)
|
81
|
+
len = xpath.length
|
82
|
+
pos = xpath.index('/')
|
83
|
+
if pos.nil?
|
84
|
+
h[xpath.to_sym] = value
|
85
|
+
else
|
86
|
+
key = xpath[0..pos - 1].to_sym
|
87
|
+
h[key] = h[key] || {}
|
88
|
+
_set_xpath(h[key], xpath[pos + 1..len], value)
|
89
|
+
end
|
90
|
+
h
|
91
|
+
end
|
92
|
+
|
93
|
+
def _set_field(name, val)
|
94
|
+
raise RequestException, "Invalid request parameter #{name}" unless data_map.key?(name)
|
95
|
+
fields[name] = val
|
96
|
+
_set_xpath(json_data, data_map[name], val)
|
97
|
+
end
|
98
|
+
|
99
|
+
def _error(msg)
|
100
|
+
if request_type == :create
|
101
|
+
raise RequestException, "Error trying to create ticket #{fields[:summary]}: #{msg}"
|
102
|
+
elsif request_type == :update
|
103
|
+
raise RequestException, "Error trying to update ticket #{key}: #{msg}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def respond_to_missing?(method_name, include_private = false)
|
108
|
+
data_map.key?(method_name) || super
|
109
|
+
end
|
110
|
+
|
111
|
+
def method_missing(name, *args, &block)
|
112
|
+
if data_map.key?(name)
|
113
|
+
_set_field(name, args[0])
|
114
|
+
else
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def run
|
120
|
+
ret = nil
|
121
|
+
_set_default_type
|
122
|
+
url = [config[:host], config[:api_path], config[:resource]].join('/')
|
123
|
+
json_str = to_json
|
124
|
+
p "Sending json #{json_str}" if config[:verbosity] >= 2
|
125
|
+
|
126
|
+
req = Typhoeus::Request.new(
|
127
|
+
url,
|
128
|
+
ssl_verifypeer: false,
|
129
|
+
method: config[:http_request_type],
|
130
|
+
userpwd: "#{config[:user]}:#{config[:password]}",
|
131
|
+
body: json_str,
|
132
|
+
headers: { 'Content-Type' => 'application/json' }
|
133
|
+
)
|
134
|
+
req.on_complete do |response|
|
135
|
+
if response.success?
|
136
|
+
self.response = JSON.parse(response.body, symbolize_names: true)
|
137
|
+
ret = self.response[:key]
|
138
|
+
self.key = ret if request_type == :create
|
139
|
+
|
140
|
+
if config[:verbosity] >= 1
|
141
|
+
if fields[:parent].nil?
|
142
|
+
prefix = 'Issue'
|
143
|
+
else
|
144
|
+
prefix = ' - sub-task'
|
145
|
+
end
|
146
|
+
p "#{prefix} #{key} updated successfully" if request_type == :update
|
147
|
+
p "#{prefix} #{key}: '#{fields[:summary]}' created successfully" if request_type == :create
|
148
|
+
end
|
149
|
+
|
150
|
+
unless children.empty?
|
151
|
+
children.each do |child|
|
152
|
+
child.parent key
|
153
|
+
child.run
|
154
|
+
end
|
155
|
+
end
|
156
|
+
elsif response.timed_out?
|
157
|
+
_error('timeout')
|
158
|
+
elsif response.code.zero?
|
159
|
+
# Could not get an http response, something's wrong.
|
160
|
+
_error(response.return_message)
|
161
|
+
else
|
162
|
+
# Received a non-successful http response.
|
163
|
+
_error('HTTP request failed: ' + response.code.to_s + ' / message: ' + response.body)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
req.run
|
168
|
+
|
169
|
+
ret
|
170
|
+
end
|
171
|
+
|
172
|
+
def create(summary, &block)
|
173
|
+
subtask(summary, &block)
|
174
|
+
end
|
175
|
+
|
176
|
+
def subtask(summary, &block)
|
177
|
+
raise RequestException, "Sub-task '#{fields[:summary]}' cannot have other sub-tasks" unless request_parent.nil?
|
178
|
+
request = Request.new(:create, config)
|
179
|
+
request.config[:http_request_type] = :post
|
180
|
+
request.request_parent = self
|
181
|
+
children.push request
|
182
|
+
request.summary summary
|
183
|
+
request.project fields[:project]
|
184
|
+
request.instance_eval(&block) if block_given?
|
185
|
+
end
|
186
|
+
|
187
|
+
def components(*args)
|
188
|
+
vals = []
|
189
|
+
args.each do |arg|
|
190
|
+
vals.push name: arg
|
191
|
+
end
|
192
|
+
|
193
|
+
_set_field(:components, vals)
|
194
|
+
end
|
195
|
+
|
196
|
+
def labels(*args)
|
197
|
+
_set_field(:labels, [*args])
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jira-script
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Florin Mihalache
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-03-28 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A Ruby DSL script implementation to automate creating and updating many
|
15
|
+
Jira issues at once.
|
16
|
+
email: florin.mihalache@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/jira-script.rb
|
22
|
+
- lib/jira-script/request.rb
|
23
|
+
homepage: http://github.com/mflorin/jira-script
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
post_install_message:
|
27
|
+
rdoc_options: []
|
28
|
+
require_paths:
|
29
|
+
- lib
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 1.8.23
|
45
|
+
signing_key:
|
46
|
+
specification_version: 3
|
47
|
+
summary: Jira DSL
|
48
|
+
test_files: []
|