rforce 0.10 → 0.15
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +15 -0
- data/{History.txt → CHANGELOG.txt} +35 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.ci +6 -0
- data/{README.txt → README.rdoc} +3 -4
- data/Rakefile +5 -29
- data/examples/oauth_setup.rb +26 -0
- data/examples/oauth_use.rb +25 -0
- data/examples/simple.rb +20 -0
- data/lib/rforce.rb +3 -3
- data/lib/rforce/binding.rb +90 -62
- data/lib/rforce/method_keys.rb +8 -3
- data/lib/rforce/soap_response.rb +0 -4
- data/lib/rforce/version.rb +1 -1
- data/rforce.gemspec +35 -0
- metadata +54 -118
- data/.gemtest +0 -0
- data/Manifest.txt +0 -19
- data/lib/rforce/soap_response_expat.rb +0 -36
- data/lib/rforce/soap_response_hpricot.rb +0 -90
- data/spec/rforce_spec.rb +0 -237
- data/spec/soap-response.xml +0 -5928
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -4
- data/tasks/timing.rake +0 -23
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c3a071a2857df828c3921bb9cd49fab785f959bac60cfd01065dde0313f59e6c
|
4
|
+
data.tar.gz: 05afbdbf418ec1d4e1a7c58a390e2fc44c7b04d1c374e6acbba058f3e0a8863e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 55baa254981ef2dbcd6c0d301579905beea00e2f0b4e1be0ac1eb0a311a805ed1409878ee0eef9d877a7cce307819cb1cbaf6aea9d8d55331d17fff3ad3b15b5
|
7
|
+
data.tar.gz: 4d2053a4e2c77d7da535fd770a779b5e313b0e753759f80e63128d94198c0095e289eeffd44c72ac23daff71e19a4f6438f45b46645219adb4eab39ce10e0a61
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -1,3 +1,38 @@
|
|
1
|
+
== 0.15.0 2021-04-22
|
2
|
+
|
3
|
+
* 1 security update
|
4
|
+
* Upgraded away from vulnerable OAuth version
|
5
|
+
* 1 compatibility update
|
6
|
+
* Dropped expat/xmlbuilder for lack of compatibility
|
7
|
+
|
8
|
+
== 0.14.0 2019-08-20
|
9
|
+
|
10
|
+
* 1 security update
|
11
|
+
* Upgraded away from vulnerable Nokogiri version
|
12
|
+
* 1 enhancement
|
13
|
+
* Slightly modernized project layout
|
14
|
+
|
15
|
+
== 0.13.0 2015-01-05
|
16
|
+
* 1 bug fix
|
17
|
+
* Attempt server connection even if caller hasn't logged in yet
|
18
|
+
(reported by desheikh)
|
19
|
+
|
20
|
+
== 0.12.0 2014-07-27
|
21
|
+
* 2 enhancements
|
22
|
+
* Clearer errors when skipping login (Victor Stan)
|
23
|
+
* Allow passing in an optional logger (Jeff Jolma)
|
24
|
+
* 3 bug fixes
|
25
|
+
* Fixed URL error in OAuth flow (reported by karthickbabuos)
|
26
|
+
* Corrected UTF-8 handling error (Maxime Rety)
|
27
|
+
* Fixed repeat login issue (Jeff Jolma)
|
28
|
+
|
29
|
+
== 0.11.0 2013-06-08
|
30
|
+
* 1 minor enhancement
|
31
|
+
* Give the README an .rdoc extension for easy Github viewing
|
32
|
+
(Brad Dunn)
|
33
|
+
* 1 bug fix
|
34
|
+
* Fixed malformed request (Jeff Jolma)
|
35
|
+
|
1
36
|
== 0.10.0 2012-09-12
|
2
37
|
* 1 bug fix
|
3
38
|
* Integer auto-parsing mangles ZIP codes (reported by Jason
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at erin.dees@stitchfix.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.ci
ADDED
data/{README.txt → README.rdoc}
RENAMED
@@ -1,14 +1,13 @@
|
|
1
1
|
= rforce
|
2
2
|
|
3
|
-
* http://rforce.rubyforge.org
|
4
|
-
* http://rubyforge.org/projects/rforce
|
5
|
-
* http://bitbucket.org/undees/rforce
|
6
3
|
* http://github.com/undees/rforce
|
7
4
|
|
8
5
|
== DESCRIPTION:
|
9
6
|
|
10
7
|
RForce is a simple, usable binding to the Salesforce API.
|
11
8
|
|
9
|
+
{<img src="https://travis-ci.org/undees/rforce.png" />}[https://travis-ci.org/undees/rforce]
|
10
|
+
|
12
11
|
== FEATURES:
|
13
12
|
|
14
13
|
Rather than enforcing adherence to the sforce.com schema, RForce assumes you are familiar with the API. Ruby method names become SOAP method names. Nested Ruby hashes become nested XML elements.
|
@@ -73,7 +72,7 @@ Rather than enforcing adherence to the sforce.com schema, RForce assumes you are
|
|
73
72
|
|
74
73
|
== LICENSE:
|
75
74
|
|
76
|
-
Copyright (c) 2005-
|
75
|
+
Copyright (c) 2005-2019 Erin Dees and contributors
|
77
76
|
|
78
77
|
Permission is hereby granted, free of charge, to any person obtaining
|
79
78
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -1,32 +1,8 @@
|
|
1
|
-
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
2
3
|
|
3
|
-
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
4
5
|
|
5
|
-
|
6
|
-
require 'hoe/gemspec2'
|
6
|
+
task :default => :spec
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
Hoe.spec 'rforce' do
|
11
|
-
developer('Ian Dees', 'undees@gmail.com')
|
12
|
-
|
13
|
-
self.extra_deps << ['builder', '~> 3.0']
|
14
|
-
self.extra_deps << ['oauth', '~> 0.4']
|
15
|
-
|
16
|
-
self.extra_dev_deps << ['rspec', '~> 2.8']
|
17
|
-
self.extra_dev_deps << ['hoe-gemspec2', '~> 1.1']
|
18
|
-
self.extra_dev_deps << ['hpricot', '~> 0.8']
|
19
|
-
self.extra_dev_deps << ['nokogiri', '~> 1.5']
|
20
|
-
self.extra_dev_deps << ['xmlparser', '~> 0.7']
|
21
|
-
|
22
|
-
self.rdoc_locations = ['undees@rforce.rubyforge.org:/var/www/gforge-projects/rforce']
|
23
|
-
self.remote_rdoc_dir = ''
|
24
|
-
|
25
|
-
self.rspec_options = ['-rubygems', '--options', 'spec/spec.opts']
|
26
|
-
end
|
27
|
-
|
28
|
-
task :test => :spec
|
29
|
-
|
30
|
-
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
31
|
-
|
32
|
-
# vim: syntax=Ruby
|
8
|
+
Dir['lib/tasks/**/*.rake'].each { |rake| load rake }
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
|
3
|
+
consumer_key = ENV['SALESFORCE_CONSUMER_KEY']
|
4
|
+
consumer_secret = ENV['SALESFORCE_CONSUMER_SECRET']
|
5
|
+
|
6
|
+
oauth_options = {
|
7
|
+
:site => 'https://login.salesforce.com',
|
8
|
+
:scheme => :body,
|
9
|
+
:request_token_path => '/_nc_external/system/security/oauth/RequestTokenHandler',
|
10
|
+
:authorize_path => '/setup/secur/RemoteAccessAuthorizationPage.apexp',
|
11
|
+
:access_token_path => '/_nc_external/system/security/oauth/AccessTokenHandler',
|
12
|
+
}
|
13
|
+
|
14
|
+
consumer = OAuth::Consumer.new consumer_key, consumer_secret, oauth_options
|
15
|
+
# consumer.http.set_debug_output STDERR # if you're curious
|
16
|
+
|
17
|
+
request = consumer.get_request_token
|
18
|
+
authorize_url = request.authorize_url :oauth_consumer_key => consumer_key
|
19
|
+
|
20
|
+
puts "Go to #{authorize_url} in your browser, then enter the verification code:"
|
21
|
+
verification_code = gets.strip
|
22
|
+
|
23
|
+
access = request.get_access_token :oauth_verifier => verification_code
|
24
|
+
|
25
|
+
puts "Access Token: " + access.token
|
26
|
+
puts "Access Secret: " + access.secret
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rforce'
|
2
|
+
|
3
|
+
oauth = {
|
4
|
+
:consumer_key => ENV['SALESFORCE_CONSUMER_KEY'],
|
5
|
+
:consumer_secret => ENV['SALESFORCE_CONSUMER_SECRET'],
|
6
|
+
:access_token => ENV['SALESFORCE_ACCESS_TOKEN'],
|
7
|
+
:access_secret => ENV['SALESFORCE_ACCESS_SECRET'],
|
8
|
+
:login_url => 'https://login.salesforce.com/services/OAuth/u/20.0'
|
9
|
+
}
|
10
|
+
|
11
|
+
binding = RForce::Binding.new \
|
12
|
+
'https://www.salesforce.com/services/Soap/u/20.0',
|
13
|
+
nil,
|
14
|
+
oauth
|
15
|
+
|
16
|
+
binding.login_with_oauth
|
17
|
+
|
18
|
+
answer = binding.search \
|
19
|
+
:searchString =>
|
20
|
+
'find {McFakerson Co} in name fields returning account(id)'
|
21
|
+
|
22
|
+
account = answer.searchResponse.result.searchRecords.record
|
23
|
+
account_id = account.Id
|
24
|
+
|
25
|
+
puts account_id
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rforce'
|
2
|
+
|
3
|
+
email = ENV['SALESFORCE_USER']
|
4
|
+
password = ENV['SALESFORCE_PASS']
|
5
|
+
token = ENV['SALESFORCE_TOKEN']
|
6
|
+
|
7
|
+
binding = RForce::Binding.new \
|
8
|
+
'https://www.salesforce.com/services/Soap/u/20.0'
|
9
|
+
|
10
|
+
binding.login \
|
11
|
+
email, password + token
|
12
|
+
|
13
|
+
answer = binding.search \
|
14
|
+
:searchString =>
|
15
|
+
'find {McFakerson Co} in name fields returning account(id)'
|
16
|
+
|
17
|
+
account = answer.searchResponse.result.searchRecords.record
|
18
|
+
account_id = account.Id
|
19
|
+
|
20
|
+
puts account_id
|
data/lib/rforce.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright (c) 2005-
|
2
|
+
Copyright (c) 2005-2019 Erin Dees and contributors
|
3
3
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -53,8 +53,8 @@ module RForce
|
|
53
53
|
# Expand Ruby data structures into XML.
|
54
54
|
def expand(builder, args, xmlns = nil)
|
55
55
|
# Nest arrays: [:a, 1, :b, 2] => [[:a, 1], [:b, 2]]
|
56
|
-
if
|
57
|
-
args
|
56
|
+
if args.is_a?(Array)
|
57
|
+
args = args.each_slice(2).to_a
|
58
58
|
end
|
59
59
|
|
60
60
|
args.each do |key, value|
|
data/lib/rforce/binding.rb
CHANGED
@@ -44,99 +44,87 @@ module RForce
|
|
44
44
|
MruHeader = '<partner:MruHeader soap:mustUnderstand="1"><partner:updateMru>true</partner:updateMru></partner:MruHeader>'
|
45
45
|
ClientIdHeader = '<partner:CallOptions soap:mustUnderstand="1"><partner:client>%s</partner:client></partner:CallOptions>'
|
46
46
|
|
47
|
-
#
|
48
|
-
#
|
47
|
+
# Create a binding to the server (after which you can call login
|
48
|
+
# or login_with_oauth to connect to it). If you pass an oauth
|
49
|
+
# hash, it must contain the keys :consumer_key, :consumer_secret,
|
49
50
|
# :access_token, :access_secret, and :login_url.
|
50
51
|
#
|
51
52
|
# proxy may be a URL of the form http://user:pass@example.com:port
|
52
53
|
#
|
53
|
-
|
54
|
+
# if a logger is specified, it will be used for very verbose SOAP logging
|
55
|
+
#
|
56
|
+
def initialize(url, sid = nil, oauth = nil, proxy = nil, logger = nil)
|
54
57
|
@session_id = sid
|
55
58
|
@oauth = oauth
|
56
59
|
@proxy = proxy
|
57
60
|
@batch_size = DEFAULT_BATCH_SIZE
|
58
|
-
|
59
|
-
|
61
|
+
@logger = logger
|
62
|
+
@url = URI.parse(url)
|
60
63
|
end
|
61
64
|
|
62
|
-
|
63
65
|
def show_debug
|
64
66
|
ENV['SHOWSOAP'] == 'true'
|
65
67
|
end
|
66
68
|
|
69
|
+
def create_server(url)
|
70
|
+
server = Net::HTTP.Proxy(@proxy).new(url.host, url.port)
|
71
|
+
server.use_ssl = (url.scheme == 'https')
|
72
|
+
server.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
if (@oauth)
|
72
|
-
consumer = OAuth::Consumer.new \
|
73
|
-
@oauth[:consumer_key],
|
74
|
-
@oauth[:consumer_secret],
|
75
|
-
{
|
76
|
-
:site => url,
|
77
|
-
:proxy => @proxy
|
78
|
-
}
|
79
|
-
|
80
|
-
consumer.http.set_debug_output $stderr if show_debug
|
81
|
-
|
82
|
-
@server = OAuth::AccessToken.new \
|
83
|
-
consumer,
|
84
|
-
@oauth[:access_token],
|
85
|
-
@oauth[:access_secret]
|
86
|
-
|
87
|
-
class << @server
|
88
|
-
alias_method :post2, :post
|
89
|
-
end
|
90
|
-
else
|
91
|
-
@server = Net::HTTP.Proxy(@proxy).new(@url.host, @url.port)
|
92
|
-
@server.use_ssl = @url.scheme == 'https'
|
93
|
-
@server.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
94
|
-
|
95
|
-
# run ruby with -d or env variable SHOWSOAP=true to see SOAP wiredumps.
|
96
|
-
@server.set_debug_output $stderr if show_debug
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Connect to remote server
|
101
|
-
#
|
102
|
-
def connect(user, password)
|
103
|
-
@user = user
|
104
|
-
@password = password
|
74
|
+
# run ruby with -d or env variable SHOWSOAP=true to see SOAP wiredumps.
|
75
|
+
server.set_debug_output $stderr if show_debug
|
105
76
|
|
106
|
-
|
77
|
+
return server
|
107
78
|
end
|
108
79
|
|
109
80
|
# Log in to the server with a user name and password, remembering
|
110
81
|
# the session ID returned to us by Salesforce.
|
111
82
|
def login(user, password)
|
112
|
-
|
83
|
+
@user = user
|
84
|
+
@password = password
|
85
|
+
@server = create_server(@url)
|
86
|
+
response = call_remote(:login, [:username, user, :password, password])
|
113
87
|
|
114
88
|
raise "Incorrect user name / password [#{response.Fault}]" unless response.loginResponse
|
115
89
|
|
116
|
-
result
|
90
|
+
result = response[:loginResponse][:result]
|
117
91
|
@session_id = result[:sessionId]
|
92
|
+
@url = URI.parse(result[:serverUrl])
|
93
|
+
@server = create_server(@url)
|
118
94
|
|
119
|
-
|
120
|
-
|
121
|
-
response
|
95
|
+
return response
|
122
96
|
end
|
123
97
|
|
124
98
|
# Log in to the server with OAuth, remembering
|
125
99
|
# the session ID returned to us by Salesforce.
|
126
100
|
def login_with_oauth
|
127
|
-
|
128
|
-
@oauth[:
|
101
|
+
consumer = OAuth::Consumer.new \
|
102
|
+
@oauth[:consumer_key],
|
103
|
+
@oauth[:consumer_secret]
|
104
|
+
|
105
|
+
access = OAuth::AccessToken.new \
|
106
|
+
consumer, @oauth[:access_token],
|
107
|
+
@oauth[:access_secret]
|
108
|
+
|
109
|
+
login_url = @oauth[:login_url]
|
110
|
+
|
111
|
+
result = access.post \
|
112
|
+
login_url,
|
129
113
|
'',
|
130
114
|
{'content-type' => 'application/x-www-form-urlencoded'}
|
131
115
|
|
132
116
|
case result
|
133
117
|
when Net::HTTPSuccess
|
134
|
-
doc
|
118
|
+
doc = REXML::Document.new result.body
|
135
119
|
@session_id = doc.elements['*/sessionId'].text
|
136
|
-
|
137
|
-
|
120
|
+
@url = URI.parse(doc.elements['*/serverUrl'].text)
|
121
|
+
@server = access
|
122
|
+
|
123
|
+
class << @server
|
124
|
+
alias_method :post2, :post
|
125
|
+
end
|
138
126
|
|
139
|
-
return {:sessionId => @
|
127
|
+
return {:sessionId => @session_id, :serverUrl => @url.to_s}
|
140
128
|
when Net::HTTPUnauthorized
|
141
129
|
raise 'Invalid OAuth tokens'
|
142
130
|
else
|
@@ -148,8 +136,13 @@ module RForce
|
|
148
136
|
# a hash or (if order is important) an array of alternating
|
149
137
|
# keys and values.
|
150
138
|
def call_remote(method, args)
|
139
|
+
@server ||= create_server(@url)
|
151
140
|
|
152
|
-
|
141
|
+
# Different URI requirements for regular vs. OAuth. This is
|
142
|
+
# *screaming* for a refactor.
|
143
|
+
fallback_soap_url = @oauth ? @url.to_s : @url.path
|
144
|
+
|
145
|
+
urn, soap_url = block_given? ? yield : ["urn:partner.soap.sforce.com", fallback_soap_url]
|
153
146
|
|
154
147
|
# Create XML text from the arguments.
|
155
148
|
expanded = ''
|
@@ -157,12 +150,12 @@ module RForce
|
|
157
150
|
expand(@builder, {method => args}, urn)
|
158
151
|
|
159
152
|
extra_headers = ""
|
160
|
-
|
153
|
+
|
161
154
|
# QueryOptions is not valid when making an Apex Webservice SOAP call
|
162
155
|
if !block_given?
|
163
156
|
extra_headers << (QueryOptions % @batch_size)
|
164
157
|
end
|
165
|
-
|
158
|
+
|
166
159
|
extra_headers << (AssignmentRuleHeaderUsingRuleId % assignment_rule_id) if assignment_rule_id
|
167
160
|
extra_headers << AssignmentRuleHeaderUsingDefaultRule if use_default_rule
|
168
161
|
extra_headers << MruHeader if update_mru
|
@@ -181,6 +174,7 @@ module RForce
|
|
181
174
|
# Fill in the blanks of the SOAP envelope with our
|
182
175
|
# session ID and the expanded XML of our request.
|
183
176
|
request = (Envelope % [@session_id, extra_headers, expanded])
|
177
|
+
@logger && @logger.info("RForce request: #{request}")
|
184
178
|
|
185
179
|
# reset the batch size for the next request
|
186
180
|
@batch_size = DEFAULT_BATCH_SIZE
|
@@ -201,29 +195,42 @@ module RForce
|
|
201
195
|
end
|
202
196
|
|
203
197
|
# Send the request to the server and read the response.
|
198
|
+
@logger && @logger.info("RForce request to host #{@server} url #{soap_url} headers: #{headers}")
|
204
199
|
response = @server.post2(soap_url, request.lstrip, headers)
|
205
200
|
|
206
201
|
# decode if we have encoding
|
207
202
|
content = decode(response)
|
208
203
|
|
204
|
+
# Fix charset encoding. Needed because the "content" variable may contain a UTF-8
|
205
|
+
# or ISO-8859-1 string, but is carrying the US-ASCII encoding.
|
206
|
+
content = fix_encoding(content)
|
207
|
+
|
209
208
|
# Check to see if INVALID_SESSION_ID was raised and try to relogin in
|
210
209
|
if method != :login and @session_id and content =~ /sf:INVALID_SESSION_ID/
|
211
|
-
|
210
|
+
if @user
|
211
|
+
login(@user, @password)
|
212
|
+
else
|
213
|
+
raise "INVALID_SESSION_ID"
|
214
|
+
end
|
212
215
|
|
213
216
|
# repackage and rencode request with the new session id
|
214
|
-
request = (Envelope % [@session_id,
|
217
|
+
request = (Envelope % [@session_id, extra_headers, expanded])
|
215
218
|
request = encode(request)
|
216
219
|
|
217
220
|
# Send the request to the server and read the response.
|
218
221
|
response = @server.post2(soap_url, request.lstrip, headers)
|
219
222
|
|
220
223
|
content = decode(response)
|
224
|
+
|
225
|
+
# Fix charset encoding. Needed because the "content" variable may contain a UTF-8
|
226
|
+
# or ISO-8859-1 string, but is carrying the US-ASCII encoding.
|
227
|
+
content = fix_encoding(content)
|
221
228
|
end
|
222
229
|
|
230
|
+
@logger && @logger.info("RForce response: #{content}")
|
223
231
|
SoapResponse.new(content).parse
|
224
232
|
end
|
225
233
|
|
226
|
-
|
227
234
|
# decode gzip
|
228
235
|
def decode(response)
|
229
236
|
encoding = response['Content-Encoding']
|
@@ -246,7 +253,6 @@ module RForce
|
|
246
253
|
end
|
247
254
|
end
|
248
255
|
|
249
|
-
|
250
256
|
# encode gzip
|
251
257
|
def encode(request)
|
252
258
|
return request if show_debug
|
@@ -261,6 +267,28 @@ module RForce
|
|
261
267
|
end
|
262
268
|
end
|
263
269
|
|
270
|
+
# fix invalid US-ASCII strings by applying the correct encoding on ruby 1.9+
|
271
|
+
def fix_encoding(string)
|
272
|
+
if [:valid_encoding?, :force_encoding].all? { |m| string.respond_to?(m) }
|
273
|
+
if !string.valid_encoding?
|
274
|
+
# The 2 possible encodings in responses are UTF-8 and ISO-8859-1
|
275
|
+
# http://www.salesforce.com/us/developer/docs/api/Content/implementation_considerations.htm#topic-title_international
|
276
|
+
#
|
277
|
+
["UTF-8", "ISO-8859-1"].each do |encoding_name|
|
278
|
+
|
279
|
+
s = string.dup.force_encoding(encoding_name)
|
280
|
+
|
281
|
+
if s.valid_encoding?
|
282
|
+
return s
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
raise "Invalid encoding in SOAP response: not in [US-ASCII, UTF-8, ISO-8859-1]"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
return string
|
291
|
+
end
|
264
292
|
|
265
293
|
# Turns method calls on this object into remote SOAP calls.
|
266
294
|
def method_missing(method, *args)
|