executrix 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -86,6 +86,16 @@ res = salesforce.query('Account', 'select id, name, createddate from Account lim
86
86
  puts res.result.records.inspect
87
87
  ~~~
88
88
 
89
+ ## File Upload
90
+
91
+ For file uploads, just add a `File` object to the binary columns.
92
+ ~~~ ruby
93
+ attachment = {'ParentId' => '00Kk0001908kqkDEAQ', 'Name' => 'attachment.pdf', 'Body' => File.new('tmp/attachment.pdf')}
94
+ records_to_insert = []
95
+ records_to_insert << attachment
96
+ salesforce.insert('Attachment', records_to_insert)
97
+ ~~~
98
+
89
99
  ### Query status
90
100
 
91
101
  The above examples all return immediately after sending the data to the Bulk API. If you want to wait, until the batch finished, call the final_status method on the batch-reference.
data/executrix.gemspec CHANGED
@@ -20,8 +20,9 @@ Gem::Specification.new do |gem|
20
20
  gem.require_paths = ["lib"]
21
21
 
22
22
  gem.add_dependency 'rake'
23
- gem.add_dependency 'nori', '< 2.3'
23
+ gem.add_dependency 'nori', '< 2.4'
24
24
  gem.add_dependency 'nokogiri', '< 1.7'
25
- gem.add_development_dependency 'rspec', '< 2.14'
26
- gem.add_development_dependency 'webmock', '< 1.12'
25
+ gem.add_dependency 'rubyzip', '< 1.1'
26
+ gem.add_development_dependency 'rspec', '< 2.15'
27
+ gem.add_development_dependency 'webmock', '< 1.14'
27
28
  end
@@ -24,12 +24,13 @@ module Executrix
24
24
  @session_id.split('!').first
25
25
  end
26
26
 
27
- def create_job operation, sobject, external_field
27
+ def create_job operation, sobject, content_type, external_field
28
28
  Executrix::Http.create_job(
29
29
  @instance,
30
30
  @session_id,
31
31
  operation,
32
32
  sobject,
33
+ content_type,
33
34
  @api_version,
34
35
  external_field)[:id]
35
36
  end
@@ -82,6 +83,15 @@ module Executrix
82
83
  )
83
84
  end
84
85
 
86
+ def add_file_upload_batch job_id, filename
87
+ Executrix::Http.add_file_upload_batch(
88
+ @instance,
89
+ @session_id,
90
+ job_id,
91
+ filename,
92
+ @api_version)[:id]
93
+ end
94
+
85
95
  def add_batch job_id, records
86
96
  return -1 if records.nil? || records.empty?
87
97
 
@@ -37,5 +37,33 @@ module Executrix
37
37
  before_sf = server_url[/^https?:\/\/(.+)\.salesforce\.com/, 1]
38
38
  before_sf.gsub(/-api$/,'')
39
39
  end
40
+
41
+ def attachment_keys records
42
+ records.map do |record|
43
+ record.select do |key, value|
44
+ value.class == File
45
+ end.keys
46
+ end.flatten.uniq
47
+ end
48
+
49
+ def transform_values! records, keys
50
+ keys.each do |key|
51
+ records.each do |record|
52
+ file_handle = record[key]
53
+ if file_handle
54
+ file_path = File.absolute_path(file_handle)
55
+ record
56
+ .merge!({
57
+ key => Executrix::Helper.absolute_to_relative_path(file_path,'#')
58
+ })
59
+ yield file_path if block_given?
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def absolute_to_relative_path input, replacement
66
+ input.gsub(/(^C:[\/\\])|(^\/)/,replacement)
67
+ end
40
68
  end
41
69
  end
@@ -41,6 +41,20 @@ module Executrix
41
41
  process_csv_response(process_http_request(r))
42
42
  end
43
43
 
44
+ def add_file_upload_batch instance, session_id, job_id, filename, api_version
45
+ data = File.read(filename)
46
+ headers = {
47
+ 'Content-Type' => 'zip/csv',
48
+ 'X-SFDC-Session' => session_id}
49
+ r = Http::Request.new(
50
+ :post,
51
+ Http::Request.generic_host(instance),
52
+ "/services/async/#{api_version}/job/#{job_id}/batch",
53
+ data,
54
+ headers)
55
+ process_xml_response(nori.parse(process_http_request(r)))
56
+ end
57
+
44
58
  def process_http_request(r)
45
59
  http = Net::HTTP.new(r.host, 443)
46
60
  http.use_ssl = true
@@ -120,7 +134,7 @@ module Executrix
120
134
  headers)
121
135
  end
122
136
 
123
- def self.create_job instance, session_id, operation, sobject, api_version, external_field = nil
137
+ def self.create_job instance, session_id, operation, sobject, content_type, api_version, external_field = nil
124
138
  external_field_line = external_field ?
125
139
  "<externalIdFieldName>#{external_field}</externalIdFieldName>" : nil
126
140
  body = %Q{<?xml version="1.0" encoding="utf-8" ?>
@@ -128,7 +142,7 @@ module Executrix
128
142
  <operation>#{operation}</operation>
129
143
  <object>#{sobject}</object>
130
144
  #{external_field_line}
131
- <contentType>CSV</contentType>
145
+ <contentType>#{content_type}</contentType>
132
146
  </jobInfo>
133
147
  }
134
148
  headers = {
@@ -209,7 +223,6 @@ module Executrix
209
223
  headers)
210
224
  end
211
225
 
212
- private
213
226
  def self.generic_host prefix
214
227
  "#{prefix}.salesforce.com"
215
228
  end
@@ -1,3 +1,3 @@
1
1
  module Executrix
2
- VERSION = '1.1.3'
2
+ VERSION = '1.2.0'
3
3
  end
data/lib/executrix.rb CHANGED
@@ -3,6 +3,7 @@ require 'executrix/helper'
3
3
  require 'executrix/batch'
4
4
  require 'executrix/http'
5
5
  require 'executrix/connection'
6
+ require 'zip'
6
7
 
7
8
  module Executrix
8
9
  class Api
@@ -40,6 +41,7 @@ module Executrix
40
41
  job_id = @connection.create_job(
41
42
  'query',
42
43
  sobject,
44
+ 'CSV',
43
45
  nil)
44
46
  batch_id = @connection.add_query(job_id, query)
45
47
  @connection.close_job job_id
@@ -49,11 +51,44 @@ module Executrix
49
51
 
50
52
  private
51
53
  def start_job(operation, sobject, records, external_field=nil)
54
+ attachment_keys = Executrix::Helper.attachment_keys(records)
55
+
56
+ content_type = 'CSV'
57
+ zip_filename = nil
58
+ request_filename = nil
59
+ batch_id = -1
60
+ if not attachment_keys.empty?
61
+ tmp_filename = Dir::Tmpname.make_tmpname('bulk_upload', '.zip')
62
+ zip_filename = "#{Dir.tmpdir}/#{tmp_filename}"
63
+ Zip::File.open(zip_filename, Zip::File::CREATE) do |zipfile|
64
+ Executrix::Helper.transform_values!(records, attachment_keys) do |path|
65
+ zipfile.add(Executrix::Helper.absolute_to_relative_path(path, ''), path)
66
+ end
67
+ tmp_filename = Dir::Tmpname.make_tmpname('request', '.txt')
68
+ request_filename = "#{Dir.tmpdir}/#{tmp_filename}"
69
+ File.open(request_filename, 'w') do |file|
70
+ file.write(Executrix::Helper.records_to_csv(records))
71
+ end
72
+ zipfile.add('request.txt', request_filename)
73
+ end
74
+
75
+ content_type = 'ZIP_CSV'
76
+ end
77
+
52
78
  job_id = @connection.create_job(
53
79
  operation,
54
80
  sobject,
81
+ content_type,
55
82
  external_field)
56
- batch_id = @connection.add_batch job_id, records
83
+ if zip_filename
84
+ batch_id = @connection.add_file_upload_batch job_id, zip_filename
85
+ [zip_filename, request_filename].each do |file|
86
+ File.delete(file) if file
87
+ end
88
+ else
89
+ batch_id = @connection.add_batch job_id, records
90
+ end
91
+
57
92
  @connection.close_job job_id
58
93
  Executrix::Batch.new @connection, job_id, batch_id
59
94
  end
@@ -3,33 +3,33 @@ require 'spec_helper'
3
3
 
4
4
  describe Executrix::Batch do
5
5
  describe '#final_status' do
6
- it 'should return the final status if it already exists' do
6
+ it 'returns the final status if it already exists' do
7
7
  b = described_class.new nil, nil, nil
8
8
  expected_status = {w: :tf}
9
- b.should_not_receive(:status)
9
+ expect(b).not_to receive(:status)
10
10
  b.instance_variable_set '@final_status', expected_status
11
11
  expect(b.final_status).to eq(expected_status)
12
12
  end
13
13
 
14
- it 'should return specific final status for -1 batch id' do
14
+ it 'returns specific final status for -1 batch id' do
15
15
  b = described_class.new nil, nil, -1
16
16
  expected_status = {
17
17
  state: 'Completed',
18
18
  state_message: 'Empty Request',
19
19
  }
20
- b.should_not_receive(:status)
20
+ expect(b).not_to receive(:status)
21
21
  expect(b.final_status).to eq(expected_status)
22
22
  end
23
23
 
24
- it 'should query the status correctly' do
24
+ it 'queries the status correctly' do
25
25
  b = described_class.new nil, nil, nil
26
- b.should_receive(:status).once.and_return({w: :tf})
26
+ expect(b).to receive(:status).once.and_return({w: :tf})
27
27
  # TODO lookup the actual result
28
- b.should_receive(:results).once.and_return({g: :tfo})
28
+ expect(b).to receive(:results).once.and_return({g: :tfo})
29
29
  expect(b.final_status).to eq({w: :tf, results: {g: :tfo}})
30
30
  end
31
31
 
32
- it 'should yield status correctly' do
32
+ it 'yields status correctly' do
33
33
  expected_running_state = {
34
34
  state: 'InProgress'
35
35
  }
@@ -37,18 +37,18 @@ describe Executrix::Batch do
37
37
  state: 'Completed'
38
38
  }
39
39
  b = described_class.new nil, nil, nil
40
- b.should_receive(:status).once.and_return(expected_running_state)
41
- b.should_receive(:status).once.and_return(expected_running_state)
42
- b.should_receive(:status).once.and_return(expected_final_state)
43
- b.should_receive(:results).once.and_return({g: :tfo})
40
+ expect(b).to receive(:status).once.and_return(expected_running_state)
41
+ expect(b).to receive(:status).once.and_return(expected_running_state)
42
+ expect(b).to receive(:status).once.and_return(expected_final_state)
43
+ expect(b).to receive(:results).once.and_return({g: :tfo})
44
44
  expect{|blk| b.final_status(0, &blk)}
45
45
  .to yield_successive_args(expected_running_state, expected_final_state)
46
46
  end
47
47
 
48
- it 'should raise exception when batch fails' do
48
+ it 'raises exception when batch fails' do
49
49
  b = described_class.new nil, nil, nil
50
50
  expected_error_message = 'Generic Error Message'
51
- b.should_receive(:status).once.and_return(
51
+ expect(b).to receive(:status).once.and_return(
52
52
  {
53
53
  state: 'Failed',
54
54
  state_message: expected_error_message})
@@ -6,16 +6,16 @@ describe Executrix::Connection do
6
6
 
7
7
  {
8
8
  login: 0,
9
- create_job: 3,
9
+ create_job: 4,
10
10
  close_job: 1,
11
11
  query_batch: 2,
12
12
  query_batch_result_id: 2,
13
13
  query_batch_result_data: 3,
14
14
  }.each do |method_name, num_of_params|
15
15
  describe "##{method_name}" do
16
- it 'should delegate correctly to Http class' do
17
- Executrix::Http
18
- .should_receive(method_name)
16
+ it 'delegates correctly to Http class' do
17
+ expect(Executrix::Http)
18
+ .to receive(method_name)
19
19
  .and_return({})
20
20
  subject.send(method_name, *Array.new(num_of_params))
21
21
  end
@@ -23,22 +23,22 @@ describe Executrix::Connection do
23
23
  end
24
24
 
25
25
  describe '#add_query' do
26
- it 'should delegate correctly to Http class' do
27
- Executrix::Http.should_receive(:add_batch)
26
+ it 'delegates correctly to Http class' do
27
+ expect(Executrix::Http).to receive(:add_batch)
28
28
  .and_return({})
29
29
  subject.add_query(nil, nil)
30
30
  end
31
31
  end
32
32
 
33
33
  describe '#org_id' do
34
- it 'should raise exception when not logged in' do
34
+ it 'raises exception when not logged in' do
35
35
  expect {subject.org_id}.to raise_error(RuntimeError)
36
36
  end
37
37
 
38
- it 'should return correct OrgId after login' do
38
+ it 'returns correct OrgId after login' do
39
39
  org_id = '00D50000000IehZ'
40
- Executrix::Http
41
- .should_receive(:login)
40
+ expect(Executrix::Http)
41
+ .to receive(:login)
42
42
  .and_return({session_id: "#{org_id}!AQcAQH0dMHZfz972Szmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1E6LYUfiDUkWe6H34r1AAwOR8B8fLEz6n04NPGRrq0FM"})
43
43
  expect(subject.login.org_id).to eq(org_id)
44
44
  end
@@ -46,20 +46,20 @@ describe Executrix::Connection do
46
46
 
47
47
 
48
48
  describe '#add_batch' do
49
- it 'should delegate correctly to underlying classes' do
50
- Executrix::Http.should_receive(:add_batch)
49
+ it 'delegates correctly to underlying classes' do
50
+ expect(Executrix::Http).to receive(:add_batch)
51
51
  .and_return({})
52
- Executrix::Helper.should_receive(:records_to_csv)
52
+ expect(Executrix::Helper).to receive(:records_to_csv)
53
53
  .and_return('My,Awesome,CSV')
54
- subject.add_batch(nil, 'non emtpy records')
54
+ subject.add_batch(nil, [{'non_emtpy' => 'records'}])
55
55
  end
56
56
 
57
- it 'should return -1 for nil input' do
57
+ it 'returns -1 for nil input' do
58
58
  return_code = subject.add_batch(nil, nil)
59
59
  expect(return_code).to eq(-1)
60
60
  end
61
61
 
62
- it 'should return -1 for empty input' do
62
+ it 'returns -1 for empty input' do
63
63
  return_code = subject.add_batch(nil, [])
64
64
  expect(return_code).to eq(-1)
65
65
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
 
4
4
  describe Executrix::Helper do
5
5
  describe '.records_to_csv' do
6
- it 'should return valid csv for single record' do
6
+ it 'returns valid csv for single record' do
7
7
  input = [
8
8
  {'Title' => 'Awesome Title', 'Name' => 'A name'},
9
9
  ]
@@ -13,7 +13,7 @@ describe Executrix::Helper do
13
13
  expect(described_class.records_to_csv(input)).to eq(expected_csv)
14
14
  end
15
15
 
16
- it 'should return valid csv for basic records' do
16
+ it 'returns valid csv for basic records' do
17
17
  input = [
18
18
  {'Title' => 'Awesome Title', 'Name' => 'A name'},
19
19
  {'Title' => 'A second Title', 'Name' => 'A second name'},
@@ -25,7 +25,7 @@ describe Executrix::Helper do
25
25
  expect(described_class.records_to_csv(input)).to eq(expected_csv)
26
26
  end
27
27
 
28
- it 'should return valid csv when first row misses a key' do
28
+ it 'returns valid csv when first row misses a key' do
29
29
  input = [
30
30
  {'Title' => 'Awesome Title', 'Name' => 'A name'},
31
31
  {'Title' => 'A second Title', 'Name' => 'A second name', 'Something' => 'Else'},
@@ -37,7 +37,7 @@ describe Executrix::Helper do
37
37
  expect(described_class.records_to_csv(input)).to eq(expected_csv)
38
38
  end
39
39
 
40
- it 'should correctly convert Array to Multi-Picklist' do
40
+ it 'correctly converts Array to Multi-Picklist' do
41
41
  input = [
42
42
  {'Title' => 'Awesome Title', 'Picklist' => ['Several', 'Values']},
43
43
  {'Title' => 'A second Title', 'Picklist' => ['SingleValue']},
@@ -49,7 +49,7 @@ describe Executrix::Helper do
49
49
  expect(described_class.records_to_csv(input)).to eq(expected_csv)
50
50
  end
51
51
 
52
- it 'should return valid csv when order of keys varies' do
52
+ it 'returns valid csv when order of keys varies' do
53
53
  input = [
54
54
  {'Title' => 'Awesome Title', 'Name' => 'A name'},
55
55
  {'Name' => 'A second name', 'Title' => 'A second Title'},
@@ -75,19 +75,146 @@ describe Executrix::Helper do
75
75
  'https://supercustomname.my.salesforce.com/services/Soap/u/28.0/00EH0000001jNQu'
76
76
  }
77
77
 
78
- it 'should return correct instance for regular salesforce server url' do
78
+ it 'returns correct instance for regular salesforce server url' do
79
79
  expect(described_class.fetch_instance_from_server_url(basic_server_url))
80
80
  .to eq('eu1')
81
81
  end
82
82
 
83
- it 'should return correct instance for api salesforce server url' do
83
+ it 'returns correct instance for api salesforce server url' do
84
84
  expect(described_class.fetch_instance_from_server_url(basic_api_server_url))
85
85
  .to eq('cs7')
86
86
  end
87
87
 
88
- it 'should return correct instance for named salesforce server url' do
88
+ it 'returns correct instance for named salesforce server url' do
89
89
  expect(described_class.fetch_instance_from_server_url(named_server_url))
90
90
  .to eq('supercustomname.my')
91
91
  end
92
92
  end
93
+
94
+ describe '.attachment_keys' do
95
+ let(:records_with_attachment) do
96
+ prefix = Dir.tmpdir
97
+ FileUtils.touch("#{prefix}/attachment.pdf")
98
+ [
99
+ {
100
+ 'normal_key' => 'normal_value1',
101
+ 'attachment_key' => nil,
102
+ },
103
+ {
104
+ 'normal_key' => 'normal_value2',
105
+ 'attachment_key' => File.new("#{prefix}/attachment.pdf"),
106
+ }
107
+ ]
108
+ end
109
+
110
+ let(:records_with_multiple_attachment) do
111
+ prefix = Dir.tmpdir
112
+ FileUtils.touch("#{prefix}/attachment1.pdf")
113
+ FileUtils.touch("#{prefix}/attachment2.pdf")
114
+ FileUtils.touch("#{prefix}/attachment3.pdf")
115
+ [
116
+ {
117
+ 'normal_key' => 'normal_value1',
118
+ 'attachment_key' => File.new("#{prefix}/attachment1.pdf"),
119
+ 'another_attachment_key' => File.new("#{prefix}/attachment2.pdf"),
120
+ },
121
+ {
122
+ 'normal_key' => 'normal_value2',
123
+ 'attachment_key' => File.new("#{prefix}/attachment3.pdf"),
124
+ }
125
+ ]
126
+ end
127
+
128
+ let(:records_without_attachment) do
129
+ [
130
+ {
131
+ 'normal_key' => 'normal_value1',
132
+ 'another_normal_key' => 'another_normal_value1',
133
+ },
134
+ {
135
+ 'normal_key' => 'normal_value2',
136
+ 'another_normal_key' => 'another_normal_value2',
137
+ }
138
+ ]
139
+ end
140
+
141
+ it 'returns correct keys for single attachment key' do
142
+ expect(described_class.attachment_keys(records_with_attachment))
143
+ .to eq(['attachment_key'])
144
+ end
145
+
146
+ it 'returns correct keys for multiple attachment keys' do
147
+ expect(described_class.attachment_keys(records_with_multiple_attachment))
148
+ .to eq(['attachment_key', 'another_attachment_key'])
149
+ end
150
+
151
+ it 'returns false for no attachment' do
152
+ expect(described_class.attachment_keys(records_without_attachment))
153
+ .to eq([])
154
+ end
155
+ end
156
+
157
+ describe '.transform_values!' do
158
+ let(:records_with_attachment) do
159
+ prefix = Dir.tmpdir
160
+ FileUtils.touch("#{prefix}/attachment.pdf")
161
+ [
162
+ {
163
+ 'normal_key' => 'normal_value1',
164
+ 'attachment_key' => nil,
165
+ },
166
+ {
167
+ 'normal_key' => 'normal_value2',
168
+ 'attachment_key' => File.new("#{prefix}/attachment.pdf"),
169
+ }
170
+ ]
171
+ end
172
+
173
+ it 'transforms values correctly' do
174
+ expect(File).to receive(:absolute_path).and_return('/an/absolute/path')
175
+ expected_output = [
176
+ {
177
+ 'normal_key' => 'normal_value1',
178
+ 'attachment_key' => nil,
179
+ },
180
+ {
181
+ 'normal_key' => 'normal_value2',
182
+ 'attachment_key' => '#an/absolute/path',
183
+ }
184
+ ]
185
+
186
+ input = records_with_attachment
187
+ described_class.transform_values!(input,['attachment_key'])
188
+ expect(input).to eq(expected_output)
189
+ end
190
+
191
+ it 'yields absolute path' do
192
+ expect(File).to receive(:absolute_path).and_return('/an/absolute/path')
193
+ input = records_with_attachment
194
+ expect do |blk|
195
+ described_class.transform_values!(input,['attachment_key'], &blk)
196
+ end.to yield_with_args('/an/absolute/path')
197
+ end
198
+ end
199
+
200
+ describe '.absolute_to_relative_path' do
201
+ let(:unix_path) { '/a/unix/path' }
202
+ let(:windows_path_backslash) { 'C:\a\backslash\path' }
203
+ let(:windows_path_forwardslash) { 'C:/a/forwardslash/path' }
204
+
205
+ it 'strips unix path correctly' do
206
+ expect(described_class.absolute_to_relative_path(unix_path,'')).
207
+ to eq('a/unix/path')
208
+ end
209
+
210
+ it 'strips windows path with backslash correctly' do
211
+ expect(described_class.absolute_to_relative_path(windows_path_backslash,'')).
212
+ to eq('a\backslash\path')
213
+ end
214
+
215
+ it 'strips windows path with forwardslash correctly' do
216
+ expect(described_class.absolute_to_relative_path(windows_path_forwardslash,'')).
217
+ to eq('a/forwardslash/path')
218
+ end
219
+ end
93
220
  end
@@ -16,7 +16,7 @@ describe Executrix::Http do
16
16
  Executrix::Http::Request.new(:get, 'test.host', '/', '', [])
17
17
  end
18
18
 
19
- it 'should return a response object' do
19
+ it 'returns a response object' do
20
20
  expected_body = 'correct result'
21
21
  stub_request(:post, 'https://test.host')
22
22
  .with(
@@ -140,16 +140,16 @@ describe Executrix::Http do
140
140
  </soapenv:Envelope>}
141
141
  end
142
142
 
143
- it 'should raise an error for faulty login' do
144
- Executrix::Http.should_receive(:process_http_request)
143
+ it 'raises an error for faulty login' do
144
+ expect(Executrix::Http).to receive(:process_http_request)
145
145
  .and_return(login_error)
146
146
  expect{ Executrix::Http.login('a','b','c', 'd') }
147
147
  .to raise_error(RuntimeError, login_error_message)
148
148
  end
149
149
 
150
- it 'should return hash for correct login' do
150
+ it 'returns hash for correct login' do
151
151
  [login_success, login_success_new].each do |login_response|
152
- Executrix::Http.should_receive(:process_http_request)
152
+ expect(Executrix::Http).to receive(:process_http_request)
153
153
  .and_return(login_response)
154
154
  result = Executrix::Http.login('a','b','c', 'd')
155
155
  expect(result).to be_a(Hash)
@@ -189,10 +189,10 @@ describe Executrix::Http do
189
189
  </jobInfo>}
190
190
  end
191
191
 
192
- it 'should return hash for creating job' do
193
- Executrix::Http.should_receive(:process_http_request)
192
+ it 'returns hash for creating job' do
193
+ expect(Executrix::Http).to receive(:process_http_request)
194
194
  .and_return(create_job_success)
195
- result = Executrix::Http.create_job('a','b','c','d', 'e')
195
+ result = Executrix::Http.create_job('a','b','c','d', 'e', 'f')
196
196
  expect(result).to be_a(Hash)
197
197
  expect(result).to have_key(:id)
198
198
  expect(result).to have_key(:operation)
@@ -218,8 +218,8 @@ describe Executrix::Http do
218
218
  }
219
219
  end
220
220
 
221
- it 'should return hash for adding batch' do
222
- Executrix::Http.should_receive(:process_http_request)
221
+ it 'returns hash for adding batch' do
222
+ expect(Executrix::Http).to receive(:process_http_request)
223
223
  .and_return(add_batch_success)
224
224
  result = Executrix::Http.add_batch(:post,'a','b','c','d')
225
225
  expect(result).to be_a(Hash)
@@ -258,8 +258,8 @@ describe Executrix::Http do
258
258
  </jobInfo>}
259
259
  end
260
260
 
261
- it 'should return hash for closing job' do
262
- Executrix::Http.should_receive(:process_http_request)
261
+ it 'returns hash for closing job' do
262
+ expect(Executrix::Http).to receive(:process_http_request)
263
263
  .and_return(close_job_success)
264
264
  result = Executrix::Http.close_job('a','b','c','d')
265
265
  expect(result).to be_a(Hash)
@@ -280,8 +280,8 @@ describe Executrix::Http do
280
280
  </error>}
281
281
  end
282
282
 
283
- it 'should raise an exception on faulty authorization' do
284
- Executrix::Http.should_receive(:process_http_request)
283
+ it 'raises an exception on faulty authorization' do
284
+ expect(Executrix::Http).to receive(:process_http_request)
285
285
  .and_return(invalid_session_id)
286
286
  expect{Executrix::Http.query_batch('a','b','c','d','e')}
287
287
  .to raise_error(RuntimeError, 'InvalidSessionId: Invalid session id')
@@ -295,8 +295,8 @@ describe Executrix::Http do
295
295
  </result-list>}
296
296
  end
297
297
 
298
- it 'should return hash including the result id' do
299
- Executrix::Http.should_receive(:process_http_request)
298
+ it 'returns hash including the result id' do
299
+ expect(Executrix::Http).to receive(:process_http_request)
300
300
  .and_return(batch_result_success)
301
301
  result = Executrix::Http.query_batch_result_id('a','b','c','d','e')
302
302
  expect(result).to be_a(Hash)
@@ -317,8 +317,8 @@ describe Executrix::Http do
317
317
  "003K001200KO82cIAD","King of the Hill"}
318
318
  end
319
319
 
320
- it 'should return array of arrays for data' do
321
- Executrix::Http.should_receive(:process_http_request)
320
+ it 'returns array of arrays for data' do
321
+ expect(Executrix::Http).to receive(:process_http_request)
322
322
  .and_return(batch_result_data_success)
