salesforklift 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem "mocha", "0.9.10"
7
+ gem "rspec", "~> 2.0"
8
+ gem 'rdoc'
9
+ end
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ #salesforklift
2
+
3
+ ## A Salesforce Bulk API Ruby Wrapper
4
+
5
+ Salesforce provides Bulk API to ease massive data synchronization from a data store to salesforce. This project provides a gem to use Salesforce Bulk API in a ruby app.
6
+
7
+ ## How to build salesforklift gem
8
+ To build a gem, run ��gem build salesforklift.gemspec
9
+
10
+ ## How to use salesforklift gem
11
+
12
+ ```ruby
13
+ require 'salesforklift'
14
+
15
+ # login
16
+ login = Salesforklift::Login.new
17
+ result = login.login_sforce(TEST_USER_NAME, TEST_USER_PW, TEST_LOGIN_URL)
18
+
19
+ # create a job to bulk insert Contact objects
20
+ job = Salesforklift::Job.new(result.server_instance,
21
+ result.session_id,
22
+ "Contact",
23
+ :insert)
24
+ job.create
25
+ puts job.sf_job_id
26
+
27
+ # create batches
28
+ batch = Salesforklift::Batch.new(result.server_instance, result.session_id, job.sf_job_id)
29
+ batch.create_from_file("spec/data/contact.csv")
30
+
31
+ # close job
32
+ job.close
33
+
34
+ # check result
35
+ batch_response = batch.query_status
36
+
37
+ ```
38
+
39
+ ## Copyright
40
+ See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rspec/core/rake_task'
10
+ desc 'Default: run specs.'
11
+ task :default => :spec
12
+
13
+ desc "Run specs"
14
+ RSpec::Core::RakeTask.new do |t|
15
+ t.pattern = "./spec/unit/*_spec.rb" # exclude integration specs
16
+ # Put spec opts in a file named .rspec in root
17
+ end
18
+
19
+ desc "Run integration specs (make sure you have valid Salesforce login in spec_helper.rb)"
20
+ RSpec::Core::RakeTask.new(:integration) do |t|
21
+ t.pattern = "./spec/integration/*_spec.rb" # run integration specs
22
+ end
23
+
24
+ require 'rdoc/task'
25
+ RDoc::Task.new do |rdoc|
26
+ rdoc.rdoc_dir = 'rdoc'
27
+ rdoc.title = 'Salesforklift'
28
+ rdoc.options << '--line-numbers' << '--inline-source'
29
+ rdoc.rdoc_files.include('README.rdoc')
30
+ rdoc.rdoc_files.include('lib/**/*.rb')
31
+ end
@@ -0,0 +1,7 @@
1
+ require "salesforklift/logging"
2
+ require "salesforklift/login_response"
3
+ require "salesforklift/job_response"
4
+ require "salesforklift/batch_response"
5
+ require "salesforklift/login"
6
+ require "salesforklift/job"
7
+ require "salesforklift/batch"
@@ -0,0 +1,69 @@
1
+ require 'restclient'
2
+
3
+ module Salesforklift
4
+
5
+ class Batch
6
+ include Salesforklift::Logging
7
+
8
+ attr_accessor :server_instance, :session_id, :sf_job_id, :batch_id
9
+
10
+ def initialize(server_instance, session_id, sf_job_id)
11
+ @server_instance = server_instance
12
+ @session_id = session_id
13
+ @sf_job_id = sf_job_id
14
+ end
15
+
16
+ def create_from_file(data_file)
17
+ batch_content = ""
18
+ File.open(data_file, "r") do |infile|
19
+ while (line = infile.gets)
20
+ batch_content += line
21
+ end
22
+ end
23
+
24
+ logger.debug batch_content
25
+ create(batch_content)
26
+ end
27
+
28
+ def create(batch_content)
29
+ response = RestClient.post("https://#{@server_instance}.salesforce.com/services/async/21.0/job/#{@sf_job_id}/batch",
30
+ batch_content.lstrip,
31
+ {"Content-Type" => "text/csv;charset=UTF-8", "X-SFDC-Session" => @session_id})
32
+ return false if (response.code/100 != 2)
33
+ response = BatchResponse.fromXML(response.body)
34
+ @batch_id = response.batch_id
35
+ end
36
+
37
+ def query_status
38
+ http = Net::HTTP.new("#{@server_instance}.salesforce.com", 443)
39
+ http.use_ssl = true
40
+
41
+ response = http.get("/services/async/21.0/job/#{@sf_job_id}/batch/#{@batch_id}",
42
+ {"X-SFDC-Session" => @session_id})
43
+
44
+ return response.body if response.class != Net::HTTPOK
45
+
46
+ return BatchResponse.fromXML(response.body)
47
+ end
48
+
49
+ def query_result
50
+ http = Net::HTTP.new("#{@server_instance}.salesforce.com", 443)
51
+ http.use_ssl = true
52
+
53
+ response = http.get("/services/async/21.0/job/#{@sf_job_id}/batch/#{@batch_id}/result",
54
+ {"X-SFDC-Session" => @session_id})
55
+ return response.body if response.class != Net::HTTPOK
56
+
57
+ result = Array.new
58
+ response.body.each_line do |line|
59
+ one_record = line.split(",")
60
+ result << {:id => one_record[0], :success => one_record[1],
61
+ :created => one_record[2], :error => one_record[3]}
62
+ end
63
+
64
+ result.drop(1) # drop header
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,18 @@
1
+ require 'rexml/document'
2
+
3
+ module Salesforklift
4
+
5
+ class BatchResponse
6
+ attr_accessor :batch_id, :state
7
+
8
+ def self.fromXML(xml)
9
+ doc = REXML::Document.new(xml)
10
+
11
+ response = BatchResponse.new
12
+ response.batch_id = doc.elements['//id'].text
13
+ response.state = doc.elements['//state'].text
14
+ response
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,81 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module Salesforklift
5
+
6
+ class Job
7
+ include Salesforklift::Logging
8
+
9
+ attr_accessor :server_instance, :session_id, :sf_object, :sf_action,
10
+ :sf_format, :sf_job_id, :external_id, :concurrency_mode
11
+
12
+ def initialize(server_instance, session_id, sf_object, sf_action,
13
+ external_id = nil, sf_format = :CSV, concurrency_mode = :Serial)
14
+ @server_instance = server_instance
15
+ @session_id = session_id
16
+ @sf_object = sf_object
17
+ @sf_action = sf_action
18
+ @sf_format = sf_format
19
+ @external_id = external_id
20
+ @concurrency_mode = concurrency_mode
21
+ end
22
+
23
+ def generate_create_request
24
+ request = <<-eos
25
+ <?xml version="1.0" encoding="UTF-8"?>
26
+ <jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
27
+ <operation>#{@sf_action}</operation>
28
+ <object>#{@sf_object}</object>
29
+ eos
30
+
31
+ if @external_id
32
+ request += <<-eos
33
+ <externalIdFieldName>#{@external_id}</externalIdFieldName>
34
+ eos
35
+ end
36
+
37
+ request += <<-eos
38
+ <concurrencyMode>#{@concurrency_mode.to_s}</concurrencyMode>
39
+ <contentType>#{@sf_format.to_s}</contentType>
40
+ </jobInfo>
41
+ eos
42
+ end
43
+
44
+ def generate_close_request
45
+ request = <<-eos
46
+ <?xml version="1.0" encoding="UTF-8"?>
47
+ <jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
48
+ <state>Closed</state>
49
+ </jobInfo>
50
+ eos
51
+ end
52
+
53
+ # Create a Salesforce Job
54
+ def create
55
+ http = Net::HTTP.new("#{@server_instance}.salesforce.com", 443)
56
+ http.use_ssl = true
57
+
58
+ job_request = generate_create_request.lstrip
59
+ logger.debug job_request
60
+ response = http.post('/services/async/21.0/job',
61
+ job_request,
62
+ {"Content-Type" => "application/xml;charset=UTF-8", "X-SFDC-Session" => @session_id})
63
+
64
+ return response.body if response.class != Net::HTTPCreated
65
+
66
+ job_response = JobResponse.fromXML(response.body)
67
+ @sf_job_id = job_response.job_id
68
+ job_response
69
+ end
70
+
71
+ def close
72
+ http = Net::HTTP.new("#{@server_instance}.salesforce.com", 443)
73
+ http.use_ssl = true
74
+
75
+ response = http.post("/services/async/21.0/job/#{@sf_job_id}",
76
+ generate_close_request.lstrip,
77
+ {"Content-Type" => "application/xml;charset=UTF-8", "X-SFDC-Session" => @session_id})
78
+ return response.class == Net::HTTPOK
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,17 @@
1
+ require 'rexml/document'
2
+
3
+ module Salesforklift
4
+
5
+ class JobResponse
6
+ attr_accessor :job_id
7
+
8
+ def self.fromXML(xml)
9
+ doc = REXML::Document.new(xml)
10
+
11
+ response = JobResponse.new
12
+ response.job_id = doc.elements['//id'].text
13
+ response
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'logger'
2
+
3
+ module Salesforklift
4
+ module Logging
5
+
6
+ @@defalut_logger = Logger.new(STDOUT)
7
+ @@defalut_logger.level = Logger::INFO
8
+
9
+ def logger
10
+ @logger ||= @@defalut_logger
11
+ end
12
+
13
+ def self.logger=(value)
14
+ @@defalut_logger = value
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module Salesforklift
5
+ class Login
6
+ include Salesforklift::Logging
7
+
8
+ def login_sforce(user, pass, url)
9
+ loginRequest = <<-eos
10
+ <?xml version="1.0" encoding="utf-8" ?>
11
+ <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
12
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
13
+ xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
14
+ <env:Body>
15
+ <n1:login xmlns:n1="urn:partner.soap.sforce.com">
16
+ <n1:username>#{user}</n1:username>
17
+ <n1:password>#{pass}</n1:password>
18
+ </n1:login>
19
+ </env:Body>
20
+ </env:Envelope>
21
+ eos
22
+
23
+ http = Net::HTTP.new(url, 443)
24
+ http.use_ssl = true
25
+
26
+ response = http.post('/services/Soap/u/21.0',
27
+ loginRequest.lstrip,
28
+ {"Content-Type" => "text/xml;charset=UTF-8", "SOAPAction" => "login"})
29
+
30
+ return false if response.class != Net::HTTPOK
31
+
32
+ return LoginResponse.fromXML(response.body)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ require 'rexml/document'
2
+
3
+ module Salesforklift
4
+
5
+ class LoginResponse
6
+
7
+ attr_accessor :server_instance, :session_id, :server_url
8
+
9
+ def self.fromXML(xml)
10
+ doc = REXML::Document.new(xml)
11
+
12
+ response = LoginResponse.new
13
+ response.server_url = doc.elements["//serverUrl"].text
14
+
15
+ if /(https:\/\/)(.+)(.salesforce.com)(.*)/.match(response.server_url)
16
+ response.server_instance = $2
17
+ end
18
+ response.session_id = doc.elements['//sessionId'].text
19
+ response
20
+ end
21
+ end
22
+
23
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salesforklift
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.8
6
+ platform: ruby
7
+ authors:
8
+ - Max Mao
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-11-15 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rest-client
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ description: Salesforce Bulk API ruby wrapper.
27
+ email: jmao@brightcove.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files: []
33
+
34
+ files:
35
+ - lib/salesforklift/batch.rb
36
+ - lib/salesforklift/batch_response.rb
37
+ - lib/salesforklift/job.rb
38
+ - lib/salesforklift/job_response.rb
39
+ - lib/salesforklift/logging.rb
40
+ - lib/salesforklift/login.rb
41
+ - lib/salesforklift/login_response.rb
42
+ - lib/salesforklift.rb
43
+ - Rakefile
44
+ - Gemfile
45
+ - README.md
46
+ homepage:
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.15
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Salesforce Bulk API ruby wrapper
73
+ test_files: []
74
+