rt-client 0.7.5 → 0.8.0
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/rt_client.rb +381 -379
- data/rtxmlsrv.rb +1 -1
- data/rtxmlsrv2.rb +277 -0
- metadata +4 -2
data/rt_client.rb
CHANGED
@@ -17,161 +17,161 @@ end
|
|
17
17
|
##default server/user/pass to connect to RT as, so that you don't have to
|
18
18
|
##specify it/update it in lots of different scripts.
|
19
19
|
##
|
20
|
-
##Thanks to
|
21
|
-
##
|
22
|
-
##
|
20
|
+
##Thanks to the following members of the RT community for patches:
|
21
|
+
##Brian McArdle for patch dealing with spaces in Custom Fields (use '_')
|
22
|
+
##Steven Craig for a bug fix and feature additions to the usersearch() method.
|
23
|
+
##Rafael Abdo for fixing a broken regex.
|
23
24
|
##
|
24
|
-
##Thanks to Robert Vinson for 1.9.x compatibility when I
|
25
|
-
##
|
25
|
+
##Extra Special Thanks to Robert Vinson for 1.9.x compatibility when I
|
26
|
+
##couldn't be buggered and refactoring the old mess into something with much fewer
|
26
27
|
##dependencies.
|
27
28
|
##
|
28
|
-
##Thanks to Steven Craig for a bug fix and feature additions to the
|
29
|
-
##usersearch() method.
|
30
29
|
##
|
31
30
|
##
|
32
31
|
##TODO: Streaming, chunking attachments in compose method
|
33
32
|
#
|
34
|
-
# See each method for sample usage. To use this, "gem install rt-client" and
|
33
|
+
# See each method for sample usage. To use this, "gem install rt-client" and
|
35
34
|
#
|
36
|
-
# require "
|
35
|
+
# require "rt_client"
|
37
36
|
|
38
37
|
class RT_Client
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
#
|
39
|
+
UA = "Mozilla/5.0 ruby RT Client Interface 0.8.0"
|
40
|
+
attr_reader :status, :site, :version, :cookies, :server, :user, :cookie
|
41
|
+
|
42
|
+
# Create a new RT_Client object. Load up our stored cookie and check it.
|
43
|
+
# Log into RT again if needed and store the new cookie. You can specify
|
44
|
+
# login and cookie storage directories in 3 different ways:
|
45
|
+
# 1. Explicity during object creation
|
46
|
+
# 2. From a .rtclientrc file in the working directory of your ruby program
|
47
|
+
# 3. From a .rtclientrc file in the same directory as the library itself
|
48
|
+
#
|
49
|
+
# These are listed in order of priority; if you have explicit parameters,
|
50
|
+
# they are always used, even if you have .rtclientrc files. If there
|
51
|
+
# is both an .rtclientrc in your program's working directory and
|
52
|
+
# in the library directory, the one from your program's working directory
|
53
|
+
# is used. If no parameters are specified either explicity or by use
|
54
|
+
# of a .rtclientrc, then the defaults of "rt_user", "rt_pass" are used
|
55
|
+
# with a default server of "http://localhost", and cookies are stored
|
56
|
+
# in the directory where the library resides.
|
57
|
+
#
|
58
|
+
# rt= RT_Client.new( :server => "https://tickets.ambulance.com/",
|
59
|
+
# :user => "rt_user",
|
60
|
+
# :pass => "rt_pass",
|
61
|
+
# :cookies => "/my/cookie/dir" )
|
62
|
+
#
|
63
|
+
# rt= RT_Client.new # use defaults from .rtclientrc
|
64
|
+
#
|
65
|
+
# .rtclientrc format:
|
66
|
+
# server=<RT server>
|
67
|
+
# user=<RT user>
|
68
|
+
# pass=<RT password>
|
69
|
+
# cookies=<directory>
|
70
|
+
def initialize(*params)
|
71
|
+
@boundary = "----xYzZY#{rand(1000000).to_s}xYzZY"
|
72
|
+
@version = "0.8.0"
|
73
|
+
@status = "Not connected"
|
74
|
+
@server = "http://localhost/"
|
75
|
+
@user = "rt_user"
|
76
|
+
@pass = "rt_pass"
|
77
|
+
@cookies = Dir.pwd
|
78
|
+
config_file = Dir.pwd + "/.rtclientrc"
|
79
|
+
config = ""
|
80
|
+
if File.file?(config_file)
|
81
|
+
config = File.read(config_file)
|
82
|
+
else
|
83
|
+
config_file = File.dirname(__FILE__) + "/.rtclientrc"
|
84
|
+
config = File.read(config_file) if File.file?(config_file)
|
85
|
+
end
|
86
|
+
@server = $~[1] if config =~ /\s*server\s*=\s*(.*)$/i
|
87
|
+
@user = $~[1] if config =~ /^\s*user\s*=\s*(.*)$/i
|
88
|
+
@pass = $~[1] if config =~ /^\s*pass\s*=\s*(.*)$/i
|
89
|
+
@cookies = $~[1] if config =~ /\s*cookies\s*=\s*(.*)$/i
|
90
|
+
@resource = "#{@server}REST/1.0/"
|
91
|
+
if params.class == Array && params[0].class == Hash
|
92
|
+
param = params[0]
|
93
|
+
@user = param[:user] if param.has_key? :user
|
94
|
+
@pass = param[:pass] if param.has_key? :pass
|
95
|
+
if param.has_key? :server
|
96
|
+
@server = param[:server]
|
97
|
+
@server += "/" if @server !~ /\/$/
|
98
|
+
@resource = "#{@server}REST/1.0/"
|
99
|
+
end
|
100
|
+
@cookies = param[:cookies] if param.has_key? :cookies
|
101
|
+
end
|
102
|
+
@login = { :user => @user, :pass => @pass }
|
103
|
+
cookiejar = "#{@cookies}/RT_Client.#{@user}.cookie" # cookie location
|
104
|
+
cookiejar.untaint
|
105
|
+
if File.file? cookiejar
|
106
|
+
@cookie = File.read(cookiejar).chomp
|
107
|
+
headers = { 'User-Agent' => UA,
|
108
|
+
'Content-Type' => "application/x-www-form-urlencoded",
|
109
|
+
'Cookie' => @cookie }
|
110
|
+
else
|
111
|
+
headers = { 'User-Agent' => UA,
|
112
|
+
'Content-Type' => "application/x-www-form-urlencoded" }
|
113
|
+
@cookie = ""
|
114
|
+
end
|
115
|
+
|
116
|
+
site = RestClient::Resource.new(@resource, :headers => headers, :timeout => 240)
|
117
|
+
data = site.post "" # a null post just to check that we are logged in
|
118
|
+
|
119
|
+
if @cookie.length == 0 or data =~ /401/ # we're not logged in
|
120
|
+
data = site.post @login, :headers => headers
|
121
|
+
# puts data
|
122
|
+
@cookie = data.headers[:set_cookie].first.split('; ')[0]
|
123
|
+
# write the new cookie
|
124
|
+
if @cookie !~ /nil/
|
125
|
+
f = File.new(cookiejar,"w")
|
126
|
+
f.puts @cookie
|
127
|
+
f.close
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
headers = { 'User-Agent' => UA,
|
132
|
+
'Content-Type' => "multipart/form-data; boundary=#{@boundary}",
|
133
|
+
'Cookie' => @cookie }
|
134
|
+
@site = RestClient::Resource.new(@resource, :headers => headers)
|
135
|
+
@status = data
|
136
|
+
self.untaint
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
# gets the detail for a single ticket/user. If its a ticket, its without
|
141
|
+
# history or attachments (to get those use the history method) . If no
|
142
|
+
# type is specified, ticket is assumed. takes a single parameter
|
143
|
+
# containing the ticket/user id, and returns a hash of RT Fields => values
|
144
|
+
#
|
145
|
+
# hash = rt.show(822)
|
146
|
+
# hash = rt.show("822")
|
147
|
+
# hash = rt.show("ticket/822")
|
148
|
+
# hash = rt.show(:id => 822)
|
149
|
+
# hash = rt.show(:id => "822")
|
150
|
+
# hash = rt.show(:id => "ticket/822")
|
151
|
+
# hash = rt.show("user/#{login}")
|
152
|
+
# email = rt.show("user/somebody")["emailaddress"]
|
153
|
+
def show(id)
|
154
|
+
id = id[:id] if id.class == Hash
|
155
|
+
id = id.to_s
|
156
|
+
type = "ticket"
|
157
|
+
sid = id
|
158
|
+
if id =~ /(\w+)\/(.+)/
|
159
|
+
type = $~[1]
|
160
|
+
sid = $~[2]
|
161
|
+
end
|
162
|
+
reply = {}
|
163
|
+
if type.downcase == 'user'
|
164
|
+
resp = @site["#{type}/#{sid}"].get
|
165
|
+
else
|
166
|
+
resp = @site["#{type}/#{sid}/show"].get
|
167
|
+
end
|
168
|
+
reply = response_to_h(resp)
|
169
|
+
end
|
170
|
+
|
171
|
+
# gets a list of ticket links for a ticket.
|
172
|
+
# takes a single parameter containing the ticket id,
|
173
173
|
# and returns a hash of RT Fields => values
|
174
|
-
#
|
174
|
+
#
|
175
175
|
# hash = rt.links(822)
|
176
176
|
# hash = rt.links("822")
|
177
177
|
# hash = rt.links("ticket/822")
|
@@ -207,8 +207,6 @@ class RT_Client
|
|
207
207
|
def create(field_hash)
|
208
208
|
field_hash[:id] = "ticket/new"
|
209
209
|
payload = compose(field_hash)
|
210
|
-
# puts "Payload for new ticket:"
|
211
|
-
# puts payload
|
212
210
|
resp = @site['ticket/new/edit'].post payload
|
213
211
|
new_id = resp.match(/Ticket\s*(\d+)/)
|
214
212
|
if new_id.class == MatchData
|
@@ -218,7 +216,7 @@ class RT_Client
|
|
218
216
|
end
|
219
217
|
new_ticket # return the ticket number, or the full REST response
|
220
218
|
end
|
221
|
-
|
219
|
+
|
222
220
|
# create a new user. Requires a hash of RT fields => values. Returns
|
223
221
|
# the newly created user ID, or the full REST response if there is an error.
|
224
222
|
# For a full list of possible parameters that you can specify, look at
|
@@ -237,7 +235,7 @@ class RT_Client
|
|
237
235
|
end
|
238
236
|
new_user # return the new user id or the full REST response
|
239
237
|
end
|
240
|
-
|
238
|
+
|
241
239
|
# edit or create a user. If the user exists, edits the user as "edit" would.
|
242
240
|
# If the user doesn't exist, creates it as "create_user" would.
|
243
241
|
def edit_or_create_user(field_hash)
|
@@ -257,10 +255,10 @@ class RT_Client
|
|
257
255
|
resp = "Edit: #{resp1}\nCreate:#{resp2}"
|
258
256
|
resp
|
259
257
|
end
|
260
|
-
|
258
|
+
|
261
259
|
# edit an existing ticket/user. Requires a hash containing RT
|
262
260
|
# form fields as keys. the key :id is required.
|
263
|
-
# returns the complete REST response, whatever it is. If the
|
261
|
+
# returns the complete REST response, whatever it is. If the
|
264
262
|
# id supplied contains "user/", it edits a user, otherwise
|
265
263
|
# it edits a ticket. For a full list of fields you can edit,
|
266
264
|
# try "/opt/rt3/bin/rt edit ticket/1"
|
@@ -286,7 +284,7 @@ class RT_Client
|
|
286
284
|
resp = @site["#{type}/#{sid}/edit"].post payload
|
287
285
|
resp
|
288
286
|
end
|
289
|
-
|
287
|
+
|
290
288
|
# Comment on a ticket. Requires a hash, which must have an :id key
|
291
289
|
# containing the ticket number. Returns the REST response. For a list of
|
292
290
|
# fields you can use in a comment, try "/opt/rt3/bin/rt comment ticket/1"
|
@@ -304,7 +302,7 @@ class RT_Client
|
|
304
302
|
payload = compose(field_hash)
|
305
303
|
@site["ticket/#{id}/comment"].post payload
|
306
304
|
end
|
307
|
-
|
305
|
+
|
308
306
|
# Find RT user details from an email address or a name
|
309
307
|
#
|
310
308
|
# rt.usersearch(:EmailAddress => 'some@email.com')
|
@@ -322,13 +320,12 @@ class RT_Client
|
|
322
320
|
return reply if resp =~ /No user named/
|
323
321
|
reply = response_to_h(resp)
|
324
322
|
end
|
325
|
-
|
323
|
+
|
326
324
|
# An alias for usersearch()
|
327
|
-
|
328
325
|
def usernamesearch(field_hash)
|
329
326
|
usersearch(field_hash)
|
330
327
|
end
|
331
|
-
|
328
|
+
|
332
329
|
# correspond on a ticket. Requires a hash, which must have an :id key
|
333
330
|
# containing the ticket number. Returns the REST response. For a list of
|
334
331
|
# fields you can use in correspondence, try "/opt/rt3/bin/rt correspond
|
@@ -352,91 +349,91 @@ class RT_Client
|
|
352
349
|
@site["ticket/#{id}/comment"].post payload
|
353
350
|
end
|
354
351
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
352
|
+
# Get a list of tickets matching some criteria.
|
353
|
+
# Takes a string Ticket-SQL query and an optional "order by" parameter.
|
354
|
+
# The order by is an RT field, prefix it with + for ascending
|
355
|
+
# or - for descending.
|
356
|
+
# Returns a nested array of arrays containing [ticket number, subject]
|
357
|
+
# The outer array is in the order requested.
|
358
|
+
#
|
359
|
+
# hash = rt.list(:query => "Queue = 'Sales'")
|
360
|
+
# hash = rt.list("Queue='Sales'")
|
361
|
+
# hash = rt.list(:query => "Queue = 'Sales'", :order => "-Id")
|
362
|
+
# hash = rt.list("Queue='Sales'","-Id")
|
363
|
+
def list(*params)
|
364
|
+
query = params[0]
|
365
|
+
order = ""
|
366
|
+
if params.size > 1
|
367
|
+
order = params[1]
|
368
|
+
end
|
369
|
+
if params[0].class == Hash
|
370
|
+
params = params[0]
|
371
|
+
query = params[:query] if params.has_key? :query
|
372
|
+
order = params[:order] if params.has_key? :order
|
373
|
+
end
|
374
|
+
reply = []
|
375
|
+
resp = @site["search/ticket/?query=#{URI.escape(query)}&orderby=#{order}&format=s"].get
|
376
|
+
raise "Invalid query (#{query})" if resp =~ /Invalid query/
|
377
|
+
resp = resp.split("\n") # convert to array of lines
|
378
|
+
resp.each do |line|
|
379
|
+
f = line.match(/^(\d+):\s*(.*)/)
|
380
|
+
reply.push [f[1],f[2]] if f.class == MatchData
|
381
|
+
end
|
382
|
+
reply
|
383
|
+
end
|
384
|
+
|
385
|
+
# A more extensive(expensive) query then the list method. Takes the same
|
386
|
+
# parameters as the list method; a string Ticket-SQL query and optional
|
387
|
+
# order, but returns a lot more information. Instead of just the ID and
|
388
|
+
# subject, you get back an array of hashes, where each hash represents
|
389
|
+
# one ticket, indentical to what you get from the show method (which only
|
390
|
+
# acts on one ticket). Use with caution; this can take a long time to
|
391
|
+
# execute.
|
392
|
+
#
|
393
|
+
# array = rt.query("Queue='Sales'")
|
394
|
+
# array = rt.query(:query => "Queue='Sales'",:order => "+Id")
|
395
|
+
# array = rt.query("Queue='Sales'","+Id")
|
396
|
+
# => array[0] = { "id" => "123", "requestors" => "someone@..", etc etc }
|
397
|
+
# => array[1] = { "id" => "126", "requestors" => "someone@else..", etc etc }
|
398
|
+
# => array[0]["id"] = "123"
|
399
|
+
def query(*params)
|
400
|
+
query = params[0]
|
401
|
+
order = ""
|
402
|
+
if params.size > 1
|
403
|
+
order = params[1]
|
404
|
+
end
|
405
|
+
if params[0].class == Hash
|
406
|
+
params = params[0]
|
407
|
+
query = params[:query] if params.has_key? :query
|
408
|
+
order = params[:order] if params.has_key? :order
|
409
|
+
end
|
410
|
+
replies = []
|
411
|
+
resp = @site["search/ticket/?query=#{URI.escape(query)}&orderby=#{order}&format=l"].get
|
412
|
+
return replies if resp =~/No matching results./
|
413
|
+
raise "Invalid query (#{query})" if resp =~ /Invalid query/
|
414
|
+
resp.gsub!(/RT\/\d+\.\d+\.\d+\s\d{3}\s.*\n\n/,"") # strip HTTP response
|
415
|
+
tickets = resp.split("\n--\n") # -- occurs between each ticket
|
416
|
+
tickets.each do |ticket|
|
417
|
+
ticket_h = response_to_h(ticket)
|
418
|
+
reply = {}
|
419
|
+
ticket_h.each do |k,v|
|
420
|
+
case k
|
421
|
+
when 'created','due','told','lastupdated','started'
|
422
|
+
begin
|
423
|
+
vv = DateTime.parse(v.to_s)
|
424
|
+
reply["#{k}"] = vv.strftime("%Y-%m-%d %H:%M:%S")
|
425
|
+
rescue ArgumentError
|
426
|
+
reply["#{k}"] = v.to_s
|
427
|
+
end
|
428
|
+
else
|
429
|
+
reply["#{k}"] = v.to_s
|
430
|
+
end
|
431
|
+
end
|
432
|
+
replies.push reply
|
433
|
+
end
|
434
|
+
replies
|
435
|
+
end
|
436
|
+
|
440
437
|
# Get a list of history transactions for a ticket. Takes a ticket ID and
|
441
438
|
# an optional format parameter. If the format is ommitted, the short
|
442
439
|
# format is assumed. If the short format is requested, it returns an
|
@@ -456,7 +453,7 @@ class RT_Client
|
|
456
453
|
# Content:: (The content of this item)
|
457
454
|
# Creator:: (the RT user that created this item)
|
458
455
|
# Created:: (Date/time this item was created)
|
459
|
-
# Attachments:: (a hash describing attachments to this item)
|
456
|
+
# Attachments:: (a hash describing attachments to this item)
|
460
457
|
#
|
461
458
|
# history = rt.history(881)
|
462
459
|
# history = rt.history(881,"short")
|
@@ -480,7 +477,7 @@ class RT_Client
|
|
480
477
|
id = $~[1] if id =~ /ticket\/(\d+)/
|
481
478
|
resp = @site["ticket/#{id}/history?format=#{format[0,1]}"].get
|
482
479
|
resp = sterilize(resp)
|
483
|
-
|
480
|
+
|
484
481
|
if format[0,1] == "s"
|
485
482
|
if comments
|
486
483
|
h = resp.split("\n").select{ |l| l =~ /^\d+:/ }
|
@@ -496,8 +493,8 @@ class RT_Client
|
|
496
493
|
while resp.match(/CF\.\{[\w_ ]*[ ]+[\w ]*\}/) #replace CF spaces with underscores
|
497
494
|
resp.gsub!(/CF\.\{([\w_ ]*)([ ]+)([\w ]*)\}/, 'CF.{\1_\3}')
|
498
495
|
end
|
499
|
-
|
500
|
-
items = resp.split("\n--\n")
|
496
|
+
|
497
|
+
items = resp.split("\n--\n")
|
501
498
|
list = []
|
502
499
|
items.each do |item|
|
503
500
|
th = response_to_h(item)
|
@@ -527,27 +524,27 @@ class RT_Client
|
|
527
524
|
end
|
528
525
|
when "content"
|
529
526
|
reply["content"] = v.to_s
|
530
|
-
temp = item.match(/^Content: (.*?)^\w+:/m)
|
527
|
+
temp = item.match(/^Content: (.*?)^\w+:/m)
|
531
528
|
reply["content"] = temp[1] if temp.class != NilClass
|
532
529
|
else
|
533
530
|
reply["#{k}"] = v.to_s
|
534
531
|
end
|
535
532
|
end
|
536
533
|
list.push reply
|
537
|
-
end
|
534
|
+
end
|
538
535
|
end
|
539
536
|
list
|
540
537
|
end
|
541
|
-
|
542
|
-
# Get the detail for a single history item. Needs a ticket ID and a
|
538
|
+
|
539
|
+
# Get the detail for a single history item. Needs a ticket ID and a
|
543
540
|
# history item ID, returns a hash of RT Fields => values. The hash
|
544
541
|
# also contains a special key named "attachments", whose value is
|
545
542
|
# an array of hashes, where each hash represents an attachment. The hash
|
546
543
|
# keys are :id, :name, and :size.
|
547
|
-
#
|
544
|
+
#
|
548
545
|
# x = rt.history_item(21, 6692)
|
549
546
|
# x = rt.history_item(:id => 21, :history => 6692)
|
550
|
-
# => x = {"ticket" => "21", "creator" => "somebody", "description" =>
|
547
|
+
# => x = {"ticket" => "21", "creator" => "somebody", "description" =>
|
551
548
|
# => "something happened", "attachments" => [{:name=>"file.txt",
|
552
549
|
# => :id=>"3289", size=>"651b"}, {:name=>"another.doc"... }]}
|
553
550
|
def history_item(*params)
|
@@ -588,12 +585,18 @@ class RT_Client
|
|
588
585
|
s[:size] = sz[2]
|
589
586
|
end
|
590
587
|
attachments.push s
|
588
|
+
if s[:name] == 'untitled' and s[:size].to_i > 0 # this is the content in HTML, I hope
|
589
|
+
unt_att = get_attachment(id,s[:id])
|
590
|
+
if (['text/html','text/plain'].include? unt_att["contenttype"]) # hurray
|
591
|
+
reply["content_html"] = sterilize(unt_att["content"])
|
592
|
+
end
|
593
|
+
end
|
591
594
|
end
|
592
595
|
reply["#{k}"] = attachments
|
593
596
|
end
|
594
597
|
when "content"
|
595
598
|
reply["content"] = v.to_s
|
596
|
-
temp = resp.match(/^Content: (.*?)^\w+:/m)
|
599
|
+
temp = resp.match(/^Content: (.*?)^\w+:/m)
|
597
600
|
reply["content"] = temp[1] if temp.class != NilClass
|
598
601
|
else
|
599
602
|
reply["#{k}"] = v.to_s
|
@@ -601,11 +604,11 @@ class RT_Client
|
|
601
604
|
end
|
602
605
|
reply
|
603
606
|
end
|
604
|
-
|
607
|
+
|
605
608
|
# Get a list of attachments related to a ticket.
|
606
609
|
# Requires a ticket id, returns an array of hashes where each hash
|
607
610
|
# represents one attachment. Hash keys are :id, :name, :type, :size.
|
608
|
-
# You can optionally request that unnamed attachments be included,
|
611
|
+
# You can optionally request that unnamed attachments be included,
|
609
612
|
# the default is to not include them.
|
610
613
|
def attachments(*params)
|
611
614
|
id = params[0]
|
@@ -625,7 +628,7 @@ class RT_Client
|
|
625
628
|
end
|
626
629
|
th = response_to_h(resp)
|
627
630
|
list = []
|
628
|
-
pattern = /(\d+:\s.*?\))
|
631
|
+
pattern = /(\d+:\s.*?\))(?:,|$)/
|
629
632
|
match = pattern.match(th['attachments'].to_s)
|
630
633
|
while match != nil
|
631
634
|
list.push match[0]
|
@@ -647,13 +650,13 @@ class RT_Client
|
|
647
650
|
end
|
648
651
|
attachments
|
649
652
|
end
|
650
|
-
|
653
|
+
|
651
654
|
# Get attachment content for single attachment. Requires a ticket ID
|
652
655
|
# and an attachment ID, which must be related. If a directory parameter
|
653
|
-
# is supplied, the attachment is written to that directory. If not,
|
654
|
-
# the attachment content is returned in the hash returned by the
|
656
|
+
# is supplied, the attachment is written to that directory. If not,
|
657
|
+
# the attachment content is returned in the hash returned by the
|
655
658
|
# function as the key 'content', along with some other keys you always get:
|
656
|
-
#
|
659
|
+
#
|
657
660
|
# transaction:: the transaction id
|
658
661
|
# creator:: the user id number who attached it
|
659
662
|
# id:: the attachment id
|
@@ -661,7 +664,7 @@ class RT_Client
|
|
661
664
|
# contenttype:: MIME content type of the attachment
|
662
665
|
# created:: date of the attachment
|
663
666
|
# parent:: an attachment id if this was an embedded MIME attachment
|
664
|
-
#
|
667
|
+
#
|
665
668
|
# x = get_attachment(21,3879)
|
666
669
|
# x = get_attachment(:ticket => 21, :attachment => 3879)
|
667
670
|
# x = get_attachment(:ticket => 21, :attachment => 3879, :dir = "/some/dir")
|
@@ -689,12 +692,12 @@ class RT_Client
|
|
689
692
|
end
|
690
693
|
content = resp.match(/Content:\s+(.*)/m)[1]
|
691
694
|
content.gsub!(/\n\s{9}/,"\n") # strip leading spaces on each line
|
692
|
-
content.chomp!
|
693
|
-
content.chomp!
|
695
|
+
content.chomp!
|
696
|
+
content.chomp!
|
694
697
|
content.chomp! # 3 carriage returns at the end
|
695
698
|
if (RUBY_VERSION.to_f >= 1.9)
|
696
699
|
binary = content.encode("ISO-8859-1","UTF-8", { :invalid => :replace, :undef => :replace })
|
697
|
-
else
|
700
|
+
else
|
698
701
|
binary = Iconv.conv("ISO-8859-1","UTF-8",content) # convert encoding
|
699
702
|
end
|
700
703
|
if dir
|
@@ -706,7 +709,7 @@ class RT_Client
|
|
706
709
|
end
|
707
710
|
reply
|
708
711
|
end
|
709
|
-
|
712
|
+
|
710
713
|
# Add a watcher to a ticket, but only if not already a watcher. Takes a
|
711
714
|
# ticket ID, an email address (or array of email addresses), and an
|
712
715
|
# optional watcher type. If no watcher type is specified, its assumed to
|
@@ -756,122 +759,121 @@ class RT_Client
|
|
756
759
|
edit(:id => tid, :Requestors => reqs.join(","))
|
757
760
|
end
|
758
761
|
end
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
end
|
762
|
+
|
763
|
+
# don't give up the password when the object is inspected
|
764
|
+
def inspect # :nodoc:
|
765
|
+
mystr = super()
|
766
|
+
mystr.gsub!(/(.)pass=.*?([,\}])/,"\\1pass=<hidden>\\2")
|
767
|
+
mystr
|
768
|
+
end
|
769
|
+
|
770
|
+
# helper to convert responses from RT REST to a hash
|
771
|
+
def response_to_h(resp) # :nodoc:
|
772
|
+
resp.gsub!(/RT\/\d+\.\d+\.\d+\s\d{3}\s.*\n\n/,"") # toss the HTTP response
|
773
|
+
|
774
|
+
# unfold folded fields
|
775
|
+
# A newline followed by one or more spaces is treated as a
|
776
|
+
# single space
|
777
|
+
resp.gsub!(/\n +/, " ")
|
778
|
+
|
779
|
+
#replace CF spaces with underscores
|
780
|
+
while resp.match(/CF\.\{[\w_ ]*[ ]+[\w ]*\}/)
|
781
|
+
resp.gsub!(/CF\.\{([\w_ ]*)([ ]+)([\w ]*)\}/, 'CF.{\1_\3}')
|
782
|
+
end
|
783
|
+
return {:error => resp } if resp =~ /does not exist./
|
784
|
+
|
785
|
+
# convert fields to key value pairs
|
786
|
+
ret = {}
|
787
|
+
resp.each_line do |ln|
|
788
|
+
next unless ln =~ /^.+?:/
|
789
|
+
ln_a = ln.split(/:/,2)
|
790
|
+
ln_a.map! {|item| item.strip}
|
791
|
+
ln_a[0].downcase!
|
792
|
+
ret[ln_a[0]] = ln_a[1]
|
793
|
+
end
|
794
|
+
|
795
|
+
return ret
|
796
|
+
end
|
797
|
+
|
798
|
+
# Helper for composing RT's "forms". Requires a hash where the
|
799
|
+
# keys are field names for an RT form. If there's a :Text key, the value
|
800
|
+
# is modified to insert whitespace on continuation lines. If there's an
|
801
|
+
# :Attachment key, the value is assumed to be a comma-separated list of
|
802
|
+
# filenames to attach. It returns a hash to be used with rest-client's
|
803
|
+
# payload class
|
804
|
+
def compose(fields) # :nodoc:
|
805
|
+
if fields.class != Hash
|
806
|
+
raise "RT_Client.compose requires parameters as a hash."
|
807
|
+
end
|
808
|
+
|
809
|
+
payload = { :multipart => true }
|
810
|
+
|
811
|
+
# attachments
|
812
|
+
if fields.has_key? :Attachments
|
813
|
+
fields[:Attachment] = fields[:Attachments]
|
814
|
+
fields.delete :Attachments
|
815
|
+
end
|
816
|
+
if fields.has_key? :Attachment
|
817
|
+
filenames = fields[:Attachment].split(',')
|
818
|
+
attachment_num = 1
|
819
|
+
filenames.each do |f|
|
820
|
+
payload["attachment_#{attachment_num.to_s}"] = File.new(f)
|
821
|
+
attachment_num += 1
|
822
|
+
end
|
823
|
+
# strip paths from filenames
|
824
|
+
fields[:Attachment] = filenames.map {|f| File.basename(f)}.join(',')
|
825
|
+
end
|
826
|
+
|
827
|
+
# fixup Text field for RT's quasi-RFC822 form
|
828
|
+
if fields.has_key? :Text
|
829
|
+
fields[:Text].gsub!(/\n/,"\n ") # insert a space on continuation lines.
|
830
|
+
end
|
831
|
+
|
832
|
+
field_array = fields.map { |k,v| "#{k}: #{v}" }
|
833
|
+
content = field_array.join("\n") # our form
|
834
|
+
payload["content"] = content
|
835
|
+
|
836
|
+
return payload
|
837
|
+
end
|
838
|
+
|
839
|
+
# Helper to replace control characters with string representations
|
840
|
+
# When some XML libraries hit these in an XML response, bad things happen.
|
841
|
+
def sterilize(str)
|
842
|
+
str.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
843
|
+
str.gsub!(0x00.chr,'[NUL]')
|
844
|
+
str.gsub!(0x01.chr,'[SOH]')
|
845
|
+
str.gsub!(0x02.chr,'[STX]')
|
846
|
+
str.gsub!(0x03.chr,'[ETX]')
|
847
|
+
str.gsub!(0x04.chr,'[EOT]')
|
848
|
+
str.gsub!(0x05.chr,'[ENQ]')
|
849
|
+
str.gsub!(0x06.chr,'[ACK]')
|
850
|
+
str.gsub!(0x07.chr,'[BEL]')
|
851
|
+
# 0x08 is backspace
|
852
|
+
# 0x09 is TAB
|
853
|
+
# 0x10 is line feed
|
854
|
+
str.gsub!(0x0B.chr,'[VT]')
|
855
|
+
# 0x0c is form feed
|
856
|
+
# 0x0d is carriage return
|
857
|
+
str.gsub!(0x0E.chr,'[SO]')
|
858
|
+
str.gsub!(0x0F.chr,'[SI]')
|
859
|
+
str.gsub!(0x10.chr,'[DLE]')
|
860
|
+
str.gsub!(0x11.chr,'[DC1]')
|
861
|
+
str.gsub!(0x12.chr,'[DC2]')
|
862
|
+
str.gsub!(0x13.chr,'[DC3]')
|
863
|
+
str.gsub!(0x14.chr,'[DC4]')
|
864
|
+
str.gsub!(0x15.chr,'[NAK]')
|
865
|
+
str.gsub!(0x16.chr,'[SYN]')
|
866
|
+
str.gsub!(0x17.chr,'[ETB]')
|
867
|
+
str.gsub!(0x18.chr,'[CAN]')
|
868
|
+
str.gsub!(0x19.chr,'[EM]')
|
869
|
+
str.gsub!(0x1a.chr,'[SUB]')
|
870
|
+
str.gsub!(0x1C.chr,'[FS]')
|
871
|
+
str.gsub!(0x1D.chr,'[GS]')
|
872
|
+
str.gsub!(0x1E.chr,'[RS]')
|
873
|
+
str.gsub!(0x1F.chr,'[US]')
|
874
|
+
str
|
875
|
+
end
|
876
|
+
|
877
|
+
end
|
876
878
|
|
877
879
|
|
data/rtxmlsrv.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
## around the rt/client library.
|
6
6
|
|
7
7
|
require "rubygems" # so we can load gems
|
8
|
-
require "rt_client/rt_client"
|
8
|
+
require "rt_client/rt_client" # rt-client REST library
|
9
9
|
require "xmlrpc/server" # that's what we're doing
|
10
10
|
require "date" # for parsing arbitrary date formats
|
11
11
|
|
data/rtxmlsrv2.rb
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
## XML RPC service to provide a cross-platform API for
|
4
|
+
## RT ticket creation/maintenance. Essentially just a wrapper
|
5
|
+
## around the rt/client library.
|
6
|
+
|
7
|
+
require "rubygems" # so we can load gems
|
8
|
+
require "rt_client/rt_client" # rt-client REST library
|
9
|
+
require "builder"
|
10
|
+
require "rack/rpc"
|
11
|
+
require "date" # for parsing arbitrary date formats
|
12
|
+
|
13
|
+
# extend the Hash class to
|
14
|
+
# translate string keys into symbol keys
|
15
|
+
class Hash # :nodoc:
|
16
|
+
def remapkeys!
|
17
|
+
n = Hash.new
|
18
|
+
self.each_key do |key|
|
19
|
+
n[key.to_sym] = self[key]
|
20
|
+
end
|
21
|
+
self.replace(n)
|
22
|
+
n = nil
|
23
|
+
$stderr.puts self.map { |k,v| "#{k} => #{v}" }
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TicketSrv < Rack::RPC::Server
|
29
|
+
|
30
|
+
# Allows watchers to be added via RT_Client::add_watcher
|
31
|
+
# You need to pass :id, :addr, and optionally :type
|
32
|
+
def add_watcher(struct)
|
33
|
+
struct.remapkeys!
|
34
|
+
if struct.has_key? :user and struct.has_key? :pass
|
35
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
36
|
+
struct.delete(:user)
|
37
|
+
struct.delete(:pass)
|
38
|
+
else
|
39
|
+
rt = RT_Client.new
|
40
|
+
end
|
41
|
+
val = rt.add_watcher(struct)
|
42
|
+
rt = nil
|
43
|
+
val
|
44
|
+
end
|
45
|
+
|
46
|
+
# Gets a list of attachments via RT_Client::attachments
|
47
|
+
# You need to pass :id, and optionally :unnamed
|
48
|
+
def attachments(struct)
|
49
|
+
struct.remapkeys!
|
50
|
+
if struct.has_key? :user and struct.has_key? :pass
|
51
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
52
|
+
struct.delete(:user)
|
53
|
+
struct.delete(:pass)
|
54
|
+
else
|
55
|
+
rt = RT_Client.new
|
56
|
+
end
|
57
|
+
rt = RT_Client.new
|
58
|
+
val = rt.attachments(struct)
|
59
|
+
rt = nil
|
60
|
+
val
|
61
|
+
end
|
62
|
+
|
63
|
+
# Adds comments to tickets via RT_Client::comment
|
64
|
+
def comment(struct)
|
65
|
+
struct.remapkeys!
|
66
|
+
if struct.has_key? :user and struct.has_key? :pass
|
67
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
68
|
+
struct.delete(:user)
|
69
|
+
struct.delete(:pass)
|
70
|
+
else
|
71
|
+
rt = RT_Client.new
|
72
|
+
end
|
73
|
+
val = rt.comment(struct)
|
74
|
+
rt = nil
|
75
|
+
val
|
76
|
+
end
|
77
|
+
|
78
|
+
# Allows new tickets to be created via RT_Client::correspond
|
79
|
+
def correspond(struct)
|
80
|
+
struct.remapkeys!
|
81
|
+
if struct.has_key? :user and struct.has_key? :pass
|
82
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
83
|
+
struct.delete(:user)
|
84
|
+
struct.delete(:pass)
|
85
|
+
else
|
86
|
+
rt = RT_Client.new
|
87
|
+
end
|
88
|
+
val = rt.correspond(struct)
|
89
|
+
rt = nil
|
90
|
+
val
|
91
|
+
end
|
92
|
+
|
93
|
+
# Allows new tickets to be created via RT_Client::create
|
94
|
+
def create(struct)
|
95
|
+
struct.remapkeys!
|
96
|
+
if struct.has_key? :user and struct.has_key? :pass
|
97
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
98
|
+
struct.delete(:user)
|
99
|
+
struct.delete(:pass)
|
100
|
+
else
|
101
|
+
rt = RT_Client.new
|
102
|
+
end
|
103
|
+
val = rt.create(struct)
|
104
|
+
rt = nil
|
105
|
+
val
|
106
|
+
end
|
107
|
+
|
108
|
+
# Allows new users to be created via RT_Client::create_user
|
109
|
+
def create_user(struct)
|
110
|
+
struct.remapkeys!
|
111
|
+
if struct.has_key? :user and struct.has_key? :pass
|
112
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
113
|
+
struct.delete(:user)
|
114
|
+
struct.delete(:pass)
|
115
|
+
else
|
116
|
+
rt = RT_Client.new
|
117
|
+
end
|
118
|
+
val = rt.create_user(struct)
|
119
|
+
rt = nil
|
120
|
+
val
|
121
|
+
end
|
122
|
+
|
123
|
+
# Find RT user details from email address via RT_Cleint::usersearch
|
124
|
+
def usersearch(struct)
|
125
|
+
struct.remapkeys!
|
126
|
+
if struct.has_key? :user and struct.has_key? :pass
|
127
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
128
|
+
struct.delete(:user)
|
129
|
+
struct.delete(:pass)
|
130
|
+
else
|
131
|
+
rt = RT_Client.new
|
132
|
+
end
|
133
|
+
val = rt.usersearch(struct)
|
134
|
+
rt = nil
|
135
|
+
val
|
136
|
+
end
|
137
|
+
|
138
|
+
# Allows new users to be edited or created if they don't exist
|
139
|
+
def edit_or_create_user(struct)
|
140
|
+
struct.remapkeys!
|
141
|
+
if struct.has_key? :user and struct.has_key? :pass
|
142
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
143
|
+
struct.delete(:user)
|
144
|
+
struct.delete(:pass)
|
145
|
+
else
|
146
|
+
rt = RT_Client.new
|
147
|
+
end
|
148
|
+
val = rt.edit_or_create_user(struct)
|
149
|
+
rt = nil
|
150
|
+
val
|
151
|
+
end
|
152
|
+
|
153
|
+
# Allows existing ticket to be modified via RT_Client::edit
|
154
|
+
def edit(struct)
|
155
|
+
struct.remapkeys!
|
156
|
+
if struct.has_key? :user and struct.has_key? :pass
|
157
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
158
|
+
struct.delete(:user)
|
159
|
+
struct.delete(:pass)
|
160
|
+
else
|
161
|
+
rt = RT_Client.new
|
162
|
+
end
|
163
|
+
val = rt.edit(struct)
|
164
|
+
rt = nil
|
165
|
+
val
|
166
|
+
end
|
167
|
+
|
168
|
+
# Retrieves attachments via RT_Client::get_attachment
|
169
|
+
def get_attachment(struct)
|
170
|
+
struct.remapkeys!
|
171
|
+
if struct.has_key? :user and struct.has_key? :pass
|
172
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
173
|
+
struct.delete(:user)
|
174
|
+
struct.delete(:pass)
|
175
|
+
else
|
176
|
+
rt = RT_Client.new
|
177
|
+
end
|
178
|
+
val = rt.get_attachment(struct)
|
179
|
+
rt = nil
|
180
|
+
val
|
181
|
+
end
|
182
|
+
|
183
|
+
# Gets the history of a ticket via RT_Client::history
|
184
|
+
def history(struct)
|
185
|
+
struct.remapkeys!
|
186
|
+
if struct.has_key? :user and struct.has_key? :pass
|
187
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
188
|
+
struct.delete(:user)
|
189
|
+
struct.delete(:pass)
|
190
|
+
else
|
191
|
+
rt = RT_Client.new
|
192
|
+
end
|
193
|
+
val = rt.history(struct)
|
194
|
+
rt = nil
|
195
|
+
val
|
196
|
+
end
|
197
|
+
|
198
|
+
# Gets a single history item via RT_Client::history_item
|
199
|
+
def history_item(struct)
|
200
|
+
struct.remapkeys!
|
201
|
+
if struct.has_key? :user and struct.has_key? :pass
|
202
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
203
|
+
struct.delete(:user)
|
204
|
+
struct.delete(:pass)
|
205
|
+
else
|
206
|
+
rt = RT_Client.new
|
207
|
+
end
|
208
|
+
val = rt.history_item(struct)
|
209
|
+
rt = nil
|
210
|
+
val
|
211
|
+
end
|
212
|
+
|
213
|
+
# Gets a list of tickets via RT_Client::list
|
214
|
+
def list(struct)
|
215
|
+
struct.remapkeys!
|
216
|
+
if struct.has_key? :user and struct.has_key? :pass
|
217
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
218
|
+
struct.delete(:user)
|
219
|
+
struct.delete(:pass)
|
220
|
+
else
|
221
|
+
rt = RT_Client.new
|
222
|
+
end
|
223
|
+
val = rt.list(struct)
|
224
|
+
rt = nil
|
225
|
+
val
|
226
|
+
end
|
227
|
+
|
228
|
+
# Gets a list of tickets via RT_Client::query
|
229
|
+
def query(struct)
|
230
|
+
struct.remapkeys!
|
231
|
+
if struct.has_key? :user and struct.has_key? :pass
|
232
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
233
|
+
struct.delete(:user)
|
234
|
+
struct.delete(:pass)
|
235
|
+
else
|
236
|
+
rt = RT_Client.new
|
237
|
+
end
|
238
|
+
val = rt.query(struct)
|
239
|
+
rt = nil
|
240
|
+
val
|
241
|
+
end
|
242
|
+
|
243
|
+
# Gets detail (minus history/attachments) via RT_Client::show
|
244
|
+
def show(struct)
|
245
|
+
struct.remapkeys!
|
246
|
+
if struct.has_key? :user and struct.has_key? :pass
|
247
|
+
rt = RT_Client.new(:user => struct[:user], :pass => struct[:pass])
|
248
|
+
struct.delete(:user)
|
249
|
+
struct.delete(:pass)
|
250
|
+
else
|
251
|
+
rt = RT_Client.new
|
252
|
+
end
|
253
|
+
val = rt.show(struct)
|
254
|
+
rt = nil
|
255
|
+
val
|
256
|
+
end
|
257
|
+
|
258
|
+
interface = [ 'add_watcher', 'attachments', 'comment', 'correspond', 'create', 'create_user',
|
259
|
+
'edit', 'edit_or_create_user', 'get_attachment', 'history', 'history_item', 'list', 'query',
|
260
|
+
'show' ]
|
261
|
+
|
262
|
+
interface.each do |meth|
|
263
|
+
# rpc meth => meth.to_sym
|
264
|
+
rpc "rt.#{meth}" => meth.to_sym
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
end # class TicketSrv
|
269
|
+
|
270
|
+
#######################################################
|
271
|
+
|
272
|
+
class RTXMLApp
|
273
|
+
def call(env)
|
274
|
+
[200, {'Content-Type' => 'text/xml'}, ['RT XML-RPC Service']]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rt-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-07-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -37,6 +37,7 @@ extra_rdoc_files: []
|
|
37
37
|
files:
|
38
38
|
- rt_client.rb
|
39
39
|
- rtxmlsrv.rb
|
40
|
+
- rtxmlsrv2.rb
|
40
41
|
- rt/client.rb
|
41
42
|
- .yardopts
|
42
43
|
homepage: http://rubygems.org/gems/rt-client
|
@@ -69,3 +70,4 @@ signing_key:
|
|
69
70
|
specification_version: 4
|
70
71
|
summary: Ruby object for RT access via REST
|
71
72
|
test_files: []
|
73
|
+
has_rdoc:
|