rt-client 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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: