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.
Files changed (4) hide show
  1. data/rt_client.rb +381 -379
  2. data/rtxmlsrv.rb +1 -1
  3. data/rtxmlsrv2.rb +277 -0
  4. 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 Brian McArdle for patch dealing with spaces in Custom Fields.
21
- ##To reference custom fields in RT that have spaces with rt-client, use an
22
- ##underscore in the rt-client code, e.g. "CF.{{Has_Space}}"
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 couldn't be buggered
25
- ##and a great job of refactoring the old mess into something with much fewer
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 "rt/client"
35
+ # require "rt_client"
37
36
 
38
37
  class RT_Client
39
38
 
40
- UA = "Mozilla/5.0 ruby RT Client Interface 0.7.5"
41
- attr_reader :status, :site, :version, :cookies, :server, :user, :cookie
42
-
43
- # Create a new RT_Client object. Load up our stored cookie and check it.
44
- # Log into RT again if needed and store the new cookie. You can specify
45
- # login and cookie storage directories in 3 different ways:
46
- # 1. Explicity during object creation
47
- # 2. From a .rtclientrc file in the working directory of your ruby program
48
- # 3. From a .rtclientrc file in the same directory as the library itself
49
- #
50
- # These are listed in order of priority; if you have explicit parameters,
51
- # they are always used, even if you have .rtclientrc files. If there
52
- # is both an .rtclientrc in your program's working directory and
53
- # in the library directory, the one from your program's working directory
54
- # is used. If no parameters are specified either explicity or by use
55
- # of a .rtclientrc, then the defaults of "rt_user", "rt_pass" are used
56
- # with a default server of "http://localhost", and cookies are stored
57
- # in the directory where the library resides.
58
- #
59
- # rt= RT_Client.new( :server => "https://tickets.ambulance.com/",
60
- # :user => "rt_user",
61
- # :pass => "rt_pass",
62
- # :cookies => "/my/cookie/dir" )
63
- #
64
- # rt= RT_Client.new # use defaults from .rtclientrc
65
- #
66
- # .rtclientrc format:
67
- # server=<RT server>
68
- # user=<RT user>
69
- # pass=<RT password>
70
- # cookies=<directory>
71
- def initialize(*params)
72
- @boundary = "----xYzZY#{rand(1000000).to_s}xYzZY"
73
- @version = "0.7.5"
74
- @status = "Not connected"
75
- @server = "http://localhost/"
76
- @user = "rt_user"
77
- @pass = "rt_pass"
78
- @cookies = Dir.pwd
79
- config_file = Dir.pwd + "/.rtclientrc"
80
- config = ""
81
- if File.file?(config_file)
82
- config = File.read(config_file)
83
- else
84
- config_file = File.dirname(__FILE__) + "/.rtclientrc"
85
- config = File.read(config_file) if File.file?(config_file)
86
- end
87
- @server = $~[1] if config =~ /\s*server\s*=\s*(.*)$/i
88
- @user = $~[1] if config =~ /^\s*user\s*=\s*(.*)$/i
89
- @pass = $~[1] if config =~ /^\s*pass\s*=\s*(.*)$/i
90
- @cookies = $~[1] if config =~ /\s*cookies\s*=\s*(.*)$/i
91
- @resource = "#{@server}REST/1.0/"
92
- if params.class == Array && params[0].class == Hash
93
- param = params[0]
94
- @user = param[:user] if param.has_key? :user
95
- @pass = param[:pass] if param.has_key? :pass
96
- if param.has_key? :server
97
- @server = param[:server]
98
- @server += "/" if @server !~ /\/$/
99
- @resource = "#{@server}REST/1.0/"
100
- end
101
- @cookies = param[:cookies] if param.has_key? :cookies
102
- end
103
- @login = { :user => @user, :pass => @pass }
104
- cookiejar = "#{@cookies}/RT_Client.#{@user}.cookie" # cookie location
105
- cookiejar.untaint
106
- if File.file? cookiejar
107
- @cookie = File.read(cookiejar).chomp
108
- headers = { 'User-Agent' => UA,
109
- 'Content-Type' => "application/x-www-form-urlencoded",
110
- 'Cookie' => @cookie }
111
- else
112
- headers = { 'User-Agent' => UA,
113
- 'Content-Type' => "application/x-www-form-urlencoded" }
114
- @cookie = ""
115
- end
116
-
117
-
118
- site = RestClient::Resource.new(@resource, :headers => headers, :timeout => 240)
119
- data = site.post "" # a null post just to check that we are logged in
120
-
121
- if @cookie.length == 0 or data =~ /401/ # we're not logged in
122
- data = site.post @login, :headers => headers
123
- # puts data
124
- @cookie = data.headers[:set_cookie].first.split('; ')[0]
125
- # write the new cookie
126
- if @cookie !~ /nil/
127
- f = File.new(cookiejar,"w")
128
- f.puts @cookie
129
- f.close
130
- end
131
- end
132
- headers = { 'User-Agent' => UA,
133
- 'Content-Type' => "multipart/form-data; boundary=#{@boundary}",
134
- 'Cookie' => @cookie }
135
- @site = RestClient::Resource.new(@resource, :headers => headers)
136
- @status = data
137
- self.untaint
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,
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
- # Get a list of tickets matching some criteria.
356
- # Takes a string Ticket-SQL query and an optional "order by" parameter.
357
- # The order by is an RT field, prefix it with + for ascending
358
- # or - for descending.
359
- # Returns a nested array of arrays containing [ticket number, subject]
360
- # The outer array is in the order requested.
361
- #
362
- # hash = rt.list(:query => "Queue = 'Sales'")
363
- # hash = rt.list("Queue='Sales'")
364
- # hash = rt.list(:query => "Queue = 'Sales'", :order => "-Id")
365
- # hash = rt.list("Queue='Sales'","-Id")
366
- def list(*params)
367
- query = params[0]
368
- order = ""
369
- if params.size > 1
370
- order = params[1]
371
- end
372
- if params[0].class == Hash
373
- params = params[0]
374
- query = params[:query] if params.has_key? :query
375
- order = params[:order] if params.has_key? :order
376
- end
377
- reply = []
378
- resp = @site["search/ticket/?query=#{URI.escape(query)}&orderby=#{order}&format=s"].get
379
- raise "Invalid query (#{query})" if resp =~ /Invalid query/
380
- resp = resp.split("\n") # convert to array of lines
381
- resp.each do |line|
382
- f = line.match(/^(\d+):\s*(.*)/)
383
- reply.push [f[1],f[2]] if f.class == MatchData
384
- end
385
- reply
386
- end
387
-
388
- # A more extensive(expensive) query then the list method. Takes the same
389
- # parameters as the list method; a string Ticket-SQL query and optional
390
- # order, but returns a lot more information. Instead of just the ID and
391
- # subject, you get back an array of hashes, where each hash represents
392
- # one ticket, indentical to what you get from the show method (which only
393
- # acts on one ticket). Use with caution; this can take a long time to
394
- # execute.
395
- #
396
- # array = rt.query("Queue='Sales'")
397
- # array = rt.query(:query => "Queue='Sales'",:order => "+Id")
398
- # array = rt.query("Queue='Sales'","+Id")
399
- # => array[0] = { "id" => "123", "requestors" => "someone@..", etc etc }
400
- # => array[1] = { "id" => "126", "requestors" => "someone@else..", etc etc }
401
- # => array[0]["id"] = "123"
402
- def query(*params)
403
- query = params[0]
404
- order = ""
405
- if params.size > 1
406
- order = params[1]
407
- end
408
- if params[0].class == Hash
409
- params = params[0]
410
- query = params[:query] if params.has_key? :query
411
- order = params[:order] if params.has_key? :order
412
- end
413
- replies = []
414
- resp = @site["search/ticket/?query=#{URI.escape(query)}&orderby=#{order}&format=l"].get
415
- return replies if resp =~/No matching results./
416
- raise "Invalid query (#{query})" if resp =~ /Invalid query/
417
- resp.gsub!(/RT\/\d+\.\d+\.\d+\s\d{3}\s.*\n\n/,"") # strip HTTP response
418
- tickets = resp.split("\n--\n") # -- occurs between each ticket
419
- tickets.each do |ticket|
420
- ticket_h = response_to_h(ticket)
421
- reply = {}
422
- ticket_h.each do |k,v|
423
- case k
424
- when 'created','due','told','lastupdated','started'
425
- begin
426
- vv = DateTime.parse(v.to_s)
427
- reply["#{k}"] = vv.strftime("%Y-%m-%d %H:%M:%S")
428
- rescue ArgumentError
429
- reply["#{k}"] = v.to_s
430
- end
431
- else
432
- reply["#{k}"] = v.to_s
433
- end
434
- end
435
- replies.push reply
436
- end
437
- replies
438
- end
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
- # don't give up the password when the object is inspected
762
- def inspect # :nodoc:
763
- mystr = super()
764
- mystr.gsub!(/(.)pass=.*?([,\}])/,"\\1pass=<hidden>\\2")
765
- mystr
766
- end
767
-
768
- # helper to convert responses from RT REST to a hash
769
- def response_to_h(resp) # :nodoc:
770
- resp.gsub!(/RT\/\d+\.\d+\.\d+\s\d{3}\s.*\n\n/,"") # toss the HTTP response
771
-
772
- # unfold folded fields
773
- # A newline followed by one or more spaces is treated as a
774
- # single space
775
- resp.gsub!(/\n +/, " ")
776
-
777
- #replace CF spaces with underscores
778
- while resp.match(/CF\.\{[\w_ ]*[ ]+[\w ]*\}/)
779
- resp.gsub!(/CF\.\{([\w_ ]*)([ ]+)([\w ]*)\}/, 'CF.{\1_\3}')
780
- end
781
- return {:error => resp } if resp =~ /does not exist./
782
-
783
- # convert fields to key value pairs
784
- ret = {}
785
- resp.each_line do |ln|
786
- next unless ln =~ /^.+?:/
787
- ln_a = ln.split(/:/,2)
788
- ln_a.map! {|item| item.strip}
789
- ln_a[0].downcase!
790
- ret[ln_a[0]] = ln_a[1]
791
- end
792
-
793
- return ret
794
- end
795
-
796
- # Helper for composing RT's "forms". Requires a hash where the
797
- # keys are field names for an RT form. If there's a :Text key, the value
798
- # is modified to insert whitespace on continuation lines. If there's an
799
- # :Attachment key, the value is assumed to be a comma-separated list of
800
- # filenames to attach. It returns a hash to be used with rest-client's
801
- # payload class
802
- def compose(fields) # :nodoc:
803
- if fields.class != Hash
804
- raise "RT_Client.compose requires parameters as a hash."
805
- end
806
-
807
- payload = { :multipart => true }
808
-
809
- # attachments
810
- if fields.has_key? :Attachments
811
- fields[:Attachment] = fields[:Attachments]
812
- fields.delete :Attachments
813
- end
814
- if fields.has_key? :Attachment
815
- filenames = fields[:Attachment].split(',')
816
- attachment_num = 1
817
- filenames.each do |f|
818
- payload["attachment_#{attachment_num.to_s}"] = File.new(f)
819
- attachment_num += 1
820
- end
821
- # strip paths from filenames
822
- fields[:Attachment] = filenames.map {|f| File.basename(f)}.join(',')
823
- end
824
-
825
- # fixup Text field for RFC822 compliance
826
- if fields.has_key? :Text
827
- fields[:Text].gsub!(/\n/,"\n ") # insert a space on continuation lines.
828
- end
829
-
830
- field_array = fields.map { |k,v| "#{k}: #{v}" }
831
- content = field_array.join("\n") # our form
832
- payload["content"] = content
833
-
834
- return payload
835
- end
836
-
837
- # Helper to replace control characters with string representations
838
- # When Microsoft XML libraries hit these in an XML response, bad
839
- # things happen.
840
- def sterilize(str)
841
- str.gsub!(0x00.chr,'[NUL]')
842
- str.gsub!(0x01.chr,'[SOH]')
843
- str.gsub!(0x02.chr,'[STX]')
844
- str.gsub!(0x03.chr,'[ETX]')
845
- str.gsub!(0x04.chr,'[EOT]')
846
- str.gsub!(0x05.chr,'[ENQ]')
847
- str.gsub!(0x06.chr,'[ACK]')
848
- str.gsub!(0x07.chr,'[BEL]')
849
- # 0x08 is backspace
850
- # 0x09 is TAB
851
- # 0x10 is line feed
852
- str.gsub!(0x0B.chr,'[VT]')
853
- # 0x0c is form feed
854
- # 0x0d is carriage return
855
- str.gsub!(0x0E.chr,'[SO]')
856
- str.gsub!(0x0F.chr,'[SI]')
857
- str.gsub!(0x10.chr,'[DLE]')
858
- str.gsub!(0x11.chr,'[DC1]')
859
- str.gsub!(0x12.chr,'[DC2]')
860
- str.gsub!(0x13.chr,'[DC3]')
861
- str.gsub!(0x14.chr,'[DC4]')
862
- str.gsub!(0x15.chr,'[NAK]')
863
- str.gsub!(0x16.chr,'[SYN]')
864
- str.gsub!(0x17.chr,'[ETB]')
865
- str.gsub!(0x18.chr,'[CAN]')
866
- str.gsub!(0x19.chr,'[EM]')
867
- str.gsub!(0x1a.chr,'[SUB]')
868
- str.gsub!(0x1C.chr,'[FS]')
869
- str.gsub!(0x1D.chr,'[GS]')
870
- str.gsub!(0x1E.chr,'[RS]')
871
- str.gsub!(0x1F.chr,'[US]')
872
- str
873
- end
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" # rt-client REST library
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.7.5
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-03-07 00:00:00.000000000 Z
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: