jirarest2 0.0.6 → 0.0.7
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.tar.gz.sig +0 -0
- data/History.txt +17 -0
- data/Manifest.txt +11 -9
- data/README.txt +1 -1
- data/Rakefile +1 -1
- data/bin/create_issue.rb +2 -2
- data/bin/jira_create_issue +397 -0
- data/bin/jira_create_issue.rb +3 -370
- data/lib/jirarest2.rb +8 -8
- data/lib/{connect.rb → jirarest2/connect.rb} +1 -1
- data/lib/{credentials.rb → jirarest2/credentials.rb} +0 -0
- data/lib/{exceptions.rb → jirarest2/exceptions.rb} +0 -0
- data/lib/{issue.rb → jirarest2/issue.rb} +15 -15
- data/lib/{madbitconfig.rb → jirarest2/madbitconfig.rb} +0 -0
- data/lib/{services.rb → jirarest2/services.rb} +0 -0
- data/lib/{services → jirarest2/services}/issuelink.rb +14 -8
- data/lib/jirarest2/services/issuelinktype.rb +108 -0
- data/lib/{services → jirarest2/services}/watcher.rb +2 -2
- data/test/data/issuespec.txt +14 -0
- data/test/test_connect.rb +28 -5
- data/test/test_issue.rb +7 -1
- data/test/test_issuelink.rb +36 -11
- data/test/test_issuelinktype.rb +30 -5
- data/test/test_madbitconfig.rb +9 -9
- data/test/test_result.rb +19 -5
- data/test/test_watcher.rb +21 -7
- metadata +30 -11
- metadata.gz.sig +0 -0
- data/lib/services/issuelinktype.rb +0 -91
data/bin/jira_create_issue.rb
CHANGED
@@ -17,374 +17,7 @@
|
|
17
17
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
#
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
puts "Sorry, I need ruby 1.9.1 or higher!"
|
24
|
-
exit1
|
25
|
-
end
|
26
|
-
|
27
|
-
require "highline/import"
|
28
|
-
require "jirarest2"
|
29
|
-
require "optparse"
|
30
|
-
require "ostruct"
|
31
|
-
require "madbitconfig"
|
32
|
-
require "uri"
|
33
|
-
require "pp"
|
34
|
-
|
35
|
-
class ParseOptions
|
36
|
-
|
37
|
-
def self.required_argument(name)
|
38
|
-
puts "Argument \"#{name}\" is mandatory."
|
39
|
-
exit 1
|
40
|
-
end
|
41
|
-
|
42
|
-
=begin
|
43
|
-
parse resturn two Hashes. The first one contains the options for the issue the second one the options for the execution of the script.
|
44
|
-
=end
|
45
|
-
def self.parse(args)
|
46
|
-
issueopts = OpenStruct.new
|
47
|
-
issueopts.project = nil
|
48
|
-
issueopts.issue = nil
|
49
|
-
scriptopts = OpenStruct.new
|
50
|
-
scriptopts.show = []
|
51
|
-
scriptopts.arrayseperator = "|"
|
52
|
-
scriptopts.configfile = "~/.jiraconfig"
|
53
|
-
|
54
|
-
opts = OptionParser.new do |opts|
|
55
|
-
opts.banner = "Usage: #{__FILE__} [options]"
|
56
|
-
opts.separator ""
|
57
|
-
|
58
|
-
opts.on("-p", "--project PROJECT", "Projectname") do |p|
|
59
|
-
issueopts.project = p
|
60
|
-
issueopts
|
61
|
-
end
|
62
|
-
|
63
|
-
opts.on("-i", "--issue ISSUETYPE", "Issuetype") do |i|
|
64
|
-
issueopts.issue = i
|
65
|
-
end
|
66
|
-
|
67
|
-
opts.on("-f", "--fields", "Display the fields available for this issue") do |f|
|
68
|
-
scriptopts.show << "fields"
|
69
|
-
end
|
70
|
-
|
71
|
-
opts.on("-r", "--requireds", "Display the the mandatory content fields for this issue") do |r|
|
72
|
-
scriptopts.show << "requireds"
|
73
|
-
end
|
74
|
-
|
75
|
-
opts.on("-c", "--content x=value,y=value,z=value", Array, "List of fields to fill") do |list|
|
76
|
-
issueopts.content = list
|
77
|
-
end
|
78
|
-
|
79
|
-
opts.on("-C", "--content-file FILENAME", "JSON formatted file (or \"-\" for STDIN) with the contents of -c (Pipes work only if URL,username AND password are in the CONFIGFILE!)") do |contentfile|
|
80
|
-
scriptopts.contentfile = contentfile
|
81
|
-
end
|
82
|
-
|
83
|
-
opts.on("-w", "--watcher USERNAME,USERNAME", Array, "List of watchers") do |w|
|
84
|
-
issueopts.watchers = w
|
85
|
-
end
|
86
|
-
|
87
|
-
opts.on("-l", "--link ISSUE=LINKTYPE", "Key of an Issue this issue should be linked to" ) do |l|
|
88
|
-
issueopts.link = l
|
89
|
-
end
|
90
|
-
|
91
|
-
opts.on("-F", "--field-seperator CHAR", "A fieldseperator if one of the fields is an array (Default \"|\")") do |fs|
|
92
|
-
scriptopts.arrayseperator = fs
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
opts.on("--config-file CONFIGFILE", "Config file containing the jira credentials. (Default: ~/.jiraconfig)") do |conffile|
|
97
|
-
scriptopts.configfile = conffile
|
98
|
-
end
|
99
|
-
|
100
|
-
opts.on("--write-config-file", "Writes the configfile with the data given if it does not alredy exist.") do |wc|
|
101
|
-
scriptopts.writeconf = :write
|
102
|
-
end
|
103
|
-
|
104
|
-
opts.on("--force-write-config-file", "Writes the configfile with the data given even if it does alredy exist.") do |wc|
|
105
|
-
scriptopts.writeconf = :forcewrite
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
opts.on("-u", "--username USERNAME", "Your Jira Username if you don't want to use the one in the master file") do |u|
|
110
|
-
scriptopts.username = u
|
111
|
-
end
|
112
|
-
|
113
|
-
opts.on("-H", "--jira-url URL", "URL to rest api (without \"/rest/api/2\").") do |url|
|
114
|
-
uri = URI(url)
|
115
|
-
splitURI = URI.split(url)
|
116
|
-
if splitURI[3] then
|
117
|
-
url = splitURI[0].to_s + "://" + splitURI[2].to_s + ":" + splitURI[3].to_s + splitURI[5].to_s
|
118
|
-
else
|
119
|
-
url = splitURI[0].to_s + "://" + splitURI[2].to_s + splitURI[5].to_s
|
120
|
-
end
|
121
|
-
scriptopts.url = url
|
122
|
-
end
|
123
|
-
|
124
|
-
opts.on_tail("-h", "--help", "Display this screen") do
|
125
|
-
puts opts
|
126
|
-
exit
|
127
|
-
end
|
128
|
-
|
129
|
-
opts.on_tail("--version", "Show version") do
|
130
|
-
puts OptionParser::Version.join(".")
|
131
|
-
exit
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
|
136
|
-
opts.parse!(args)
|
137
|
-
|
138
|
-
if issueopts.project.nil? && scriptopts.writeconf.nil? then
|
139
|
-
required_argument("project")
|
140
|
-
end
|
141
|
-
if issueopts.issue.nil? && scriptopts.writeconf.nil? then
|
142
|
-
required_argument("issue")
|
143
|
-
end
|
144
|
-
return issueopts, scriptopts
|
145
|
-
end #parse()
|
146
|
-
|
147
|
-
|
148
|
-
end # class ParseOptions
|
149
|
-
|
150
|
-
@issueopts, @scriptopts = ParseOptions.parse(ARGV)
|
151
|
-
|
152
|
-
|
153
|
-
def no_issue(type,issue)
|
154
|
-
puts "The #{type}type you entered (\"#{issue}\") does no exist."
|
155
|
-
puts "Maybe you entered the wrong type or made a typo? (Case is relevant!)"
|
156
|
-
exit 1
|
157
|
-
end
|
158
|
-
|
159
|
-
=begin
|
160
|
-
Get the password from an interactive shell
|
161
|
-
=end
|
162
|
-
def get_password
|
163
|
-
ask("Enter your password for user \"#{@scriptopts.username}\": ") { |q|
|
164
|
-
q.echo = "*"
|
165
|
-
}
|
166
|
-
end
|
167
|
-
|
168
|
-
=begin
|
169
|
-
Gather all the credentials and build the credentials file
|
170
|
-
=end
|
171
|
-
def get_credentials
|
172
|
-
filefail = false
|
173
|
-
begin
|
174
|
-
fileconf = MadbitConfig::read_configfile(@scriptopts.configfile)
|
175
|
-
# We don't want to set the Values from the configfile if we have them already set.
|
176
|
-
@scriptopts.username = fileconf["username"] if ( @scriptopts.username.nil? && fileconf["username"] )
|
177
|
-
@scriptopts.pass = fileconf["password"] if ( @scriptopts.pass.nil? && fileconf["password"] )
|
178
|
-
if ( @scriptopts.url.nil? && fileconf["URL"] ) then
|
179
|
-
@scriptopts.url = fileconf["URL"]
|
180
|
-
end
|
181
|
-
rescue IOError => e
|
182
|
-
puts e
|
183
|
-
filefail = false
|
184
|
-
end
|
185
|
-
@scriptopts.url = @scriptopts.url + "/rest/api/2/"
|
186
|
-
|
187
|
-
|
188
|
-
if @scriptopts.pass.nil? && !( @scriptopts.username.nil?) then
|
189
|
-
@scriptopts.pass = get_password
|
190
|
-
end
|
191
|
-
|
192
|
-
missing = Array.new
|
193
|
-
missing << "URL" if @scriptopts.url.nil?
|
194
|
-
missing << "username" if @scriptopts.username.nil?
|
195
|
-
# missing << "password" if @scriptopts.pass.nil?
|
196
|
-
if missing != [] then
|
197
|
-
puts "Missing essential parameter(s) #{missing.join(",")}. Exiting..."
|
198
|
-
exit 1
|
199
|
-
else
|
200
|
-
return Credentials.new(@scriptopts.url, @scriptopts.username, @scriptopts.pass)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
=begin
|
205
|
-
If there is already a conenction known returns that connection. If not or if the parameter is true it tries to create a new Connect object
|
206
|
-
=end
|
207
|
-
def get_connection(reconnect = false)
|
208
|
-
if ! @connection || reconnect then
|
209
|
-
begin
|
210
|
-
@connection = Connect.new(get_credentials)
|
211
|
-
@connection.heal_uri! # We want to be sure so we try to heal the connection_url if possible
|
212
|
-
return @connection
|
213
|
-
rescue Jirarest2::CouldNotHealURIError => e
|
214
|
-
puts "REST API not found at #{e.to_s}"
|
215
|
-
exit 3
|
216
|
-
end
|
217
|
-
else
|
218
|
-
return @connection
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
=begin
|
223
|
-
create the issue on our side
|
224
|
-
=end
|
225
|
-
def open_issue
|
226
|
-
begin
|
227
|
-
credentials = get_credentials
|
228
|
-
issue=Issue.new(@issueopts.project,@issueopts.issue,get_connection)
|
229
|
-
rescue Jirarest2::AuthentificationError => e
|
230
|
-
puts "Password not accepted."
|
231
|
-
@scriptopts.pass = get_password
|
232
|
-
retry
|
233
|
-
rescue Jirarest2::AuthentificationCaptchaError => e
|
234
|
-
puts "Wrong Password too many times.\nCaptcha time at #{e.to_s} to reenable your account."
|
235
|
-
exit 1
|
236
|
-
rescue Jirarest2::WrongProjectException => e
|
237
|
-
no_issue("project",e)
|
238
|
-
rescue Jirarest2::WrongIssuetypeException => e
|
239
|
-
no_issue("project",e)
|
240
|
-
end
|
241
|
-
return issue
|
242
|
-
end
|
243
|
-
|
244
|
-
=begin
|
245
|
-
Show available fields and required fields
|
246
|
-
=end
|
247
|
-
def show_scheme
|
248
|
-
issue = open_issue
|
249
|
-
if @scriptopts.show.include?("fields") then
|
250
|
-
print "Available fields: "
|
251
|
-
puts issue.get_fieldnames.join(", ")
|
252
|
-
end
|
253
|
-
if @scriptopts.show.include?("requireds") then
|
254
|
-
print "Required fields: "
|
255
|
-
puts issue.get_requireds.join(", ")
|
256
|
-
end
|
257
|
-
exit
|
258
|
-
end
|
259
|
-
|
260
|
-
# Split the content from the command line parameter "-c"
|
261
|
-
def split_content(issue)
|
262
|
-
fields = Hash.new
|
263
|
-
@issueopts.content.each { |value|
|
264
|
-
split = value.split("=")
|
265
|
-
if issue.fieldtype(split[0]) == "array" then # If the fieldtype is an array we want to use our arrayseparator to split the fields
|
266
|
-
if ! split[1].nil? then
|
267
|
-
split[1] = split[1].split(@scriptopts.arrayseperator)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
fields[split[0]] = split[1]
|
271
|
-
}
|
272
|
-
return fields
|
273
|
-
end
|
274
|
-
|
275
|
-
# Prepare a new ticket. It will not be persisted yet.
|
276
|
-
def prepare_new_ticket
|
277
|
-
issue = open_issue
|
278
|
-
begin
|
279
|
-
if @scriptopts.contentfile then
|
280
|
-
#Input from file or STDIN
|
281
|
-
puts "Your Input now"
|
282
|
-
fields = MadbitConfig::read_configfile(@scriptopts.contentfile)
|
283
|
-
else
|
284
|
-
#Input from the command line
|
285
|
-
fields = split_content(issue)
|
286
|
-
end
|
287
|
-
valueNotAllowedRaised = false
|
288
|
-
fields.each { |name,value|
|
289
|
-
issue.set_field(name,value)
|
290
|
-
}
|
291
|
-
rescue JSON::ParserError => e
|
292
|
-
raise JSON::ParserError, e # Maybe I want to make this nice sometimes
|
293
|
-
rescue Jirarest2::WrongFieldnameException => e
|
294
|
-
no_issue("field",e)
|
295
|
-
rescue Jirarest2::ValueNotAllowedException => e
|
296
|
-
puts "Value #{split[1]} not allowed for field #{split[0]}."
|
297
|
-
puts "Please use one of: \"" + e.message.join("\", \"") + "\""
|
298
|
-
valueNotAllowedRaised = true
|
299
|
-
end
|
300
|
-
=begin
|
301
|
-
}
|
302
|
-
=end
|
303
|
-
if valueNotAllowedRaised then
|
304
|
-
raise Jirarest2::ValueNotAllowedException
|
305
|
-
end
|
306
|
-
return issue
|
307
|
-
end
|
308
|
-
|
309
|
-
=begin
|
310
|
-
a little bit to fine - could be put into the method below
|
311
|
-
=end
|
312
|
-
def set_watchers(issue)
|
313
|
-
issue.set_watcher(credentials,@issueopts.watchers)
|
314
|
-
end
|
315
|
-
|
316
|
-
=begin
|
317
|
-
do all the work to actually create a new ticket (persist, watchers, links)
|
318
|
-
=end
|
319
|
-
def create_new_ticket(issue)
|
320
|
-
begin
|
321
|
-
connection = get_connection # We need it so often in the next few lines that I prefer to get the result in a variable
|
322
|
-
result = issue.persist(connection).result
|
323
|
-
# Set the watchers
|
324
|
-
if @issueopts.watchers then
|
325
|
-
watcherssuccess = issue.add_watchers(connection,@issueopts.watchers)
|
326
|
-
end
|
327
|
-
rescue Jirarest2::RequiredFieldNotSetException => e
|
328
|
-
puts "Required field \"#{e.to_s}\" not set."
|
329
|
-
return 1
|
330
|
-
end
|
331
|
-
if result["key"] then
|
332
|
-
puts "Created new issue with issue id #{result["key"]} ."
|
333
|
-
if ! watcherssuccess && @issueopts.watchers then
|
334
|
-
puts "Watchers could not be set though."
|
335
|
-
end
|
336
|
-
if @issueopts.link then
|
337
|
-
link = IssueLink.new(connection)
|
338
|
-
remoteIssue,linktype = @issueopts.link.split("=")
|
339
|
-
linkresult = link.link(result["key"],remoteIssue,linktype)
|
340
|
-
end
|
341
|
-
return 0
|
342
|
-
elsif result["errors"] then
|
343
|
-
puts "An error occured. The error message was: #{result["errors"].to_s}"
|
344
|
-
return 2
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
=begin
|
349
|
-
called to write the config file
|
350
|
-
=end
|
351
|
-
def write_configfile
|
352
|
-
text = Hash.new
|
353
|
-
if @scriptopts.url.nil? then
|
354
|
-
text["#URL"] = "https://host.domain.com:port/path/"
|
355
|
-
else
|
356
|
-
text["URL"] = "#{@scriptopts.url}"
|
357
|
-
end
|
358
|
-
if @scriptopts.username.nil? then
|
359
|
-
text["#username"] = "USERNAME"
|
360
|
-
else
|
361
|
-
text["username"] = "#{@scriptopts.username}"
|
362
|
-
end
|
363
|
-
text["#password"] = "Your!PassW0rd"
|
364
|
-
begin
|
365
|
-
if @scriptopts.writeconf == :forcewrite then
|
366
|
-
MadbitConfig::write_configfile(@scriptopts.configfile,text,:force)
|
367
|
-
else
|
368
|
-
MadbitConfig::write_configfile(@scriptopts.configfile,text)
|
369
|
-
end
|
370
|
-
puts "Configfile written to #{@scriptopts.configfile}. Exiting."
|
371
|
-
exit 0
|
372
|
-
rescue MadbitConfig::FileExistsException => e
|
373
|
-
puts "Configfile #{e} already exists. Use \"--force-write-config-file\" to replace."
|
374
|
-
exit 1
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
|
379
|
-
# The "main function"
|
380
|
-
if @scriptopts.show != [] then
|
381
|
-
show_scheme
|
382
|
-
end
|
383
|
-
if @scriptopts.writeconf then
|
384
|
-
write_configfile
|
385
|
-
end
|
386
|
-
if ! @issueopts.content.nil? then # If the -c option is set. (-c and no content leads to another exception)
|
387
|
-
content = prepare_new_ticket
|
388
|
-
exit create_new_ticket(content)
|
389
|
-
end
|
20
|
+
puts "File has been renamed to jira_create_issue"
|
21
|
+
newfile = File.dirname($0) + "/jira_create_issue"
|
22
|
+
puts "Please use: #{newfile} #{$*.join(" ")}"
|
390
23
|
|
data/lib/jirarest2.rb
CHANGED
@@ -17,12 +17,12 @@
|
|
17
17
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
#
|
19
19
|
|
20
|
-
VERSION = "0.0.
|
20
|
+
VERSION = "0.0.7"
|
21
21
|
|
22
|
-
require_relative "connect"
|
23
|
-
require_relative "issue"
|
24
|
-
require_relative "credentials"
|
25
|
-
require_relative "exceptions"
|
26
|
-
require_relative "services/watcher"
|
27
|
-
require_relative "services"
|
28
|
-
require_relative "services/issuelink"
|
22
|
+
require_relative "jirarest2/connect"
|
23
|
+
require_relative "jirarest2/issue"
|
24
|
+
require_relative "jirarest2/credentials"
|
25
|
+
require_relative "jirarest2/exceptions"
|
26
|
+
require_relative "jirarest2/services/watcher"
|
27
|
+
require_relative "jirarest2/services"
|
28
|
+
require_relative "jirarest2/services/issuelink"
|
File without changes
|
File without changes
|
@@ -47,14 +47,14 @@ class Issue
|
|
47
47
|
@issuetype = ""
|
48
48
|
jhash["projects"].each { |value|
|
49
49
|
@project = value["key"]
|
50
|
-
value["issuetypes"].each { |
|
51
|
-
@issuetype =
|
52
|
-
|
53
|
-
|
50
|
+
value["issuetypes"].each { |value1|
|
51
|
+
@issuetype = value1["name"]
|
52
|
+
value1["fields"].delete("project") #The project key is duplicate and will make us live harder afterwards. It is marked as required but nothing happens if this key is not set.
|
53
|
+
value1["fields"].each { |key,value2|
|
54
54
|
fields = Hash.new
|
55
55
|
fields["id"] = key
|
56
|
-
if
|
57
|
-
name =
|
56
|
+
if value2["name"] then
|
57
|
+
name = value2["name"]
|
58
58
|
else
|
59
59
|
name = key
|
60
60
|
end
|
@@ -62,22 +62,22 @@ class Issue
|
|
62
62
|
if value["allowedValues"] then
|
63
63
|
# With custom fields the identifier is "value" with the built in ones it's "name"
|
64
64
|
identifier = "name"
|
65
|
-
if
|
65
|
+
if value2["schema"]["custom"] then
|
66
66
|
identifier = "value"
|
67
67
|
end
|
68
68
|
allowedValues = Array.new
|
69
|
-
|
70
|
-
allowedValues <<
|
71
|
-
}
|
69
|
+
value2["allowedValues"].each { |value3|
|
70
|
+
allowedValues << value3[identifier]
|
71
|
+
} # value3
|
72
72
|
fields["allowedValuesIdentifier"] = identifier
|
73
73
|
fields["allowedValues"] = allowedValues
|
74
74
|
end
|
75
|
-
fields["required"] =
|
76
|
-
fields["type"] =
|
75
|
+
fields["required"] = value2["required"]
|
76
|
+
fields["type"] = value2["schema"]["type"]
|
77
77
|
@issuefields[name] = fields if name != "Issue Type" # "Issue Type" is not really a required field as we have to assign it at another place anyway
|
78
|
-
}
|
79
|
-
}
|
80
|
-
}
|
78
|
+
} # value1
|
79
|
+
} # value
|
80
|
+
} # jhash
|
81
81
|
end
|
82
82
|
|
83
83
|
# @param [String] field Name of the field
|