salesforce_bulk 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ = salesforce-bulk
2
+
3
+ ==Overview
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).
6
+
7
+ ==How to use
8
+
9
+ Using this gem is simple and straight forward.
10
+
11
+ To initialize:
12
+
13
+ require 'salesforce_bulk'
14
+ salesforce = SalesforceBulk::Api.new("YOUR_SALESFORCE_USERNAME", "YOUR_SALESFORCE_PASSWORD")
15
+
16
+ Sample operations:
17
+
18
+ # Insert/Create
19
+ new_account = Hash["name" => "Test Account", "type" => "Other"] # Add as many fields per record as needed.
20
+ records_to_insert = Array.new
21
+ 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.
22
+ result = salesforce.create("Account", records_to_insert)
23
+ puts "result is: #{result.inspect}"
24
+
25
+ # Update
26
+ updated_account = Hash["name" => "Test Account -- Updated", id => "a00A0001009zA2m"] # Nearly identical to an insert, but we need to pass the salesforce id.
27
+ records_to_update = Array.new
28
+ records_to_update.push(updated_account)
29
+ salesforce.update("Account", records_to_update)
30
+
31
+ # Upsert
32
+ upserted_account = Hash["name" => "Test Account -- Upserted", "External_Field_Name" => "123456"] # Fields to be updated. External field must be included
33
+ records_to_upsert = Array.new
34
+ records_to_upsert.push(upserted_account)
35
+ salesforce.upsert("Account", records_to_upsert, "External_Field_Name") # Note that upsert accepts an extra parameter for the external field name
36
+
37
+ # Delete
38
+ deleted_account = Hash["id" => "a00A0001009zA2m"] # We only specify the id of the records to delete
39
+ records_to_delete = Array.new
40
+ records_to_delete.push(deleted_account)
41
+ salesforce.delete("Account", records_to_delete)
42
+
43
+ # Query
44
+ res = salesforce.query("Account", "select id, name, createddate from Account limit 3") # We just need to pass the sobject name and the query string
45
+
46
+ ==Installation
47
+ [sudo] gem install salesforce_bulk
48
+
49
+ == Copyright
50
+
51
+ Copyright (c) 2011 Jorge Valdivia.
52
+
53
+ ===end
54
+
@@ -51,7 +51,7 @@ module SalesforceBulk
51
51
 
52
52
  state = job.check_batch_status()
53
53
  while true
54
- puts "\nstate is #{state}\n"
54
+ #puts "\nstate is #{state}\n"
55
55
  state = job.check_batch_status()
56
56
  if state != "Queued"
57
57
  break
