class RForce::Binding

Implements the connection to the Salesforce server.

Constants

AssignmentRuleHeaderUsingDefaultRule
AssignmentRuleHeaderUsingRuleId
ClientIdHeader
DEFAULT_BATCH_SIZE

Increase the maximum fetch size to 2000, as allowed by Salesforce Added by Raymond Gao

Envelope

Fill in the guts of this typical SOAP envelope with the session ID and the body of the SOAP request.

MruHeader
QueryOptions

Attributes

assignment_rule_id[RW]
batch_size[RW]
client_id[RW]
trigger_auto_response_email[RW]
trigger_other_email[RW]
trigger_user_email[RW]
update_mru[RW]
url[RW]
use_default_rule[RW]

Public Class Methods

new(url, sid = nil, oauth = nil, proxy = nil) click to toggle source

Connect to the server securely. If you pass an oauth hash, it must contain the keys :consumer_key, :consumer_secret, :access_token, :access_secret, and :login_url.

proxy may be a URL of the form user:pass@example.com:port

# File lib/rforce/binding.rb, line 53
def initialize(url, sid = nil, oauth = nil, proxy = nil)
  @session_id = sid
  @oauth = oauth
  @proxy = proxy
  @batch_size = DEFAULT_BATCH_SIZE

  init_server(url)
end

Public Instance Methods

call_remote(method, args) { |: ["soap.com", path]| ... } click to toggle source

Call a method on the remote server. Arguments can be a hash or (if order is important) an array of alternating keys and values.

# File lib/rforce/binding.rb, line 150
def call_remote(method, args)

  urn, soap_url = block_given? ? yield : ["urn:partner.soap.sforce.com", @url.path]

  # Create XML text from the arguments.
  expanded = ''
  @builder = Builder::XmlMarkup.new(:target => expanded)
  expand(@builder, {method => args}, urn)

  extra_headers = ""

  # QueryOptions is not valid when making an Apex Webservice SOAP call
  if !block_given?
    extra_headers << (QueryOptions % @batch_size)
  end

  extra_headers << (AssignmentRuleHeaderUsingRuleId % assignment_rule_id) if assignment_rule_id
  extra_headers << AssignmentRuleHeaderUsingDefaultRule if use_default_rule
  extra_headers << MruHeader if update_mru
  extra_headers << (ClientIdHeader % client_id) if client_id

  if trigger_user_email or trigger_other_email or trigger_auto_response_email
    extra_headers << '<partner:EmailHeader soap:mustUnderstand="1">'

    extra_headers << '<partner:triggerUserEmail>true</partner:triggerUserEmail>' if trigger_user_email
    extra_headers << '<partner:triggerOtherEmail>true</partner:triggerOtherEmail>' if trigger_other_email
    extra_headers << '<partner:triggerAutoResponseEmail>true</partner:triggerAutoResponseEmail>' if trigger_auto_response_email

    extra_headers << '</partner:EmailHeader>'
  end

  # Fill in the blanks of the SOAP envelope with our
  # session ID and the expanded XML of our request.
  request = (Envelope % [@session_id, extra_headers, expanded])

  # reset the batch size for the next request
  @batch_size = DEFAULT_BATCH_SIZE

  # gzip request
  request = encode(request)

  headers = {
    'Connection' => 'Keep-Alive',
    'Content-Type' => 'text/xml',
    'SOAPAction' => '""',
    'User-Agent' => 'activesalesforce rforce/1.0'
  }

  unless show_debug
    headers['Accept-Encoding'] = 'gzip'
    headers['Content-Encoding'] = 'gzip'
  end

  # Send the request to the server and read the response.
  response = @server.post2(soap_url, request.lstrip, headers)

  # decode if we have encoding
  content = decode(response)

  # Check to see if INVALID_SESSION_ID was raised and try to relogin in
  if method != :login and @session_id and content =~ %rsf:INVALID_SESSION_ID/
    login(@user, @password)

    # repackage and rencode request with the new session id
    request = (Envelope % [@session_id, extra_headers, expanded])
    request = encode(request)

    # Send the request to the server and read the response.
    response = @server.post2(soap_url, request.lstrip, headers)

    content = decode(response)
  end

  SoapResponse.new(content).parse
end
connect(user, password) click to toggle source

Connect to remote server

# File lib/rforce/binding.rb, line 102
def connect(user, password)
  @user = user
  @password = password

  call_remote(:login, [:username, user, :password, password])
end
decode(response) click to toggle source

decode gzip

# File lib/rforce/binding.rb, line 228
def decode(response)
  encoding = response['Content-Encoding']

  # return body if no encoding
  if !encoding then return response.body end

  # decode gzip
  case encoding.strip
  when 'gzip' then
    begin
      gzr = Zlib::GzipReader.new(StringIO.new(response.body))
      decoded = gzr.read
    ensure
      gzr.close
    end
    decoded
  else
    response.body
  end
end
encode(request) click to toggle source

encode gzip

# File lib/rforce/binding.rb, line 251
def encode(request)
  return request if show_debug

  begin
    ostream = StringIO.new
    gzw = Zlib::GzipWriter.new(ostream)
    gzw.write(request)
    ostream.string
  ensure
    gzw.close
  end
end
init_server(url) click to toggle source
# File lib/rforce/binding.rb, line 68
def init_server(url)
  @url = URI.parse(url)

  if (@oauth)
    consumer = OAuth::Consumer.new            @oauth[:consumer_key],
      @oauth[:consumer_secret],
      {
        :site => url,
        :proxy => @proxy
      }

    consumer.http.set_debug_output $stderr if show_debug

    @server  = OAuth::AccessToken.new            consumer,
      @oauth[:access_token],
      @oauth[:access_secret]

    class << @server
      alias_method :post2, :post
    end
  else
    @server = Net::HTTP.Proxy(@proxy).new(@url.host, @url.port)
    @server.use_ssl = @url.scheme == 'https'
    @server.verify_mode = OpenSSL::SSL::VERIFY_NONE

    # run ruby with -d or env variable SHOWSOAP=true to see SOAP wiredumps.
    @server.set_debug_output $stderr if show_debug
  end
end
login(user, password) click to toggle source

Log in to the server with a user name and password, remembering the session ID returned to us by Salesforce.

# File lib/rforce/binding.rb, line 111
def login(user, password)
  response = connect(user, password)

  raise "Incorrect user name / password [#{response.Fault}]" unless response.loginResponse

  result = response[:loginResponse][:result]
  @session_id = result[:sessionId]

  init_server(result[:serverUrl])

  response
end
login_with_oauth() click to toggle source

Log in to the server with OAuth, remembering the session ID returned to us by Salesforce.

# File lib/rforce/binding.rb, line 126
def login_with_oauth
  result = @server.post          @oauth[:login_url],
    '',
    {'content-type' => 'application/x-www-form-urlencoded'}

  case result
  when Net::HTTPSuccess
    doc = REXML::Document.new result.body
    @session_id = doc.elements['*/sessionId'].text
    server_url  = doc.elements['*/serverUrl'].text
    init_server server_url

    return {:sessionId => @sessionId, :serverUrl => server_url}
  when Net::HTTPUnauthorized
    raise 'Invalid OAuth tokens'
  else
    raise "Unexpected error: #{response.inspect}"
  end
end
method_missing(method, *args) click to toggle source

Turns method calls on this object into remote SOAP calls.

# File lib/rforce/binding.rb, line 266
def method_missing(method, *args)
  unless args.empty? || (args.size == 1 && [Hash, Array].include?(args[0].class))
    raise 'Expected at most 1 Hash or Array argument'
  end

  call_remote method, args[0] || []
end
show_debug() click to toggle source
# File lib/rforce/binding.rb, line 63
def show_debug
  ENV['SHOWSOAP'] == 'true'
end