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.
@@ -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: []