salesforce_bulk_query-edge 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ require 'csv'
2
+
3
+ module SalesforceBulkQuery
4
+ class Utils
5
+ # record count if they want to
6
+ def self.line_count(f)
7
+ i = 0
8
+ CSV.foreach(f, :headers => true,:encoding => "UTF-8") {|_| i += 1}
9
+ i
10
+ end
11
+
12
+ def self.header(f)
13
+ File.open(f, &:readline).split(',').map{ |c| c.strip.delete('"') }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module SalesforceBulkQuery
2
+ VERSION = '0.2.1'
3
+ end
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+
3
+ # get the new version
4
+ VERSION=`bundle exec ruby <<-EORUBY
5
+
6
+ require 'salesforce_bulk_query'
7
+ puts SalesforceBulkQuery::VERSION
8
+
9
+ EORUBY`
10
+
11
+ # create tag and push it
12
+ TAG="v$VERSION"
13
+ git tag $TAG
14
+ git push origin $TAG
15
+
16
+ # build and push the gem
17
+ gem build salesforce_bulk_query.gemspec
18
+ gem push "salesforce_bulk_query-$VERSION.gem"
19
+
20
+ # update the gem after a few secs
21
+ sleep 30
22
+ gem update salesforce_bulk_query
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib/", __FILE__)
3
+ require "salesforce_bulk_query/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'salesforce_bulk_query-edge'
7
+ s.version = SalesforceBulkQuery::VERSION
8
+ s.authors = ['Petr Cvengros']
9
+ s.email = ['petr.cvengros@gooddata.com']
10
+
11
+ s.required_ruby_version = '>= 1.9'
12
+
13
+ s.homepage = 'https://github.com/cvengros/salesforce_bulk_query'
14
+ s.summary = %q{Downloading data from Salesforce Bulk API made easy and scalable.}
15
+ s.description = %q{A library for downloading data from Salesforce Bulk API. We only focus on querying, other operations of the API aren't supported. Designed to handle a lot of data.}
16
+ s.license = 'BSD'
17
+
18
+ s.add_dependency 'json', '<= 2'
19
+ s.add_dependency 'xml-simple', '~> 1.1'
20
+
21
+ s.add_development_dependency 'multi_json', '~> 1.9'
22
+ s.add_development_dependency 'restforce', '~>1.4'
23
+ s.add_development_dependency 'rspec', '~>2.14'
24
+ s.add_development_dependency 'pry', '~>0.9'
25
+ s.add_development_dependency 'pry-stack_explorer', '~>0.4' if RUBY_PLATFORM != 'java'
26
+ s.add_development_dependency 'rake', '~> 10.3'
27
+ s.add_development_dependency 'coveralls', '~> 0.7'
28
+ s.add_development_dependency 'webmock', '~> 1.20'
29
+
30
+ s.files = `git ls-files`.split($/)
31
+ s.require_paths = ['lib']
32
+
33
+ s.rubygems_version = "1.3.7"
34
+ end
@@ -0,0 +1,227 @@
1
+ require 'multi_json'
2
+ require 'csv'
3
+ require 'tmpdir'
4
+ require 'logger'
5
+ require 'set'
6
+ require 'salesforce_bulk_query'
7
+ require 'restforce'
8
+ require 'webmock/rspec'
9
+
10
+ class Helper
11
+ DEFAULT_API_VERSION = '30.0'
12
+ def self.create_default_restforce
13
+ Restforce.new(
14
+ :username => ENV['USERNAME'],
15
+ :password => ENV['PASSWORD'],
16
+ :security_token => ENV['TOKEN'],
17
+ :client_id => ENV['CLIENT_ID'],
18
+ :client_secret => ENV['CLIENT_SECRET'],
19
+ :api_version => api_version
20
+ )
21
+ end
22
+
23
+ def self.api_version
24
+ ENV['API_VERSION'] || DEFAULT_API_VERSION
25
+ end
26
+
27
+ def self.create_default_api(restforce)
28
+ # switch off the normal logging
29
+ Restforce.log = false
30
+
31
+ SalesforceBulkQuery::Api.new(restforce,
32
+ :api_version => ENV['API_VERSION'] || DEFAULT_API_VERSION,
33
+ :logger => ENV['LOGGING'] ? Logger.new(STDOUT): nil
34
+ )
35
+ end
36
+ end
37
+
38
+ # test co nejak nafakuje tu situaci v twc
39
+ describe SalesforceBulkQuery do
40
+ before :all do
41
+ WebMock.allow_net_connect!
42
+
43
+ @client = Helper.create_default_restforce
44
+ @api = Helper.create_default_api(@client)
45
+ @entity = ENV['ENTITY'] || 'Opportunity'
46
+ @field_list = (ENV['FIELD_LIST'] || "Id,CreatedDate").split(',')
47
+ @api_version = Helper.api_version
48
+ end
49
+
50
+ describe "instance_url" do
51
+ it "gives you some reasonable url" do
52
+ url = @api.instance_url
53
+ url.should_not be_empty
54
+ url.should match(/salesforce\.com\//)
55
+ end
56
+ end
57
+
58
+ describe "query" do
59
+ context "if you give it an invalid SOQL" do
60
+ it "fails with argument error" do
61
+ expect{@api.query(@entity, "SELECT Id, SomethingInvalid FROM #{@entity}")}.to raise_error(ArgumentError)
62
+ end
63
+ end
64
+ context "when you give it no options" do
65
+ it "downloads the data to a few files", :constraint => 'slow' do
66
+ result = @api.query(@entity, "SELECT #{@field_list.join(', ')} FROM #{@entity}", :count_lines => true)
67
+ filenames = result[:filenames]
68
+ filenames.should have_at_least(2).items
69
+ result[:jobs_done].should_not be_empty
70
+
71
+ # no duplicate filenames
72
+ expect(Set.new(filenames).length).to eq(filenames.length)
73
+
74
+ filenames.each do |filename|
75
+ File.size?(filename).should be_true
76
+
77
+ lines = CSV.read(filename)
78
+
79
+ if lines.length > 1
80
+ # first line should be the header
81
+ lines[0].should eql(@field_list)
82
+
83
+ # first id shouldn't be emtpy
84
+ lines[1][0].should_not be_empty
85
+ end
86
+ end
87
+ end
88
+ end
89
+ context "when we want to mock things" do
90
+ before(:each) do
91
+ WebMock.allow_net_connect!
92
+ end
93
+ after(:each) do
94
+ WebMock.allow_net_connect!
95
+ end
96
+ it "catches the timeout error for query" do
97
+ # stub the timeout on query
98
+ host = URI.parse(@api.instance_url).host
99
+ query_url = "#{host}/services/data/v#{@api_version}/query"
100
+ query_regexp = Regexp.new(query_url)
101
+ # 4 timeouts (first get the oldest record), then fake a
102
+ # 0 count query response
103
+ stub_request(:get, query_regexp).to_timeout.times(4).then.to_return(
104
+ :body => "{\"totalSize\":0,\"done\":true,\"records\":[]}",
105
+ :headers => {
106
+ "date"=>"Wed, 04 Feb 2015 01:18:45 GMT",
107
+ "set-cookie"=>"BrowserId=hahaha;Path=/;Domain=.salesforce.com;Expires=never",
108
+ "expires"=>"Thu, 01 Jan 1970 00:00:00 GMT",
109
+ "sforce-limit-info"=>"api-usage=6666/15000",
110
+ "content-type"=>"application/json;charset=UTF-8",
111
+ "transfer-encoding"=>"chunked"}
112
+ )
113
+
114
+ # do the actual request
115
+ WebMock.allow_net_connect!
116
+ result = @api.query(
117
+ @entity,
118
+ "SELECT #{@field_list.join(', ')} FROM #{@entity}",
119
+ :count_lines => true,
120
+ :single_batch => true
121
+ )
122
+
123
+ # check it
124
+ expect(result[:succeeded]).to be_true
125
+ expect(result[:unfinished_subqueries]).to be_empty
126
+ expect(result[:filenames]).not_to be_empty
127
+ expect(result[:jobs_done]).not_to be_empty
128
+ end
129
+ end
130
+ context "when you give it all the options" do
131
+ it "downloads a single file" do
132
+ tmp = Dir.mktmpdir
133
+ frm = "2000-01-01"
134
+ from = "#{frm}T00:00:00.000Z"
135
+ t = "2020-01-01"
136
+ to = "#{t}T00:00:00.000Z"
137
+ field = 'SystemModstamp'
138
+ result = @api.query(
139
+ "Account",
140
+ "SELECT Id, Name, Industry, Type FROM Account",
141
+ :check_interval => 30,
142
+ :directory_path => tmp,
143
+ :date_from => from,
144
+ :date_to => to,
145
+ :single_batch => true,
146
+ :count_lines => true,
147
+ :date_field => field
148
+ )
149
+
150
+ result[:filenames].should have(1).items
151
+ result[:jobs_done].should_not be_empty
152
+
153
+ filename = result[:filenames][0]
154
+
155
+ File.size?(filename).should be_true
156
+ lines = CSV.read(filename)
157
+
158
+ # first line should be the header
159
+ lines[0].should eql(["Id", "Name", "Industry", "Type"])
160
+
161
+ # first id shouldn't be emtpy
162
+ lines[1][0].should_not be_empty
163
+
164
+ filename.should match(tmp)
165
+ filename.should match(frm)
166
+ filename.should match(t)
167
+ filename.should match(field)
168
+ end
169
+ end
170
+ context "when you give it a bad date_field" do
171
+ it "fails with argument error with no from date" do
172
+ expect{@api.query(@entity, "SELECT Id, CreatedDate FROM #{@entity}", :date_field => 'SomethingInvalid')}.to raise_error(ArgumentError)
173
+ end
174
+ it "fails with argument error with given from date" do
175
+ from = "2000-01-01T00:00:00.000Z"
176
+ expect{
177
+ @api.query(
178
+ @entity,
179
+ "SELECT Id, CreatedDate FROM #{@entity}",
180
+ :date_field => 'SomethingInvalid',
181
+ :date_from => from
182
+ )
183
+ }.to raise_error(ArgumentError)
184
+ end
185
+
186
+ end
187
+ context "when you give it a short time limit" do
188
+ it "downloads some stuff is unfinished" do
189
+ result = @api.query(
190
+ "Opportunity",
191
+ "SELECT Id, Name, CreatedDate FROM Opportunity",
192
+ :time_limit => 15
193
+ )
194
+ # one of them should be non-empty
195
+ expect((! result[:unfinished_subqueries].empty?) || (! result[:filenames].empty?)).to eq true
196
+ end
197
+ end
198
+ context "when you pass a short job time limit" do
199
+ it "creates quite a few jobs quickly", :skip => true do
200
+ # development only
201
+ result = @api.query(
202
+ @entity,
203
+ "SELECT Id, CreatedDate FROM #{@entity}",
204
+ :count_lines => true,
205
+ :job_time_limit => 60
206
+ )
207
+ require 'pry'; binding.pry
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "start_query" do
213
+ it "starts a query that finishes some time later" do
214
+ query = @api.start_query("Opportunity", "SELECT Id, Name, CreatedDate FROM Opportunity", :single_batch => true)
215
+
216
+ # get a cofee
217
+ sleep(60*2)
218
+
219
+ # check the status
220
+ result = query.get_available_results
221
+ expect(result[:succeeded]).to eq true
222
+ result[:filenames].should have_at_least(1).items
223
+ result[:jobs_done].should_not be_empty
224
+ end
225
+
226
+ end
227
+ end
@@ -0,0 +1,9 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ RSpec.configure do |c|
5
+ c.filter_run :focus => true
6
+ c.run_all_when_everything_filtered = true
7
+ c.filter_run_excluding :skip => true
8
+ end
9
+
metadata ADDED
@@ -0,0 +1,207 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salesforce_bulk_query-edge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Petr Cvengros
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "<="
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "<="
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: xml-simple
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: multi_json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: restforce
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.14'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.9'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-stack_explorer
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.4'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.3'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.3'
125
+ - !ruby/object:Gem::Dependency
126
+ name: coveralls
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.7'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.7'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webmock
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.20'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.20'
153
+ description: A library for downloading data from Salesforce Bulk API. We only focus
154
+ on querying, other operations of the API aren't supported. Designed to handle a
155
+ lot of data.
156
+ email:
157
+ - petr.cvengros@gooddata.com
158
+ executables: []
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - ".gitignore"
163
+ - ".rspec"
164
+ - ".travis.yml"
165
+ - Gemfile
166
+ - LICENSE
167
+ - README.md
168
+ - Rakefile
169
+ - env_setup-example.sh
170
+ - lib/salesforce_bulk_query.rb
171
+ - lib/salesforce_bulk_query/batch.rb
172
+ - lib/salesforce_bulk_query/connection.rb
173
+ - lib/salesforce_bulk_query/job.rb
174
+ - lib/salesforce_bulk_query/logger.rb
175
+ - lib/salesforce_bulk_query/query.rb
176
+ - lib/salesforce_bulk_query/utils.rb
177
+ - lib/salesforce_bulk_query/version.rb
178
+ - new-version.sh
179
+ - salesforce_bulk_query.gemspec
180
+ - spec/salesforce_bulk_query_spec.rb
181
+ - spec/spec_helper.rb
182
+ homepage: https://github.com/cvengros/salesforce_bulk_query
183
+ licenses:
184
+ - BSD
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '1.9'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.6.8
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Downloading data from Salesforce Bulk API made easy and scalable.
206
+ test_files: []
207
+ has_rdoc: