jirarest2 0.0.12 → 0.0.13
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 +18 -0
- data/Manifest.txt +6 -0
- data/bin/jira_create_issue +5 -5
- data/lib/jirarest2.rb +3 -1
- data/lib/jirarest2/connect.rb +12 -3
- data/lib/jirarest2/cookie_credentials.rb +4 -1
- data/lib/jirarest2/credentials.rb +4 -1
- data/lib/jirarest2/exceptions.rb +5 -0
- data/lib/jirarest2/field.rb +645 -0
- data/lib/jirarest2/issue.rb +7 -0
- data/lib/jirarest2/issuetype.rb +256 -0
- data/lib/jirarest2/madbitconfig.rb +3 -1
- data/lib/jirarest2/newissue.rb +30 -162
- data/lib/jirarest2/result.rb +1 -8
- data/lib/jirarest2/services.rb +1 -1
- data/lib/jirarest2/services/comment.rb +1 -0
- data/lib/jirarest2/services/issuelink.rb +1 -0
- data/lib/jirarest2bin.rb +2 -2
- data/test/data/createmeta +1 -0
- data/test/test_fieldcreatemeta.rb +820 -0
- data/test/test_fields.rb +395 -0
- data/test/test_issue.rb +4 -3
- data/test/test_issuetype.rb +74 -0
- data/test/test_newissue.rb +3 -4
- metadata +30 -46
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/History.txt
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 0.0.13 / 2012-09-18
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
|
5
|
+
* Field types are now somewhat mirrored from the server - This should enable working with different fieldtypes without the "expected an object" error. Hopefu
|
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.
|
10
|
+
* Added fieldtypes for the known fields with support for allowed values
|
11
|
+
|
12
|
+
* 4 unknowns:
|
13
|
+
|
14
|
+
* Added reading of values for fields from the json results
|
15
|
+
* Fixed a bug with authentication but found a new bug that is not fixed yet ( #25 )
|
16
|
+
* Some minor work on tests and warnings
|
17
|
+
* Some work on documentation
|
18
|
+
|
1
19
|
=== 0.0.12 / 2012-08-07
|
2
20
|
|
3
21
|
* 1 major enhancement:
|
data/Manifest.txt
CHANGED
@@ -14,7 +14,9 @@ lib/jirarest2/connect.rb
|
|
14
14
|
lib/jirarest2/cookie_credentials.rb
|
15
15
|
lib/jirarest2/credentials.rb
|
16
16
|
lib/jirarest2/exceptions.rb
|
17
|
+
lib/jirarest2/field.rb
|
17
18
|
lib/jirarest2/issue.rb
|
19
|
+
lib/jirarest2/issuetype.rb
|
18
20
|
lib/jirarest2/madbitconfig.rb
|
19
21
|
lib/jirarest2/newissue.rb
|
20
22
|
lib/jirarest2/password_credentials.rb
|
@@ -26,6 +28,7 @@ lib/jirarest2/services/issuelinktype.rb
|
|
26
28
|
lib/jirarest2/services/watcher.rb
|
27
29
|
lib/jirarest2bin.rb
|
28
30
|
test/data/cookiejar
|
31
|
+
test/data/createmeta
|
29
32
|
test/data/get-comments.txt
|
30
33
|
test/data/issuespec.txt
|
31
34
|
test/data/test.config.data
|
@@ -37,9 +40,12 @@ test/test_comment.rb
|
|
37
40
|
test/test_connect.rb
|
38
41
|
test/test_cookie_credentials.rb
|
39
42
|
test/test_credentials.rb
|
43
|
+
test/test_fieldcreatemeta.rb
|
44
|
+
test/test_fields.rb
|
40
45
|
test/test_issue.rb
|
41
46
|
test/test_issuelink.rb
|
42
47
|
test/test_issuelinktype.rb
|
48
|
+
test/test_issuetype.rb
|
43
49
|
test/test_madbitconfig.rb
|
44
50
|
test/test_newissue.rb
|
45
51
|
test/test_password_credentials.rb
|
data/bin/jira_create_issue
CHANGED
@@ -130,7 +130,7 @@ end # class ParseOptions
|
|
130
130
|
|
131
131
|
|
132
132
|
def no_issue(type,issue)
|
133
|
-
puts "The #{type}type you entered (\"#{issue}\") does
|
133
|
+
puts "The #{type}type you entered (\"#{issue}\") does not exist."
|
134
134
|
puts "Maybe you entered the wrong type or made a typo? (Case is relevant!)"
|
135
135
|
exit 1
|
136
136
|
end
|
@@ -148,9 +148,9 @@ def open_issue
|
|
148
148
|
end
|
149
149
|
@connection,issue = Jirarest2Bin::command(@scriptopts,@connection,:issue,@issueopts.project,@issueopts.issue)
|
150
150
|
rescue Jirarest2::WrongProjectException => e
|
151
|
-
no_issue("project"
|
151
|
+
no_issue("project",@issueopts.project)
|
152
152
|
rescue Jirarest2::WrongIssuetypeException => e
|
153
|
-
no_issue("
|
153
|
+
no_issue("issue",@issueopts.issue)
|
154
154
|
end
|
155
155
|
return issue
|
156
156
|
end
|
@@ -172,6 +172,7 @@ def show_scheme
|
|
172
172
|
end
|
173
173
|
|
174
174
|
# Split the content from the command line parameter "-c"
|
175
|
+
# TODO coupling is way to strong here. Fucks up.
|
175
176
|
def split_content(issue)
|
176
177
|
fields = Hash.new
|
177
178
|
@issueopts.content.each { |value|
|
@@ -224,8 +225,7 @@ def prepare_new_ticket
|
|
224
225
|
rescue Jirarest2::WrongFieldnameException => e
|
225
226
|
no_issue("field",e)
|
226
227
|
rescue Jirarest2::ValueNotAllowedException => e
|
227
|
-
puts "
|
228
|
-
puts "Please use one of: \"" + e.allowed.join("\", \"") + "\""
|
228
|
+
puts "Problem with #{e.fieldname}: Value #{e}"
|
229
229
|
valueNotAllowedRaised = true
|
230
230
|
end
|
231
231
|
|
data/lib/jirarest2.rb
CHANGED
data/lib/jirarest2/connect.rb
CHANGED
@@ -40,6 +40,14 @@ class Connect
|
|
40
40
|
# @param [String, "Get", "Post", "Delete", "Put"] operation HTTP method: GET, POST, DELETE, PUT
|
41
41
|
# @param [String] uritail The last part of the REST URI
|
42
42
|
# @param [Hash] data Data to be sent.
|
43
|
+
# @raise [Jirarest2::BadRequestError] Raised if the servers returns statuscode 400 (bad request)
|
44
|
+
# @raise [Jirarest2::PasswordAuthenticationError] Raised if authentication failed (status code 401) and the credentials were username/password based
|
45
|
+
# @raise [Jirarest2::CookieAuthenticationError] Raised if authentication failed (status code 401) and the credentials were cookie based
|
46
|
+
# @raise [Jirarest2::AuthenticationError] Raised if authentication failed (status code 401) and the credentials were neither cookie or username/password based
|
47
|
+
# @raise [Jirarest2::AuthentificationCaptchaError] Raised if the server sends a forbidden status (status code 403) and an login url which means the user needs to answer a captcha
|
48
|
+
# @raise [Jirarest2::ForbiddenError] Raised if the server sends a forbidden status (status code 403) and no login url
|
49
|
+
# @raise [Jirarest2::NotFoundError] Raised if the server returns statuscode 404 (Not found)
|
50
|
+
# @raise [Jirarest2::MethodNotAllowedError] Raised if the server returns statuscode 405 (Method not allowed)
|
43
51
|
# @return [Jirarest2::Result]
|
44
52
|
def execute(operation,uritail,data)
|
45
53
|
uri = nil
|
@@ -112,7 +120,7 @@ class Connect
|
|
112
120
|
# @return [Boolean]
|
113
121
|
def check_uri
|
114
122
|
begin
|
115
|
-
|
123
|
+
return execute("Get","dashboard","").code == "200"
|
116
124
|
# TODO is the 404 really possible?
|
117
125
|
rescue Jirarest2::NotFoundError
|
118
126
|
return false
|
@@ -141,7 +149,8 @@ class Connect
|
|
141
149
|
|
142
150
|
|
143
151
|
# try to fix the connecturl of this instance
|
144
|
-
# @
|
152
|
+
# @raise [Jirarest2::CouldNotHealURIError] Raised if the url can not be healed automatically
|
153
|
+
# @return [String] Fixed URL
|
145
154
|
def heal_uri!
|
146
155
|
if ! check_uri then
|
147
156
|
@credentials.connecturl = heal_uri(@credentials.connecturl)
|
@@ -157,7 +166,7 @@ class Connect
|
|
157
166
|
# @return [Boolean] true if the authentication seems to be valid (actually it checks if there is a session)
|
158
167
|
def verify_auth
|
159
168
|
ret = execute("Get","auth/latest/session","")
|
160
|
-
store_cookiejar if @credentials.instance_of?(CookieCredentials) && @credentials.autosave
|
169
|
+
@credentials.store_cookiejar if @credentials.instance_of?(CookieCredentials) && @credentials.autosave
|
161
170
|
return ret.code == "200"
|
162
171
|
end
|
163
172
|
|
@@ -25,9 +25,12 @@ require "pstore"
|
|
25
25
|
class CookieCredentials < Credentials
|
26
26
|
|
27
27
|
# Location of the file the cookie is persited on a harddrive. Default is "~/.jirarest2.cookie
|
28
|
+
# @return [String] Location of the cookie on the harddrive
|
28
29
|
attr_accessor :cookiestore
|
30
|
+
attr_reader :autosave
|
29
31
|
|
30
|
-
# @param [String]
|
32
|
+
# @param [String] connecturl URL to JIRA(tm) instance
|
33
|
+
# @param [String] username Username to connect to the server
|
31
34
|
# @param [Boolean] autosave Save the cookie on the harddisk whenever something happens?
|
32
35
|
def initialize(connecturl, username, autosave = false )
|
33
36
|
super(connecturl,username)
|
@@ -29,6 +29,8 @@ class Credentials
|
|
29
29
|
attr_reader :baseurl
|
30
30
|
|
31
31
|
# @param [String] url URL to JIRA(tm) instance
|
32
|
+
# @param [String] username Username of the user who connects to jira
|
33
|
+
# @raise [Jirarest2::NotAnURLError] Raised if the given URL is not recogniced as an url
|
32
34
|
def initialize(url,username)
|
33
35
|
uri = URI(url)
|
34
36
|
if uri.instance_of?(URI::HTTP) || uri.instance_of?(URI::HTTPS) then
|
@@ -40,8 +42,9 @@ class Credentials
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
|
-
#
|
45
|
+
# Set the connect url a later date in the life of the instance
|
44
46
|
# @param [String] url
|
47
|
+
# @raise [Jirarest2::NotAnURLError] Raised if the given URL is not recogniced as an url
|
45
48
|
def connecturl=(url)
|
46
49
|
uri = URI(url)
|
47
50
|
if uri.instance_of?(URI::HTTP) || uri.instance_of?(URI::HTTPS) then
|
data/lib/jirarest2/exceptions.rb
CHANGED
@@ -67,4 +67,9 @@ module Jirarest2
|
|
67
67
|
# A field that is defined as "required" has not been given a value
|
68
68
|
class RequiredFieldNotSetException < ArgumentError; end
|
69
69
|
|
70
|
+
#Could not determine the right fieldtype as this seems to be new
|
71
|
+
class CouldNotDetermineFieldtypeException < ArgumentError; end
|
72
|
+
#Hashfiels always have to have a key assigned to them
|
73
|
+
class HashKeyMissingException < ArgumentError; end
|
74
|
+
|
70
75
|
end
|
@@ -0,0 +1,645 @@
|
|
1
|
+
# Copyright (C) 2012 Cyril Bitterich
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "jirarest2/exceptions"
|
18
|
+
# All the fieldtypes in their own namespace (hopefully easier in the documentation)
|
19
|
+
# @todo operations "add","set","remove" are ignored right now.
|
20
|
+
module Jirarest2Field
|
21
|
+
# Superclass for all fields
|
22
|
+
class Field
|
23
|
+
# Is this field mandatory?
|
24
|
+
# @return [Boolean] Default: false. True if your field has to be set for the issuetype
|
25
|
+
attr_reader :required
|
26
|
+
# Is this field readonly?
|
27
|
+
# @return [Boolean] Default: false
|
28
|
+
attr_reader :readonly
|
29
|
+
# The field id in JIRA(tm)
|
30
|
+
# @return [String] The id in your JIRA(tm) instance
|
31
|
+
attr_reader :id
|
32
|
+
# The name given to the field (not unique in jira!)
|
33
|
+
# @return [String] The name in your JIRA(tm) instance
|
34
|
+
attr_reader :name
|
35
|
+
# The raw value
|
36
|
+
# @return [Hash] The value in it's raw form
|
37
|
+
attr_reader :raw_value
|
38
|
+
# Allowed values for the fields
|
39
|
+
# @return [Array] The values allowed for this kind of field
|
40
|
+
attr_accessor :allowed_values
|
41
|
+
|
42
|
+
# @attr [String] id The fields identifier in JIRA(tm)
|
43
|
+
# @attr [String] name The fields name in JIRA(tm)
|
44
|
+
# @attr [Hash] args, :required if this is a mandatory field
|
45
|
+
def initialize(id,name,args)
|
46
|
+
@id = id
|
47
|
+
@name = name
|
48
|
+
if args[:required] then
|
49
|
+
@required = true
|
50
|
+
else
|
51
|
+
@required = false
|
52
|
+
end
|
53
|
+
@allowed_values = []
|
54
|
+
if args[:allowed_values] then
|
55
|
+
allowed_values = args[:allowed_values]
|
56
|
+
end
|
57
|
+
@value = nil
|
58
|
+
@readonly = false
|
59
|
+
if args[:createmeta] then
|
60
|
+
createmeta(args[:createmeta])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get the value of the field
|
65
|
+
# @param [Boolean] raw true returns a date Object, false a String
|
66
|
+
# @return [String] if raw is false (default)
|
67
|
+
# @return [Object] if raw is true
|
68
|
+
def value(raw = false)
|
69
|
+
return @value
|
70
|
+
end
|
71
|
+
|
72
|
+
# Checks if the value is in the list of allowed values. If the list is empty every value is allowed
|
73
|
+
# @param [Object] value The value to check for
|
74
|
+
# @raise [Jirarest2::ValueNotAllowedException] Raised if the value is not allowed
|
75
|
+
# @return [Boolean] true if the value is allowed, false if not
|
76
|
+
def value_allowed?(value)
|
77
|
+
return true if @allowed_values == [] # If there is no list get out of here fast
|
78
|
+
if @allowed_values.include?(value) then
|
79
|
+
return true
|
80
|
+
else
|
81
|
+
raise Jirarest2::ValueNotAllowedException.new(@name,@allowed_values), "#{value} is not a valid value. Please use one of #{@allowed_values.join("; ").to_s}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Set the value of the field
|
86
|
+
# @param [Object] content The value of this field
|
87
|
+
def value=(content)
|
88
|
+
@value = content if value_allowed?(content)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Representation to be used for json and jira
|
92
|
+
# @param [String,Hash] value the to be put into the representation.
|
93
|
+
# @return [Hash] if the value is set
|
94
|
+
# @return [Nil] if the value is not set
|
95
|
+
def to_j(value = @value)
|
96
|
+
if value.nil? then
|
97
|
+
return nil
|
98
|
+
else
|
99
|
+
return {@id => value}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#Interpret the result of createmeta for one field
|
104
|
+
# If there is only one value allowed this value will be set
|
105
|
+
# @attr [Hash] structure The JSON result for one field
|
106
|
+
# @todo change @allowed_values here. -> suggestion has to go to and build the correct type
|
107
|
+
def createmeta(structure)
|
108
|
+
@readonly = true if structure["operations"] == []
|
109
|
+
if structure["allowedValues"] then
|
110
|
+
structure["allowedValues"].flatten!(1)
|
111
|
+
if ! structure["allowedValues"][0].nil? then
|
112
|
+
if structure["allowedValues"][0].has_key?("value") then
|
113
|
+
@key = "value"
|
114
|
+
elsif structure["allowedValues"][0].has_key?("key") then
|
115
|
+
@key = "key"
|
116
|
+
elsif structure["allowedValues"][0].has_key?("name") then
|
117
|
+
@key = "name"
|
118
|
+
else
|
119
|
+
@key = "id"
|
120
|
+
end
|
121
|
+
structure["allowedValues"].each{ |suggestion|
|
122
|
+
@allowed_values << suggestion[@key]
|
123
|
+
}
|
124
|
+
if structure["allowedValues"].size == 1 && !structure["allowedValues"][0].instance_of?(Array) then # If there is only one value allowed it might as well be set at the earliest convenience
|
125
|
+
@value = structure["allowedValues"][0][@key]
|
126
|
+
end
|
127
|
+
else
|
128
|
+
@key = ""
|
129
|
+
@allowed_values == []
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Parse the value of this field as sent by the server
|
135
|
+
# @attr [String,Hash,Array] jvalue The part of the response that is connected to this instance
|
136
|
+
def parse_value(jvalue)
|
137
|
+
@rawvalue = jvalue
|
138
|
+
@value = jvalue
|
139
|
+
end
|
140
|
+
|
141
|
+
protected
|
142
|
+
# Representation to be used for json and jira - don't return the fieldid
|
143
|
+
# @param [String,Hash] value the to be put into the representation.
|
144
|
+
# @return [String,Hash] if the value is set
|
145
|
+
# @return [Nil] if the value is not set
|
146
|
+
def to_j_inner(value = @value)
|
147
|
+
if value.nil? then
|
148
|
+
return nil
|
149
|
+
else
|
150
|
+
return value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end # class Field
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
# A simple text field. JSON representation will be "Fieldid" : "Value"
|
159
|
+
class TextField < Field
|
160
|
+
end # TextField
|
161
|
+
|
162
|
+
# A simple Date field.
|
163
|
+
class DateField < Field
|
164
|
+
require "date"
|
165
|
+
|
166
|
+
# Set the value
|
167
|
+
# @param [String] content The date in a string representation (Either [YY]YY-[M]M-[D]D or [D]D.[M]M.YYYY or YY.[M]M.[D]D See Date.parse)
|
168
|
+
def value=(content)
|
169
|
+
value = Date.parse(content)
|
170
|
+
@value = value if value_allowed?(value)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Get the value
|
174
|
+
# @param [Boolean] raw true returns a date Object, false a String
|
175
|
+
# @return [String] if raw is false (default)
|
176
|
+
# @return [Date] if raw is true
|
177
|
+
def value(raw = false)
|
178
|
+
if raw then
|
179
|
+
super
|
180
|
+
else
|
181
|
+
return @value.to_s
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Representation to be used for json and jira
|
186
|
+
# @return [Hash]
|
187
|
+
def to_j(value = @value)
|
188
|
+
if value.nil? then
|
189
|
+
super(nil)
|
190
|
+
else
|
191
|
+
super(value.to_s)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Parse the value of this field as sent by the server
|
196
|
+
# @attr [String] jvalue The part of the response that is connected to this instance
|
197
|
+
def parse_value(jvalue)
|
198
|
+
super
|
199
|
+
@value = Date.parse(jvalue)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Representation to be used for json and jira without the fieldId
|
203
|
+
# @return [Hash]
|
204
|
+
def to_j_inner
|
205
|
+
if @value.nil? then
|
206
|
+
super(nil)
|
207
|
+
else
|
208
|
+
super(@value.to_s)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end # end class DateField
|
213
|
+
|
214
|
+
# A field resembling a DateTime
|
215
|
+
class DateTimeField < DateField
|
216
|
+
# require "date"
|
217
|
+
|
218
|
+
# Set the value
|
219
|
+
# @param [String] content The DateTime in a string representation (Use "YYYY-MM-DD HH:MM:SS" although others like "HH:MM:SS YYYY-MM-DD" or "HH:MM:SS DD.MM.YYYY" work too. See DateTime.parse )
|
220
|
+
def value=(content)
|
221
|
+
value = DateTime.parse(content)
|
222
|
+
@value = value if value_allowed?(value)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Parse the value of this field as sent by the server
|
226
|
+
# @attr [String] jvalue The part of the response that is connected to this instance
|
227
|
+
def parse_value(jvalue)
|
228
|
+
super
|
229
|
+
@value = DateTime.parse(jvalue)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Representation to be used for json and JIRA(tm)
|
233
|
+
# JIRA(tm) expects certain format which we try to accomodate here
|
234
|
+
# @return [Hash]
|
235
|
+
def to_j(value = @value)
|
236
|
+
if value.nil? then
|
237
|
+
super(nil)
|
238
|
+
else
|
239
|
+
strvalue = value.strftime("%FT%T.%L%z")
|
240
|
+
super(strvalue)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end #class DateTimeField
|
244
|
+
|
245
|
+
# A field representing Numbers (not Strings with Numbers)
|
246
|
+
# @todo See to recognize allowed - might hide in schema
|
247
|
+
class NumberField < TextField
|
248
|
+
# Set the value
|
249
|
+
# @param [String,Fixnum] content A number
|
250
|
+
def value=(content)
|
251
|
+
if content.instance_of?(String) then
|
252
|
+
value = content.to_f
|
253
|
+
else
|
254
|
+
value = content
|
255
|
+
end
|
256
|
+
@value = value if value_allowed?(value)
|
257
|
+
end
|
258
|
+
end # class NumberField
|
259
|
+
|
260
|
+
# A Field that presents its value in an hash that has additional information ("name","id","key","value")
|
261
|
+
class HashField < TextField
|
262
|
+
# The key element for the answers - It should not be needed - but it's easer on the checks if it's exposed
|
263
|
+
# @return [String] The key element for the way to Jira
|
264
|
+
attr_reader :key
|
265
|
+
|
266
|
+
# @attr [String] id The fields identifier in JIRA(tm)
|
267
|
+
# @attr [String] name The fields name in JIRA(tm)
|
268
|
+
# @attr [Hash] args :key ist mandatory and a String, :required (a Boolean if this is a mandatory field)
|
269
|
+
# (key should be one of "id", "key", "name", "value" )
|
270
|
+
# @raise [Jirarest2::HashKeyMissingException] If the key determining the base value of this field in it's hash is not given.
|
271
|
+
def initialize(id,name,args)
|
272
|
+
@key ||= nil # Trying to initialize without overwriting something that might come from the subclass
|
273
|
+
@key = args[:key].downcase if ( ! args[:createmeta] && args[:key])
|
274
|
+
super
|
275
|
+
raise Jirarest2::HashKeyMissingException, "HashTypes like in #{id} alway require a key!" if @key.nil?
|
276
|
+
end
|
277
|
+
|
278
|
+
# Representation to be used for json and jira
|
279
|
+
# @return [Hash] if value is set
|
280
|
+
def to_j(value = @value)
|
281
|
+
if value.nil? then
|
282
|
+
super(nil)
|
283
|
+
else
|
284
|
+
valuehash = {@key => value}
|
285
|
+
super(valuehash)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Parse the value of this field as sent by the server
|
290
|
+
# @attr [String,Hash,Array] jvalue The part of the response that is connected to this instance
|
291
|
+
def parse_value(jvalue)
|
292
|
+
super
|
293
|
+
if jvalue.nil? then
|
294
|
+
@value = nil
|
295
|
+
else
|
296
|
+
@value = jvalue[key]
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# Representation to be used for json and jira without the fieldID
|
301
|
+
# @return [Hash] if value is set
|
302
|
+
def to_j_inner(value = @value)
|
303
|
+
if value.nil? then
|
304
|
+
super(nil)
|
305
|
+
else
|
306
|
+
valuehash = {@key => value}
|
307
|
+
super(valuehash)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
end # class HashField
|
312
|
+
|
313
|
+
# A field containing one or more other fields (usually only TextField or HashField)
|
314
|
+
class MultiField < Field
|
315
|
+
# @attr [String] id The fields identifier in JIRA(tm)
|
316
|
+
# @attr [String] name The fields name in JIRA(tm)
|
317
|
+
# @attr [Hash] args, :required if this is a mandatory field
|
318
|
+
def initialize(id,name,args)
|
319
|
+
super(id,name,args)
|
320
|
+
@value = []
|
321
|
+
@delete = false
|
322
|
+
end
|
323
|
+
|
324
|
+
# Checks if the value is in the list of allowed values. If the list is empty every value is allowed
|
325
|
+
# @param [Object] value The value to check for
|
326
|
+
# @raise [Jirarest2::ValueNotAllowedException] Raised if the value is not allowed
|
327
|
+
# @return [Boolean] true if the value is allowed, false if not
|
328
|
+
def value_allowed?(value)
|
329
|
+
return true if @allowed_values == [] # If there is no list get out of here fast
|
330
|
+
if value.instance_of?(Array) then
|
331
|
+
value.each { |entry|
|
332
|
+
value_allowed?(entry)
|
333
|
+
}
|
334
|
+
else
|
335
|
+
if @allowed_values.include?(value) then
|
336
|
+
return true
|
337
|
+
else
|
338
|
+
raise Jirarest2::ValueNotAllowedException.new(@name,@allowed_values), "#{value} is not a valid value. Please use one of #{@allowed_values.join("; ").to_s}"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# Set the value of the field
|
344
|
+
# @attribute [w] value
|
345
|
+
# @param [Object] content The value of this field
|
346
|
+
# @return [Array] All the contained fields
|
347
|
+
def value=(content)
|
348
|
+
if ! content.instance_of?(Array) then
|
349
|
+
content = [content]
|
350
|
+
end
|
351
|
+
super(content)
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
# Return for JSON representation
|
356
|
+
# if @value == [] or nil and @delete is false set super will return nil
|
357
|
+
def to_j(value = @value)
|
358
|
+
if ((value == [] || value.nil?) and ! @delete) then
|
359
|
+
super(nil)
|
360
|
+
else
|
361
|
+
value.compact!
|
362
|
+
fields = Array.new
|
363
|
+
if value[0].class < Jirarest2Field::Field then
|
364
|
+
value.each {|field|
|
365
|
+
fields << field.to_j_inner
|
366
|
+
}
|
367
|
+
else
|
368
|
+
fields = value
|
369
|
+
end
|
370
|
+
super(fields)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# Delete items
|
375
|
+
# @param [Object] object The object to delete (If the object is self it all fields and sets @delete)
|
376
|
+
# @return The deleted object
|
377
|
+
def delete(object)
|
378
|
+
if object == self then
|
379
|
+
@delete = true
|
380
|
+
@value = []
|
381
|
+
else
|
382
|
+
@value.delete(object)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Delete items based on their value attribute
|
387
|
+
# @param [Object] ovalue The value that defines the objects to delete from this MultiField
|
388
|
+
# @return [Array] The remaining Objects
|
389
|
+
def delete_by_value(ovalue)
|
390
|
+
@value.delete_if {|x| x.value == ovalue}
|
391
|
+
end
|
392
|
+
|
393
|
+
# Add another field to the MultiField
|
394
|
+
# @param [Field,String] content the content to add to the hash
|
395
|
+
def <<(content)
|
396
|
+
|
397
|
+
if @value.length > 0 then
|
398
|
+
raise Jirarest2::ValueNotAllowedException.new(@name,@value[0].class), "#{@value[0].class} vs #{content.class}" if @value[0].class != content.class
|
399
|
+
end
|
400
|
+
|
401
|
+
@value << content if value_allowed?(content)
|
402
|
+
end
|
403
|
+
|
404
|
+
# One field from the MultiField
|
405
|
+
# @param [Integer] index Position of the field
|
406
|
+
def [](index)
|
407
|
+
@value[index]
|
408
|
+
end
|
409
|
+
|
410
|
+
# Set the content of one special field
|
411
|
+
# @param [Integer] index Position of the field
|
412
|
+
# @param [Object] content Value to put at the place marked by index
|
413
|
+
# @raise [Jirarest2::ValueNotAllowedException] Raised if Classes of the fields are to be mixed
|
414
|
+
def []=(index,content)
|
415
|
+
raise Jirarest2::ValueNotAllowedException.new(@name,@value[0].class), "#{@value[0].class} vs #{content.class}" if @value[0].class != content.class
|
416
|
+
@value[index] = content if value_allowed?(content)
|
417
|
+
end
|
418
|
+
end # class MultiField
|
419
|
+
|
420
|
+
# The class to represent CascadingSelectFields
|
421
|
+
class CascadingField < Field
|
422
|
+
# The key element for the answers - It should not be needed - but it's easer on the checks if it's exposed
|
423
|
+
# @return [String] The key element for the way to Jira
|
424
|
+
attr_reader :key
|
425
|
+
# @!attribute [w] allowed_values
|
426
|
+
# @attr [Hash<Array>] value The Hashes with the allowed values
|
427
|
+
def allowed_values=(value)
|
428
|
+
@allowed_values = value
|
429
|
+
end
|
430
|
+
|
431
|
+
# Checks if the value is in the list of allowed values. If the list is empty every value is allowed
|
432
|
+
# @param [Object] value The value to check for
|
433
|
+
# @raise [Jirarest2::ValueNotAllowedException] Raised if the value is not allowed
|
434
|
+
# @return [Boolean] true if the value is allowed, false if not
|
435
|
+
def value_allowed?(value)
|
436
|
+
return true if @allowed_values == [] # If there is no list get out of here fast
|
437
|
+
if @allowed_values[0].has_key?(value[0]) && @allowed_values[0][value[0]].include?(value[1]) then
|
438
|
+
return true
|
439
|
+
else
|
440
|
+
raise Jirarest2::ValueNotAllowedException.new(@name,@allowed_values), "#{value.to_s} is not a valid value. Please use one of #{@allowed_values}"
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
# Set the value of the field
|
445
|
+
# @!attribute [w] value
|
446
|
+
# @param [Array(String,String)] content The value of this field
|
447
|
+
# @raise [Jirarest2::ValueNotAllowedException] Raised if Classes of the fields are to be mixed
|
448
|
+
def value=(content)
|
449
|
+
if ! content.instance_of?(Array) or content.size != 2 then
|
450
|
+
raise Jirarest2::ValueNotAllowedException.new(@name,"Array"), "needs to be an Array with exactly 2 parameters. Was #{content.class}."
|
451
|
+
end
|
452
|
+
super
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
# Representation to be used for json and jira
|
457
|
+
# @return [Hash] if the value is set
|
458
|
+
# @return [Nil] if the value is not set
|
459
|
+
def to_j
|
460
|
+
if @value.nil? then
|
461
|
+
super(nil)
|
462
|
+
else
|
463
|
+
super({"value" => @value[0], "child" => {"value" => @value[1]}})
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
# Parse the value of this field as sent by the server
|
468
|
+
# @attr [Array] jvalue The part of the response that is connected to this instance
|
469
|
+
def parse_value(jvalue)
|
470
|
+
super
|
471
|
+
@value = [jvalue["value"],jvalue["child"]["value"]]
|
472
|
+
end
|
473
|
+
|
474
|
+
#Interpret the result of createmeta for one field
|
475
|
+
# @attr [Hash](structure)
|
476
|
+
# @note fills allowed_values with a straight list of allowed values
|
477
|
+
# @todo Nothing is done here yet!
|
478
|
+
def createmeta(structure)
|
479
|
+
@readonly = true if structure["operations"] == []
|
480
|
+
@key = "value"
|
481
|
+
if structure["allowedValues"] then
|
482
|
+
structure["allowedValues"].each{ |suggestion|
|
483
|
+
subentries = Array.new
|
484
|
+
suggestion["children"].each{ |entry|
|
485
|
+
subentries << entry["value"]
|
486
|
+
}
|
487
|
+
@allowed_values << {suggestion[@key] => subentries}
|
488
|
+
}
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end # class CascadingField
|
492
|
+
|
493
|
+
# At the moment it's only there to keep the HashField company
|
494
|
+
class MultiStringField < MultiField ; end
|
495
|
+
|
496
|
+
# Hash Fields are always somewhat different to normal fields
|
497
|
+
# @todo to_j is shot
|
498
|
+
class MultiHashField < MultiField
|
499
|
+
# The key element for the answers - It should not be needed - but it's easer on the checks if it's exposed
|
500
|
+
# @return [String] The key element for the way to Jira
|
501
|
+
attr_reader :key
|
502
|
+
|
503
|
+
# @attr [String] id The fields identifier in JIRA(tm)
|
504
|
+
# @attr [String] name The fields name in JIRA(tm)
|
505
|
+
# @attr [Hash] args, :required if this is a mandatory field
|
506
|
+
# @raise [Jirarest2::HashKeyMissingException] If the key determining the base value of this field in it's hash is not given.
|
507
|
+
# @todo Test to not really work for to_j
|
508
|
+
# @todo Hash_identifier is not alway "name", we should have some "value" fields as well. Whole thing needs to be rewritten work with HashFields as parts
|
509
|
+
def initialize(id,name,args)
|
510
|
+
@key ||= nil # Trying to initialize without overwriting something that might come from the subclass
|
511
|
+
@key = args[:key].downcase if ( ! args[:createmeta] && args[:key])
|
512
|
+
super
|
513
|
+
raise Jirarest2::HashKeyMissingException, "HashTypes like in #{id} always require a key!" if @key.nil?
|
514
|
+
end
|
515
|
+
|
516
|
+
# Parse the value of this field as sent by the server
|
517
|
+
# @attr [Array] jvalue The part of the response that is connected to this instance
|
518
|
+
def parse_value(jvalue)
|
519
|
+
@rawvalue = jvalue
|
520
|
+
jvalue.each{ |item|
|
521
|
+
@value << item[@key]
|
522
|
+
}
|
523
|
+
end
|
524
|
+
|
525
|
+
# Return for JSON representation
|
526
|
+
# if @value == [] or nil and @delete is false set super will return nil
|
527
|
+
def to_j(value = @value)
|
528
|
+
if ((value == [] || value.nil?) and ! @delete) then
|
529
|
+
super(nil)
|
530
|
+
else
|
531
|
+
value.compact!
|
532
|
+
fields = Array.new
|
533
|
+
if value[0].class < Jirarest2Field::Field then # This is how it should be
|
534
|
+
value.each {|field|
|
535
|
+
fields << field.to_j_inner
|
536
|
+
}
|
537
|
+
else
|
538
|
+
value.each{ |field|
|
539
|
+
f = HashField.new("10000a",field,{:createmeta => {"operations" => ["set"], "allowedValues" => [{@key => field}] } })
|
540
|
+
fields << f.to_j_inner
|
541
|
+
}
|
542
|
+
end
|
543
|
+
super(fields)
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
# Representation to be used for json and jira without the fieldID
|
548
|
+
# @return [Hash] if value is set
|
549
|
+
def to_j_inner(value = @value)
|
550
|
+
if value.nil? then
|
551
|
+
super(nil)
|
552
|
+
else
|
553
|
+
valuehash = {@key => value}
|
554
|
+
super(valuehash)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
end
|
559
|
+
|
560
|
+
# Versions might behave in another way somewhere
|
561
|
+
class MultiVersionField < MultiHashField ; end
|
562
|
+
|
563
|
+
# Unfortunately Users and Groups don't give us any clue as to how to set their "key" element. Therefore this own class
|
564
|
+
class MultiUserField < MultiHashField
|
565
|
+
def initialize(id,name,args)
|
566
|
+
@key = "name"
|
567
|
+
super
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# Unfortunately Users and Groups don't give us any clue as to how to set their "key" element. Therefore this own class
|
572
|
+
class UserField < HashField
|
573
|
+
def initialize(id,name,args)
|
574
|
+
@key = "name"
|
575
|
+
super
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
class VersionField < HashField ; end
|
580
|
+
|
581
|
+
# Projects are a little bit special
|
582
|
+
class ProjectField < VersionField
|
583
|
+
=begin
|
584
|
+
def createmeta(structure)
|
585
|
+
super
|
586
|
+
if ! structure["allowedValues"][0].instance_of?(Array) then # If there is only one value allowed it might as well be set at the earliest convenience
|
587
|
+
@value = structure["allowedValues"][0]["key"]
|
588
|
+
end
|
589
|
+
end
|
590
|
+
=end
|
591
|
+
end
|
592
|
+
|
593
|
+
# Timetracking is very special
|
594
|
+
# @todo This class is not really doing anything usefull
|
595
|
+
class TimetrackingField < Field; end
|
596
|
+
|
597
|
+
=begin
|
598
|
+
class CascadingSelect < CascadingField ; end # Look for "custom" Key
|
599
|
+
class DateTime < DateTimeField ; end
|
600
|
+
class GroupPicker < HashField ; end
|
601
|
+
class ImportId ; end
|
602
|
+
class Labels < MultiStringField ; end
|
603
|
+
class MultiGroupPicker < MultiHashField ; end
|
604
|
+
class MultiUserPicker < MultiHashField ; end
|
605
|
+
class ProjectPicker < HashField; end
|
606
|
+
class ReadOnlyTextField < TextField ; end
|
607
|
+
class SingleVersionPicker < HashField ; end
|
608
|
+
class URLField < TextField ; end
|
609
|
+
class VersionPicker < MultiHashField ; end
|
610
|
+
class DatePicker < DateField ; end
|
611
|
+
class FreeTextField < TextField ; end
|
612
|
+
class HiddenJobSwitch ; end
|
613
|
+
class JobCheckbox ; end
|
614
|
+
class MultiCheckboxes < MultiField ; end #unsure
|
615
|
+
class MultiSelect < MultiHashField ; end
|
616
|
+
# class NumberField < NumberField ; end
|
617
|
+
class RadioButtons < HashField ; end
|
618
|
+
class SelectList < HashField ; end
|
619
|
+
# class TextField < TextField ; end
|
620
|
+
class UserPicker < HashField ; end
|
621
|
+
|
622
|
+
class String < TextField ; end
|
623
|
+
class Progress ; end
|
624
|
+
# class Timetracking < TimeTrackingEntry ; end # "timetracking" : { "originalEstimate" : "1w2h", "remainingEstimate" : "3h23m" }
|
625
|
+
class Issuetype < HashField ; end
|
626
|
+
class Number < NumberField ; end
|
627
|
+
class User < UserPicker ; end
|
628
|
+
# class Datetime ; end # See above
|
629
|
+
class Priority < TextField ; end # Not HashField?
|
630
|
+
class Date < DateField ; end
|
631
|
+
class Array < MultiField ; end # Never alone always with an items parameter
|
632
|
+
class Status ; end
|
633
|
+
class Project < HashField ; end
|
634
|
+
class Component ; end
|
635
|
+
class Comment ; end
|
636
|
+
class Votes ; end
|
637
|
+
class Resolution < TextField ; end
|
638
|
+
class Version < HashField ; end
|
639
|
+
class Watches ; end
|
640
|
+
class Worklog ; end
|
641
|
+
class Attachment ; end # readonly
|
642
|
+
=end
|
643
|
+
end
|
644
|
+
|
645
|
+
|