lib/executrix/http.rb
require 'net/https'
require 'nori'
require 'csv'
module Executrix
module Http
extend self
def login *args
r = Http::Request.login(*args)
process_soap_response(nori.parse(process_http_request(r)))
end
def create_job *args
r = Http::Request.create_job(*args)
process_xml_response(nori.parse(process_http_request(r)))
end
def close_job *args
r = Http::Request.close_job(*args)
process_xml_response(nori.parse(process_http_request(r)))
end
def add_batch *args
r = Http::Request.add_batch(*args)
process_xml_response(nori.parse(process_http_request(r)))
end
def query_batch *args
r = Http::Request.query_batch(*args)
process_xml_response(nori.parse(process_http_request(r)))
end
def query_batch_result_id *args
r = Http::Request.query_batch_result_id(*args)
process_xml_response(nori.parse(process_http_request(r)))
end
def query_batch_result_data *args
r = Http::Request.query_batch_result_data(*args)
normalize_csv(process_http_request(r))
end
def add_file_upload_batch instance, session_id, job_id, data, api_version
headers = {
'Content-Type' => 'zip/csv',
'X-SFDC-Session' => session_id}
r = Http::Request.new(
:post,
Http::Request.generic_host(instance),
"/services/async/#{api_version}/job/#{job_id}/batch",
data,
headers)
process_xml_response(nori.parse(process_http_request(r)))
end
def process_http_request(r)
http = Net::HTTP.new(r.host, 443)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http_request = Net::HTTP.
const_get(r.http_method.capitalize).
new(r.path, r.headers)
http_request.body = r.body if r.body
http.request(http_request).body
end
private
def nori
Nori.new(
:advanced_typecasting => true,
:strip_namespaces => true,
:convert_tags_to => lambda { |tag| tag.snakecase.to_sym })
end
def process_xml_response res
if res[:error]
raise "#{res[:error][:exception_code]}: #{res[:error][:exception_message]}"
end
res.values.first
end
def normalize_csv res
res.gsub(/\n\s+/, "\n")
end
def process_soap_response res
raw_result = res.fetch(:body){res.fetch(:envelope).fetch(:body)}
raise raw_result[:fault][:faultstring] if raw_result[:fault]
login_result = raw_result[:login_response][:result]
instance = Helper.fetch_instance_from_server_url(login_result[:server_url])
login_result.merge(instance: instance)
end
class Request
attr_reader :path
attr_reader :host
attr_reader :body
attr_reader :headers
attr_reader :http_method
def initialize http_method, host, path, body, headers
@http_method = http_method
@host = host
@path = path
@body = body
@headers = headers
end
def self.login sandbox, username, password, api_version
body = %Q{<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<n1:login xmlns:n1="urn:partner.soap.sforce.com">
<n1:username>#{username}</n1:username>
<n1:password>#{password}</n1:password>
</n1:login>
</env:Body>
</env:Envelope>}
headers = {
'Content-Type' => 'text/xml; charset=utf-8',
'SOAPAction' => 'login'
}
Http::Request.new(
:post,
generic_host(sandbox ? 'test' : 'login'),
"/services/Soap/u/#{api_version}",
body,
headers)
end
def self.create_job instance, session_id, operation, sobject, content_type, api_version, external_field = nil
external_field_line = external_field ?
"<externalIdFieldName>#{external_field}</externalIdFieldName>" : nil
body = %Q{<?xml version="1.0" encoding="utf-8" ?>
<jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
<operation>#{operation}</operation>
<object>#{sobject}</object>
#{external_field_line}
<contentType>#{content_type}</contentType>
</jobInfo>
}
headers = {
'Content-Type' => 'application/xml; charset=utf-8',
'X-SFDC-Session' => session_id}
Http::Request.new(
:post,
generic_host(instance),
"/services/async/#{api_version}/job",
body,
headers)
end
def self.close_job instance, session_id, job_id, api_version
body = %Q{<?xml version="1.0" encoding="utf-8" ?>
<jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
<state>Closed</state>
</jobInfo>
}
headers = {
'Content-Type' => 'application/xml; charset=utf-8',
'X-SFDC-Session' => session_id}
Http::Request.new(
:post,
generic_host(instance),
"/services/async/#{api_version}/job/#{job_id}",
body,
headers)
end
def self.add_batch instance, session_id, job_id, data, api_version
headers = {'Content-Type' => 'text/csv; charset=UTF-8', 'X-SFDC-Session' => session_id}
Http::Request.new(
:post,
generic_host(instance),
"/services/async/#{api_version}/job/#{job_id}/batch",
data,
headers)
end
def self.query_batch instance, session_id, job_id, batch_id, api_version
headers = {'X-SFDC-Session' => session_id}
Http::Request.new(
:get,
generic_host(instance),
"/services/async/#{api_version}/job/#{job_id}/batch/#{batch_id}",
nil,
headers)
end
def self.query_batch_result_id instance, session_id, job_id, batch_id, api_version
headers = {
'Content-Type' => 'application/xml; charset=utf-8',
'X-SFDC-Session' => session_id}
Http::Request.new(
:get,
generic_host(instance),
"/services/async/#{api_version}/job/#{job_id}/batch/#{batch_id}/result",
nil,
headers)
end
def self.query_batch_result_data(instance,
session_id,
job_id,
batch_id,
result_id,
api_version)
headers = {
'Content-Type' => 'text/csv; charset=UTF-8',
'X-SFDC-Session' => session_id}
Http::Request.new(
:get,
generic_host(instance),
"/services/async/#{api_version}" \
"/job/#{job_id}/batch/#{batch_id}/result/#{result_id}",
nil,
headers)
end
def self.generic_host prefix
"#{prefix}.salesforce.com"
end
end
end
end