restforce 2.5.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +56 -0
- data/.rubocop.yml +27 -14
- data/.rubocop_todo.yml +128 -81
- data/CHANGELOG.md +37 -3
- data/CONTRIBUTING.md +3 -3
- data/Gemfile +4 -2
- data/Guardfile +3 -1
- data/LICENSE +1 -1
- data/README.md +120 -19
- data/Rakefile +2 -1
- data/lib/restforce.rb +23 -1
- data/lib/restforce/abstract_client.rb +3 -0
- data/lib/restforce/attachment.rb +3 -0
- data/lib/restforce/client.rb +2 -0
- data/lib/restforce/collection.rb +3 -1
- data/lib/restforce/concerns/api.rb +20 -14
- data/lib/restforce/concerns/authentication.rb +2 -0
- data/lib/restforce/concerns/base.rb +2 -0
- data/lib/restforce/concerns/batch_api.rb +87 -0
- data/lib/restforce/concerns/caching.rb +4 -2
- data/lib/restforce/concerns/canvas.rb +3 -0
- data/lib/restforce/concerns/connection.rb +26 -20
- data/lib/restforce/concerns/picklists.rb +9 -6
- data/lib/restforce/concerns/streaming.rb +60 -1
- data/lib/restforce/concerns/verbs.rb +3 -1
- data/lib/restforce/config.rb +4 -1
- data/lib/restforce/data/client.rb +2 -0
- data/lib/restforce/document.rb +3 -0
- data/lib/restforce/mash.rb +2 -0
- data/lib/restforce/middleware.rb +2 -0
- data/lib/restforce/middleware/authentication.rb +8 -6
- data/lib/restforce/middleware/authentication/password.rb +2 -0
- data/lib/restforce/middleware/authentication/token.rb +2 -0
- data/lib/restforce/middleware/authorization.rb +3 -1
- data/lib/restforce/middleware/caching.rb +3 -1
- data/lib/restforce/middleware/custom_headers.rb +2 -0
- data/lib/restforce/middleware/gzip.rb +5 -3
- data/lib/restforce/middleware/instance_url.rb +7 -3
- data/lib/restforce/middleware/logger.rb +2 -0
- data/lib/restforce/middleware/mashify.rb +2 -0
- data/lib/restforce/middleware/multipart.rb +8 -4
- data/lib/restforce/middleware/raise_error.rb +26 -8
- data/lib/restforce/patches/parts.rb +2 -0
- data/lib/restforce/signed_request.rb +3 -0
- data/lib/restforce/sobject.rb +3 -0
- data/lib/restforce/tooling/client.rb +5 -3
- data/lib/restforce/upload_io.rb +2 -0
- data/lib/restforce/version.rb +3 -1
- data/restforce.gemspec +19 -12
- data/spec/fixtures/sobject/sobject_describe_success_response.json +48 -1
- data/spec/integration/abstract_client_spec.rb +51 -7
- data/spec/integration/data/client_spec.rb +24 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/support/client_integration.rb +2 -0
- data/spec/support/concerns.rb +2 -0
- data/spec/support/event_machine.rb +2 -0
- data/spec/support/fixture_helpers.rb +4 -2
- data/spec/support/matchers.rb +2 -0
- data/spec/support/middleware.rb +3 -1
- data/spec/support/mock_cache.rb +4 -2
- data/spec/unit/abstract_client_spec.rb +2 -0
- data/spec/unit/attachment_spec.rb +2 -0
- data/spec/unit/collection_spec.rb +5 -3
- data/spec/unit/concerns/api_spec.rb +40 -11
- data/spec/unit/concerns/authentication_spec.rb +4 -2
- data/spec/unit/concerns/base_spec.rb +2 -0
- data/spec/unit/concerns/batch_api_spec.rb +107 -0
- data/spec/unit/concerns/caching_spec.rb +2 -0
- data/spec/unit/concerns/canvas_spec.rb +3 -1
- data/spec/unit/concerns/connection_spec.rb +5 -3
- data/spec/unit/concerns/streaming_spec.rb +115 -1
- data/spec/unit/config_spec.rb +10 -8
- data/spec/unit/data/client_spec.rb +2 -0
- data/spec/unit/document_spec.rb +2 -0
- data/spec/unit/mash_spec.rb +3 -1
- data/spec/unit/middleware/authentication/password_spec.rb +2 -0
- data/spec/unit/middleware/authentication/token_spec.rb +2 -0
- data/spec/unit/middleware/authentication_spec.rb +3 -1
- data/spec/unit/middleware/authorization_spec.rb +2 -0
- data/spec/unit/middleware/custom_headers_spec.rb +3 -1
- data/spec/unit/middleware/gzip_spec.rb +4 -2
- data/spec/unit/middleware/instance_url_spec.rb +2 -0
- data/spec/unit/middleware/logger_spec.rb +2 -0
- data/spec/unit/middleware/mashify_spec.rb +3 -1
- data/spec/unit/middleware/raise_error_spec.rb +34 -11
- data/spec/unit/signed_request_spec.rb +2 -0
- data/spec/unit/sobject_spec.rb +5 -3
- data/spec/unit/tooling/client_spec.rb +2 -0
- metadata +38 -20
- data/.travis.yml +0 -16
- data/Gemfile.travis +0 -8
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
shared_examples_for Restforce::Data::Client do
|
@@ -8,15 +10,20 @@ shared_examples_for Restforce::Data::Client do
|
|
8
10
|
context 'when given a picklist field' do
|
9
11
|
subject { client.picklist_values('Account', 'Picklist_Field') }
|
10
12
|
it { should be_an Array }
|
11
|
-
its(:length) { should eq
|
12
|
-
it {
|
13
|
+
its(:length) { should eq 10 }
|
14
|
+
it {
|
15
|
+
should include_picklist_values %w[
|
16
|
+
one two three control_four control_five
|
17
|
+
control_six control_seven control_eight control_nine control_ten
|
18
|
+
]
|
19
|
+
}
|
13
20
|
end
|
14
21
|
|
15
22
|
context 'when given a multipicklist field' do
|
16
23
|
subject { client.picklist_values('Account', 'Picklist_Multiselect_Field') }
|
17
24
|
it { should be_an Array }
|
18
25
|
its(:length) { should eq 3 }
|
19
|
-
it { should include_picklist_values %w
|
26
|
+
it { should include_picklist_values %w[four five six] }
|
20
27
|
end
|
21
28
|
|
22
29
|
describe 'dependent picklists' do
|
@@ -29,10 +36,22 @@ shared_examples_for Restforce::Data::Client do
|
|
29
36
|
|
30
37
|
it { should be_an Array }
|
31
38
|
its(:length) { should eq 2 }
|
32
|
-
it { should include_picklist_values %w
|
39
|
+
it { should include_picklist_values %w[seven eight] }
|
33
40
|
it { should_not include_picklist_values ['nine'] }
|
34
41
|
end
|
35
42
|
|
43
|
+
context 'when given a picklist field that has a dependency index greater than 8' do
|
44
|
+
subject do
|
45
|
+
client.picklist_values('Account',
|
46
|
+
'Dependent_Picklist_Field',
|
47
|
+
valid_for: 'control_ten')
|
48
|
+
end
|
49
|
+
|
50
|
+
it { should be_an Array }
|
51
|
+
its(:length) { should eq 1 }
|
52
|
+
it { should include_picklist_values %w[ten] }
|
53
|
+
end
|
54
|
+
|
36
55
|
context 'when given a picklist field that does not have a dependency' do
|
37
56
|
subject do
|
38
57
|
client.picklist_values('Account', 'Picklist_Field', valid_for: 'one')
|
@@ -90,7 +109,7 @@ shared_examples_for Restforce::Data::Client do
|
|
90
109
|
it 'subscribes to each pushtopic' do
|
91
110
|
client.faye.should_receive(:subscribe).with(['/topic/PushTopic1',
|
92
111
|
'/topic/PushTopic2'])
|
93
|
-
client.subscribe(%w
|
112
|
+
client.subscribe(%w[PushTopic1 PushTopic2])
|
94
113
|
end
|
95
114
|
end
|
96
115
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/concerns.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FixtureHelpers
|
2
4
|
module InstanceMethods
|
3
5
|
def stub_api_request(endpoint, options = {})
|
@@ -25,8 +27,8 @@ module FixtureHelpers
|
|
25
27
|
stub
|
26
28
|
end
|
27
29
|
|
28
|
-
def fixture(
|
29
|
-
File.read(File.expand_path("../../fixtures/#{
|
30
|
+
def fixture(filename)
|
31
|
+
File.read(File.expand_path("../../fixtures/#{filename}.json", __FILE__))
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
data/spec/support/matchers.rb
CHANGED
data/spec/support/middleware.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MiddlewareExampleGroup
|
2
4
|
def self.included(base)
|
3
5
|
base.class_eval do
|
4
|
-
let(:app) { double('@app', call: nil)
|
6
|
+
let(:app) { double('@app', call: nil) }
|
5
7
|
let(:env) { { request_headers: {}, response_headers: {} } }
|
6
8
|
let(:retries) { 3 }
|
7
9
|
let(:options) { {} }
|
data/spec/support/mock_cache.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class MockCache
|
2
4
|
def initialize
|
3
5
|
@storage = {}
|
@@ -11,8 +13,8 @@ class MockCache
|
|
11
13
|
@storage[key] = value
|
12
14
|
end
|
13
15
|
|
14
|
-
def fetch(key
|
15
|
-
@storage[key] ||=
|
16
|
+
def fetch(key)
|
17
|
+
@storage[key] ||= yield
|
16
18
|
end
|
17
19
|
|
18
20
|
def delete(key)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Collection do
|
@@ -16,7 +18,7 @@ describe Restforce::Collection do
|
|
16
18
|
its(:page_size) { should eq 1 }
|
17
19
|
|
18
20
|
describe 'each record' do
|
19
|
-
it { should
|
21
|
+
it { should(be_all { |record| expect(record).to be_a Restforce::SObject }) }
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -51,11 +53,11 @@ describe Restforce::Collection do
|
|
51
53
|
end
|
52
54
|
|
53
55
|
its(:pages) do
|
54
|
-
should
|
56
|
+
should(be_all { |page| expect(page).to be_a Restforce::Collection })
|
55
57
|
end
|
56
58
|
|
57
59
|
its(:has_next_page?) { should be_true }
|
58
|
-
it { should
|
60
|
+
it { should(be_all { |record| expect(record).to be_a Restforce::SObject }) }
|
59
61
|
its(:next_page) { should be_a Restforce::Collection }
|
60
62
|
end
|
61
63
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Concerns::API do
|
@@ -27,7 +29,7 @@ describe Restforce::Concerns::API do
|
|
27
29
|
it 'returns the body' do
|
28
30
|
start_string = '2002-10-31T00:02:02Z'
|
29
31
|
end_string = '2003-10-31T00:02:02Z'
|
30
|
-
url = "
|
32
|
+
url = "sobjects/Whizbang/updated/?start=#{start_string}&end=#{end_string}"
|
31
33
|
client.should_receive(:api_get).
|
32
34
|
with(url).
|
33
35
|
and_return(response)
|
@@ -43,7 +45,7 @@ describe Restforce::Concerns::API do
|
|
43
45
|
it 'returns the body' do
|
44
46
|
start_string = '2002-10-31T00:02:02Z'
|
45
47
|
end_string = '2003-10-31T00:02:02Z'
|
46
|
-
url = "
|
48
|
+
url = "sobjects/Whizbang/deleted/?start=#{start_string}&end=#{end_string}"
|
47
49
|
client.should_receive(:api_get).
|
48
50
|
with(url).
|
49
51
|
and_return(response)
|
@@ -259,7 +261,7 @@ describe Restforce::Concerns::API do
|
|
259
261
|
end
|
260
262
|
end
|
261
263
|
|
262
|
-
[
|
264
|
+
%i[create update upsert destroy].each do |method|
|
263
265
|
describe ".#{method}" do
|
264
266
|
let(:args) { [] }
|
265
267
|
subject(:result) { client.send(method, *args) }
|
@@ -289,7 +291,7 @@ describe Restforce::Concerns::API do
|
|
289
291
|
|
290
292
|
describe '.create!' do
|
291
293
|
let(:sobject) { 'Whizbang' }
|
292
|
-
let(:attrs) {
|
294
|
+
let(:attrs) { {} }
|
293
295
|
subject(:result) { client.create!(sobject, attrs) }
|
294
296
|
|
295
297
|
it 'send an HTTP POST, and returns the id of the record' do
|
@@ -303,7 +305,7 @@ describe Restforce::Concerns::API do
|
|
303
305
|
|
304
306
|
describe '.update!' do
|
305
307
|
let(:sobject) { 'Whizbang' }
|
306
|
-
let(:attrs) {
|
308
|
+
let(:attrs) { {} }
|
307
309
|
subject(:result) { client.update!(sobject, attrs) }
|
308
310
|
|
309
311
|
context 'when the id field is present' do
|
@@ -342,17 +344,27 @@ describe Restforce::Concerns::API do
|
|
342
344
|
|
343
345
|
context 'when the record is found and updated' do
|
344
346
|
it 'returns true' do
|
345
|
-
response.body
|
347
|
+
response.stub(:body) { {} }
|
346
348
|
client.should_receive(:api_patch).
|
347
349
|
with('sobjects/Whizbang/External_ID__c/1234', {}).
|
348
350
|
and_return(response)
|
349
351
|
expect(result).to be_true
|
350
352
|
end
|
353
|
+
|
354
|
+
context 'and the response body is a string' do
|
355
|
+
it 'returns true' do
|
356
|
+
response.stub(:body) { '' }
|
357
|
+
client.should_receive(:api_patch).
|
358
|
+
with('sobjects/Whizbang/External_ID__c/1234', {}).
|
359
|
+
and_return(response)
|
360
|
+
expect(result).to be_true
|
361
|
+
end
|
362
|
+
end
|
351
363
|
end
|
352
364
|
|
353
365
|
context 'when the record is found and created' do
|
354
366
|
it 'returns the id of the record' do
|
355
|
-
response.
|
367
|
+
response.stub(:body) { { "id" => "4321" } }
|
356
368
|
client.should_receive(:api_patch).
|
357
369
|
with('sobjects/Whizbang/External_ID__c/1234', {}).
|
358
370
|
and_return(response)
|
@@ -361,7 +373,7 @@ describe Restforce::Concerns::API do
|
|
361
373
|
end
|
362
374
|
|
363
375
|
context 'when the external id field is missing from the attrs' do
|
364
|
-
let(:attrs) {
|
376
|
+
let(:attrs) { {} }
|
365
377
|
|
366
378
|
it 'raises an argument error' do
|
367
379
|
expect { client.upsert!(sobject, field, attrs) }.
|
@@ -376,7 +388,7 @@ describe Restforce::Concerns::API do
|
|
376
388
|
|
377
389
|
context 'and the value for Id is provided' do
|
378
390
|
it 'returns the id of the record, and original record still contains id' do
|
379
|
-
response.
|
391
|
+
response.stub(:body) { { "id" => "4321" } }
|
380
392
|
client.should_receive(:api_patch).
|
381
393
|
with('sobjects/Whizbang/Id/4321', {}).
|
382
394
|
and_return(response)
|
@@ -389,7 +401,7 @@ describe Restforce::Concerns::API do
|
|
389
401
|
let(:attrs) { { 'External_ID__c' => '1234' } }
|
390
402
|
|
391
403
|
it 'uses POST to create the record' do
|
392
|
-
response.
|
404
|
+
response.stub(:body) { { "id" => "4321" } }
|
393
405
|
client.should_receive(:options).and_return(api_version: 38.0)
|
394
406
|
client.should_receive(:api_post).
|
395
407
|
with('sobjects/Whizbang/Id', attrs).
|
@@ -415,7 +427,7 @@ describe Restforce::Concerns::API do
|
|
415
427
|
|
416
428
|
context 'when the record is found and updated' do
|
417
429
|
it 'returns true' do
|
418
|
-
response.body
|
430
|
+
response.stub(:body) { {} }
|
419
431
|
client.should_receive(:api_patch).
|
420
432
|
with('sobjects/Whizbang/External_ID__c/%E3%81%82', {}).
|
421
433
|
and_return(response)
|
@@ -423,6 +435,23 @@ describe Restforce::Concerns::API do
|
|
423
435
|
end
|
424
436
|
end
|
425
437
|
end
|
438
|
+
|
439
|
+
describe '.upsert! with Fixnum argument' do
|
440
|
+
let(:sobject) { 'Whizbang' }
|
441
|
+
let(:field) { :External_ID__c }
|
442
|
+
let(:attrs) { { 'External_ID__c' => 1234 } }
|
443
|
+
subject(:result) { client.upsert!(sobject, field, attrs) }
|
444
|
+
|
445
|
+
context 'when the record is found and updated' do
|
446
|
+
it 'returns true' do
|
447
|
+
response.stub(:body) { {} }
|
448
|
+
client.should_receive(:api_patch).
|
449
|
+
with('sobjects/Whizbang/External_ID__c/1234', {}).
|
450
|
+
and_return(response)
|
451
|
+
expect(result).to be_true
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
426
455
|
end
|
427
456
|
|
428
457
|
describe '.destroy!' do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Concerns::Authentication do
|
@@ -54,7 +56,7 @@ describe Restforce::Concerns::Authentication do
|
|
54
56
|
|
55
57
|
describe '.username_password?' do
|
56
58
|
subject { client.username_password? }
|
57
|
-
let(:options) {
|
59
|
+
let(:options) { {} }
|
58
60
|
|
59
61
|
before do
|
60
62
|
client.stub options: options
|
@@ -78,7 +80,7 @@ describe Restforce::Concerns::Authentication do
|
|
78
80
|
|
79
81
|
describe '.oauth_refresh?' do
|
80
82
|
subject { client.oauth_refresh? }
|
81
|
-
let(:options) {
|
83
|
+
let(:options) { {} }
|
82
84
|
|
83
85
|
before do
|
84
86
|
client.stub options: options
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Restforce::Concerns::BatchAPI do
|
6
|
+
let(:endpoint) { 'composite/batch' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
client.should_receive(:options).and_return(api_version: 34.0)
|
10
|
+
end
|
11
|
+
|
12
|
+
shared_examples_for 'batched requests' do
|
13
|
+
it '#create' do
|
14
|
+
client.
|
15
|
+
should_receive(:api_post).
|
16
|
+
with(endpoint, { batchRequests: [
|
17
|
+
{ method: 'POST', url: 'v34.0/sobjects/Object', richInput: { name: 'test' } }
|
18
|
+
], haltOnError: halt_on_error }.to_json).
|
19
|
+
and_return(response)
|
20
|
+
|
21
|
+
client.send(method) do |subrequests|
|
22
|
+
subrequests.create('Object', name: 'test')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it '#update' do
|
27
|
+
client.
|
28
|
+
should_receive(:api_post).
|
29
|
+
with(endpoint, { batchRequests: [
|
30
|
+
{ method: 'PATCH', url: "v34.0/sobjects/Object/123", richInput: {
|
31
|
+
name: 'test'
|
32
|
+
} }
|
33
|
+
], haltOnError: halt_on_error }.to_json).
|
34
|
+
and_return(response)
|
35
|
+
|
36
|
+
client.send(method) do |subrequests|
|
37
|
+
subrequests.update('Object', id: '123', name: 'test')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it '#destroy' do
|
42
|
+
client.
|
43
|
+
should_receive(:api_post).
|
44
|
+
with(endpoint, { batchRequests: [
|
45
|
+
{ method: 'DELETE', url: "v34.0/sobjects/Object/123" }
|
46
|
+
], haltOnError: halt_on_error }.to_json).
|
47
|
+
and_return(response)
|
48
|
+
|
49
|
+
client.send(method) do |subrequests|
|
50
|
+
subrequests.destroy('Object', '123')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it '#upsert' do
|
55
|
+
client.
|
56
|
+
should_receive(:api_post).
|
57
|
+
with(endpoint, { batchRequests: [
|
58
|
+
{ method: 'PATCH', url: 'v34.0/sobjects/Object/extIdField__c/456', richInput: {
|
59
|
+
name: 'test'
|
60
|
+
} }
|
61
|
+
], haltOnError: halt_on_error }.to_json).
|
62
|
+
and_return(response)
|
63
|
+
|
64
|
+
client.send(method) do |subrequests|
|
65
|
+
subrequests.upsert('Object', 'extIdField__c',
|
66
|
+
extIdField__c: '456', name: 'test')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'multiple subrequests' do
|
71
|
+
client.
|
72
|
+
should_receive(:api_post).
|
73
|
+
with(endpoint, { batchRequests: [
|
74
|
+
{ method: 'POST', url: 'v34.0/sobjects/Object', richInput: {
|
75
|
+
name: 'test'
|
76
|
+
} },
|
77
|
+
{ method: 'PATCH', url: "v34.0/sobjects/Object/123", richInput: {
|
78
|
+
name: 'test'
|
79
|
+
} },
|
80
|
+
{ method: 'DELETE', url: "v34.0/sobjects/Object/123" }
|
81
|
+
], haltOnError: halt_on_error }.to_json).
|
82
|
+
and_return(response)
|
83
|
+
|
84
|
+
client.send(method) do |subrequests|
|
85
|
+
subrequests.create('Object', name: 'test')
|
86
|
+
subrequests.update('Object', id: '123', name: 'test')
|
87
|
+
subrequests.destroy('Object', '123')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#batch' do
|
93
|
+
let(:method) { :batch }
|
94
|
+
let(:halt_on_error) { false }
|
95
|
+
let(:response) { double('Faraday::Response', body: { 'results' => [] }) }
|
96
|
+
it_behaves_like 'batched requests'
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#batch!' do
|
100
|
+
let(:method) { :batch! }
|
101
|
+
let(:halt_on_error) { true }
|
102
|
+
let(:response) {
|
103
|
+
double('Faraday::Response', body: { 'hasErrors' => false, 'results' => [] })
|
104
|
+
}
|
105
|
+
it_behaves_like 'batched requests'
|
106
|
+
end
|
107
|
+
end
|