salesforce_bulk 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +16 -7
- data/lib/salesforce_bulk.rb +22 -9
- data/lib/salesforce_bulk/connection.rb +24 -4
- data/lib/salesforce_bulk/job.rb +53 -4
- data/lib/salesforce_bulk/version.rb +1 -1
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -2,8 +2,12 @@
|
|
2
2
|
|
3
3
|
==Overview
|
4
4
|
|
5
|
-
Salesforce bulk is a simple ruby gem for connecting to and using the Salesforce Bulk API (http://www.salesforce.com/us/developer/docs/api_asynch/index.htm). There are already some gems out there that provide connectivity to the Salesforce SOAP and Rest APIs, if your needs are simple, I recommend using those, specifically raygao's asf-rest-adapter (https://github.com/raygao/asf-rest-adapter).
|
5
|
+
Salesforce bulk is a simple ruby gem for connecting to and using the Salesforce Bulk API (http://www.salesforce.com/us/developer/docs/api_asynch/index.htm). There are already some gems out there that provide connectivity to the Salesforce SOAP and Rest APIs, if your needs are simple, I recommend using those, specifically raygao's asf-rest-adapter (https://github.com/raygao/asf-rest-adapter) or databasedotcom (https://rubygems.org/gems/databasedotcom).
|
6
6
|
|
7
|
+
==Installation
|
8
|
+
|
9
|
+
sudo gem install salesforce_bulk
|
10
|
+
|
7
11
|
==How to use
|
8
12
|
|
9
13
|
Using this gem is simple and straight forward.
|
@@ -28,7 +32,7 @@ Sample operations:
|
|
28
32
|
puts "result is: #{result.inspect}"
|
29
33
|
|
30
34
|
# Update
|
31
|
-
updated_account = Hash["name" => "Test Account -- Updated", id => "a00A0001009zA2m"] # Nearly identical to an insert, but we need to pass the salesforce id.
|
35
|
+
updated_account = Hash["name" => "Test Account -- Updated", "id" => "a00A0001009zA2m"] # Nearly identical to an insert, but we need to pass the salesforce id.
|
32
36
|
records_to_update = Array.new
|
33
37
|
records_to_update.push(updated_account)
|
34
38
|
salesforce.update("Account", records_to_update)
|
@@ -51,16 +55,21 @@ Sample operations:
|
|
51
55
|
|
52
56
|
# Query
|
53
57
|
res = salesforce.query("Account", "select id, name, createddate from Account limit 3") # We just need to pass the sobject name and the query string
|
58
|
+
puts res.records.inspect
|
54
59
|
|
55
|
-
|
56
|
-
sudo gem install salesforce_bulk
|
60
|
+
Result reporting:
|
57
61
|
|
58
|
-
|
59
|
-
|
62
|
+
new_account = Hash["type" => "Other"] # Missing required field "name."
|
63
|
+
records_to_insert = Array.new
|
64
|
+
records_to_insert.push(new_account) # You can add as many records as you want here, just keep in mind that Salesforce has governor limits.
|
65
|
+
result = salesforce.create("Account", records_to_insert)
|
66
|
+
puts result.success? # false
|
67
|
+
puts result.has_errors? # true
|
68
|
+
puts result.errors # An indexed hash detailing the errors
|
60
69
|
|
61
70
|
== Copyright
|
62
71
|
|
63
|
-
Copyright (c)
|
72
|
+
Copyright (c) 2012 Jorge Valdivia.
|
64
73
|
|
65
74
|
===end
|
66
75
|
|
data/lib/salesforce_bulk.rb
CHANGED
@@ -19,16 +19,16 @@ module SalesforceBulk
|
|
19
19
|
self.do_operation('upsert', sobject, records, external_field, wait)
|
20
20
|
end
|
21
21
|
|
22
|
-
def update(sobject, records)
|
23
|
-
self.do_operation('update', sobject, records, nil)
|
22
|
+
def update(sobject, records, wait=false)
|
23
|
+
self.do_operation('update', sobject, records, nil, wait)
|
24
24
|
end
|
25
25
|
|
26
|
-
def create(sobject, records)
|
27
|
-
self.do_operation('insert', sobject, records, nil)
|
26
|
+
def create(sobject, records, wait=false)
|
27
|
+
self.do_operation('insert', sobject, records, nil, wait)
|
28
28
|
end
|
29
29
|
|
30
|
-
def delete(sobject, records)
|
31
|
-
self.do_operation('delete', sobject, records, nil)
|
30
|
+
def delete(sobject, records, wait=false)
|
31
|
+
self.do_operation('delete', sobject, records, nil, wait)
|
32
32
|
end
|
33
33
|
|
34
34
|
def query(sobject, query)
|
@@ -50,7 +50,6 @@ module SalesforceBulk
|
|
50
50
|
if wait or operation == 'query'
|
51
51
|
while true
|
52
52
|
state = job.check_batch_status()
|
53
|
-
#puts "\nstate is #{state}\n"
|
54
53
|
if state != "Queued" && state != "InProgress"
|
55
54
|
break
|
56
55
|
end
|
@@ -59,12 +58,26 @@ module SalesforceBulk
|
|
59
58
|
|
60
59
|
if state == 'Completed'
|
61
60
|
job.get_batch_result()
|
61
|
+
job
|
62
62
|
else
|
63
|
-
|
63
|
+
job.result.message = "There is an error in your job. The response returned a state of #{state}. Please check your query/parameters and try again."
|
64
|
+
job.result.success = false
|
65
|
+
return job
|
66
|
+
|
64
67
|
end
|
65
68
|
else
|
66
|
-
return
|
69
|
+
return job
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_batch_result result
|
75
|
+
begin
|
76
|
+
CSV.parse(result, :headers => true)
|
77
|
+
rescue
|
78
|
+
result
|
67
79
|
end
|
68
80
|
end
|
81
|
+
|
69
82
|
end # End class
|
70
83
|
end # End module
|
@@ -40,7 +40,8 @@ module SalesforceBulk
|
|
40
40
|
headers = Hash['Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => 'login']
|
41
41
|
|
42
42
|
response = post_xml(@@LOGIN_HOST, @@LOGIN_PATH, xml, headers)
|
43
|
-
response_parsed = XmlSimple.xml_in(response)
|
43
|
+
# response_parsed = XmlSimple.xml_in(response)
|
44
|
+
response_parsed = parse_response response
|
44
45
|
|
45
46
|
@session_id = response_parsed['Body'][0]['loginResponse'][0]['result'][0]['sessionId'][0]
|
46
47
|
@server_url = response_parsed['Body'][0]['loginResponse'][0]['result'][0]['serverUrl'][0]
|
@@ -55,12 +56,9 @@ module SalesforceBulk
|
|
55
56
|
|
56
57
|
if host != @@LOGIN_HOST # Not login, need to add session id to header
|
57
58
|
headers['X-SFDC-Session'] = @session_id;
|
58
|
-
#puts "session id is: #{@session_id} --- #{headers.inspect}\n"
|
59
59
|
path = "#{@@PATH_PREFIX}#{path}"
|
60
60
|
end
|
61
61
|
|
62
|
-
#puts "#{host} -- #{path} -- #{headers.inspect}\n"
|
63
|
-
|
64
62
|
https(host).post(path, xml, headers).body
|
65
63
|
end
|
66
64
|
|
@@ -87,6 +85,28 @@ module SalesforceBulk
|
|
87
85
|
@instance = $~.captures[0]
|
88
86
|
end
|
89
87
|
|
88
|
+
def parse_response response
|
89
|
+
response_parsed = XmlSimple.xml_in(response)
|
90
|
+
|
91
|
+
if response.downcase.include?("faultstring") || response.downcase.include?("exceptionmessage")
|
92
|
+
begin
|
93
|
+
|
94
|
+
if response.downcase.include?("faultstring")
|
95
|
+
error_message = response_parsed["Body"][0]["Fault"][0]["faultstring"][0]
|
96
|
+
elsif response.downcase.include?("exceptionmessage")
|
97
|
+
error_message = response_parsed["exceptionMessage"][0]
|
98
|
+
end
|
99
|
+
|
100
|
+
rescue
|
101
|
+
raise "An unknown error has occured within the salesforce_bulk gem. This is most likely caused by bad request, but I am unable to parse the correct error message. Here is a dump of the response for your convenience. #{response}"
|
102
|
+
end
|
103
|
+
|
104
|
+
raise error_message
|
105
|
+
end
|
106
|
+
|
107
|
+
response_parsed
|
108
|
+
end
|
109
|
+
|
90
110
|
end
|
91
111
|
|
92
112
|
end
|
data/lib/salesforce_bulk/job.rb
CHANGED
@@ -2,6 +2,8 @@ module SalesforceBulk
|
|
2
2
|
|
3
3
|
class Job
|
4
4
|
|
5
|
+
attr :result
|
6
|
+
|
5
7
|
def initialize(operation, sobject, records, external_field, connection)
|
6
8
|
|
7
9
|
@@operation = operation
|
@@ -11,6 +13,9 @@ module SalesforceBulk
|
|
11
13
|
@@connection = connection
|
12
14
|
@@XML_HEADER = '<?xml version="1.0" encoding="utf-8" ?>'
|
13
15
|
|
16
|
+
# @result = {"errors" => [], "success" => nil, "records" => [], "raw" => nil, "message" => 'The job has been queued.'}
|
17
|
+
@result = JobResult.new
|
18
|
+
|
14
19
|
end
|
15
20
|
|
16
21
|
def create_job()
|
@@ -27,7 +32,7 @@ module SalesforceBulk
|
|
27
32
|
headers = Hash['Content-Type' => 'application/xml; charset=utf-8']
|
28
33
|
|
29
34
|
response = @@connection.post_xml(nil, path, xml, headers)
|
30
|
-
response_parsed =
|
35
|
+
response_parsed = @@connection.parse_response response
|
31
36
|
|
32
37
|
@@job_id = response_parsed['id'][0]
|
33
38
|
end
|
@@ -110,16 +115,60 @@ module SalesforceBulk
|
|
110
115
|
path = "job/#{@@job_id}/batch/#{@@batch_id}/result/#{result_id}"
|
111
116
|
headers = Hash.new
|
112
117
|
headers = Hash["Content-Type" => "text/xml; charset=UTF-8"]
|
113
|
-
#puts "path is: #{path}\n"
|
114
118
|
|
115
119
|
response = @@connection.get_request(nil, path, headers)
|
116
|
-
#puts "\n\nres2: #{response.inspect}\n\n"
|
117
120
|
|
118
121
|
end
|
119
122
|
|
123
|
+
parse_results response
|
124
|
+
|
120
125
|
response = response.lines.to_a[1..-1].join
|
121
|
-
csvRows = CSV.parse(response)
|
126
|
+
# csvRows = CSV.parse(response, :headers => true)
|
122
127
|
end
|
123
128
|
|
129
|
+
def parse_results response
|
130
|
+
@result.success = true
|
131
|
+
@result.raw = response.lines.to_a[1..-1].join
|
132
|
+
csvRows = CSV.parse(response, :headers => true)
|
133
|
+
|
134
|
+
csvRows.each_with_index do |row, index|
|
135
|
+
if @@operation != "query"
|
136
|
+
row["Created"] = row["Created"] == "true" ? true : false
|
137
|
+
row["Success"] = row["Success"] == "true" ? true : false
|
138
|
+
end
|
139
|
+
|
140
|
+
@result.records.push row
|
141
|
+
if row["Success"] == false
|
142
|
+
@result.success = false
|
143
|
+
@result.errors.push({"#{index}" => row["Error"]}) if row["Error"]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@result.message = "The job has been closed."
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
class JobResult
|
154
|
+
attr_writer :errors, :success, :records, :raw, :message
|
155
|
+
attr_reader :errors, :success, :records, :raw, :message
|
156
|
+
|
157
|
+
def initialize
|
158
|
+
@errors = []
|
159
|
+
@success = nil
|
160
|
+
@records = []
|
161
|
+
@raw = nil
|
162
|
+
@message = 'The job has been queued.'
|
163
|
+
end
|
164
|
+
|
165
|
+
def success?
|
166
|
+
@success
|
167
|
+
end
|
168
|
+
|
169
|
+
def has_errors?
|
170
|
+
@errors.count > 0
|
171
|
+
end
|
124
172
|
end
|
173
|
+
|
125
174
|
end
|