323
323
  result = Executrix::Http.query_batch_result_data('a','b','c','d','e','f')
324
324
  expect(result).to eq([
@@ -326,8 +326,8 @@ describe Executrix::Http do
326
326
  {'Id' => '003M001200KO82cIAD', 'my_external_id__c' => 'K-015699'}])
327
327
  end
328
328
 
329
- it 'should return correct array with spaces' do
330
- Executrix::Http.should_receive(:process_http_request)
329
+ it 'returns correct array with spaces' do
330
+ expect(Executrix::Http).to receive(:process_http_request)
331
331
  .and_return(batch_result_data_with_spaces_success)
332
332
  result = Executrix::Http.query_batch_result_data('a','b','c','d','e','f')
333
333
  expect(result).to eq([
@@ -11,53 +11,96 @@ describe Executrix::Api do
11
11
  end
12
12
 
13
13
  {
14
- upsert: 3,
15
- update: 2,
16
- insert: 2,
17
- delete: 2,
18
- }.each do |method_name, num_of_params|
14
+ upsert: [nil,[{'no' => 'value'}], 'upsert_id'],
15
+ update: [nil,[{'no' => 'value'}]],
16
+ insert: [nil,[{'no' => 'value'}]],
17
+ delete: [nil,[{'no' => 'value'}]],
18
+ }.each do |method_name, values|
19
19
  describe "##{method_name}" do
20
- it 'should delegate to #start_job' do
21
- Executrix::Connection
22
- .should_receive(:connect)
20
+ it 'delegates to #start_job' do
21
+ expect(Executrix::Connection)
22
+ .to receive(:connect)
23
23
  .and_return(empty_connection)
24
24
  s = described_class.new(nil, nil)
25
- s.should_receive(:start_job)
26
- .with(method_name.to_s, *Array.new(num_of_params))
27
- s.send(method_name, *Array.new(num_of_params))
25
+ expect(s).to receive(:start_job)
26
+ .with(method_name.to_s, *values)
27
+ s.send(method_name, *values)
28
28
  end
29
29
 
30
- it 'should trigger correct workflow' do
31
- Executrix::Connection
32
- .should_receive(:connect)
30
+ it 'triggers correct workflow' do
31
+ expect(Executrix::Connection)
32
+ .to receive(:connect)
33
33
  .and_return(empty_connection)
34
34
  s = described_class.new(nil, nil)
35
- empty_connection.should_receive(:create_job).ordered
36
- empty_connection.should_receive(:add_batch).ordered
37
- empty_connection.should_receive(:close_job).ordered
38
- res = s.send(method_name, *Array.new(num_of_params))
35
+ expect(empty_connection).to receive(:create_job).ordered
36
+ expect(empty_connection).to receive(:add_batch).ordered
37
+ expect(empty_connection).to receive(:close_job).ordered
38
+ res = s.send(method_name, *values)
39
39
  expect(res).to be_a(Executrix::Batch)
40
40
  end
41
41
  end
42
42
  end
43
43
 
44
44
  describe '#query' do
45
- it 'should trigger correct workflow' do
46
- Executrix::Connection
47
- .should_receive(:connect)
45
+ it 'triggers correct workflow' do
46
+ expect(Executrix::Connection)
47
+ .to receive(:connect)
48
48
  .and_return(empty_connection)
49
- Executrix::Batch
50
- .should_receive(:new)
49
+ expect(Executrix::Batch)
50
+ .to receive(:new)
51
51
  .and_return(empty_batch)
52
52
 
53
53
  s = described_class.new(nil, nil)
54
54
  sobject_input = 'sobject_stub'
55
55
  query_input = 'query_stub'
56
- empty_connection.should_receive(:create_job).ordered
57
- empty_connection.should_receive(:add_query).ordered
58
- empty_connection.should_receive(:close_job).ordered
59
- empty_batch.should_receive(:final_status).ordered
56
+ expect(empty_connection).to receive(:create_job).ordered
57
+ expect(empty_connection).to receive(:add_query).ordered
58
+ expect(empty_connection).to receive(:close_job).ordered
59
+ expect(empty_batch).to receive(:final_status).ordered
60
60
  s.query(sobject_input, query_input)
61
61
  end
62
62
  end
63
+
64
+ context 'file upload' do
65
+ describe '#insert' do
66
+ prefix = Dir.tmpdir
67
+ FileUtils.touch("#{prefix}/attachment.pdf")
68
+ attachment_data = {
69
+ 'ParentId' => '00Kk0001908kqkDEAQ',
70
+ 'Name' => 'attachment.pdf',
71
+ 'Body' => File.new("#{prefix}/attachment.pdf")
72
+ }
73
+
74
+ {
75
+ upsert: [nil,[attachment_data.dup], 'upsert_id'],
76
+ update: [nil,[attachment_data.dup]],
77
+ insert: [nil,[attachment_data.dup]],
78
+ delete: [nil,[attachment_data.dup]],
79
+ }.each do |method_name, values|
80
+ describe "##{method_name}" do
81
+ it 'delegates to #start_job' do
82
+ expect(Executrix::Connection)
83
+ .to receive(:connect)
84
+ .and_return(empty_connection)
85
+ s = described_class.new(nil, nil)
86
+ expect(s).to receive(:start_job)
87
+ .with(method_name.to_s, *values)
88
+ s.send(method_name, *values)
89
+ end
90
+
91
+ it 'triggers correct workflow' do
92
+ expect(Executrix::Connection)
93
+ .to receive(:connect)
94
+ .and_return(empty_connection)
95
+ s = described_class.new(nil, nil)
96
+ expect(empty_connection).to receive(:create_job).ordered
97
+ expect(empty_connection).to receive(:add_file_upload_batch).ordered
98
+ expect(empty_connection).to receive(:close_job).ordered
99
+ res = s.send(method_name, *values)
100
+ expect(res).to be_a(Executrix::Batch)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
63
106
  end
metadata CHANGED
@@ -1,99 +1,114 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: executrix
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
5
- prerelease:
4
+ version: 1.2.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jorge Valdivia
9
9
  - Leif Gensert
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-16 00:00:00.000000000 Z
13
+ date: 2013-08-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
17
- requirement: !ruby/object:Gem::Requirement
18
- none: false
17
+ version_requirements: !ruby/object:Gem::Requirement
19
18
  requirements:
20
- - - ! '>='
19
+ - - '>='
21
20
  - !ruby/object:Gem::Version
22
21
  version: '0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
22
  none: false
23
+ requirement: !ruby/object:Gem::Requirement
27
24
  requirements:
28
- - - ! '>='
25
+ - - '>='
29
26
  - !ruby/object:Gem::Version
30
27
  version: '0'
28
+ none: false
29
+ prerelease: false
30
+ type: :runtime
31
31
  - !ruby/object:Gem::Dependency
32
32
  name: nori
33
- requirement: !ruby/object:Gem::Requirement
34
- none: false
33
+ version_requirements: !ruby/object:Gem::Requirement
35
34
  requirements:
36
35
  - - <
37
36
  - !ruby/object:Gem::Version
38
- version: '2.3'
39
- type: :runtime
40
- prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
37
+ version: '2.4'
42
38
  none: false
39
+ requirement: !ruby/object:Gem::Requirement
43
40
  requirements:
44
41
  - - <
45
42
  - !ruby/object:Gem::Version
46
- version: '2.3'
43
+ version: '2.4'
44
+ none: false
45
+ prerelease: false
46
+ type: :runtime
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: nokogiri
49
- requirement: !ruby/object:Gem::Requirement
50
- none: false
49
+ version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - <
53
52
  - !ruby/object:Gem::Version
54
53
  version: '1.7'
55
- type: :runtime
56
- prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
54
  none: false
55
+ requirement: !ruby/object:Gem::Requirement
59
56
  requirements:
60
57
  - - <
61
58
  - !ruby/object:Gem::Version
62
59
  version: '1.7'
60
+ none: false
61
+ prerelease: false
62
+ type: :runtime
63
63
  - !ruby/object:Gem::Dependency
64
- name: rspec
65
- requirement: !ruby/object:Gem::Requirement
64
+ name: rubyzip
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - <
68
+ - !ruby/object:Gem::Version
69
+ version: '1.1'
66
70
  none: false
71
+ requirement: !ruby/object:Gem::Requirement
67
72
  requirements:
68
73
  - - <
69
74
  - !ruby/object:Gem::Version
70
- version: '2.14'
71
- type: :development
75
+ version: '1.1'
76
+ none: false
72
77
  prerelease: false
78
+ type: :runtime
79
+ - !ruby/object:Gem::Dependency
80
+ name: rspec
73
81
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
82
  requirements:
76
83
  - - <
77
84
  - !ruby/object:Gem::Version
78
- version: '2.14'
79
- - !ruby/object:Gem::Dependency
80
- name: webmock
81
- requirement: !ruby/object:Gem::Requirement
85
+ version: '2.15'
82
86
  none: false
87
+ requirement: !ruby/object:Gem::Requirement
83
88
  requirements:
84
89
  - - <
85
90
  - !ruby/object:Gem::Version
86
- version: '1.12'
87
- type: :development
91
+ version: '2.15'
92
+ none: false
88
93
  prerelease: false
94
+ type: :development
95
+ - !ruby/object:Gem::Dependency
96
+ name: webmock
89
97
  version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - <
100
+ - !ruby/object:Gem::Version
101
+ version: '1.14'
90
102
  none: false
103
+ requirement: !ruby/object:Gem::Requirement
91
104
  requirements:
92
105
  - - <
93
106
  - !ruby/object:Gem::Version
94
- version: '1.12'
95
- description: This gem provides a super simple interface for the Salesforce Bulk API.
96
- It provides support for insert, update, upsert, delete, and query.
107
+ version: '1.14'
108
+ none: false
109
+ prerelease: false
110
+ type: :development
111
+ description: This gem provides a super simple interface for the Salesforce Bulk API. It provides support for insert, update, upsert, delete, and query.
97
112
  email:
98
113
  - jorge@valdivia.me
99
114
  - leif@propertybase.com
@@ -125,32 +140,32 @@ files:
125
140
  - spec/spec_helper.rb
126
141
  homepage: https://github.com/propertybase/executrix
127
142
  licenses: []
128
- post_install_message:
143
+ post_install_message:
129
144
  rdoc_options: []
130
145
  require_paths:
131
146
  - lib
132
147
  required_ruby_version: !ruby/object:Gem::Requirement
133
- none: false
134
148
  requirements:
135
- - - ! '>='
149
+ - - '>='
136
150
  - !ruby/object:Gem::Version
137
- version: '0'
138
151
  segments:
139
152
  - 0
140
- hash: -2027489645870901694
141
- required_rubygems_version: !ruby/object:Gem::Requirement
153
+ version: '0'
154
+ hash: 2
142
155
  none: false
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
157
  requirements:
144
- - - ! '>='
158
+ - - '>='
145
159
  - !ruby/object:Gem::Version
146
- version: '0'
147
160
  segments:
148
161
  - 0
149
- hash: -2027489645870901694
162
+ version: '0'
163
+ hash: 2
164
+ none: false
150
165
  requirements: []
151
166
  rubyforge_project: executrix
152
- rubygems_version: 1.8.23
153
- signing_key:
167
+ rubygems_version: 1.8.24
168
+ signing_key:
154
169
  specification_version: 3
155
170
  summary: Ruby support for the Salesforce Bulk API
156
171
  test_files: