salesforce_bulk_api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []