jirarest2 0.0.11 → 0.0.12
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 +12 -0
- data/Manifest.txt +2 -0
- data/README.txt +0 -1
- data/lib/jirarest2.rb +3 -1
- data/lib/jirarest2/connect.rb +26 -6
- data/lib/jirarest2/cookie_credentials.rb +4 -13
- data/lib/jirarest2/credentials.rb +8 -3
- data/lib/jirarest2/exceptions.rb +2 -0
- data/lib/jirarest2/issue.rb +33 -215
- data/lib/jirarest2/madbitconfig.rb +0 -2
- data/lib/jirarest2/newissue.rb +247 -0
- data/lib/jirarest2/password_credentials.rb +2 -24
- data/lib/jirarest2/services/issuelink.rb +1 -1
- data/lib/jirarest2bin.rb +3 -3
- data/test/test_comment.rb +4 -1
- data/test/test_connect.rb +20 -5
- data/test/test_cookie_credentials.rb +1 -1
- data/test/test_credentials.rb +8 -3
- data/test/test_issue.rb +35 -53
- data/test/test_issuelink.rb +1 -0
- data/test/test_issuelinktype.rb +1 -0
- data/test/test_newissue.rb +69 -0
- data/test/test_result.rb +1 -0
- data/test/test_watcher.rb +4 -0
- metadata +5 -2
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== 0.0.12 / 2012-08-07
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
|
5
|
+
* Renamed Issue to NewIssue
|
6
|
+
|
7
|
+
* 2 minor enhancements:
|
8
|
+
|
9
|
+
* Added feature to see if we are really authenticated. Had to change credentials around a bit for that. Hoperfully fixes #23
|
10
|
+
* Added new Issue class to work with the issues themselves. Doesn't work and needs a lot of work yet.
|
11
|
+
|
12
|
+
|
1
13
|
=== 0.0.11 / 2012-08-05
|
2
14
|
|
3
15
|
* 1 major enhancements:
|
data/Manifest.txt
CHANGED
@@ -16,6 +16,7 @@ lib/jirarest2/credentials.rb
|
|
16
16
|
lib/jirarest2/exceptions.rb
|
17
17
|
lib/jirarest2/issue.rb
|
18
18
|
lib/jirarest2/madbitconfig.rb
|
19
|
+
lib/jirarest2/newissue.rb
|
19
20
|
lib/jirarest2/password_credentials.rb
|
20
21
|
lib/jirarest2/result.rb
|
21
22
|
lib/jirarest2/services.rb
|
@@ -40,6 +41,7 @@ test/test_issue.rb
|
|
40
41
|
test/test_issuelink.rb
|
41
42
|
test/test_issuelinktype.rb
|
42
43
|
test/test_madbitconfig.rb
|
44
|
+
test/test_newissue.rb
|
43
45
|
test/test_password_credentials.rb
|
44
46
|
test/test_result.rb
|
45
47
|
test/test_watcher.rb
|
data/README.txt
CHANGED
data/lib/jirarest2.rb
CHANGED
@@ -17,10 +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.12"
|
21
|
+
Version = VERSION
|
21
22
|
|
22
23
|
require_relative "jirarest2/connect"
|
23
24
|
require_relative "jirarest2/issue"
|
25
|
+
require_relative "jirarest2/newissue"
|
24
26
|
require_relative "jirarest2/credentials"
|
25
27
|
require_relative "jirarest2/password_credentials"
|
26
28
|
require_relative "jirarest2/cookie_credentials"
|
data/lib/jirarest2/connect.rb
CHANGED
@@ -43,7 +43,14 @@ class Connect
|
|
43
43
|
# @return [Jirarest2::Result]
|
44
44
|
def execute(operation,uritail,data)
|
45
45
|
uri = nil
|
46
|
-
|
46
|
+
if (uritail == "auth/latest/session" ) then # this is the exception regarding the base path
|
47
|
+
uristring = @credentials.baseurl+uritail
|
48
|
+
else
|
49
|
+
uristring = @credentials.connecturl+uritail
|
50
|
+
end
|
51
|
+
|
52
|
+
uristring.gsub!(/\/\//,"/").gsub!(/^(http[s]*:)/,'\1/')
|
53
|
+
uri = URI(uristring)
|
47
54
|
if data != "" then
|
48
55
|
if ! (operation == "Post" || operation == "Put") then # POST carries the payload in the body that's why we have to wait
|
49
56
|
uri.query = URI.encode_www_form(data)
|
@@ -64,12 +71,15 @@ class Connect
|
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
67
|
-
|
68
74
|
# Ask the server
|
69
75
|
result = Net::HTTP.start(uri.host, uri.port) {|http|
|
70
76
|
http.request(req)
|
71
77
|
}
|
72
78
|
# deal with output
|
79
|
+
|
80
|
+
if ((result["x-ausername"] != @credentials.username) && (uritail != "auth/latest/session" )) then # this is not the right authentication
|
81
|
+
verify_auth # make sure
|
82
|
+
end
|
73
83
|
case result
|
74
84
|
when Net::HTTPBadRequest # 400
|
75
85
|
raise Jirarest2::BadRequestError, result.body
|
@@ -82,14 +92,15 @@ class Connect
|
|
82
92
|
raise Jirarest2::AuthenticationError, result.body
|
83
93
|
end
|
84
94
|
when Net::HTTPForbidden # 403
|
85
|
-
if result.get_fields("x-authentication-denied-reason")[0] =~ /.*login-url=(.*)/ then #Captcha-Time
|
86
|
-
raise Jirarest2::
|
95
|
+
if result.get_fields("x-authentication-denied-reason") && result.get_fields("x-authentication-denied-reason")[0] =~ /.*login-url=(.*)/ then #Captcha-Time
|
96
|
+
raise Jirarest2::AuthentificationCaptchaError, $1
|
87
97
|
else
|
88
98
|
raise Jirarest2::ForbiddenError, result.body
|
89
99
|
end
|
90
100
|
when Net::HTTPNotFound # 404
|
91
101
|
raise Jirarest2::NotFoundError, result.body
|
92
|
-
|
102
|
+
when Net::HTTPMethodNotAllowed # 405
|
103
|
+
raise Jirarest2::MethodNotAllowedError, result.body
|
93
104
|
end
|
94
105
|
ret = Jirarest2::Result.new(result)
|
95
106
|
@credentials.bake_cookies(ret.header["set.cookie"]) if @credentials.instance_of?(CookieCredentials) # Make sure cookies are always up to date if we use them.
|
@@ -116,7 +127,7 @@ class Connect
|
|
116
127
|
def heal_uri(url = @credentials.connecturl)
|
117
128
|
splitURI = URI.split(url) # [Scheme,Userinfo,Host,Port,Registry,Path,Opaque,Query,Fragment]
|
118
129
|
splitURI[5].gsub!(/^(.*)2$/,'\12/')
|
119
|
-
splitURI[5].gsub!(
|
130
|
+
splitURI[5].gsub!(/[\/]+/,'/') # get rid of duplicate /
|
120
131
|
splitURI[5].gsub!(/(rest\/api\/2\/)+/,'\1') # duplicate path to rest
|
121
132
|
splitURI[5].gsub!(/^(.*)\/login.jsp(\/rest\/api\/2\/)$/,'\1\2') # dedicated login page
|
122
133
|
splitURI[5].gsub!(/^(.*)\/secure\/Dashboard.jspa(\/rest\/api\/2\/)$/,'\1\2') # copied the dashboard URL (or the login Page)
|
@@ -142,6 +153,15 @@ class Connect
|
|
142
153
|
end
|
143
154
|
end
|
144
155
|
|
156
|
+
# Verify that we are authenticated
|
157
|
+
# @return [Boolean] true if the authentication seems to be valid (actually it checks if there is a session)
|
158
|
+
def verify_auth
|
159
|
+
ret = execute("Get","auth/latest/session","")
|
160
|
+
store_cookiejar if @credentials.instance_of?(CookieCredentials) && @credentials.autosave
|
161
|
+
return ret.code == "200"
|
162
|
+
end
|
163
|
+
|
164
|
+
|
145
165
|
end # class
|
146
166
|
|
147
167
|
=begin
|
@@ -29,8 +29,8 @@ class CookieCredentials < Credentials
|
|
29
29
|
|
30
30
|
# @param [String] url URL to JIRA(tm) instance
|
31
31
|
# @param [Boolean] autosave Save the cookie on the harddisk whenever something happens?
|
32
|
-
def initialize(connecturl, autosave = false )
|
33
|
-
super(connecturl)
|
32
|
+
def initialize(connecturl, username, autosave = false )
|
33
|
+
super(connecturl,username)
|
34
34
|
@cookiejar = {}
|
35
35
|
@autosave = autosave
|
36
36
|
@cookiestore = "~/.jirarest2.cookie"
|
@@ -80,29 +80,20 @@ class CookieCredentials < Credentials
|
|
80
80
|
# @param [String] username Username to use for login
|
81
81
|
# @param [String] password Password to use for login
|
82
82
|
def login(username,password)
|
83
|
-
|
84
|
-
pcred = PasswordCredentials.new(pconnecturl,username,password)
|
83
|
+
pcred = PasswordCredentials.new(@connecturl,username,password)
|
85
84
|
pconnect = Connect.new(pcred)
|
86
85
|
result = pconnect.execute("Post","auth/latest/session",{"username" => username, "password" => password})
|
87
|
-
bake_cookies(result.header["set-cookie"]) # I already had them seperated into an
|
86
|
+
bake_cookies(result.header["set-cookie"]) # I already had them seperated into an array.
|
88
87
|
return @cookiejar["JSESSIONID"]
|
89
88
|
end
|
90
89
|
|
91
90
|
# Invalidates the current cookie
|
92
91
|
# @return [Boolean] true if successful
|
93
92
|
def logout
|
94
|
-
originalConnectUrl = @connecturl
|
95
|
-
begin
|
96
|
-
@connecturl = @connecturl.gsub(/rest\/api\/.+/,"rest/") # Unfortunately the session information is not in the same tree as all the other rest calls
|
97
93
|
con = Connect.new(self)
|
98
94
|
ret = con.execute("Delete","auth/latest/session","").code
|
99
95
|
store_cookiejar if @autosave
|
100
96
|
return true if ret == "204"
|
101
|
-
rescue
|
102
|
-
raise
|
103
|
-
ensure
|
104
|
-
@connecturl = originalConnectUrl
|
105
|
-
end
|
106
97
|
end
|
107
98
|
|
108
99
|
# Loads a cookiejar from disk
|
@@ -23,13 +23,18 @@ class Credentials
|
|
23
23
|
|
24
24
|
# url to connect to the JIRA(tm) instance
|
25
25
|
attr_reader :connecturl
|
26
|
-
|
26
|
+
# username to use
|
27
|
+
attr_accessor :username
|
28
|
+
# basepath for the REST methods
|
29
|
+
attr_reader :baseurl
|
27
30
|
|
28
31
|
# @param [String] url URL to JIRA(tm) instance
|
29
|
-
def initialize(url)
|
32
|
+
def initialize(url,username)
|
30
33
|
uri = URI(url)
|
31
34
|
if uri.instance_of?(URI::HTTP) || uri.instance_of?(URI::HTTPS) then
|
32
35
|
@connecturl = url
|
36
|
+
@username = username
|
37
|
+
@baseurl = @connecturl.gsub(/rest\/api\/.+/,"rest/")
|
33
38
|
else
|
34
39
|
raise Jirarest2::NotAnURLError
|
35
40
|
end
|
@@ -45,7 +50,7 @@ class Credentials
|
|
45
50
|
raise Jirarest2::NotAnURLError
|
46
51
|
end
|
47
52
|
end
|
48
|
-
|
53
|
+
|
49
54
|
# Get the auth header to send to the server
|
50
55
|
# @param [Net:::HTTP::Post,Net:::HTTP::Put,Net:::HTTP::Get,Net:::HTTP::Delete] request Request object
|
51
56
|
def get_auth_header(request)
|
data/lib/jirarest2/exceptions.rb
CHANGED
@@ -33,6 +33,8 @@ module Jirarest2
|
|
33
33
|
class ForbiddenError < StandardError ; end
|
34
34
|
# 404 - Results in HTML body - not JSON
|
35
35
|
class NotFoundError < StandardError ; end
|
36
|
+
# 405 - Method not allowed
|
37
|
+
class MethodNotAllowedError < StandardError ; end
|
36
38
|
# Could not heal URI
|
37
39
|
class CouldNotHealURIError < StandardError ; end
|
38
40
|
|
data/lib/jirarest2/issue.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# class to handle new issues (building them up, changing fields, persistence)
|
2
1
|
# Copyright (C) 2012 Cyril Bitterich
|
3
2
|
#
|
4
3
|
# This program is free software: you can redistribute it and/or modify
|
@@ -15,233 +14,52 @@
|
|
15
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
15
|
#
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
require "jirarest2/connect"
|
18
|
+
|
19
|
+
# represent an issue
|
21
20
|
class Issue
|
22
|
-
|
23
|
-
|
24
|
-
attr_reader :issuetype
|
25
|
-
# project the issue belongs to
|
26
|
-
attr_reader :project
|
27
|
-
# The issue numer if we got it somehow
|
28
|
-
attr_reader :issuekey
|
29
|
-
|
30
|
-
# @param [String] project Key of the JIRA(tm) project the issue belongs to
|
31
|
-
# @param [String] type Issuetype the issue belongs to
|
32
|
-
# @param [Connection] connection
|
33
|
-
def initialize (project,type,connection)
|
34
|
-
query = {:projectKeys => project, :issuetypeNames => type, :expand => "projects.issuetypes.fields" }
|
35
|
-
answer = connection.execute("Get","issue/createmeta/",query)
|
36
|
-
jhash = answer.result
|
37
|
-
parse_json(jhash)
|
38
|
-
raise Jirarest2::WrongProjectException, project if @project == ""
|
39
|
-
raise Jirarest2::WrongIssuetypeException, type if @issuetype == ""
|
40
|
-
end
|
21
|
+
#Person the issue is assigned to
|
22
|
+
attr_reader :assignee
|
41
23
|
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
@issuefields = Hash.new
|
46
|
-
@project = ""
|
47
|
-
@issuetype = ""
|
48
|
-
jhash["projects"].each { |value|
|
49
|
-
@project = value["key"]
|
50
|
-
value["issuetypes"].each { |value1|
|
51
|
-
@issuetype = value1["name"]
|
52
|
-
value1["fields"].delete("project") #The project key is duplicate and will make our live harder afterwards. It is marked as required but nothing happens if this key is not set.
|
53
|
-
value1["fields"].each { |key,value2|
|
54
|
-
fields = Hash.new
|
55
|
-
fields["id"] = key
|
56
|
-
if value2["name"] then
|
57
|
-
name = value2["name"]
|
58
|
-
else
|
59
|
-
name = key
|
60
|
-
end
|
61
|
-
# If the allowed reponses are limited we want to know them.
|
62
|
-
if value2["allowedValues"] then
|
63
|
-
# With custom fields the identifier is "value" with the built in ones it's "name"
|
64
|
-
identifier = "name"
|
65
|
-
if value2["schema"]["custom"] then
|
66
|
-
identifier = "value"
|
67
|
-
end
|
68
|
-
allowedValues = Array.new
|
69
|
-
value2["allowedValues"].each { |value3|
|
70
|
-
allowedValues << value3[identifier]
|
71
|
-
} # value3
|
72
|
-
fields["allowedValuesIdentifier"] = identifier
|
73
|
-
fields["allowedValues"] = allowedValues
|
74
|
-
end
|
75
|
-
fields["required"] = value2["required"]
|
76
|
-
fields["type"] = value2["schema"]["type"]
|
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
|
-
} # value1
|
79
|
-
} # value
|
80
|
-
} # jhash
|
24
|
+
# @param [String] issueid Key or ID of the issue
|
25
|
+
def initialize(issueid)
|
26
|
+
@issueid = issueid
|
81
27
|
end
|
82
28
|
|
83
|
-
#
|
84
|
-
# @
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
return @issuefields[field]["type"]
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# @return [Array] Names of required fields
|
95
|
-
def get_requireds
|
96
|
-
names = Array.new
|
97
|
-
@issuefields.each {|key,value|
|
98
|
-
if value["required"] then
|
99
|
-
names << key
|
100
|
-
end
|
101
|
-
}
|
102
|
-
return names
|
103
|
-
end
|
104
|
-
|
105
|
-
# @return [Array] Names of all fields
|
106
|
-
def get_fieldnames
|
107
|
-
names = Array.new
|
108
|
-
@issuefields.each {|key,value|
|
109
|
-
names << key
|
110
|
-
}
|
111
|
-
return names
|
112
|
-
end
|
113
|
-
|
114
|
-
# @param [String] name Name of a field
|
115
|
-
# @return [String] id of the field
|
116
|
-
protected
|
117
|
-
def get_id(name)
|
118
|
-
return @issuefields["name"]["id"]
|
119
|
-
end
|
120
|
-
|
121
|
-
=begin
|
122
|
-
# query=
|
123
|
-
# {"fields"=>
|
124
|
-
# { "project"=>{"key"=>"MFTP"},
|
125
|
-
# "environment"=>"REST ye merry gentlemen.",
|
126
|
-
# "My own text"=>"Creating of an issue using project keys and issue type names using the REST API",
|
127
|
-
# "issuetype"=> {"name"=>"My own type"}
|
128
|
-
# }
|
129
|
-
# }
|
130
|
-
=end
|
131
|
-
|
132
|
-
# @return [Hash] Hash to be sent to JIRA(tm) in a JSON representation
|
133
|
-
public
|
134
|
-
def jirahash
|
135
|
-
h = Hash.new
|
136
|
-
issuetype = {"issuetype" => {"name" => @issuetype}}
|
137
|
-
project = {"key" => @project}
|
138
|
-
fields = Hash.new
|
139
|
-
fields["project"] = project
|
140
|
-
# here we need to enter the relevant fields and their values
|
141
|
-
@issuefields.each { |key,value|
|
142
|
-
if key != "project" then
|
143
|
-
id = value["id"]
|
144
|
-
if ! value["value"].nil? then
|
145
|
-
fields[id] = value["value"]
|
146
|
-
end
|
147
|
-
end
|
148
|
-
}
|
149
|
-
fields = fields.merge!(issuetype)
|
150
|
-
h = {"fields" => fields}
|
151
|
-
return h
|
152
|
-
end
|
153
|
-
|
154
|
-
# check if the value is allowed for this field
|
155
|
-
# @param [String] key Name of the field
|
156
|
-
# @param [String] value Value to be checked
|
157
|
-
# @return [Boolean, Jirarest2::ValueNotAllowedException]
|
158
|
-
protected
|
159
|
-
def value_allowed?(key,value)
|
160
|
-
if @issuefields[key]["allowedValues"].include?(value)
|
161
|
-
return true
|
162
|
-
else
|
163
|
-
# puts "Value #{value} not allowed for field #{key}."
|
164
|
-
raise Jirarest2::ValueNotAllowedException.new(key, @issuefields[key]["allowedValues"]), value
|
165
|
-
end
|
29
|
+
# Receive an issue and all it's fields from jira
|
30
|
+
# @param [Connection] connection
|
31
|
+
def receive(connection)
|
32
|
+
uritail = "issue/#{@issueid}"
|
33
|
+
result = connection.execute("Get",uritail,{"expand" => "metadata"}) # get the issue AND the metadata because we already know how to parse that.
|
34
|
+
# TODO Many and more fields
|
166
35
|
end
|
167
36
|
|
168
|
-
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
# @
|
173
|
-
|
174
|
-
|
175
|
-
if
|
176
|
-
|
177
|
-
value.each {|item|
|
178
|
-
if value_allowed?(key,item) then
|
179
|
-
array << {@issuefields[key]["allowedValuesIdentifier"] => item}
|
180
|
-
end
|
181
|
-
}
|
182
|
-
@issuefields[key]["value"] = array
|
37
|
+
# Set an assignee to an issue
|
38
|
+
# @param [Connection] connection
|
39
|
+
# @param [String] name Username of the new Assignee, Defaultassignee if "-1" is given
|
40
|
+
# @param [Nil] name Deletes Assignee
|
41
|
+
# @return [Boolean] true if successfull, false if not
|
42
|
+
def set_assignee(connection, name)
|
43
|
+
uritail = "issue/#{@issueid}/assignee"
|
44
|
+
if ! name.nil? then
|
45
|
+
setname = {"name" => name}
|
183
46
|
else
|
184
|
-
|
185
|
-
@issuefields[key]["value"] = {@issuefields[key]["allowedValuesIdentifier"] => value}
|
186
|
-
end
|
47
|
+
setname = nil
|
187
48
|
end
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
# @param [String] key Name of the field
|
193
|
-
# @param [String] value Value the field should be set to, this is either a String or an Array (don't know if numbers work too)
|
194
|
-
public
|
195
|
-
def set_field(key, value)
|
196
|
-
if @issuefields.include?(key) then
|
197
|
-
if @issuefields[key].include?("allowedValues") then
|
198
|
-
set_allowed_value(key,value)
|
199
|
-
else
|
200
|
-
@issuefields[key]["value"] = value
|
201
|
-
end
|
49
|
+
retcode = connection.execute("Put",uritail,setname).code
|
50
|
+
if retcode == "204" then
|
51
|
+
@assignee = name
|
52
|
+
return true
|
202
53
|
else
|
203
|
-
|
204
|
-
puts "Unknown Field: #{key}"
|
54
|
+
return false
|
205
55
|
end
|
206
56
|
end
|
207
57
|
|
208
|
-
#
|
209
|
-
# @return [String] value of the field
|
210
|
-
def get_field(field)
|
211
|
-
@issuefields[field]["value"]
|
212
|
-
end
|
213
|
-
|
214
|
-
# persitence of this Issue object instance
|
58
|
+
# Deletes an assignee. It actually only calls set_assignee with name = nil .
|
215
59
|
# @param [Connection] connection
|
216
|
-
|
217
|
-
|
218
|
-
get_requireds.each { |fieldname|
|
219
|
-
if @issuefields[fieldname]["value"].nil? then
|
220
|
-
raise Jirarest2::RequiredFieldNotSetException, fieldname
|
221
|
-
end
|
222
|
-
}
|
223
|
-
hash = jirahash
|
224
|
-
ret = connection.execute("Post","issue/",hash)
|
225
|
-
if ret.code == "201" then # Ticket sucessfully created
|
226
|
-
@issuekey = ret.result["key"]
|
227
|
-
end
|
228
|
-
return ret
|
60
|
+
def remove_assignee(connection)
|
61
|
+
set_assignee(connection, nil)
|
229
62
|
end
|
230
63
|
|
231
64
|
|
232
|
-
|
233
|
-
# Set the watchers for this Ticket
|
234
|
-
# @param [Connection] connection
|
235
|
-
# @param [Array] watchers Watchers to be added
|
236
|
-
# @return [Boolean] True if successful for all
|
237
|
-
def add_watchers(connection,watchers)
|
238
|
-
success = false # Return whether we were successful with the watchers
|
239
|
-
watch = Watcher.new(connection,@issuekey)
|
240
|
-
watchers.each { |person|
|
241
|
-
success = watch.add_watcher(person)
|
242
|
-
}
|
243
|
-
return success
|
244
|
-
end
|
245
|
-
|
246
|
-
|
247
|
-
end # class
|
65
|
+
end # Issue
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# class to handle new issues (building them up, changing fields, persistence)
|
2
|
+
# Copyright (C) 2012 Cyril Bitterich
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
|
18
|
+
=begin
|
19
|
+
# An Issue object contains all the data of an issue
|
20
|
+
=end
|
21
|
+
class NewIssue
|
22
|
+
|
23
|
+
# issue type of the issue
|
24
|
+
attr_reader :issuetype
|
25
|
+
# project the issue belongs to
|
26
|
+
attr_reader :project
|
27
|
+
# The issue numer if we got it somehow
|
28
|
+
attr_reader :issuekey
|
29
|
+
|
30
|
+
# @param [String] project Key of the JIRA(tm) project the issue belongs to
|
31
|
+
# @param [String] type Issuetype the issue belongs to
|
32
|
+
# @param [Connection] connection
|
33
|
+
def initialize (project,type,connection)
|
34
|
+
query = {:projectKeys => project, :issuetypeNames => type, :expand => "projects.issuetypes.fields" }
|
35
|
+
answer = connection.execute("Get","issue/createmeta/",query)
|
36
|
+
jhash = answer.result
|
37
|
+
parse_json(jhash)
|
38
|
+
raise Jirarest2::WrongProjectException, project if @project == ""
|
39
|
+
raise Jirarest2::WrongIssuetypeException, type if @issuetype == ""
|
40
|
+
end
|
41
|
+
|
42
|
+
# produces an instance-variable @issuefields that can be
|
43
|
+
# @param [Hash] jhash Hashed version of the json-snippet JIRA(tm) returns
|
44
|
+
def parse_json (jhash)
|
45
|
+
@issuefields = Hash.new
|
46
|
+
@project = ""
|
47
|
+
@issuetype = ""
|
48
|
+
jhash["projects"].each { |value|
|
49
|
+
@project = value["key"]
|
50
|
+
value["issuetypes"].each { |value1|
|
51
|
+
@issuetype = value1["name"]
|
52
|
+
value1["fields"].delete("project") #The project key is duplicate and will make our live harder afterwards. It is marked as required but nothing happens if this key is not set.
|
53
|
+
value1["fields"].each { |key,value2|
|
54
|
+
fields = Hash.new
|
55
|
+
fields["id"] = key
|
56
|
+
if value2["name"] then
|
57
|
+
name = value2["name"]
|
58
|
+
else
|
59
|
+
name = key
|
60
|
+
end
|
61
|
+
# If the allowed reponses are limited we want to know them.
|
62
|
+
if value2["allowedValues"] then
|
63
|
+
# With custom fields the identifier is "value" with the built in ones it's "name"
|
64
|
+
identifier = "name"
|
65
|
+
if value2["schema"]["custom"] then
|
66
|
+
identifier = "value"
|
67
|
+
end
|
68
|
+
allowedValues = Array.new
|
69
|
+
value2["allowedValues"].each { |value3|
|
70
|
+
allowedValues << value3[identifier]
|
71
|
+
} # value3
|
72
|
+
fields["allowedValuesIdentifier"] = identifier
|
73
|
+
fields["allowedValues"] = allowedValues
|
74
|
+
end
|
75
|
+
fields["required"] = value2["required"]
|
76
|
+
fields["type"] = value2["schema"]["type"]
|
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
|
+
} # value1
|
79
|
+
} # value
|
80
|
+
} # jhash
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param [String] field Name of the field
|
84
|
+
# @return [String] type of the Field
|
85
|
+
def fieldtype(field)
|
86
|
+
# If the fieldname is wrong we want to tell this and stop execution (or maybe let the caller fix it)
|
87
|
+
if @issuefields[field].nil? then
|
88
|
+
raise Jirarest2::WrongFieldnameException, field
|
89
|
+
else
|
90
|
+
return @issuefields[field]["type"]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return [Array] Names of required fields
|
95
|
+
def get_requireds
|
96
|
+
names = Array.new
|
97
|
+
@issuefields.each {|key,value|
|
98
|
+
if value["required"] then
|
99
|
+
names << key
|
100
|
+
end
|
101
|
+
}
|
102
|
+
return names
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Array] Names of all fields
|
106
|
+
def get_fieldnames
|
107
|
+
names = Array.new
|
108
|
+
@issuefields.each {|key,value|
|
109
|
+
names << key
|
110
|
+
}
|
111
|
+
return names
|
112
|
+
end
|
113
|
+
|
114
|
+
# @param [String] name Name of a field
|
115
|
+
# @return [String] id of the field
|
116
|
+
protected
|
117
|
+
def get_id(name)
|
118
|
+
return @issuefields["name"]["id"]
|
119
|
+
end
|
120
|
+
|
121
|
+
=begin
|
122
|
+
# query=
|
123
|
+
# {"fields"=>
|
124
|
+
# { "project"=>{"key"=>"MFTP"},
|
125
|
+
# "environment"=>"REST ye merry gentlemen.",
|
126
|
+
# "My own text"=>"Creating of an issue using project keys and issue type names using the REST API",
|
127
|
+
# "issuetype"=> {"name"=>"My own type"}
|
128
|
+
# }
|
129
|
+
# }
|
130
|
+
=end
|
131
|
+
|
132
|
+
# @return [Hash] Hash to be sent to JIRA(tm) in a JSON representation
|
133
|
+
public
|
134
|
+
def jirahash
|
135
|
+
h = Hash.new
|
136
|
+
issuetype = {"issuetype" => {"name" => @issuetype}}
|
137
|
+
project = {"key" => @project}
|
138
|
+
fields = Hash.new
|
139
|
+
fields["project"] = project
|
140
|
+
# here we need to enter the relevant fields and their values
|
141
|
+
@issuefields.each { |key,value|
|
142
|
+
if key != "project" then
|
143
|
+
id = value["id"]
|
144
|
+
if ! value["value"].nil? then
|
145
|
+
fields[id] = value["value"]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
}
|
149
|
+
fields = fields.merge!(issuetype)
|
150
|
+
h = {"fields" => fields}
|
151
|
+
return h
|
152
|
+
end
|
153
|
+
|
154
|
+
# check if the value is allowed for this field
|
155
|
+
# @param [String] key Name of the field
|
156
|
+
# @param [String] value Value to be checked
|
157
|
+
# @return [Boolean, Jirarest2::ValueNotAllowedException]
|
158
|
+
protected
|
159
|
+
def value_allowed?(key,value)
|
160
|
+
if @issuefields[key]["allowedValues"].include?(value)
|
161
|
+
return true
|
162
|
+
else
|
163
|
+
# puts "Value #{value} not allowed for field #{key}."
|
164
|
+
raise Jirarest2::ValueNotAllowedException.new(key, @issuefields[key]["allowedValues"]), value
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
# Special setter for fields that have a limited numer of allowed values.
|
170
|
+
#
|
171
|
+
# This setter might be included in set_field at a later date.
|
172
|
+
# @param [String] key Name of the field
|
173
|
+
# @param [String] value Value to be checked
|
174
|
+
def set_allowed_value(key,value)
|
175
|
+
if @issuefields[key]["type"] == "array" && value.instance_of?(Array) then
|
176
|
+
array = Array.new
|
177
|
+
value.each {|item|
|
178
|
+
if value_allowed?(key,item) then
|
179
|
+
array << {@issuefields[key]["allowedValuesIdentifier"] => item}
|
180
|
+
end
|
181
|
+
}
|
182
|
+
@issuefields[key]["value"] = array
|
183
|
+
else
|
184
|
+
if value_allowed?(key,value) then
|
185
|
+
@issuefields[key]["value"] = {@issuefields[key]["allowedValuesIdentifier"] => value}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
# TODO We are not yet able to work with "Cascading Select" fields ( "custom": "com.atlassian.jira.plugin.system.customfieldtypes:cascadingselect")
|
192
|
+
# @param [String] key Name of the field
|
193
|
+
# @param [String] value Value the field should be set to, this is either a String or an Array (don't know if numbers work too)
|
194
|
+
public
|
195
|
+
def set_field(key, value)
|
196
|
+
if @issuefields.include?(key) then
|
197
|
+
if @issuefields[key].include?("allowedValues") then
|
198
|
+
set_allowed_value(key,value)
|
199
|
+
else
|
200
|
+
@issuefields[key]["value"] = value
|
201
|
+
end
|
202
|
+
else
|
203
|
+
raise Jirarest2::WrongFieldnameException, key
|
204
|
+
puts "Unknown Field: #{key}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# @param [String] field Name of the field
|
209
|
+
# @return [String] value of the field
|
210
|
+
def get_field(field)
|
211
|
+
@issuefields[field]["value"]
|
212
|
+
end
|
213
|
+
|
214
|
+
# persitence of this Issue object instance
|
215
|
+
# @param [Connection] connection
|
216
|
+
# @return [Jirarest2::Result]
|
217
|
+
def persist(connection)
|
218
|
+
get_requireds.each { |fieldname|
|
219
|
+
if @issuefields[fieldname]["value"].nil? then
|
220
|
+
raise Jirarest2::RequiredFieldNotSetException, fieldname
|
221
|
+
end
|
222
|
+
}
|
223
|
+
hash = jirahash
|
224
|
+
ret = connection.execute("Post","issue/",hash)
|
225
|
+
if ret.code == "201" then # Ticket sucessfully created
|
226
|
+
@issuekey = ret.result["key"]
|
227
|
+
end
|
228
|
+
return ret
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
|
233
|
+
# Set the watchers for this Ticket
|
234
|
+
# @param [Connection] connection
|
235
|
+
# @param [Array] watchers Watchers to be added
|
236
|
+
# @return [Boolean] True if successfull for all
|
237
|
+
def add_watchers(connection,watchers)
|
238
|
+
success = false # Return whether we were successful with the watchers
|
239
|
+
watch = Watcher.new(connection,@issuekey)
|
240
|
+
watchers.each { |person|
|
241
|
+
success = watch.add_watcher(person)
|
242
|
+
}
|
243
|
+
return success
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
end # class
|
@@ -21,8 +21,6 @@ require_relative "credentials"
|
|
21
21
|
# A Credentials object contains the data required to connect to a JIRA(tm) instance.
|
22
22
|
class PasswordCredentials < Credentials
|
23
23
|
|
24
|
-
# username to use
|
25
|
-
attr_accessor :username
|
26
24
|
# password for the connection
|
27
25
|
attr_accessor :password
|
28
26
|
|
@@ -30,30 +28,10 @@ class PasswordCredentials < Credentials
|
|
30
28
|
# @param [String] username
|
31
29
|
# @param [String] password
|
32
30
|
def initialize(url,username,password)
|
33
|
-
super(url)
|
34
|
-
@username = username
|
31
|
+
super(url,username)
|
35
32
|
@password = password
|
36
|
-
=begin
|
37
|
-
uri = URI(url)
|
38
|
-
if uri.instance_of?(URI::HTTP) || uri.instance_of?(URI::HTTPS) then
|
39
|
-
@connecturl = url
|
40
|
-
else
|
41
|
-
raise Jirarest2::NotAnURLError
|
42
|
-
end
|
43
|
-
=end
|
44
33
|
end
|
45
|
-
|
46
|
-
# Throws an Jirarest2::NotAnURLError if the given String is not an URI.
|
47
|
-
# @param [String] url
|
48
|
-
def connecturl=(url)
|
49
|
-
uri = URI(url)
|
50
|
-
if uri.instance_of?(URI::HTTP) || uri.instance_of?(URI::HTTPS) then
|
51
|
-
@connecturl = url
|
52
|
-
else
|
53
|
-
raise Jirarest2::NotAnURLError
|
54
|
-
end
|
55
|
-
end
|
56
|
-
=end
|
34
|
+
|
57
35
|
# Get the auth header to send to the server
|
58
36
|
# @param [Net:::HTTP::Post,Net:::HTTP::Put,Net:::HTTP::Get,Net:::HTTP::Delete] request Request object
|
59
37
|
def get_auth_header(request)
|
data/lib/jirarest2bin.rb
CHANGED
@@ -114,7 +114,7 @@ module Jirarest2Bin
|
|
114
114
|
end
|
115
115
|
|
116
116
|
if !scriptopts.nocookieauth then
|
117
|
-
credentials = CookieCredentials.new(scriptopts.url,true)
|
117
|
+
credentials = CookieCredentials.new(scriptopts.url,scriptopts.username,true)
|
118
118
|
credentials.load_cookiejar
|
119
119
|
return credentials
|
120
120
|
# TODO What to do if the cookie expired?
|
@@ -168,7 +168,7 @@ module Jirarest2Bin
|
|
168
168
|
end
|
169
169
|
case command
|
170
170
|
when :issue
|
171
|
-
return connection,
|
171
|
+
return connection, NewIssue.new(args[0],args[1],connection)
|
172
172
|
when :connection
|
173
173
|
return connection
|
174
174
|
end
|
@@ -234,7 +234,7 @@ module Jirarest2Bin
|
|
234
234
|
end
|
235
235
|
|
236
236
|
opts.on_tail("--version", "Show version") do
|
237
|
-
puts
|
237
|
+
puts opts.ver
|
238
238
|
exit
|
239
239
|
end
|
240
240
|
|
data/test/test_comment.rb
CHANGED
@@ -8,6 +8,8 @@ class TestComment < MiniTest::Unit::TestCase
|
|
8
8
|
def setup
|
9
9
|
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
10
10
|
@con = Connect.new(cred)
|
11
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
12
|
+
|
11
13
|
end
|
12
14
|
|
13
15
|
def test_get_comment_filled
|
@@ -43,7 +45,8 @@ class TestComment < MiniTest::Unit::TestCase
|
|
43
45
|
stub_request(:post, "http://test:1234@localhost:2990/jira/rest/api/2/issue/SP-3/comment").with(:body => "{\"body\":\"Text for the long run\"}",:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 201, :body => '{"self":"http://localhost:2990/jira/rest/api/2/issue/10104/comment/10110","id":"10110","author":{"self":"http://localhost:2990/jira/rest/api/2/user?username=test","name":"test","emailAddress":"jira-test@localhost","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122","48x48":"http://localhost:2990/jira/secure/useravatar?avatarId=10122"},"displayName":"Test User","active":true},"body":"Text for the long run","updateAuthor":{"self":"http://localhost:2990/jira/rest/api/2/user?username=test","name":"test","emailAddress":"jira-test@localhost","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122","48x48":"http://localhost:2990/jira/secure/useravatar?avatarId=10122"},"displayName":"Test User","active":true},"created":"2012-07-27T20:36:07.832+0200","updated":"2012-07-27T20:36:07.832+0200"}', :headers => {})
|
44
46
|
|
45
47
|
comment = Comment.new(@con,"SP-3")
|
46
|
-
|
48
|
+
match = Regexp.new(',"body":"Text for the long run","updateAuthor"')
|
49
|
+
assert_match match, comment.add("Text for the long run").body
|
47
50
|
assert_instance_of Jirarest2::Result, comment.add("Text for the long run")
|
48
51
|
end
|
49
52
|
|
data/test/test_connect.rb
CHANGED
@@ -8,6 +8,8 @@ class TestConnect < MiniTest::Unit::TestCase
|
|
8
8
|
def setup
|
9
9
|
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
10
10
|
@con = Connect.new(cred)
|
11
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
12
|
+
|
11
13
|
end
|
12
14
|
|
13
15
|
def test_access
|
@@ -33,6 +35,8 @@ class TestConnect < MiniTest::Unit::TestCase
|
|
33
35
|
|
34
36
|
def test_check_uri_false
|
35
37
|
stub_request(:get, "http://test:1234@localhost:2990/rest/api/2/dashboard").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 400, :body => "", :headers => {})
|
38
|
+
stub_request(:get, "http://test:1234@localhost:2990/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
39
|
+
|
36
40
|
cred = PasswordCredentials.new("http://localhost:2990/rest/api/2/","test","1234")
|
37
41
|
con1 = Connect.new(cred)
|
38
42
|
assert_equal false,con1.check_uri
|
@@ -56,11 +60,11 @@ class TestConnect < MiniTest::Unit::TestCase
|
|
56
60
|
end
|
57
61
|
|
58
62
|
def test_working_heal_uri!
|
59
|
-
|
60
|
-
|
61
|
-
|
63
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/login.jsp/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
64
|
+
|
65
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/login.jsp/rest/api/2/dashboard").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 404, :body => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><status><status-code>404</status-code><message>null for uri: http://localhost:2990/jira//rest/api//2//dashboard</message></status>', :headers => {"X-AUSERNAME" => "test" })
|
62
66
|
## First an URL we can fix
|
63
|
-
cred = PasswordCredentials.new("http://localhost:2990/jira
|
67
|
+
cred = PasswordCredentials.new("http://localhost:2990/jira/login.jsp/rest/api//2//","test","1234")
|
64
68
|
con = Connect.new(cred)
|
65
69
|
assert_equal false,con.check_uri
|
66
70
|
|
@@ -71,6 +75,7 @@ class TestConnect < MiniTest::Unit::TestCase
|
|
71
75
|
|
72
76
|
def test_notworking_heal_uri!
|
73
77
|
stub_request(:get, "http://test:1234@localhost:2990/secure/Dashboard.jspadashboard").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 400, :body => "", :headers => {})
|
78
|
+
stub_request(:get, "http://test:1234@localhost:2990/secure/Dashboard.jspaauth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
74
79
|
## And now one we cant't fix
|
75
80
|
cred = PasswordCredentials.new("http://localhost:2990/secure/Dashboard.jspa","test","1234")
|
76
81
|
con = Connect.new(cred)
|
@@ -78,5 +83,15 @@ class TestConnect < MiniTest::Unit::TestCase
|
|
78
83
|
assert_raises(Jirarest2::CouldNotHealURIError){ con.heal_uri! }
|
79
84
|
assert_equal false,con.check_uri
|
80
85
|
end
|
81
|
-
|
86
|
+
|
87
|
+
#
|
88
|
+
# Missing tests
|
89
|
+
#
|
90
|
+
def test_verify_auth_session
|
91
|
+
# returncode 200
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_verify_auth_nosession
|
95
|
+
# returncode 401
|
96
|
+
end
|
82
97
|
end
|
@@ -7,7 +7,7 @@ require "deb"
|
|
7
7
|
class TestCookieCredentials < MiniTest::Unit::TestCase
|
8
8
|
|
9
9
|
def setup
|
10
|
-
@cred = CookieCredentials.new("http://localhost:2990/jira/rest/api/2/")
|
10
|
+
@cred = CookieCredentials.new("http://localhost:2990/jira/rest/api/2/","test")
|
11
11
|
@header = ["a.xsrf.token=BP8Q-WXN6-SKX3-NB5M|11ca22ad2bf3467bee711e5b912536d1fb046a4a|lout; Path=/myapp", "JSESSIONID=6C3AE9205FFC6E0DEC3353C2D10745D8; Path=/"]
|
12
12
|
end
|
13
13
|
|
data/test/test_credentials.rb
CHANGED
@@ -4,13 +4,13 @@ require "jirarest2/exceptions"
|
|
4
4
|
|
5
5
|
class TestCredentials < MiniTest::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
@cred = Credentials.new("https://localhost:2990")
|
7
|
+
@cred = Credentials.new("https://localhost:2990","test")
|
8
8
|
end
|
9
9
|
|
10
10
|
def testInitialize
|
11
|
-
assert_instance_of(Credentials, Credentials.new("https://localhost:2990"))
|
11
|
+
assert_instance_of(Credentials, Credentials.new("https://localhost:2990","test"))
|
12
12
|
assert_raises(Jirarest2::NotAnURLError) {
|
13
|
-
Credentials.new("localhost:2990")
|
13
|
+
Credentials.new("localhost:2990","Minime")
|
14
14
|
}
|
15
15
|
assert_equal "https://localhost:2990", @cred.connecturl
|
16
16
|
end
|
@@ -23,4 +23,9 @@ class TestCredentials < MiniTest::Unit::TestCase
|
|
23
23
|
}
|
24
24
|
end
|
25
25
|
|
26
|
+
def test_baseurl
|
27
|
+
cre = Credentials.new("https://localhost:2990/blah/blubb/rest/api/latest","test")
|
28
|
+
assert_equal "https://localhost:2990/blah/blubb/rest/",cre.baseurl
|
29
|
+
end
|
30
|
+
|
26
31
|
end
|
data/test/test_issue.rb
CHANGED
@@ -1,68 +1,50 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require "minitest/autorun"
|
3
|
-
require "jirarest2"
|
3
|
+
require "jirarest2/connect"
|
4
|
+
require "jirarest2/credentials"
|
5
|
+
require "jirarest2/password_credentials"
|
6
|
+
require "jirarest2/cookie_credentials"
|
7
|
+
require "jirarest2/issue"
|
8
|
+
require "pp"
|
4
9
|
require "webmock/minitest"
|
5
10
|
|
6
11
|
class TestIssue < MiniTest::Unit::TestCase
|
7
|
-
def setup
|
8
|
-
@credentials = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
9
|
-
@connect = Connect.new(@credentials)
|
10
|
-
raw_response_file = File.new(File.dirname(__FILE__)+"/data/issuespec.txt")
|
11
|
-
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issue/createmeta/?expand=projects.issuetypes.fields&issuetypeNames=My%20issue%20type&projectKeys=MFTP").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(raw_response_file)
|
12
|
-
@existentIssue = Issue.new("MFTP","My issue type",@connect)
|
13
|
-
end
|
14
12
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
}
|
20
|
-
end
|
13
|
+
def setup
|
14
|
+
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
15
|
+
@con = Connect.new(cred)
|
16
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
21
17
|
|
22
|
-
def testNonExistentIssuetype
|
23
|
-
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issue/createmeta/?expand=projects.issuetypes.fields&issuetypeNames=fasel&projectKeys=MFTP").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => '{"expand":"projects","projects":[{"expand":"issuetypes","self":"http://localhost:2990/jira/rest/api/2/project/MFTP","id":"10000","key":"MFTP","name":"My first Test Project","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/projectavatar?size=small&pid=10000&avatarId=10011","48x48":"http://localhost:2990/jira/secure/projectavatar?pid=10000&avatarId=10011"},"issuetypes":[]}]}', :headers => {})
|
24
|
-
assert_raises(Jirarest2::WrongIssuetypeException) {
|
25
|
-
nonexistentIssue = Issue.new("MFTP","fasel",@connect)
|
26
|
-
}
|
27
18
|
end
|
28
19
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
20
|
+
def test_receive
|
21
|
+
issue = Issue.new("SP-2")
|
22
|
+
WebMock.disable!
|
23
|
+
# pp issue.receive(@con)
|
24
|
+
WebMock.enable!
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_set_assignee_no_rights
|
28
|
+
stub_request(:put, "http://test:1234@localhost:2990/jira/rest/api/2/issue/SP-2/assignee").with(:body => "{\"name\":\"cebit\"}",:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 403, :body => "", :headers => {})
|
29
|
+
issue = Issue.new("SP-2")
|
30
|
+
assert_raises(Jirarest2::ForbiddenError) {
|
31
|
+
issue.set_assignee(@con,"cebit")
|
32
|
+
}
|
41
33
|
end
|
42
34
|
|
43
|
-
def
|
44
|
-
issue =
|
45
|
-
issue.
|
46
|
-
issue.
|
47
|
-
issue.set_field("Description","And a little bit for \n the big \n text field")
|
48
|
-
issue.set_field("Priority","Trivial")
|
49
|
-
issue.set_field("List select","Räuber")
|
50
|
-
issue.set_field("Multi Select",["Glocke","Kabale und Liebe"])
|
51
|
-
blankissue = issue.jirahash
|
52
|
-
assert blankissue
|
53
|
-
assert_equal "MFTP", blankissue["fields"]["project"]["key"]
|
54
|
-
assert_equal "Summary Text", issue.get_field("Summary")
|
35
|
+
def test_set_assignee_with_rights
|
36
|
+
stub_request(:put, "http://test:1234@localhost:2990/jira/rest/api/2/issue/SP-2/assignee").with(:body => "{\"name\":\"cebit\"}",:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 204, :body => "", :headers => {})
|
37
|
+
issue = Issue.new("SP-2")
|
38
|
+
assert_equal true,issue.set_assignee(@con,"cebit")
|
55
39
|
end
|
56
40
|
|
57
|
-
|
58
|
-
|
59
|
-
issue =
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
issue.persist(@connect)
|
41
|
+
def test_remove_assignee
|
42
|
+
stub_request(:get, "http://admin:admin@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
43
|
+
stub_request(:put, "http://admin:admin@localhost:2990/jira/rest/api/2/issue/SP-2/assignee").with(:body => "null",:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 204, :body => "", :headers => {})
|
44
|
+
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","admin","admin")
|
45
|
+
con = Connect.new(cred)
|
46
|
+
issue = Issue.new("SP-2")
|
47
|
+
assert_equal true,issue.remove_assignee(con)
|
65
48
|
end
|
66
|
-
|
67
|
-
|
68
49
|
end
|
50
|
+
|
data/test/test_issuelink.rb
CHANGED
@@ -11,6 +11,7 @@ class TestIssueLink < MiniTest::Unit::TestCase
|
|
11
11
|
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
12
12
|
@con = Connect.new(cred)
|
13
13
|
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issueLinkType").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => '{"issueLinkTypes":[{"id":"10000","name":"Blocks","inward":"is blocked by","outward":"blocks","self":"http://localhost:2990/jira/rest/api/2/issueLinkType/10000"},{"id":"10001","name":"Cloners","inward":"is cloned by","outward":"clones","self":"http://localhost:2990/jira/rest/api/2/issueLinkType/10001"},{"id":"10002","name":"Duplicate","inward":"is duplicated by","outward":"duplicates","self":"http://localhost:2990/jira/rest/api/2/issueLinkType/10002"},{"id":"10003","name":"Relates","inward":"relates to","outward":"relates to","self":"http://localhost:2990/jira/rest/api/2/issueLinkType/10003"}]}', :headers => {})
|
14
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
14
15
|
@link = IssueLink.new(@con)
|
15
16
|
end
|
16
17
|
|
data/test/test_issuelinktype.rb
CHANGED
@@ -10,6 +10,7 @@ class TestIssueLinkType < MiniTest::Unit::TestCase
|
|
10
10
|
def setup
|
11
11
|
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
12
12
|
@con = Connect.new(cred)
|
13
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
13
14
|
end
|
14
15
|
|
15
16
|
def test_single_name
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "jirarest2"
|
4
|
+
require "webmock/minitest"
|
5
|
+
|
6
|
+
class TestNewIssue < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@credentials = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
9
|
+
@connect = Connect.new(@credentials)
|
10
|
+
raw_response_file = File.new(File.dirname(__FILE__)+"/data/issuespec.txt")
|
11
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issue/createmeta/?expand=projects.issuetypes.fields&issuetypeNames=My%20issue%20type&projectKeys=MFTP").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(raw_response_file)
|
12
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
13
|
+
@existentIssue = NewIssue.new("MFTP","My issue type",@connect)
|
14
|
+
end
|
15
|
+
|
16
|
+
def testNonExistentProject
|
17
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issue/createmeta/?expand=projects.issuetypes.fields&issuetypeNames=fasel&projectKeys=blubber").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => '{"expand":"projects","projects":[]}', :headers => {})
|
18
|
+
assert_raises(Jirarest2::WrongProjectException) {
|
19
|
+
nonexistentProject = NewIssue.new("blubber","fasel",@connect)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def testNonExistentIssuetype
|
24
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issue/createmeta/?expand=projects.issuetypes.fields&issuetypeNames=fasel&projectKeys=MFTP").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => '{"expand":"projects","projects":[{"expand":"issuetypes","self":"http://localhost:2990/jira/rest/api/2/project/MFTP","id":"10000","key":"MFTP","name":"My first Test Project","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/projectavatar?size=small&pid=10000&avatarId=10011","48x48":"http://localhost:2990/jira/secure/projectavatar?pid=10000&avatarId=10011"},"issuetypes":[]}]}', :headers => {})
|
25
|
+
assert_raises(Jirarest2::WrongIssuetypeException) {
|
26
|
+
nonexistentIssue = NewIssue.new("MFTP","fasel",@connect)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def testExistent
|
31
|
+
assert_equal "My issue type", @existentIssue.issuetype
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_requireds
|
35
|
+
req = @existentIssue.get_requireds
|
36
|
+
assert_equal true, req.include?("Summary")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_get_fieldnames
|
40
|
+
req = @existentIssue.get_fieldnames
|
41
|
+
assert_equal true, req.include?("Priority")
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_jirahash
|
45
|
+
issue = @existentIssue
|
46
|
+
issue.set_field("Summary","Summary Text")
|
47
|
+
issue.set_field("Großes Text","My own text as well")
|
48
|
+
issue.set_field("Description","And a little bit for \n the big \n text field")
|
49
|
+
issue.set_field("Priority","Trivial")
|
50
|
+
issue.set_field("List select","Räuber")
|
51
|
+
issue.set_field("Multi Select",["Glocke","Kabale und Liebe"])
|
52
|
+
blankissue = issue.jirahash
|
53
|
+
assert blankissue
|
54
|
+
assert_equal "MFTP", blankissue["fields"]["project"]["key"]
|
55
|
+
assert_equal "Summary Text", issue.get_field("Summary")
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def test_persist
|
60
|
+
issue = @existentIssue
|
61
|
+
issue.set_field("Summary","Summary Text")
|
62
|
+
issue.set_field("Priority","Trivial")
|
63
|
+
stub_request(:post, "http://test:1234@localhost:2990/jira/rest/api/2/issue/").with(:body => "{\"fields\":{\"project\":{\"key\":\"MFTP\"},\"summary\":\"Summary Text\",\"priority\":{\"name\":\"Trivial\"},\"issuetype\":{\"name\":\"My issue type\"}}}",:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 201, :body => '{"id":"10608","key":"MFTP-11","self":"http://localhost:2990/jira/rest/api/2/issue/10608"}', :headers => {})
|
64
|
+
|
65
|
+
issue.persist(@connect)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
data/test/test_result.rb
CHANGED
@@ -8,6 +8,7 @@ class TestResult < MiniTest::Unit::TestCase
|
|
8
8
|
def setup
|
9
9
|
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
10
10
|
@con = Connect.new(cred)
|
11
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
11
12
|
end
|
12
13
|
|
13
14
|
def test_code
|
data/test/test_watcher.rb
CHANGED
@@ -9,10 +9,14 @@ class TestWatcher < MiniTest::Unit::TestCase
|
|
9
9
|
def setup
|
10
10
|
cred = PasswordCredentials.new("http://localhost:2990/jira/rest/api/2/","test","1234")
|
11
11
|
@con = Connect.new(cred)
|
12
|
+
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
13
|
+
|
12
14
|
end
|
13
15
|
|
14
16
|
def test_get_watchers_filled
|
15
17
|
stub_request(:get, "http://test:1234@localhost:2990/jira/rest/api/2/issue/MFTP-7/watchers").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => '{"self":"http://localhost:2990/jira/rest/api/2/issue/MFTP-7/watchers","isWatching":true,"watchCount":3,"watchers":[{"self":"http://localhost:2990/jira/rest/api/2/user?username=admin","name":"admin","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122","48x48":"http://localhost:2990/jira/secure/useravatar?avatarId=10122"},"displayName":"admin","active":true},{"self":"http://localhost:2990/jira/rest/api/2/user?username=cebit","name":"cebit","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122","48x48":"http://localhost:2990/jira/secure/useravatar?avatarId=10122"},"displayName":"Cyril Bitterich","active":true},{"self":"http://localhost:2990/jira/rest/api/2/user?username=test","name":"test","avatarUrls":{"16x16":"http://localhost:2990/jira/secure/useravatar?size=small&avatarId=10122","48x48":"http://localhost:2990/jira/secure/useravatar?avatarId=10122"},"displayName":"Test User","active":true}]}', :headers => {})
|
18
|
+
# stub_request(:get, "http://test:1234@localhost:2990/jira/rest/auth/latest/session").with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json;charset=UTF-8', 'User-Agent'=>'Ruby'}).to_return(:status => 200, :body => "", :headers => {})
|
19
|
+
|
16
20
|
watchers = Watcher.new(@con,"MFTP-7")
|
17
21
|
assert_equal ["admin","cebit","test"], watchers.get_watchers
|
18
22
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jirarest2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -50,7 +50,7 @@ cert_chain:
|
|
50
50
|
-----END CERTIFICATE-----
|
51
51
|
|
52
52
|
'
|
53
|
-
date: 2012-08-
|
53
|
+
date: 2012-08-07 00:00:00.000000000 Z
|
54
54
|
dependencies:
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- lib/jirarest2/exceptions.rb
|
172
172
|
- lib/jirarest2/issue.rb
|
173
173
|
- lib/jirarest2/madbitconfig.rb
|
174
|
+
- lib/jirarest2/newissue.rb
|
174
175
|
- lib/jirarest2/password_credentials.rb
|
175
176
|
- lib/jirarest2/result.rb
|
176
177
|
- lib/jirarest2/services.rb
|
@@ -195,6 +196,7 @@ files:
|
|
195
196
|
- test/test_issuelink.rb
|
196
197
|
- test/test_issuelinktype.rb
|
197
198
|
- test/test_madbitconfig.rb
|
199
|
+
- test/test_newissue.rb
|
198
200
|
- test/test_password_credentials.rb
|
199
201
|
- test/test_result.rb
|
200
202
|
- test/test_watcher.rb
|
@@ -236,6 +238,7 @@ test_files:
|
|
236
238
|
- test/test_credentials.rb
|
237
239
|
- test/test_issue.rb
|
238
240
|
- test/test_password_credentials.rb
|
241
|
+
- test/test_newissue.rb
|
239
242
|
- test/test_result.rb
|
240
243
|
- test/test_issuelink.rb
|
241
244
|
- test/test_watcher.rb
|
metadata.gz.sig
CHANGED
Binary file
|