class Roust

Constants

VERSION

Public Class Methods

new(credentials, headers = {}) click to toggle source
# File lib/roust.rb, line 15
def initialize(credentials, headers = {})
  @server   = credentials[:server]
  @username = credentials[:username]
  @password = credentials[:password]

  if @server =~ /REST\/1\.0/
    raise ArgumentError, 'The supplied :server has REST in the URL. You only need to specify the base, e.g. http://rt.example.org/'
  end

  headers['User-Agent'] = 'Roust' unless headers.key?('User-Agent')
  self.class.headers.merge!(headers)
  authenticate!
end

Public Instance Methods

authenticate!() click to toggle source
# File lib/roust.rb, line 29
def authenticate!
  # - There is no way to authenticate against the API. The only way to log
  #   in is to fill out the same HTML form humans fill in, cache the cookies
  #   returned, and send them on every subsequent request.
  # - RT does not provide *any* indication that the authentication request
  #   has succeeded or failed. RT will always return a HTTP 200.

  self.class.base_uri(@server)

  response = self.class.post(
    '/index.html',
    :body => {
      :user => @username,
      :pass => @password
    }
  )

  cookie = response.headers['set-cookie']
  self.class.headers['Cookie'] = cookie if cookie

  # Switch the base uri over to the actual REST API base uri.
  self.class.base_uri "#{@server}/REST/1.0"

  # - The easiest way to programatically check if an authentication request
  #   succeeded is by doing a request for a ticket, and seeing if the API
  #   responds with some specific text ("401 Credentials required") that
  #   indicates authentication has previously failed.
  # - The authenticated? method will return false if an Unauthenticated
  #   exception bubbles up from response handling. We (dirtily) rethrow the
  #   exception.
  raise Unauthenticated unless authenticated?
end
authenticated?() click to toggle source
# File lib/roust.rb, line 62
def authenticated?
  return true if show('1')
rescue Unauthenticated
  return false
end

Private Instance Methods

body_to_hash(body) click to toggle source
# File lib/roust.rb, line 134
def body_to_hash(body)
  body.gsub!(/\n\s*\n/, "\n") # remove blank lines for Mail

  message = Mail.new(body)
  pairs = message.header.fields.map do |header|
    key   = header.name.to_s
    value = header.value.to_s
    [ key, value ]
  end
  hash = Hash[pairs]
end
compose_content(type, id, attrs) click to toggle source

compose_content turns a Hash into an RFC2822 “key: value”-like header blob

This is the fucked up format RT demands all content is sent and received in.

# File lib/roust.rb, line 73
def compose_content(type, id, attrs)
  default_attrs = {
    'id' => [ type, id ].join('/')
  }
  attrs = default_attrs.merge(attrs).stringify_keys!

  content = attrs.map do |k, v|
    # Don't lowercase strings if they're already camel cased.
    k = case
        when k.is_a?(Symbol)
          k.to_s
        when k == 'id'
          k
        when k =~ /^[a-z]/
          k.capitalize
        else
          k
        end

    k = sanitize_content_key(k)
    v = v.join(', ') if v.respond_to?(:join)

    "#{k}: #{v}"
  end

  content.join("\n")
end
explode_response(response) click to toggle source

explode_response separates RT's response content from the response status.

All HTTP-level response codes from RT are a lie. The only way to check if the request was successful is by inspecting the body of the content back from RT, and separating the first line from the rest of the content.

  • The first line contains the status of the operation.

  • All subsequent lines (if there are any) are the message body.

# File lib/roust.rb, line 122
def explode_response(response)
  body   = response.body
  status = body[/RT\/\d+\.\d+\.\d+\s(\d{3}\s.*)\n/, 1]

  body.gsub!(/RT\/\d+\.\d+\.\d+\s\d{3}\s.*\n/, '')
  body = body.empty? ? nil : body.lstrip

  raise Unauthenticated, 'Invalid username or password' if status =~ /401 Credentials required/

  return body, status
end
sanitize_content_key(k) click to toggle source
# File lib/roust.rb, line 101
def sanitize_content_key(k)
  case k.downcase
  when 'requestors'
    'Requestors'
  when 'cc'
    'Cc'
  when 'admincc'
    'AdminCc'
  else
    k
  end
end