salesforce_bulk_oauth2 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,72 @@
1
+ = salesforce-bulk
2
+
3
+ ==Overview
4
+
5
+ Salesforce Bulk Oauth2 is an extension of jorgevaldivia's Salesforce Bulk ruby gem for connecting to and using the Salesforce Bulk API (http://www.salesforce.com/us/developer/docs/api_asynch/index.htm) via Oauth2 service instead of username/password.
6
+
7
+ ==Installation
8
+
9
+ sudo gem install salesforce_bulk
10
+
11
+ ==How to use
12
+
13
+ Using this gem is simple and straight forward.
14
+
15
+ To initialize:
16
+
17
+ require 'salesforce_bulk'
18
+ salesforce = SalesforceBulk::Api.new(:token=>"YOUR_SALESFORCE_TOKEN", :instance_url=>"YOUR_SALESFORCE_INSTANCE_URL")
19
+
20
+ Sample operations:
21
+
22
+ Same as mentioned on (https://github.com/jorgevaldivia/salesforce_bulk).
23
+
24
+ # Insert/Create
25
+ new_account = Hash["name" => "Test Account", "type" => "Other"] # Add as many fields per record as needed.
26
+ records_to_insert = Array.new
27
+ 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.
28
+ result = salesforce.create("Account", records_to_insert)
29
+ puts "result is: #{result.inspect}"
30
+
31
+ # Update
32
+ updated_account = Hash["name" => "Test Account -- Updated", "id" => "a00A0001009zA2m"] # Nearly identical to an insert, but we need to pass the salesforce id.
33
+ records_to_update = Array.new
34
+ records_to_update.push(updated_account)
35
+ salesforce.update("Account", records_to_update)
36
+
37
+ # Upsert
38
+ upserted_account = Hash["name" => "Test Account -- Upserted", "External_Field_Name" => "123456"] # Fields to be updated. External field must be included
39
+ records_to_upsert = Array.new
40
+ records_to_upsert.push(upserted_account)
41
+ salesforce.upsert("Account", records_to_upsert, "External_Field_Name") # Note that upsert accepts an extra parameter for the external field name
42
+
43
+ OR
44
+
45
+ salesforce.upsert("Account", records_to_upsert, "External_Field_Name", true) # last parameter indicates whether to wait until the batch finishes
46
+
47
+ # Delete
48
+ deleted_account = Hash["id" => "a00A0001009zA2m"] # We only specify the id of the records to delete
49
+ records_to_delete = Array.new
50
+ records_to_delete.push(deleted_account)
51
+ salesforce.delete("Account", records_to_delete)
52
+
53
+ # Query
54
+ res = salesforce.query("Account", "select id, name, createddate from Account limit 3") # We just need to pass the sobject name and the query string
55
+ puts res.result.records.inspect
56
+
57
+ Result reporting:
58
+
59
+ array of true/false depending on wether record was pushed
60
+
61
+ example: if you push an array having 10 records. Result maybe
62
+ result = [true,true,true,true,false,true,false,true,true,true]
63
+
64
+
65
+ Thanks to jorgevaldivia for Salesforce Bulk gem
66
+
67
+ == Copyright
68
+
69
+ Copyright (c) 2012 Bhushan Lodha.
70
+
71
+ ===end
72
+
@@ -0,0 +1,70 @@
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(api_version,client)
11
+ #@username = username
12
+ @client=client
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
+
28
+ @session_id=@client.oauth_token
29
+
30
+ @server_url=@client.instance_url
31
+ @instance = parse_instance()
32
+ @@INSTANCE_HOST = "#{@instance}.salesforce.com"
33
+ end
34
+
35
+ def post_xml(host, path, xml, headers)
36
+ host = host || @@INSTANCE_HOST
37
+ if host != @@LOGIN_HOST # Not login, need to add session id to header
38
+ headers['X-SFDC-Session'] = @session_id;
39
+ path = "#{@@PATH_PREFIX}#{path}"
40
+ end
41
+ https(host).post(path, xml, headers).body
42
+ end
43
+
44
+ def get_request(host, path, headers)
45
+ host = host || @@INSTANCE_HOST
46
+ path = "#{@@PATH_PREFIX}#{path}"
47
+
48
+ if host != @@LOGIN_HOST # Not login, need to add session id to header
49
+ headers['X-SFDC-Session'] = @session_id;
50
+ end
51
+
52
+ https(host).get(path, headers).body
53
+ end
54
+
55
+ def https(host)
56
+ req = Net::HTTP.new(host, 443)
57
+ req.use_ssl = true
58
+ req.verify_mode = OpenSSL::SSL::VERIFY_NONE
59
+ req
60
+ end
61
+
62
+ def parse_instance()
63
+ @instance=@server_url.match(/https:\/\/[a-z]{2}[0-9]{1,2}/).to_s.gsub("https://","")
64
+ #@server_url =~ /https:\/\/[a-z]{2}[0-9]{1,2}/
65
+ #@instance = "ap1"
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,125 @@
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>XML</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
+ @@job_id = response_parsed['id'][0]
32
+ end
33
+
34
+ def close_job()
35
+ xml = "#{@@XML_HEADER}<jobInfo xmlns=\"http://www.force.com/2009/06/asyncapi/dataload\">"
36
+ xml += "<state>Closed</state>"
37
+ xml += "</jobInfo>"
38
+
39
+ path = "job/#{@@job_id}"
40
+ headers = Hash['Content-Type' => 'application/xml; charset=utf-8']
41
+
42
+ response = @@connection.post_xml(nil, path, xml, headers)
43
+ response_parsed = XmlSimple.xml_in(response)
44
+
45
+ #job_id = response_parsed['id'][0]
46
+ end
47
+
48
+ def add_query
49
+ path = "job/#{@@job_id}/batch/"
50
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
51
+ response = @@connection.post_xml(nil, path, @@records, headers)
52
+ response_parsed = XmlSimple.xml_in(response)
53
+ @@batch_id = response_parsed['id'][0]
54
+ end
55
+
56
+ def add_batch()
57
+ keys = @@records.inject({}) {|h,pairs| pairs.each {|k,v| (h[k] ||= []) << v}; h}.keys ## Target here
58
+ headers = keys
59
+ @@records_dup=@@records.clone
60
+ super_records=[]
61
+ (@@records_dup.size/10000).times do
62
+ super_records<<@@records_dup.pop(10000)
63
+ end
64
+ super_records<<@@records_dup
65
+
66
+ super_records.each do|batch|
67
+ xml = "#{@@XML_HEADER}<sObjects xmlns=\"http://www.force.com/2009/06/asyncapi/dataload\">"
68
+ batch.each do |r|
69
+ xml += "<sObject>"
70
+ keys.each do |k|
71
+ xml += "<#{k}>#{r[k]}</#{k}>"
72
+ end
73
+ xml += "</sObject>"
74
+ end
75
+ xml += "</sObjects>"
76
+
77
+
78
+ path = "job/#{@@job_id}/batch/"
79
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
80
+ response = @@connection.post_xml(nil, path, xml, headers)
81
+ response_parsed = XmlSimple.xml_in(response)
82
+ @@batch_id = response_parsed['id'][0]
83
+ puts @@batch_id
84
+ end
85
+ end
86
+
87
+ def check_batch_status()
88
+ headers = Hash.new
89
+ response = @@connection.get_request(nil, path, headers)
90
+ response_parsed = XmlSimple.xml_in(response)
91
+ puts response_parsed
92
+ begin
93
+ #puts "check: #{response_parsed.inspect}\n"
94
+ response_parsed['state'][0]
95
+ rescue Exception => e
96
+ #puts "check: #{response_parsed.inspect}\n"
97
+
98
+ nil
99
+ end
100
+ end
101
+
102
+ def get_batch_result()
103
+ path = "job/#{@@job_id}/batch/#{@@batch_id}/result"
104
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
105
+
106
+ response = @@connection.get_request(nil, path, headers)
107
+
108
+ if(@@operation == "query") # The query op requires us to do another request to get the results
109
+ response_parsed = XmlSimple.xml_in(response)
110
+ result_id = response_parsed["result"][0]
111
+
112
+ path = "job/#{@@job_id}/batch/#{@@batch_id}/result/#{result_id}"
113
+ headers = Hash.new
114
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
115
+ response = @@connection.get_request(nil, path, headers)
116
+ end
117
+ titles = []
118
+ doc = REXML::Document.new(response)
119
+ doc.elements.each('results/result/success') do |ele|
120
+ titles << ele.text
121
+ end
122
+ return titles
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,3 @@
1
+ module SalesforceBulk
2
+ VERSION = "0.0.5"
3
+ end
@@ -0,0 +1,78 @@
1
+ require 'net/https'
2
+ require 'rubygems'
3
+ require 'xmlsimple'
4
+ require 'csv'
5
+ require "#{File.dirname(File.expand_path(__FILE__))}/salesforce_bulk/version"
6
+ require "#{File.dirname(File.expand_path(__FILE__))}/salesforce_bulk/job"
7
+ require "#{File.dirname(File.expand_path(__FILE__))}/salesforce_bulk/connection"
8
+ require 'databasedotcom'
9
+
10
+ module SalesforceBulk
11
+ class Api
12
+
13
+ @@SALESFORCE_API_VERSION = '23.0'
14
+
15
+ def initialize(options)
16
+ client = databasedotcom_client(options[:token], options[:instance_url])
17
+ @connection = SalesforceBulk::Connection.new(@@SALESFORCE_API_VERSION,client)
18
+ end
19
+
20
+ def databasedotcom_client(token, instance_url)
21
+ client = Databasedotcom::Client.new("#{Rails.root}/config/databasedotcom.yml")
22
+ client.authenticate :token => token, :instance_url => instance_url
23
+ client
24
+ end
25
+
26
+ def upsert(sobject, records, external_field)
27
+ self.do_operation('upsert', sobject, records, external_field)
28
+ end
29
+
30
+ def update(sobject, records)
31
+ self.do_operation('update', sobject, records, nil)
32
+ end
33
+
34
+ def create(sobject, records)
35
+ self.do_operation('insert', sobject, records, nil)
36
+ end
37
+
38
+ def delete(sobject, records)
39
+ self.do_operation('delete', sobject, records, nil)
40
+ end
41
+
42
+ def query(sobject, query)
43
+ self.do_operation('query', sobject, query, nil)
44
+ end
45
+
46
+ #private
47
+
48
+ def do_operation(operation, sobject, records, external_field)
49
+ job = SalesforceBulk::Job.new(operation, sobject, records, external_field, @connection)
50
+
51
+ # TODO: put this in one function
52
+ job_id = job.create_job()
53
+ if(operation == "query")
54
+ batch_id = job.add_query()
55
+ else
56
+ batch_id = job.add_batch()
57
+ end
58
+ job.close_job()
59
+
60
+ while true
61
+ state = job.check_batch_status()
62
+ #puts "\nstate is #{state}\n"
63
+ if state != "Queued" && state != "InProgress"
64
+ break
65
+ end
66
+ sleep(2) # wait x seconds and check again
67
+ end
68
+
69
+ if state == 'Completed'
70
+ return job.get_batch_result()
71
+ else
72
+ return ["error"]
73
+ end
74
+
75
+ end
76
+
77
+ end # End class
78
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salesforce_bulk_oauth2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bhushan Lodha
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: xml-simple
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: databasedotcom
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: This gem provides a super simple interface for the Salesforce Bulk API
47
+ through Oauth2. It provides support for insert, update, upsert, delete, and query.
48
+ email:
49
+ - bhushanlodha@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - README.rdoc
55
+ - lib/salesforce_bulk/connection.rb
56
+ - lib/salesforce_bulk/job.rb
57
+ - lib/salesforce_bulk/version.rb
58
+ - lib/salesforce_bulk_oauth2.rb
59
+ homepage: https://github.com/bhushan/salesforce_bulk_oauth2
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.24
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Ruby support for the Salesforce Bulk API using Oauth2
83
+ test_files: []