@@ -0,0 +1,90 @@
1
+ module SalesforceBulk
2
+
3
+ class Connection
4
+
5
+ @@XML_HEADER = '<?xml version="1.0" encoding="utf-8" ?>'
6
+ @@API_VERSION = nil
7
+ @@LOGIN_HOST = 'login.salesforce.com'
8
+ @@INSTANCE_HOST = nil # Gets set in login()
9
+
10
+ def initialize(username, password, api_version)
11
+ @username = username
12
+ @password = password
13
+ @session_id = nil
14
+ @server_url = nil
15
+ @instance = nil
16
+ @@API_VERSION = api_version
17
+ @@LOGIN_PATH = "/services/Soap/u/#{@@API_VERSION}"
18
+ @@PATH_PREFIX = "/services/async/#{@@API_VERSION}/"
19
+
20
+ login()
21
+ end
22
+
23
+ #private
24
+
25
+ def login()
26
+
27
+ xml = '<?xml version="1.0" encoding="utf-8" ?>'
28
+ xml += "<env:Envelope xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
29
+ xml += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
30
+ xml += " xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">"
31
+ xml += " <env:Body>"
32
+ xml += " <n1:login xmlns:n1=\"urn:partner.soap.sforce.com\">"
33
+ xml += " <n1:username>#{@username}</n1:username>"
34
+ xml += " <n1:password>#{@password}</n1:password>"
35
+ xml += " </n1:login>"
36
+ xml += " </env:Body>"
37
+ xml += "</env:Envelope>"
38
+
39
+ headers = Hash['Content-Type' => 'text/xml; charset=utf-8', 'SOAPAction' => 'login']
40
+
41
+ response = post_xml(@@LOGIN_HOST, @@LOGIN_PATH, xml, headers)
42
+ response_parsed = XmlSimple.xml_in(response)
43
+
44
+ @session_id = response_parsed['Body'][0]['loginResponse'][0]['result'][0]['sessionId'][0]
45
+ @server_url = response_parsed['Body'][0]['loginResponse'][0]['result'][0]['serverUrl'][0]
46
+ @instance = parse_instance()
47
+
48
+ @@INSTANCE_HOST = "#{@instance}.salesforce.com"
49
+ end
50
+
51
+ def post_xml(host, path, xml, headers)
52
+
53
+ host = host || @@INSTANCE_HOST
54
+
55
+ if host != @@LOGIN_HOST # Not login, need to add session id to header
56
+ headers['X-SFDC-Session'] = @session_id;
57
+ #puts "session id is: #{@session_id} --- #{headers.inspect}\n"
58
+ path = "#{@@PATH_PREFIX}#{path}"
59
+ end
60
+
61
+ #puts "#{host} -- #{path} -- #{headers.inspect}\n"
62
+
63
+ http = Net::HTTP.new(host)
64
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
65
+ resp = http.post(path, xml, headers)
66
+ return resp.body
67
+ end
68
+
69
+ def get_request(host, path, headers)
70
+ host = host || @@INSTANCE_HOST
71
+ path = "#{@@PATH_PREFIX}#{path}"
72
+
73
+ if host != @@LOGIN_HOST # Not login, need to add session id to header
74
+ headers['X-SFDC-Session'] = @session_id;
75
+ end
76
+
77
+ http = Net::HTTP.new(host)
78
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
79
+ resp = http.get(path, headers)
80
+ return resp.body
81
+ end
82
+
83
+ def parse_instance()
84
+ @server_url =~ /https:\/\/([a-z]{2,2}[0-9]{1,1})-api/
85
+ @instance = $~.captures[0]
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,126 @@
1
+ module SalesforceBulk
2
+
3
+ class Job
4
+
5
+ def initialize(operation, sobject, records, external_field, connection)
6
+
7
+ @@operation = operation
8
+ @@sobject = sobject
9
+ @@external_field = external_field
10
+ @@records = records
11
+ @@connection = connection
12
+ @@XML_HEADER = '<?xml version="1.0" encoding="utf-8" ?>'
13
+
14
+ end
15
+
16
+ def create_job()
17
+ xml = "#{@@XML_HEADER}<jobInfo xmlns=\"http://www.force.com/2009/06/asyncapi/dataload\">"
18
+ xml += "<operation>#{@@operation}</operation>"
19
+ xml += "<object>#{@@sobject}</object>"
20
+ if !@@external_field.nil? # This only happens on upsert
21
+ xml += "<externalIdFieldName>#{@@external_field}</externalIdFieldName>"
22
+ end
23
+ xml += "<contentType>CSV</contentType>"
24
+ xml += "</jobInfo>"
25
+
26
+ path = "job"
27
+ headers = Hash['Content-Type' => 'application/xml; charset=utf-8']
28
+
29
+ response = @@connection.post_xml(nil, path, xml, headers)
30
+ response_parsed = XmlSimple.xml_in(response)
31
+
32
+ @@job_id = response_parsed['id'][0]
33
+ end
34
+
35
+ def close_job()
36
+ xml = "#{@@XML_HEADER}<jobInfo xmlns=\"http://www.force.com/2009/06/asyncapi/dataload\">"
37
+ xml += "state>Closed</state>"
38
+ xml += "</jobInfo>"
39
+
40
+ path = "job/#{@@job_id}"
41
+ headers = Hash['Content-Type' => 'application/xml; charset=utf-8']
42
+
43
+ response = @@connection.post_xml(nil, path, xml, headers)
44
+ response_parsed = XmlSimple.xml_in(response)
45
+
46
+ #job_id = response_parsed['id'][0]
47
+ end
48
+
49
+ def add_query
50
+ path = "job/#{@@job_id}/batch/"
51
+ headers = Hash["Content-Type" => "text/csv; charset=UTF-8"]
52
+
53
+ response = @@connection.post_xml(nil, path, @@records, headers)
54
+ response_parsed = XmlSimple.xml_in(response)
55
+
56
+ @@batch_id = response_parsed['id'][0]
57
+ end
58
+
59
+ def add_batch()
60
+ keys = @@records.reduce({}) {|h,pairs| pairs.each {|k,v| (h[k] ||= []) << v}; h}.keys
61
+ headers = keys.to_csv
62
+
63
+ output_csv = headers
64
+
65
+ @@records.each do |r|
66
+ fields = Array.new
67
+ keys.each do |k|
68
+ fields.push(r[k])
69
+ end
70
+
71
+ row_csv = fields.to_csv
72
+ output_csv += row_csv
73
+ end
74
+
75
+ path = "job/#{@@job_id}/batch/"
76
+ headers = Hash["Content-Type" => "text/csv; charset=UTF-8"]
77
+
78
+ response = @@connection.post_xml(nil, path, output_csv, headers)
79
+ response_parsed = XmlSimple.xml_in(response)
80
+
81
+ @@batch_id = response_parsed['id'][0]
82
+ end
83
+
84
+ def check_batch_status()
85
+ path = "job/#{@@job_id}/batch/#{@@batch_id}"
86
+ headers = Hash.new
87
+
88
+ response = @@connection.get_request(nil, path, headers)
89
+ response_parsed = XmlSimple.xml_in(response)
90
+
91
+ begin
92
+ #puts "check: #{response_parsed.inspect}\n"
93
+ response_parsed['state'][0]
94
+ rescue Exception => e
95
+ #puts "check: #{response_parsed.inspect}\n"
96
+
97
+ nil
98
+ end
99
+ end
100
+
101
+ def get_batch_result()
102
+ path = "job/#{@@job_id}/batch/#{@@batch_id}/result"
103
+ headers = Hash["Content-Type" => "text/xml; charset=UTF-8"]
104
+
105
+ response = @@connection.get_request(nil, path, headers)
106
+
107
+ if(@@operation == "query") # The query op requires us to do another request to get the results
108
+ response_parsed = XmlSimple.xml_in(response)
109
+ result_id = response_parsed["result"][0]
110
+
111
+ path = "job/#{@@job_id}/batch/#{@@batch_id}/result/#{result_id}"
112
+ headers = Hash.new
113
+ headers = Hash["Content-Type" => "text/xml; charset=UTF-8"]
114
+ #puts "path is: #{path}\n"
115
+
116
+ response = @@connection.get_request(nil, path, headers)
117
+ #puts "\n\nres2: #{response.inspect}\n\n"
118
+
119
+ end
120
+
121
+ response = response.lines.to_a[1..-1].join
122
+ csvRows = CSV.parse(response)
123
+ end
124
+
125
+ end
126
+ end
@@ -1,3 +1,3 @@
1
1
  module SalesforceBulk
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = SalesforceBulk::VERSION
8
8
  s.authors = ["Jorge Valdivia"]
9
9
  s.email = ["jorge@valdivia.me"]
10
- s.homepage = ""
10
+ s.homepage = "https://github.com/jorgevaldivia/salesforce_bulk"
11
11
  s.summary = %q{Ruby support for the Salesforce Bulk API}
12
12
  s.description = %q{This gem provides a super simple interface for the Salesforce Bulk API. It provides support for insert, update, upsert, delete, and query.}
13
13
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: salesforce_bulk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -21,11 +21,14 @@ extra_rdoc_files: []
21
21
  files:
22
22
  - .gitignore
23
23
  - Gemfile
24
+ - README.rdoc
24
25
  - Rakefile
25
26
  - lib/salesforce_bulk.rb
27
+ - lib/salesforce_bulk/connection.rb
28
+ - lib/salesforce_bulk/job.rb
26
29
  - lib/salesforce_bulk/version.rb
27
30
  - salesforce_bulk.gemspec
28
- homepage: ''
31
+ homepage: https://github.com/jorgevaldivia/salesforce_bulk
29
32
  licenses: []
30
33
  post_install_message:
31
34
  rdoc_options: []