gd_salesforce 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.markdown +47 -0
- data/Rakefile +1 -0
- data/lib/salesforce/version.rb +3 -0
- data/lib/salesforce.rb +174 -0
- data/salesforce.gemspec +27 -0
- metadata +116 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
This is a wrapper for couple of things I usually do with SF
|
2
|
+
===========================================================
|
3
|
+
|
4
|
+
Installation
|
5
|
+
------------
|
6
|
+
|
7
|
+
Prerequisite is having git, ruby and gems installed (also you should have XCode installed since there probably will be need to compile some C code in the background).
|
8
|
+
|
9
|
+
gem install bundler
|
10
|
+
git clone git://github.com/fluke777/salesforce.git
|
11
|
+
cd salesforce
|
12
|
+
bundle install
|
13
|
+
rake install
|
14
|
+
|
15
|
+
|
16
|
+
Grabbing fields of a module
|
17
|
+
---------------------------
|
18
|
+
Since Force.com explorer cannot search in fields and I love my text editor too much. I created a command how to download the list of fields
|
19
|
+
|
20
|
+
require 'rubygems'
|
21
|
+
require 'salesforce'
|
22
|
+
|
23
|
+
client = Salesforce::Client.new('login', 'pass+token')
|
24
|
+
fields = client.fields('Account')
|
25
|
+
|
26
|
+
# if you are looking for a specific one
|
27
|
+
fields.grep /Customer/i
|
28
|
+
|
29
|
+
# if you did not want a api name you can do whatever you want with describe. For example here I am grabbing label
|
30
|
+
response = client.describe('Account')
|
31
|
+
response[:describeSObjectResponse][:result][:fields].map {|field| field[:label]}
|
32
|
+
|
33
|
+
Downloading data
|
34
|
+
----------------
|
35
|
+
You can grab easily some data. Paging is implemented so it will download all the data.
|
36
|
+
|
37
|
+
# grabbing into array (this should probably be the default and it should return the field rather than pasing a reference inside)
|
38
|
+
x = []
|
39
|
+
client.grab :module => "User", :output => x, :fields => 'Id, Name'
|
40
|
+
x.count
|
41
|
+
|
42
|
+
# storing it into a file as CSV is easy as well
|
43
|
+
require 'fastercsv'
|
44
|
+
|
45
|
+
FasterCSV.open('my-csv.csv', 'w') do |csv|
|
46
|
+
client.grab :module => "User", :output => csv, :fields => 'Id, Name'
|
47
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/salesforce.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'salesforce/version'
|
2
|
+
require 'rforce'
|
3
|
+
require 'fastercsv'
|
4
|
+
require 'active_support/time'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
module Salesforce
|
8
|
+
|
9
|
+
class Client
|
10
|
+
|
11
|
+
attr_accessor :rforce_binding
|
12
|
+
|
13
|
+
def initialize(login, pass, options = {})
|
14
|
+
server = options[:server] || "www.salesforce.com"
|
15
|
+
url = options[:url] || "https://#{server}/services/Soap/u/26.0"
|
16
|
+
@rforce_binding = RForce::Binding.new url
|
17
|
+
@rforce_binding.login login, pass
|
18
|
+
end
|
19
|
+
|
20
|
+
def describe(mod)
|
21
|
+
@rforce_binding.describeSObject(:sObject => mod)
|
22
|
+
end
|
23
|
+
|
24
|
+
def modules
|
25
|
+
g = @rforce_binding.describeGlobal
|
26
|
+
modules = g[:describeGlobalResponse][:result][:sobjects]
|
27
|
+
end
|
28
|
+
|
29
|
+
def fields(mod)
|
30
|
+
result = @rforce_binding.describeSObject(:sObject => mod)
|
31
|
+
if result.has_key?(:Fault)
|
32
|
+
fail result
|
33
|
+
end
|
34
|
+
fields = result[:describeSObjectResponse][:result][:fields]
|
35
|
+
fields.map {|f| f[:name]}
|
36
|
+
end
|
37
|
+
|
38
|
+
def grab(options)
|
39
|
+
sf_module = options[:module] || fail("Specify SFDC module")
|
40
|
+
fields = options[:fields]
|
41
|
+
rforce_binding = @rforce_binding
|
42
|
+
output = options[:output]
|
43
|
+
|
44
|
+
if fields == :all
|
45
|
+
fields = fields(sf_module)
|
46
|
+
elsif fields.kind_of? String
|
47
|
+
fields = fields.split(',')
|
48
|
+
fields = fields.map {|f| f.strip}
|
49
|
+
end
|
50
|
+
|
51
|
+
values = fields.map {|v| v.to_sym}
|
52
|
+
|
53
|
+
query = "SELECT #{values.join(', ')} from #{sf_module}"
|
54
|
+
query(query, options.merge(:values => values))
|
55
|
+
end
|
56
|
+
|
57
|
+
def query(query, options)
|
58
|
+
values = options[:values]
|
59
|
+
as_hash = options[:as_hash]
|
60
|
+
counter = 1
|
61
|
+
fail "If you want to return array you need to specify fields in values key" if !as_hash && values.nil?
|
62
|
+
|
63
|
+
rforce_binding = @rforce_binding
|
64
|
+
output = options[:output]
|
65
|
+
begin
|
66
|
+
answer = rforce_binding.query({:queryString => query, :batchSize => 2000})
|
67
|
+
rescue Timeout::Error => e
|
68
|
+
puts "Timeout occured retrying"
|
69
|
+
retry
|
70
|
+
end
|
71
|
+
|
72
|
+
if answer[:queryResponse].nil? || answer[:queryResponse][:result].nil?
|
73
|
+
fail answer[:Fault][:faultstring] if answer[:Fault] && answer[:Fault][:faultstring]
|
74
|
+
fail "An unknown error occured while querying salesforce."
|
75
|
+
end
|
76
|
+
|
77
|
+
answer[:queryResponse][:result][:records].each {|row| output << (as_hash ? row : row.values_at(*values))} if answer[:queryResponse][:result][:size].to_i > 0
|
78
|
+
|
79
|
+
more_locator = answer[:queryResponse][:result][:queryLocator]
|
80
|
+
|
81
|
+
while more_locator do
|
82
|
+
answer_more = rforce_binding.queryMore({:queryLocator => more_locator, :batchSize => 2000})
|
83
|
+
answer_more[:queryMoreResponse][:result][:records].each do |row|
|
84
|
+
output << (as_hash ? row : row.values_at(*values))
|
85
|
+
end
|
86
|
+
more_locator = answer_more[:queryMoreResponse][:result][:queryLocator]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_deleted(options={})
|
91
|
+
rforce_binding = @rforce_binding
|
92
|
+
sf_module = options[:module]
|
93
|
+
end_time = options[:end_time] || Time.now
|
94
|
+
start_time = options[:start_time] || end_time.advance(:days => -15)
|
95
|
+
fail "The specified start_time cannot be the same value as, or later than, the specified end_time value" unless end_time > start_time
|
96
|
+
puts "Downloading from #{start_time} to #{end_time}"
|
97
|
+
answer = rforce_binding.getDeleted([:sObjectType, sf_module, :startDate, start_time.utc.iso8601, :endDate, end_time.utc.iso8601])
|
98
|
+
end
|
99
|
+
|
100
|
+
def download_deleted(options={})
|
101
|
+
output_file = options[:output_file]
|
102
|
+
fail "Output file not specified" if output_file.nil?
|
103
|
+
|
104
|
+
answer = get_deleted(options)
|
105
|
+
if answer[:getDeletedResponse].nil?
|
106
|
+
fail answer[:Fault][:faultstring] if answer[:Fault] && answer[:Fault][:faultstring]
|
107
|
+
fail "An unknown error occured during deleted records extraction."
|
108
|
+
end
|
109
|
+
|
110
|
+
result = answer[:getDeletedResponse][:result]
|
111
|
+
FasterCSV.open(output_file,"w") do |csv|
|
112
|
+
csv << ["Timestamp", "Id", "IsDeleted"]
|
113
|
+
unless result[:deletedRecords].nil? then
|
114
|
+
result[:deletedRecords].each do |record|
|
115
|
+
timestamp = Time.parse(record[:deletedDate]).to_i
|
116
|
+
csv << [timestamp, record[:id], "true"]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
return result[:earliestDateAvailable], result[:latestDateCovered]
|
122
|
+
end
|
123
|
+
|
124
|
+
def download_updated(options={})
|
125
|
+
module_name = options[:module]
|
126
|
+
end_time = options[:end_time] || Time.now
|
127
|
+
start_time = options[:start_time] || end_time.advance(:days => -1)
|
128
|
+
fields_list = options[:fields] || []
|
129
|
+
puts "Downloading #{module_name} from #{start_time} to #{end_time}"
|
130
|
+
update_answer = rforce_binding.getUpdated([:sObjectType, module_name, :startDate, start_time.utc.iso8601, :endDate, end_time.utc.iso8601])
|
131
|
+
results = update_answer[:getUpdatedResponse][:result][:ids]
|
132
|
+
if results.nil?
|
133
|
+
puts "#{module_name} is empty"
|
134
|
+
FasterCSV.open(options[:output_file], 'w') do |csv|
|
135
|
+
csv << fields_list
|
136
|
+
end
|
137
|
+
return update_answer[:getUpdatedResponse][:result][:latestDateCovered]
|
138
|
+
end
|
139
|
+
puts "Found and downloaded #{results.size} records"
|
140
|
+
fields_list = fields_list.map {|x| x.strip.to_sym}
|
141
|
+
|
142
|
+
converters = {
|
143
|
+
"datetime" => lambda {|f| Time.parse(f).utc.to_i rescue f},
|
144
|
+
"string" => lambda {|f| f[0..128] rescue f},
|
145
|
+
"textarea" => lambda {|f| f[0..128] rescue f},
|
146
|
+
"date" => lambda {|f| Time.parse(f).utc.to_i + (12 * 3600) rescue f},
|
147
|
+
}
|
148
|
+
|
149
|
+
answer = describe(module_name)
|
150
|
+
fields_description = answer[:describeSObjectResponse][:result][:fields]
|
151
|
+
my_fields_description = fields_list.map {|f| fields_description.detect {|fl| fl[:name].to_sym == f}}
|
152
|
+
|
153
|
+
my_converters = my_fields_description.map {|fd| converters[fd[:type]]}
|
154
|
+
FasterCSV.open(options[:output_file], 'w') do |csv|
|
155
|
+
csv << ['Timestamp'] + fields_list
|
156
|
+
results.each_slice(2000) do |slice|
|
157
|
+
ids = slice.reduce([]) do |memo, item|
|
158
|
+
memo.concat [:ID, item]
|
159
|
+
end
|
160
|
+
result = rforce_binding.retrieve([:fieldList, fields_list.join(', '), :sObjectType, module_name].concat(ids) )
|
161
|
+
result[:retrieveResponse][:result].each do |line|
|
162
|
+
|
163
|
+
values = line.values_at(*fields_list).zip(my_converters).map do |value, converter|
|
164
|
+
converter.nil? ? value : converter.call(value)
|
165
|
+
end
|
166
|
+
csv << [Time.parse(update_answer[:getUpdatedResponse][:result][:latestDateCovered]).utc.to_i] + values
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
update_answer[:getUpdatedResponse][:result][:latestDateCovered]
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
end
|
data/salesforce.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "salesforce/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gd_salesforce"
|
7
|
+
s.version = Salesforce::VERSION
|
8
|
+
s.authors = ["Tomas Svarovsky"]
|
9
|
+
s.email = ["svarovsky.tomas@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/fluke777/salesforce"
|
11
|
+
s.summary = %q{Small gem that wraps common tasks that I do with Salesforce}
|
12
|
+
s.description = %q{Small gem that wraps common tasks that I do with Salesforce}
|
13
|
+
|
14
|
+
s.rubyforge_project = "salesforce"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
s.add_runtime_dependency "rforce"
|
24
|
+
s.add_runtime_dependency "fastercsv"
|
25
|
+
s.add_runtime_dependency "activesupport"
|
26
|
+
s.add_runtime_dependency "pry"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gd_salesforce
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tomas Svarovsky
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rforce
|
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: fastercsv
|
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
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Small gem that wraps common tasks that I do with Salesforce
|
79
|
+
email:
|
80
|
+
- svarovsky.tomas@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Gemfile
|
87
|
+
- README.markdown
|
88
|
+
- Rakefile
|
89
|
+
- lib/salesforce.rb
|
90
|
+
- lib/salesforce/version.rb
|
91
|
+
- salesforce.gemspec
|
92
|
+
homepage: https://github.com/fluke777/salesforce
|
93
|
+
licenses: []
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project: salesforce
|
112
|
+
rubygems_version: 1.8.25
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Small gem that wraps common tasks that I do with Salesforce
|
116
|
+
test_files: []
|