bulkforce 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +19 -0
- data/Gemfile +14 -0
- data/Guardfile +8 -0
- data/LICENSE +9 -0
- data/README.md +211 -0
- data/Rakefile +6 -0
- data/bulkforce.gemspec +35 -0
- data/lib/bulkforce.rb +109 -0
- data/lib/bulkforce/batch.rb +61 -0
- data/lib/bulkforce/configuration.rb +42 -0
- data/lib/bulkforce/connection.rb +114 -0
- data/lib/bulkforce/connection_builder.rb +40 -0
- data/lib/bulkforce/helper.rb +77 -0
- data/lib/bulkforce/http.rb +272 -0
- data/lib/bulkforce/version.rb +3 -0
- data/spec/integration/delete_spec.rb +18 -0
- data/spec/integration/insert_spec.rb +22 -0
- data/spec/integration/query_spec.rb +25 -0
- data/spec/integration/update_spec.rb +18 -0
- data/spec/integration/upsert_spec.rb +22 -0
- data/spec/lib/bulkforce/batch_spec.rb +128 -0
- data/spec/lib/bulkforce/configuration_spec.rb +121 -0
- data/spec/lib/bulkforce/connection_builder_spec.rb +114 -0
- data/spec/lib/bulkforce/connection_spec.rb +99 -0
- data/spec/lib/bulkforce/helper_spec.rb +249 -0
- data/spec/lib/bulkforce/http_spec.rb +375 -0
- data/spec/lib/bulkforce_spec.rb +128 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/integration_helpers.rb +64 -0
- data/spec/support/shared_examples.rb +21 -0
- metadata +199 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Bulkforce::Connection do
|
5
|
+
let(:subject) do
|
6
|
+
described_class.new(
|
7
|
+
session_id: session_id,
|
8
|
+
instance: instance,
|
9
|
+
api_version: api_version,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:session_id) { "org_id!session_id" }
|
14
|
+
let(:instance) { "eu2" }
|
15
|
+
let(:api_version) { "33.0" }
|
16
|
+
|
17
|
+
{
|
18
|
+
login: 0,
|
19
|
+
create_job: 4,
|
20
|
+
close_job: 1,
|
21
|
+
query_batch: 2,
|
22
|
+
query_batch_result_id: 2,
|
23
|
+
query_batch_result_data: 3,
|
24
|
+
}.each do |method_name, num_of_params|
|
25
|
+
describe "##{method_name}" do
|
26
|
+
it "delegates correctly to Http class" do
|
27
|
+
allow(Bulkforce::Helper).to receive(:parse_csv).and_return([])
|
28
|
+
expect(Bulkforce::Http)
|
29
|
+
.to receive(method_name)
|
30
|
+
.and_return({})
|
31
|
+
subject.send(method_name, *Array.new(num_of_params))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#add_query" do
|
37
|
+
it "delegates correctly to Http class" do
|
38
|
+
expect(Bulkforce::Http).to receive(:add_batch)
|
39
|
+
.and_return({})
|
40
|
+
subject.add_query(nil, nil)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#org_id" do
|
45
|
+
let(:org_id) { "00D50000000IehZ" }
|
46
|
+
let(:session_id) { "#{org_id}!AQcAQH0dMHZfz972Szmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1E6LYUfiDUkWe6H34r1AAwOR8B8fLEz6n04NPGRrq0FM" }
|
47
|
+
|
48
|
+
it "returns correct OrgId" do
|
49
|
+
expect(subject.org_id).to eq(org_id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#add_batch" do
|
54
|
+
it "delegates correctly to underlying classes" do
|
55
|
+
expect(Bulkforce::Http).to receive(:add_batch)
|
56
|
+
.and_return({})
|
57
|
+
expect(Bulkforce::Helper).to receive(:records_to_csv)
|
58
|
+
.and_return("My,Awesome,CSV")
|
59
|
+
subject.add_batch(nil, [{"non_emtpy" => "records"}])
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns -1 for nil input" do
|
63
|
+
return_code = subject.add_batch(nil, nil)
|
64
|
+
expect(return_code).to eq(-1)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns -1 for empty input" do
|
68
|
+
return_code = subject.add_batch(nil, [])
|
69
|
+
expect(return_code).to eq(-1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#query_batch_result_id" do
|
74
|
+
let(:job_id) {"12345"}
|
75
|
+
let(:batch_id) {"67890"}
|
76
|
+
|
77
|
+
context "with a single page of results" do
|
78
|
+
let(:single_result) {{:result=>"M75200000001Vgt", :@xmlns=>"http://www.force.com/2009/06/asyncapi/dataload"}}
|
79
|
+
it "returns the result_id as a string" do
|
80
|
+
expect(Bulkforce::Http).to receive(:query_batch_result_id).
|
81
|
+
with(instance, session_id, job_id, batch_id, api_version).
|
82
|
+
and_return(single_result)
|
83
|
+
|
84
|
+
expect(subject.query_batch_result_id(job_id, batch_id)).to eq(single_result)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with an array of page of results" do
|
89
|
+
let(:multiple_result) {{:result=>["752M00000001Vgt", "752M00000001Vgy"], :@xmlns=>"http://www.force.com/2009/06/asyncapi/dataload"}}
|
90
|
+
it "returns the result_id as a string" do
|
91
|
+
expect(Bulkforce::Http).to receive(:query_batch_result_id).
|
92
|
+
with(instance, session_id, job_id, batch_id, api_version).
|
93
|
+
and_return(multiple_result)
|
94
|
+
|
95
|
+
expect(subject.query_batch_result_id(job_id, batch_id)).to eq(multiple_result)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Bulkforce::Helper do
|
5
|
+
describe ".records_to_csv" do
|
6
|
+
it "returns valid csv for single record" do
|
7
|
+
input = [
|
8
|
+
{"Title" => "Awesome Title", "Name" => "A name"},
|
9
|
+
]
|
10
|
+
|
11
|
+
expected_csv = "\"Title\",\"Name\"\n" \
|
12
|
+
"\"Awesome Title\",\"A name\"\n"
|
13
|
+
expect(described_class.records_to_csv(input)).to eq(expected_csv)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns valid csv for basic records" do
|
17
|
+
input = [
|
18
|
+
{"Title" => "Awesome Title", "Name" => "A name"},
|
19
|
+
{"Title" => "A second Title", "Name" => "A second name"},
|
20
|
+
]
|
21
|
+
|
22
|
+
expected_csv = "\"Title\",\"Name\"\n" \
|
23
|
+
"\"Awesome Title\",\"A name\"\n" \
|
24
|
+
"\"A second Title\",\"A second name\"\n"
|
25
|
+
expect(described_class.records_to_csv(input)).to eq(expected_csv)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns valid csv when first row misses a key" do
|
29
|
+
input = [
|
30
|
+
{"Title" => "Awesome Title", "Name" => "A name"},
|
31
|
+
{"Title" => "A second Title", "Name" => "A second name", "Something" => "Else"},
|
32
|
+
]
|
33
|
+
|
34
|
+
expected_csv = "\"Title\",\"Name\",\"Something\"\n" \
|
35
|
+
"\"Awesome Title\",\"A name\",\"\"\n" \
|
36
|
+
"\"A second Title\",\"A second name\",\"Else\"\n"
|
37
|
+
expect(described_class.records_to_csv(input)).to eq(expected_csv)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "correctly converts Array to Multi-Picklist" do
|
41
|
+
input = [
|
42
|
+
{"Title" => "Awesome Title", "Picklist" => ["Several", "Values"]},
|
43
|
+
{"Title" => "A second Title", "Picklist" => ["SingleValue"]},
|
44
|
+
]
|
45
|
+
|
46
|
+
expected_csv = "\"Title\",\"Picklist\"\n" \
|
47
|
+
"\"Awesome Title\",\"Several;Values\"\n" \
|
48
|
+
"\"A second Title\",\"SingleValue\"\n"
|
49
|
+
expect(described_class.records_to_csv(input)).to eq(expected_csv)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns valid csv when order of keys varies" do
|
53
|
+
input = [
|
54
|
+
{"Title" => "Awesome Title", "Name" => "A name"},
|
55
|
+
{"Name" => "A second name", "Title" => "A second Title"},
|
56
|
+
]
|
57
|
+
|
58
|
+
expected_csv = "\"Title\",\"Name\"\n" \
|
59
|
+
"\"Awesome Title\",\"A name\"\n" \
|
60
|
+
"\"A second Title\",\"A second name\"\n"
|
61
|
+
expect(described_class.records_to_csv(input)).to eq(expected_csv)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "correctly converts empty arrays, nil values and blank strings to #N/A" do
|
65
|
+
input = [
|
66
|
+
{"Title" => nil, "Picklist" => ["Several", "Values"]},
|
67
|
+
{"Title" => "", "Picklist" => []},
|
68
|
+
]
|
69
|
+
|
70
|
+
expected_csv = "\"Title\",\"Picklist\"\n" \
|
71
|
+
"\"#N/A\",\"Several;Values\"\n" \
|
72
|
+
"\"#N/A\",\"#N/A\"\n"
|
73
|
+
expect(described_class.records_to_csv(input)).to eq(expected_csv)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe ".fetch_instance_from_server_url" do
|
78
|
+
let(:basic_api_server_url) {
|
79
|
+
"https://cs7-api.salesforce.com/services/Soap/u/28.0/00CS00000095Y5b"
|
80
|
+
}
|
81
|
+
|
82
|
+
let(:basic_server_url) {
|
83
|
+
"https://eu1.salesforce.com/services/Soap/u/28.0/00EU00000096Y8c"
|
84
|
+
}
|
85
|
+
|
86
|
+
let(:named_server_url) {
|
87
|
+
"https://supercustomname.my.salesforce.com/services/Soap/u/28.0/00EH0000001jNQu"
|
88
|
+
}
|
89
|
+
|
90
|
+
it "returns correct instance for regular salesforce server url" do
|
91
|
+
expect(described_class.fetch_instance_from_server_url(basic_server_url))
|
92
|
+
.to eq("eu1")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "returns correct instance for api salesforce server url" do
|
96
|
+
expect(described_class.fetch_instance_from_server_url(basic_api_server_url))
|
97
|
+
.to eq("cs7")
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns correct instance for named salesforce server url" do
|
101
|
+
expect(described_class.fetch_instance_from_server_url(named_server_url))
|
102
|
+
.to eq("supercustomname.my")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe ".attachment_keys" do
|
107
|
+
let(:records_with_attachment) do
|
108
|
+
prefix = Dir.tmpdir
|
109
|
+
FileUtils.touch("#{prefix}/attachment.pdf")
|
110
|
+
[
|
111
|
+
{
|
112
|
+
"normal_key" => "normal_value1",
|
113
|
+
"attachment_key" => nil,
|
114
|
+
},
|
115
|
+
{
|
116
|
+
"normal_key" => "normal_value2",
|
117
|
+
"attachment_key" => File.new("#{prefix}/attachment.pdf"),
|
118
|
+
}
|
119
|
+
]
|
120
|
+
end
|
121
|
+
|
122
|
+
let(:records_with_multiple_attachment) do
|
123
|
+
prefix = Dir.tmpdir
|
124
|
+
FileUtils.touch("#{prefix}/attachment1.pdf")
|
125
|
+
FileUtils.touch("#{prefix}/attachment2.pdf")
|
126
|
+
FileUtils.touch("#{prefix}/attachment3.pdf")
|
127
|
+
[
|
128
|
+
{
|
129
|
+
"normal_key" => "normal_value1",
|
130
|
+
"attachment_key" => File.new("#{prefix}/attachment1.pdf"),
|
131
|
+
"another_attachment_key" => File.new("#{prefix}/attachment2.pdf"),
|
132
|
+
},
|
133
|
+
{
|
134
|
+
"normal_key" => "normal_value2",
|
135
|
+
"attachment_key" => File.new("#{prefix}/attachment3.pdf"),
|
136
|
+
}
|
137
|
+
]
|
138
|
+
end
|
139
|
+
|
140
|
+
let(:records_without_attachment) do
|
141
|
+
[
|
142
|
+
{
|
143
|
+
"normal_key" => "normal_value1",
|
144
|
+
"another_normal_key" => "another_normal_value1",
|
145
|
+
},
|
146
|
+
{
|
147
|
+
"normal_key" => "normal_value2",
|
148
|
+
"another_normal_key" => "another_normal_value2",
|
149
|
+
}
|
150
|
+
]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "returns correct keys for single attachment key" do
|
154
|
+
expect(described_class.attachment_keys(records_with_attachment))
|
155
|
+
.to eq(["attachment_key"])
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns correct keys for multiple attachment keys" do
|
159
|
+
expect(described_class.attachment_keys(records_with_multiple_attachment))
|
160
|
+
.to eq(["attachment_key", "another_attachment_key"])
|
161
|
+
end
|
162
|
+
|
163
|
+
it "returns false for no attachment" do
|
164
|
+
expect(described_class.attachment_keys(records_without_attachment))
|
165
|
+
.to eq([])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe ".transform_values!" do
|
170
|
+
let(:records_with_attachment) do
|
171
|
+
prefix = Dir.tmpdir
|
172
|
+
FileUtils.touch("#{prefix}/attachment.pdf")
|
173
|
+
[
|
174
|
+
{
|
175
|
+
"normal_key" => "normal_value1",
|
176
|
+
"attachment_key" => nil,
|
177
|
+
},
|
178
|
+
{
|
179
|
+
"normal_key" => "normal_value2",
|
180
|
+
"attachment_key" => File.new("#{prefix}/attachment.pdf"),
|
181
|
+
}
|
182
|
+
]
|
183
|
+
end
|
184
|
+
|
185
|
+
it "transforms values correctly" do
|
186
|
+
expect(File).to receive(:absolute_path).and_return("/an/absolute/path")
|
187
|
+
expected_output = [
|
188
|
+
{
|
189
|
+
"normal_key" => "normal_value1",
|
190
|
+
"attachment_key" => nil,
|
191
|
+
},
|
192
|
+
{
|
193
|
+
"normal_key" => "normal_value2",
|
194
|
+
"attachment_key" => "#an/absolute/path",
|
195
|
+
}
|
196
|
+
]
|
197
|
+
|
198
|
+
input = records_with_attachment
|
199
|
+
described_class.transform_values!(input,["attachment_key"])
|
200
|
+
expect(input).to eq(expected_output)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "yields absolute path" do
|
204
|
+
expect(File).to receive(:absolute_path).and_return("/an/absolute/path")
|
205
|
+
input = records_with_attachment
|
206
|
+
expect do |blk|
|
207
|
+
described_class.transform_values!(input,["attachment_key"], &blk)
|
208
|
+
end.to yield_with_args("/an/absolute/path")
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe ".absolute_to_relative_path" do
|
213
|
+
let(:unix_path) { "/a/unix/path" }
|
214
|
+
let(:windows_path_backslash) { "C:\\a\\backslash\\path" }
|
215
|
+
let(:windows_path_forwardslash) { "C:/a/forwardslash/path" }
|
216
|
+
|
217
|
+
it "strips unix path correctly" do
|
218
|
+
expect(described_class.absolute_to_relative_path(unix_path,"")).
|
219
|
+
to eq("a/unix/path")
|
220
|
+
end
|
221
|
+
|
222
|
+
it "strips windows path with backslash correctly" do
|
223
|
+
expect(described_class.absolute_to_relative_path(windows_path_backslash,"")).
|
224
|
+
to eq("a\\backslash\\path")
|
225
|
+
end
|
226
|
+
|
227
|
+
it "strips windows path with forwardslash correctly" do
|
228
|
+
expect(described_class.absolute_to_relative_path(windows_path_forwardslash,"")).
|
229
|
+
to eq("a/forwardslash/path")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe ".parse_csv" do
|
234
|
+
let(:csv_string) {
|
235
|
+
"Id,my_external_id__c\n" \
|
236
|
+
"003M000057GH39aIAD,K-00J799\n" \
|
237
|
+
"003M001200KO82cIAD,K-015699"
|
238
|
+
}
|
239
|
+
let(:expected_result) {
|
240
|
+
[
|
241
|
+
{ "Id" => "003M000057GH39aIAD", "my_external_id__c" => "K-00J799" },
|
242
|
+
{ "Id" => "003M001200KO82cIAD", "my_external_id__c" => "K-015699" },
|
243
|
+
]
|
244
|
+
}
|
245
|
+
it "correctly transforms csv string" do
|
246
|
+
expect(described_class.parse_csv(csv_string)).to eq(expected_result)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,375 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Bulkforce::Http do
|
5
|
+
describe "#process_http_request" do
|
6
|
+
let(:post_request) do
|
7
|
+
Bulkforce::Http::Request.new(
|
8
|
+
:post,
|
9
|
+
"test.host",
|
10
|
+
"/",
|
11
|
+
"post body",
|
12
|
+
{"X-SFDC-Session" => "super_secret"})
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:get_request) do
|
16
|
+
Bulkforce::Http::Request.new(:get, "test.host", "/", "", [])
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns a response object" do
|
20
|
+
expected_body = "correct result"
|
21
|
+
stub_request(:post, "https://test.host")
|
22
|
+
.with(
|
23
|
+
body: post_request.body,
|
24
|
+
headers: post_request.headers)
|
25
|
+
.to_return(:body => expected_body)
|
26
|
+
res = Bulkforce::Http.process_http_request(post_request)
|
27
|
+
expect(res).to eq(expected_body)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#login" do
|
32
|
+
let(:login_error_message) do
|
33
|
+
"INVALID_LOGIN: Invalid username, password, security token;" \
|
34
|
+
"or user locked out."
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:sf_instance) {"cs7"}
|
38
|
+
|
39
|
+
let(:login_success) do
|
40
|
+
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
41
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
42
|
+
<soapenv:Body>
|
43
|
+
<loginResponse>
|
44
|
+
<result>
|
45
|
+
<metadataServerUrl>https://#{sf_instance}-api.salesforce.com/services/Soap/m/27.0/00EU00000095Y5b</metadataServerUrl>
|
46
|
+
<passwordExpired>false</passwordExpired>
|
47
|
+
<sandbox>true</sandbox>
|
48
|
+
<serverUrl>https://#{sf_instance}-api.salesforce.com/services/Soap/u/27.0/00EU00000095Y5b</serverUrl>
|
49
|
+
<sessionId>00DM00000099X9a!AQ7AQJ9BjrYfF2h_G9_VERsCRVjTMAPaWjz2zuqqRBduYvKCmHexVbKdtxFMrgTnV9Xi.M80AhIkjnwuEVxI00ChG09._Q_X</sessionId>
|
50
|
+
<userId>005K001100243YPIAY</userId>
|
51
|
+
<userInfo>
|
52
|
+
<accessibilityMode>false</accessibilityMode>
|
53
|
+
<currencySymbol xsi:nil="true"/>
|
54
|
+
<orgAttachmentFileSizeLimit>2621440</orgAttachmentFileSizeLimit>
|
55
|
+
<orgDefaultCurrencyIsoCode xsi:nil="true"/>
|
56
|
+
<orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
|
57
|
+
<orgHasPersonAccounts>false</orgHasPersonAccounts>
|
58
|
+
<organizationId>00ID0000OrgFoo</organizationId>
|
59
|
+
<organizationMultiCurrency>true</organizationMultiCurrency>
|
60
|
+
<organizationName>Hinz & Kunz</organizationName>
|
61
|
+
<profileId>00eA0000000nJEY</profileId>
|
62
|
+
<roleId xsi:nil="true"/>
|
63
|
+
<sessionSecondsValid>3600</sessionSecondsValid>
|
64
|
+
<userDefaultCurrencyIsoCode>EUR</userDefaultCurrencyIsoCode>
|
65
|
+
<userEmail>theadmin@example.com</userEmail>
|
66
|
+
<userFullName>John Doe</userFullName>
|
67
|
+
<userId>005D0000002b3SPIAY</userId>
|
68
|
+
<userLanguage>de</userLanguage>
|
69
|
+
<userLocale>de_DE_EURO</userLocale>
|
70
|
+
<userName>theadmin@exammple.com.euvconfig</userName>
|
71
|
+
<userTimeZone>Europe/Berlin</userTimeZone>
|
72
|
+
<userType>Standard</userType>
|
73
|
+
<userUiSkin>Theme42</userUiSkin>
|
74
|
+
</userInfo>
|
75
|
+
</result>
|
76
|
+
</loginResponse>
|
77
|
+
</soapenv:Body>
|
78
|
+
</soapenv:Envelope>}
|
79
|
+
end
|
80
|
+
|
81
|
+
let(:login_success_new) do
|
82
|
+
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
83
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
84
|
+
<soapenv:Body>
|
85
|
+
<loginResponse>
|
86
|
+
<result>
|
87
|
+
<metadataServerUrl>https://#{sf_instance}.salesforce.com/services/Soap/m/27.0/00EU00000095Y5b</metadataServerUrl>
|
88
|
+
<passwordExpired>false</passwordExpired>
|
89
|
+
<sandbox>true</sandbox>
|
90
|
+
<serverUrl>https://#{sf_instance}.salesforce.com/services/Soap/u/27.0/00EU00000095Y5b</serverUrl>
|
91
|
+
<sessionId>00DM00000099X9a!AQ7AQJ9BjrYfF2h_G9_VERsCRVjTMAPaWjz2zuqqRBduYvKCmHexVbKdtxFMrgTnV9Xi.M80AhIkjnwuEVxI00ChG09._Q_X</sessionId>
|
92
|
+
<userId>005K001100243YPIAY</userId>
|
93
|
+
<userInfo>
|
94
|
+
<accessibilityMode>false</accessibilityMode>
|
95
|
+
<currencySymbol xsi:nil="true"/>
|
96
|
+
<orgAttachmentFileSizeLimit>2621440</orgAttachmentFileSizeLimit>
|
97
|
+
<orgDefaultCurrencyIsoCode xsi:nil="true"/>
|
98
|
+
<orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
|
99
|
+
<orgHasPersonAccounts>false</orgHasPersonAccounts>
|
100
|
+
<organizationId>00ID0000OrgFoo</organizationId>
|
101
|
+
<organizationMultiCurrency>true</organizationMultiCurrency>
|
102
|
+
<organizationName>Hinz & Kunz</organizationName>
|
103
|
+
<profileId>00eA0000000nJEY</profileId>
|
104
|
+
<roleId xsi:nil="true"/>
|
105
|
+
<sessionSecondsValid>3600</sessionSecondsValid>
|
106
|
+
<userDefaultCurrencyIsoCode>EUR</userDefaultCurrencyIsoCode>
|
107
|
+
<userEmail>theadmin@example.com</userEmail>
|
108
|
+
<userFullName>John Doe</userFullName>
|
109
|
+
<userId>005D0000002b3SPIAY</userId>
|
110
|
+
<userLanguage>de</userLanguage>
|
111
|
+
<userLocale>de_DE_EURO</userLocale>
|
112
|
+
<userName>theadmin@exammple.com.euvconfig</userName>
|
113
|
+
<userTimeZone>Europe/Berlin</userTimeZone>
|
114
|
+
<userType>Standard</userType>
|
115
|
+
<userUiSkin>Theme42</userUiSkin>
|
116
|
+
</userInfo>
|
117
|
+
</result>
|
118
|
+
</loginResponse>
|
119
|
+
</soapenv:Body>
|
120
|
+
</soapenv:Envelope>}
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:login_error) do
|
124
|
+
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
125
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
126
|
+
xmlns:sf="urn:fault.partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
127
|
+
<soapenv:Body>
|
128
|
+
<soapenv:Fault>
|
129
|
+
<faultcode>INVALID_LOGIN</faultcode>
|
130
|
+
<faultstring>#{login_error_message}</faultstring>
|
131
|
+
<detail>
|
132
|
+
<sf:LoginFault xsi:type="sf:LoginFault">
|
133
|
+
<sf:exceptionCode>INVALID_LOGIN</sf:exceptionCode>
|
134
|
+
<sf:exceptionMessage>Invalid username, password, │Your bundle is complete!security token; or user locked out.
|
135
|
+
</sf:exceptionMessage>
|
136
|
+
</sf:LoginFault>
|
137
|
+
</detail>
|
138
|
+
</soapenv:Fault>
|
139
|
+
</soapenv:Body>
|
140
|
+
</soapenv:Envelope>}
|
141
|
+
end
|
142
|
+
|
143
|
+
it "raises an error for faulty login" do
|
144
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
145
|
+
.and_return(login_error)
|
146
|
+
expect{ Bulkforce::Http.login("a","b","c", "d") }
|
147
|
+
.to raise_error(RuntimeError, login_error_message)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "returns hash for correct login" do
|
151
|
+
[login_success, login_success_new].each do |login_response|
|
152
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
153
|
+
.and_return(login_response)
|
154
|
+
result = Bulkforce::Http.login("a","b","c", "d")
|
155
|
+
expect(result).to be_a(Hash)
|
156
|
+
expect(result).to have_key(:session_id)
|
157
|
+
expect(result).to have_key(:server_url)
|
158
|
+
expect(result[:instance]).to eq(sf_instance)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "#oauth_login" do
|
164
|
+
let(:login_error) do
|
165
|
+
%Q{<OAuth>
|
166
|
+
<error_description>invalid client credentials</error_description>
|
167
|
+
<error>invalid_client</error>
|
168
|
+
</OAuth>}
|
169
|
+
end
|
170
|
+
|
171
|
+
let(:login_error_message) { "invalid_client: invalid client credentials" }
|
172
|
+
|
173
|
+
let(:sf_instance) {"eu2"}
|
174
|
+
|
175
|
+
let(:login_success) do
|
176
|
+
%Q{<OAuth>
|
177
|
+
<id>https://login.salesforce.com/id/00Db0000000bHH3EAM/005b0000000K44BAAS</id>
|
178
|
+
<issued_at>1435621555861</issued_at>
|
179
|
+
<scope>full</scope>
|
180
|
+
<instance_url>https://eu2.salesforce.com</instance_url>
|
181
|
+
<token_type>Bearer</token_type>
|
182
|
+
<signature>mbfZerj8DRVXVF9WD28kM/p8j6d74fE29RRGleTxELE=</signature>
|
183
|
+
<access_token>00Db0000000bHH3!AREAQFjvfelEv7D6TM3QqoqhWAJ52sUPSw.yMuIJ0x4NExqxey7xwvezoTG6DgUypEp8.FW_l6LdyK.viB4kxZY.Tn63oRP5</access_token>
|
184
|
+
</OAuth>}
|
185
|
+
end
|
186
|
+
|
187
|
+
it "raises an error for faulty login" do
|
188
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
189
|
+
.and_return(login_error)
|
190
|
+
expect{ Bulkforce::Http.oauth_login("a","b","c", "d") }
|
191
|
+
.to raise_error(RuntimeError, login_error_message)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "returns hash for correct login" do
|
195
|
+
expect(Bulkforce::Http).to receive(:process_http_request).and_return(login_success)
|
196
|
+
result = Bulkforce::Http.oauth_login("a","b","c", "d")
|
197
|
+
expect(result).to be_a(Hash)
|
198
|
+
expect(result).to have_key(:session_id)
|
199
|
+
expect(result).to have_key(:server_url)
|
200
|
+
expect(result[:instance]).to eq(sf_instance)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "#create_job" do
|
205
|
+
let(:create_job_success) do
|
206
|
+
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
207
|
+
<jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
|
208
|
+
<id>750D0000000002lIAA</id>
|
209
|
+
<operation>upsert</operation>
|
210
|
+
<object>Contact</object>
|
211
|
+
<createdById>005D0000001ALVFIA4</createdById>
|
212
|
+
<createdDate>2013-04-10T15:52:02.000Z</createdDate>
|
213
|
+
<systemModstamp>2013-04-10T15:52:02.000Z</systemModstamp>
|
214
|
+
<state>Open</state>
|
215
|
+
<externalIdFieldName>my_external_field__c</externalIdFieldName>
|
216
|
+
<concurrencyMode>Parallel</concurrencyMode>
|
217
|
+
<contentType>CSV</contentType>
|
218
|
+
<numberBatchesQueued>0</numberBatchesQueued>
|
219
|
+
<numberBatchesInProgress>0</numberBatchesInProgress>
|
220
|
+
<numberBatchesCompleted>0</numberBatchesCompleted>
|
221
|
+
<numberBatchesFailed>0</numberBatchesFailed>
|
222
|
+
<numberBatchesTotal>0</numberBatchesTotal>
|
223
|
+
<numberRecordsProcessed>0</numberRecordsProcessed>
|
224
|
+
<numberRetries>0</numberRetries>
|
225
|
+
<apiVersion>27.0</apiVersion>
|
226
|
+
<numberRecordsFailed>0</numberRecordsFailed>
|
227
|
+
<totalProcessingTime>0</totalProcessingTime>
|
228
|
+
<apiActiveProcessingTime>0</apiActiveProcessingTime>
|
229
|
+
<apexProcessingTime>0</apexProcessingTime>
|
230
|
+
</jobInfo>}
|
231
|
+
end
|
232
|
+
|
233
|
+
it "returns hash for creating job" do
|
234
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
235
|
+
.and_return(create_job_success)
|
236
|
+
result = Bulkforce::Http.create_job("a","b","c","d", "e", "f")
|
237
|
+
expect(result).to be_a(Hash)
|
238
|
+
expect(result).to have_key(:id)
|
239
|
+
expect(result).to have_key(:operation)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#add_batch" do
|
244
|
+
let(:add_batch_success) do
|
245
|
+
%Q{
|
246
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
247
|
+
<batchInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
|
248
|
+
<id>750M0000000B1Z6IAL</id>
|
249
|
+
<jobId>751K00000009x71IAA</jobId>
|
250
|
+
<state>Queued</state>
|
251
|
+
<createdDate>2013-04-10T15:53:46.000Z</createdDate>
|
252
|
+
<systemModstamp>2013-04-10T15:53:46.000Z</systemModstamp>
|
253
|
+
<numberRecordsProcessed>0</numberRecordsProcessed>
|
254
|
+
<numberRecordsFailed>0</numberRecordsFailed>
|
255
|
+
<totalProcessingTime>0</totalProcessingTime>
|
256
|
+
<apiActiveProcessingTime>0</apiActiveProcessingTime>
|
257
|
+
<apexProcessingTime>0</apexProcessingTime>
|
258
|
+
</batchInfo>
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
it "returns hash for adding batch" do
|
263
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
264
|
+
.and_return(add_batch_success)
|
265
|
+
result = Bulkforce::Http.add_batch(:post,"a","b","c","d")
|
266
|
+
expect(result).to be_a(Hash)
|
267
|
+
expect(result).to have_key(:id)
|
268
|
+
expect(result).to have_key(:job_id)
|
269
|
+
expect(result).to have_key(:state)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
describe "#close_job" do
|
274
|
+
let(:close_job_success) do
|
275
|
+
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
276
|
+
<jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
|
277
|
+
<id>750D0000000002jIAA</id>
|
278
|
+
<operation>upsert</operation>
|
279
|
+
<object>Contact</object>
|
280
|
+
<createdById>005D0000002b3SPIAY</createdById>
|
281
|
+
<createdDate>2013-04-10T16:27:56.000Z</createdDate>
|
282
|
+
<systemModstamp>2013-04-10T16:27:56.000Z</systemModstamp>
|
283
|
+
<state>Closed</state>
|
284
|
+
<externalIdFieldName>my_external_id__c</externalIdFieldName>
|
285
|
+
<concurrencyMode>Parallel</concurrencyMode>
|
286
|
+
<contentType>CSV</contentType>
|
287
|
+
<numberBatchesQueued>0</numberBatchesQueued>
|
288
|
+
<numberBatchesInProgress>0</numberBatchesInProgress>
|
289
|
+
<numberBatchesCompleted>1</numberBatchesCompleted>
|
290
|
+
<numberBatchesFailed>0</numberBatchesFailed>
|
291
|
+
<numberBatchesTotal>1</numberBatchesTotal>
|
292
|
+
<numberRecordsProcessed>4</numberRecordsProcessed>
|
293
|
+
<numberRetries>0</numberRetries>
|
294
|
+
<apiVersion>27.0</apiVersion>
|
295
|
+
<numberRecordsFailed>0</numberRecordsFailed>
|
296
|
+
<totalProcessingTime>838</totalProcessingTime>
|
297
|
+
<apiActiveProcessingTime>355</apiActiveProcessingTime>
|
298
|
+
<apexProcessingTime>253</apexProcessingTime>
|
299
|
+
</jobInfo>}
|
300
|
+
end
|
301
|
+
|
302
|
+
it "returns hash for closing job" do
|
303
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
304
|
+
.and_return(close_job_success)
|
305
|
+
result = Bulkforce::Http.close_job("a","b","c","d")
|
306
|
+
expect(result).to be_a(Hash)
|
307
|
+
expect(result).to have_key(:id)
|
308
|
+
expect(result).to have_key(:object)
|
309
|
+
expect(result).to have_key(:operation)
|
310
|
+
expect(result).to have_key(:state)
|
311
|
+
expect(result).to have_key(:content_type)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe "#add_batch" do
|
316
|
+
let(:invalid_session_id) do
|
317
|
+
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
318
|
+
<error xmlns="http://www.force.com/2009/06/asyncapi/dataload">
|
319
|
+
<exceptionCode>InvalidSessionId</exceptionCode>
|
320
|
+
<exceptionMessage>Invalid session id</exceptionMessage>
|
321
|
+
</error>}
|
322
|
+
end
|
323
|
+
|
324
|
+
it "raises an exception on faulty authorization" do
|
325
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
326
|
+
.and_return(invalid_session_id)
|
327
|
+
expect{Bulkforce::Http.query_batch("a","b","c","d","e")}
|
328
|
+
.to raise_error(RuntimeError, "InvalidSessionId: Invalid session id")
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "#query_batch_result_id" do
|
333
|
+
let(:batch_result_success) do
|
334
|
+
%Q{<result-list xmlns="http://www.force.com/2009/06/asyncapi/dataload">
|
335
|
+
<result>750D0000002jIAA</result>
|
336
|
+
</result-list>}
|
337
|
+
end
|
338
|
+
|
339
|
+
it "returns hash including the result id" do
|
340
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
341
|
+
.and_return(batch_result_success)
|
342
|
+
result = Bulkforce::Http.query_batch_result_id("a","b","c","d","e")
|
343
|
+
expect(result).to be_a(Hash)
|
344
|
+
expect(result).to have_key(:result)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "#query_batch_result_data" do
|
349
|
+
let(:batch_result_data_success) do
|
350
|
+
%Q{"Id","my_external_id__c"
|
351
|
+
"003M000057GH39aIAD","K-00J799"
|
352
|
+
"003M001200KO82cIAD","K-015699"}
|
353
|
+
end
|
354
|
+
|
355
|
+
let(:batch_result_data_with_spaces_success) do
|
356
|
+
%Q{"Id","Name"
|
357
|
+
"003K000057GH39aIAD","Master of Disaster"
|
358
|
+
"003K001200KO82cIAD","King of the Hill"}
|
359
|
+
end
|
360
|
+
|
361
|
+
it "returns a correctly formatted CSV string" do
|
362
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
363
|
+
.and_return(batch_result_data_success)
|
364
|
+
result = Bulkforce::Http.query_batch_result_data("a","b","c","d","e","f")
|
365
|
+
expect(result).to eq(batch_result_data_success.gsub(/^\s+/, ""))
|
366
|
+
end
|
367
|
+
|
368
|
+
it "returns a correctly formatted CSV string including spaces" do
|
369
|
+
expect(Bulkforce::Http).to receive(:process_http_request)
|
370
|
+
.and_return(batch_result_data_with_spaces_success)
|
371
|
+
result = Bulkforce::Http.query_batch_result_data("a","b","c","d","e","f")
|
372
|
+
expect(result).to eq(batch_result_data_with_spaces_success.gsub(/^\s+/, ""))
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|