jirarest2 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,20 @@
1
+ === 0.0.7 / 2012-07-23
2
+
3
+ * 2 major enhancements:
4
+
5
+ * Moved the library files all one directory down under lib/jirarest2/ and edited the requires fields accodringly. - closes #6
6
+ * jira_create_issue.rb moved yet again. Now to jira_create_issue - closes #8
7
+
8
+ * 1 minor enhancement:
9
+
10
+ * rewrote issuelinktype
11
+
12
+ * 3 bug fixes:
13
+
14
+ * Changed tests to WebMock. No more dependency to existing server fixes #16
15
+ * Changed the output to something more clear if the linktype is not correct. fixes #11
16
+ * It is now suficient to enter only -C - fixes #5
17
+
1
18
  === 0.0.6 / 2012-07-18
2
19
 
3
20
  * 1 minor enhancement:
@@ -5,19 +5,21 @@ Manifest.txt
5
5
  README.txt
6
6
  Rakefile
7
7
  bin/create_issue.rb
8
+ bin/jira_create_issue
8
9
  bin/jira_create_issue.rb
9
10
  copyright
10
- lib/connect.rb
11
- lib/credentials.rb
12
- lib/exceptions.rb
13
- lib/issue.rb
14
11
  lib/jirarest2.rb
12
+ lib/jirarest2/connect.rb
13
+ lib/jirarest2/credentials.rb
14
+ lib/jirarest2/exceptions.rb
15
+ lib/jirarest2/issue.rb
16
+ lib/jirarest2/madbitconfig.rb
15
17
  lib/jirarest2/result.rb
16
- lib/madbitconfig.rb
17
- lib/services.rb
18
- lib/services/issuelink.rb
19
- lib/services/issuelinktype.rb
20
- lib/services/watcher.rb
18
+ lib/jirarest2/services.rb
19
+ lib/jirarest2/services/issuelink.rb
20
+ lib/jirarest2/services/issuelinktype.rb
21
+ lib/jirarest2/services/watcher.rb
22
+ test/data/issuespec.txt
21
23
  test/data/test.config.data
22
24
  test/data/test.json
23
25
  test/data/test.nojson
data/README.txt CHANGED
@@ -19,7 +19,7 @@ The script allows you to create new issues with watchers and link those to exist
19
19
 
20
20
  == FEATURES/PROBLEMS:
21
21
 
22
- * Still in the very first alpha stages. The classes are still pretty volatile.
22
+ * Still in the alpha stages. The classes are still pretty volatile.
23
23
  * The script allows you to create new issues with watchers and link those to existing issues
24
24
 
25
25
  == SYNOPSIS:
data/Rakefile CHANGED
@@ -24,7 +24,7 @@ Hoe.spec 'jirarest2' do
24
24
 
25
25
  extra_deps << ['json', ">= 1.6.0"]
26
26
  extra_deps << ['highline', ">= 1.1.0"]
27
-
27
+ extra_dev_deps << ['webmock', ">= 1.7.0"]
28
28
 
29
29
  # self.yard_title = 'Jirarest2'
30
30
  # self.yard_markup = :markdown
@@ -17,7 +17,7 @@
17
17
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
  #
19
19
 
20
- puts "File has been renamed to jira_create_issue.rb"
21
- newfile = File.dirname($0) + "/jira_create_issue.rb"
20
+ puts "File has been renamed to jira_create_issue"
21
+ newfile = File.dirname($0) + "/jira_create_issue"
22
22
  puts "Please use: #{newfile} #{$*.join(" ")}"
23
23
 
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Script to create a new issue with jira.
4
+ # Copyright (C) 2012 Cyril Bitterich
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+
21
+
22
+ if RUBY_VERSION < "1.9"
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 "jirarest2/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 which will be ignored (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 ISSUEKEY=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
+ begin
278
+ issue = open_issue
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
+ begin
337
+ if @issueopts.link then
338
+ link = IssueLink.new(connection)
339
+ remoteIssue,linktype = @issueopts.link.split("=")
340
+ linkresult = link.link(result["key"],remoteIssue,linktype)
341
+ end
342
+ rescue Jirarest2::ValueNotAllowedException => e
343
+ puts "Link not created. Issuetype \"#{e.message}\" not valid."
344
+ puts "Please use one of the these:"
345
+ puts link.valid_issuelinktypes("\n")
346
+ exit 1
347
+ end
348
+ return 0
349
+ elsif result["errors"] then
350
+ puts "An error occured. The error message was: #{result["errors"].to_s}"
351
+ return 2
352
+ end
353
+ end
354
+
355
+ =begin
356
+ called to write the config file
357
+ =end
358
+ def write_configfile
359
+ text = Hash.new
360
+ if @scriptopts.url.nil? then
361
+ text["#URL"] = "https://host.domain.com:port/path/"
362
+ else
363
+ text["URL"] = "#{@scriptopts.url}"
364
+ end
365
+ if @scriptopts.username.nil? then
366
+ text["#username"] = "USERNAME"
367
+ else
368
+ text["username"] = "#{@scriptopts.username}"
369
+ end
370
+ text["#password"] = "Your!PassW0rd"
371
+ begin
372
+ if @scriptopts.writeconf == :forcewrite then
373
+ MadbitConfig::write_configfile(@scriptopts.configfile,text,:force)
374
+ else
375
+ MadbitConfig::write_configfile(@scriptopts.configfile,text)
376
+ end
377
+ puts "Configfile written to #{@scriptopts.configfile}. Exiting."
378
+ exit 0
379
+ rescue MadbitConfig::FileExistsException => e
380
+ puts "Configfile #{e} already exists. Use \"--force-write-config-file\" to replace."
381
+ exit 1
382
+ end
383
+ end
384
+
385
+
386
+ # The "main function"
387
+ if @scriptopts.show != [] then
388
+ show_scheme
389
+ end
390
+ if @scriptopts.writeconf then
391
+ write_configfile
392
+ end
393
+ if ! (@scriptopts.contentfile.nil? and @issueopts.content.nil?) then # If there is no content set it makes no sense to try to build a ticket
394
+ content = prepare_new_ticket
395
+ exit create_new_ticket(content)
396
+ end
397
+