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 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
+