salesforce_bulk_api 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in salesforce_bulk_api.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,64 @@
1
+ = Salesforce-Bulk-Api
2
+
3
+ ==Overview
4
+
5
+ Salesforce bulk Api is a simple ruby gem for connecting to and using the Salesforce Bulk API. It is actually a re-written code from salesforce_bulk[https://github.com/jorgevaldivia/salesforce_bulk].Written to suit many more other features as well.
6
+
7
+ ==How to use
8
+
9
+ Using this gem is simple and straight forward.
10
+
11
+ To initialize:
12
+
13
+ Pl do check the entire documentation of the databasedotcom gem and it various ways of authentication
14
+ Databasedotcom[https://github.com/heroku/databasedotcom]
15
+
16
+ You can use username password combo,OmniAuth,Oauth2
17
+
18
+ require 'salesforce_bulk_api'
19
+ client = Databasedotcom::Client.new :client_id => $SFDC_APP_CONFIG["client_id"], :client_secret => $SFDC_APP_CONFIG["client_secret"] #client_id and client_secret respectively
20
+ client.authenticate :token => "my-oauth-token", :instance_url => "http://na1.salesforce.com" #=> "my-oauth-token"
21
+ salesforce = SalesforceBulk::Api.new(client)
22
+
23
+ Sample operations:
24
+
25
+ # Insert/Create
26
+ new_account = Hash["name" => "Test Account", "type" => "Other"] # Add as many fields per record as needed.
27
+ records_to_insert = Array.new
28
+ 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.
29
+ result = salesforce.create("Account", records_to_insert)
30
+ puts "result is: #{result.inspect}"
31
+
32
+ # Update
33
+ updated_account = Hash["name" => "Test Account -- Updated", id => "a00A0001009zA2m"] # Nearly identical to an insert, but we need to pass the salesforce id.
34
+ records_to_update = Array.new
35
+ records_to_update.push(updated_account)
36
+ salesforce.update("Account", records_to_update)
37
+
38
+ # Upsert
39
+ upserted_account = Hash["name" => "Test Account -- Upserted", "External_Field_Name" => "123456"] # Fields to be updated. External field must be included
40
+ records_to_upsert = Array.new
41
+ records_to_upsert.push(upserted_account)
42
+ salesforce.upsert("Account", records_to_upsert, "External_Field_Name") # Note that upsert accepts an extra parameter for the external field name
43
+
44
+ # Delete
45
+ deleted_account = Hash["id" => "a00A0001009zA2m"] # We only specify the id of the records to delete
46
+ records_to_delete = Array.new
47
+ records_to_delete.push(deleted_account)
48
+ salesforce.delete("Account", records_to_delete)
49
+
50
+ # Query
51
+ res = salesforce.query("Account", "select id, name, createddate from Account limit 3") # We just need to pass the sobject name and the query string
52
+
53
+ ==Installation
54
+ sudo gem install salesforce_bulk_api
55
+
56
+ ==TODO
57
+ This is a rough early version of the gem. Immediate plans include better error reporting as there currently is none.
58
+
59
+ == Copyright
60
+
61
+ Copyright (c) 2012 Yatish Mehta
62
+
63
+ ===end
64
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,64 @@
1
+ module SalesforceBulkApi
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
+ @client=client
12
+ @session_id = nil
13
+ @server_url = nil
14
+ @instance = nil
15
+ @@API_VERSION = api_version
16
+ @@LOGIN_PATH = "/services/Soap/u/#{@@API_VERSION}"
17
+ @@PATH_PREFIX = "/services/async/#{@@API_VERSION}/"
18
+
19
+ login()
20
+ end
21
+
22
+ #private
23
+
24
+ def login()
25
+ @session_id=@client.oauth_token
26
+ @server_url=@client.instance_url
27
+ @instance = parse_instance()
28
+ puts @instance
29
+ @@INSTANCE_HOST = "#{@instance}.salesforce.com"
30
+ puts @@INSTANCE_HOST
31
+ end
32
+
33
+ def post_xml(host, path, xml, headers)
34
+ host = host || @@INSTANCE_HOST
35
+ if host != @@LOGIN_HOST # Not login, need to add session id to header
36
+ headers['X-SFDC-Session'] = @session_id;
37
+ path = "#{@@PATH_PREFIX}#{path}"
38
+ end
39
+ https(host).post(path, xml, headers).body
40
+ end
41
+
42
+ def get_request(host, path, headers)
43
+ host = host || @@INSTANCE_HOST
44
+ path = "#{@@PATH_PREFIX}#{path}"
45
+ if host != @@LOGIN_HOST # Not login, need to add session id to header
46
+ headers['X-SFDC-Session'] = @session_id;
47
+ end
48
+ https(host).get(path, headers).body
49
+ end
50
+
51
+ def https(host)
52
+ req = Net::HTTP.new(host, 443)
53
+ req.use_ssl = true
54
+ req.verify_mode = OpenSSL::SSL::VERIFY_NONE
55
+ req
56
+ end
57
+
58
+ def parse_instance()
59
+ @instance=@server_url.match(/https:\/\/[a-z]{2}[0-9]{1,2}/).to_s.gsub("https://","")
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,129 @@
1
+ module SalesforceBulkApi
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
+ puts response
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" => "application/xml; 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
62
+ @@records_dup=@@records.clone
63
+ super_records=[]
64
+ (@@records_dup.size/10000).times do
65
+ super_records<<@@records_dup.pop(10000)
66
+ end
67
+ super_records<<@@records_dup
68
+
69
+ super_records.each do|batch|
70
+ xml = "#{@@XML_HEADER}<sObjects xmlns=\"http://www.force.com/2009/06/asyncapi/dataload\">"
71
+ batch.each do |r|
72
+ xml += "<sObject>"
73
+ keys.each do |k|
74
+ xml += "<#{k}>#{r[k]}</#{k}>"
75
+ end
76
+ xml += "</sObject>"
77
+ end
78
+ xml += "</sObjects>"
79
+
80
+
81
+ path = "job/#{@@job_id}/batch/"
82
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
83
+ response = @@connection.post_xml(nil, path, xml, headers)
84
+ response_parsed = XmlSimple.xml_in(response)
85
+
86
+ @@batch_id = response_parsed['id'][0]
87
+ puts @@batch_id
88
+ end
89
+ end
90
+
91
+ def check_batch_status()
92
+ path = "job/#{@@job_id}/batch/#{@@batch_id}"
93
+ headers = Hash.new
94
+
95
+ response = @@connection.get_request(nil, path, headers)
96
+ response_parsed = XmlSimple.xml_in(response)
97
+
98
+ puts response_parsed
99
+ begin
100
+ #puts "check: #{response_parsed.inspect}\n"
101
+ response_parsed['state'][0]
102
+ rescue Exception => e
103
+ #puts "check: #{response_parsed.inspect}\n"
104
+
105
+ nil
106
+ end
107
+ end
108
+
109
+ def get_batch_result()
110
+ path = "job/#{@@job_id}/batch/#{@@batch_id}/result"
111
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
112
+
113
+ response = @@connection.get_request(nil, path, headers)
114
+
115
+ if(@@operation == "query") # The query op requires us to do another request to get the results
116
+ response_parsed = XmlSimple.xml_in(response)
117
+ result_id = response_parsed["result"][0]
118
+
119
+ path = "job/#{@@job_id}/batch/#{@@batch_id}/result/#{result_id}"
120
+ headers = Hash.new
121
+ headers = Hash["Content-Type" => "application/xml; charset=UTF-8"]
122
+ response = @@connection.get_request(nil, path, headers)
123
+ end
124
+
125
+
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,3 @@
1
+ module SalesforceBulkApi
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,73 @@
1
+ require "salesforce_bulk_api/version"
2
+ require 'net/https'
3
+ require 'rubygems'
4
+ require 'xmlsimple'
5
+ require 'csv'
6
+ require "salesforce_bulk_api/version"
7
+ require 'salesforce_bulk_api/job'
8
+ require 'salesforce_bulk_api/connection'
9
+ module SalesforceBulkApi
10
+ # Your code goes here...
11
+ class Api
12
+
13
+ @@SALESFORCE_API_VERSION = '23.0'
14
+
15
+ def initialize(client)
16
+ @connection = SalesforceBulk::Connection.new(@@SALESFORCE_API_VERSION,client)
17
+ end
18
+
19
+ def upsert(sobject, records, external_field)
20
+ self.do_operation('upsert', sobject, records, external_field)
21
+ end
22
+
23
+ def update(sobject, records)
24
+ self.do_operation('update', sobject, records, nil)
25
+ end
26
+
27
+ def create(sobject, records)
28
+ self.do_operation('insert', sobject, records, nil)
29
+ end
30
+
31
+ def delete(sobject, records)
32
+ self.do_operation('delete', sobject, records, nil)
33
+ end
34
+
35
+ def query(sobject, query)
36
+ self.do_operation('query', sobject, query, nil)
37
+ end
38
+
39
+ #private
40
+
41
+ def do_operation(operation, sobject, records, external_field)
42
+ job = SalesforceBulk::Job.new(operation, sobject, records, external_field, @connection)
43
+
44
+ # TODO: put this in one function
45
+ job_id = job.create_job()
46
+ if(operation == "query")
47
+ batch_id = job.add_query()
48
+ else
49
+ batch_id = job.add_batch()
50
+ end
51
+ job.close_job()
52
+
53
+ while true
54
+ state = job.check_batch_status()
55
+ #puts "\nstate is #{state}\n"
56
+ if state != "Queued" && state != "InProgress"
57
+ break
58
+ end
59
+ sleep(2) # wait x seconds and check again
60
+ end
61
+
62
+ if state == 'Completed'
63
+ job.get_batch_result()
64
+ else
65
+ return "error"
66
+ end
67
+
68
+ end
69
+
70
+ end # End class
71
+ end
72
+
73
+
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "salesforce_bulk_api/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "salesforce_bulk_api"
7
+ s.version = SalesforceBulkApi::VERSION
8
+ s.authors = ["Yatish Mehta"]
9
+ s.email = ["yatishmehta27@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{It uses the bulk api of salesforce to communicate with teh salesforce CRM}
12
+ s.description = %q{Salesforce Bulk API with governor limits taken care of}
13
+
14
+ s.rubyforge_project = "salesforce_bulk_api"
15
+ s.add_dependency(%q<oauth2>, ["0.4.1"])
16
+ s.add_dependency(%q<databasedotcom>, [">= 0"])
17
+ s.add_dependency(%q<json>, [">= 0"])
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ # specify any dependencies here; for example:
25
+ # s.add_development_dependency "rspec"
26
+ # s.add_runtime_dependency "rest-client"
27
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salesforce_bulk_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Yatish Mehta
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-12 00:00:00.000000000 +05:30
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: oauth2
17
+ requirement: &21802940 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - =
21
+ - !ruby/object:Gem::Version
22
+ version: 0.4.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *21802940
26
+ - !ruby/object:Gem::Dependency
27
+ name: databasedotcom
28
+ requirement: &21802320 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *21802320
37
+ - !ruby/object:Gem::Dependency
38
+ name: json
39
+ requirement: &21801740 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *21801740
48
+ description: Salesforce Bulk API with governor limits taken care of
49
+ email:
50
+ - yatishmehta27@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - README.rdoc
58
+ - Rakefile
59
+ - lib/salesforce_bulk_api.rb
60
+ - lib/salesforce_bulk_api/connection.rb
61
+ - lib/salesforce_bulk_api/job.rb
62
+ - lib/salesforce_bulk_api/version.rb
63
+ - salesforce_bulk_api.gemspec
64
+ has_rdoc: true
65
+ homepage: ''
66
+ licenses: []
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project: salesforce_bulk_api
85
+ rubygems_version: 1.6.2
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: It uses the bulk api of salesforce to communicate with teh salesforce CRM
89
+ test_files: []