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
|
describe Restforce::Concerns::Connection do
|
@@ -34,7 +36,7 @@ describe Restforce::Concerns::Connection do
|
|
34
36
|
describe 'with mashify not specified' do
|
35
37
|
it 'includes the Mashify middleware' do
|
36
38
|
client.middleware.handlers.index(Restforce::Middleware::Mashify).
|
37
|
-
|
39
|
+
should_not be_nil
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
@@ -45,7 +47,7 @@ describe Restforce::Concerns::Connection do
|
|
45
47
|
|
46
48
|
it 'includes the Mashify middleware' do
|
47
49
|
client.middleware.handlers.index(Restforce::Middleware::Mashify).
|
48
|
-
|
50
|
+
should_not be_nil
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
@@ -56,7 +58,7 @@ describe Restforce::Concerns::Connection do
|
|
56
58
|
|
57
59
|
it 'does not include the Mashify middleware' do
|
58
60
|
client.middleware.handlers.index(Restforce::Middleware::Mashify).
|
59
|
-
|
61
|
+
should be_nil
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Concerns::Streaming, event_machine: true do
|
4
6
|
describe '.subscribe' do
|
5
|
-
let(:channels) { %w
|
7
|
+
let(:channels) { %w[channel1 channel2] }
|
6
8
|
let(:topics) { channels.map { |c| "/topic/#{c}" } }
|
7
9
|
let(:subscribe_block) { lambda { 'subscribe' } }
|
8
10
|
let(:faye_double) { double('Faye') }
|
@@ -15,6 +17,33 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
15
17
|
|
16
18
|
client.subscribe(channels, &subscribe_block)
|
17
19
|
end
|
20
|
+
|
21
|
+
context "replay_handlers" do
|
22
|
+
before {
|
23
|
+
faye_double.should_receive(:subscribe).at_least(1)
|
24
|
+
client.stub faye: faye_double
|
25
|
+
}
|
26
|
+
|
27
|
+
it 'registers nil handlers when no replay option is given' do
|
28
|
+
client.subscribe(channels, &subscribe_block)
|
29
|
+
client.replay_handlers.should eq('channel1' => nil, 'channel2' => nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'registers a replay_handler for each channel given' do
|
33
|
+
client.subscribe(channels, replay: -2, &subscribe_block)
|
34
|
+
client.replay_handlers.should eq('channel1' => -2, 'channel2' => -2)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'replaces earlier handlers in subsequent calls' do
|
38
|
+
client.subscribe(%w[channel1 channel2], replay: 2, &subscribe_block)
|
39
|
+
client.subscribe(%w[channel2 channel3], replay: 3, &subscribe_block)
|
40
|
+
client.replay_handlers.should eq(
|
41
|
+
'channel1' => 2,
|
42
|
+
'channel2' => 3,
|
43
|
+
'channel3' => 3
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
18
47
|
end
|
19
48
|
|
20
49
|
describe '.faye' do
|
@@ -40,6 +69,8 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
40
69
|
faye_double.should_receive(:set_header).with('Authorization', 'OAuth secret2')
|
41
70
|
faye_double.should_receive(:bind).with('transport:down').and_yield
|
42
71
|
faye_double.should_receive(:bind).with('transport:up').and_yield
|
72
|
+
faye_double.should_receive(:add_extension).with \
|
73
|
+
kind_of(Restforce::Concerns::Streaming::ReplayExtension)
|
43
74
|
subject
|
44
75
|
end
|
45
76
|
end
|
@@ -50,4 +81,87 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
50
81
|
end
|
51
82
|
end
|
52
83
|
end
|
84
|
+
|
85
|
+
describe Restforce::Concerns::Streaming::ReplayExtension do
|
86
|
+
let(:handlers) { {} }
|
87
|
+
let(:extension) { Restforce::Concerns::Streaming::ReplayExtension.new(handlers) }
|
88
|
+
|
89
|
+
it 'sends nil without a specified handler' do
|
90
|
+
output = subscribe(extension, to: "channel1")
|
91
|
+
read_replay(output).should eq('/topic/channel1' => nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'with a scalar replay id' do
|
95
|
+
handlers['channel1'] = -2
|
96
|
+
output = subscribe(extension, to: "channel1")
|
97
|
+
read_replay(output).should eq('/topic/channel1' => -2)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'with a hash' do
|
101
|
+
hash_handler = { 'channel1' => -1, 'channel2' => -2 }
|
102
|
+
|
103
|
+
handlers['channel1'] = hash_handler
|
104
|
+
handlers['channel2'] = hash_handler
|
105
|
+
|
106
|
+
output = subscribe(extension, to: "channel1")
|
107
|
+
read_replay(output).should eq('/topic/channel1' => -1)
|
108
|
+
|
109
|
+
output = subscribe(extension, to: "channel2")
|
110
|
+
read_replay(output).should eq('/topic/channel2' => -2)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'with an object' do
|
114
|
+
custom_handler = double('custom_handler')
|
115
|
+
custom_handler.should_receive(:[]).and_return(123)
|
116
|
+
handlers['channel1'] = custom_handler
|
117
|
+
|
118
|
+
output = subscribe(extension, to: "channel1")
|
119
|
+
read_replay(output).should eq('/topic/channel1' => 123)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'remembers the last replayId' do
|
123
|
+
handler = { 'channel1' => 41 }
|
124
|
+
handlers['channel1'] = handler
|
125
|
+
message = {
|
126
|
+
'channel' => '/topic/channel1',
|
127
|
+
'data' => {
|
128
|
+
'event' => { 'replayId' => 42 }
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
extension.incoming(message, ->(m) {})
|
133
|
+
handler.should eq('channel1' => 42)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'when an incoming message has no replayId' do
|
137
|
+
handler = { 'channel1' => 41 }
|
138
|
+
handlers['channel1'] = handler
|
139
|
+
|
140
|
+
message = {
|
141
|
+
'channel' => '/topic/channel1',
|
142
|
+
'data' => {}
|
143
|
+
}
|
144
|
+
|
145
|
+
extension.incoming(message, ->(m) {})
|
146
|
+
handler.should eq('channel1' => 41)
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def subscribe(extension, options = {})
|
152
|
+
output = nil
|
153
|
+
message = {
|
154
|
+
'channel' => '/meta/subscribe',
|
155
|
+
'subscription' => "/topic/#{options[:to]}"
|
156
|
+
}
|
157
|
+
extension.outgoing(message, ->(m) {
|
158
|
+
output = m
|
159
|
+
})
|
160
|
+
output
|
161
|
+
end
|
162
|
+
|
163
|
+
def read_replay(message)
|
164
|
+
message.fetch('ext', {})['replay']
|
165
|
+
end
|
166
|
+
end
|
53
167
|
end
|
data/spec/unit/config_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce do
|
@@ -25,9 +27,9 @@ describe Restforce do
|
|
25
27
|
its(:authentication_retries) { should eq 3 }
|
26
28
|
its(:adapter) { should eq Faraday.default_adapter }
|
27
29
|
its(:ssl) { should eq({}) }
|
28
|
-
[
|
29
|
-
|
30
|
-
|
30
|
+
%i[username password security_token client_id client_secret
|
31
|
+
oauth_token refresh_token instance_url compress timeout
|
32
|
+
proxy_uri authentication_callback mashify request_headers].each do |attr|
|
31
33
|
its(attr) { should be_nil }
|
32
34
|
end
|
33
35
|
end
|
@@ -42,7 +44,7 @@ describe Restforce do
|
|
42
44
|
'SALESFORCE_PROXY_URI' => 'proxy',
|
43
45
|
'SALESFORCE_HOST' => 'test.host.com',
|
44
46
|
'SALESFORCE_API_VERSION' => '37.0' }.
|
45
|
-
|
47
|
+
each { |var, value| ENV.stub(:[]).with(var).and_return(value) }
|
46
48
|
end
|
47
49
|
|
48
50
|
its(:username) { should eq 'foo' }
|
@@ -57,10 +59,10 @@ describe Restforce do
|
|
57
59
|
end
|
58
60
|
|
59
61
|
describe '#configure' do
|
60
|
-
[
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
%i[username password security_token client_id client_secret compress
|
63
|
+
timeout oauth_token refresh_token instance_url api_version host mashify
|
64
|
+
authentication_retries proxy_uri authentication_callback ssl
|
65
|
+
request_headers log_level logger].each do |attr|
|
64
66
|
it "allows #{attr} to be set" do
|
65
67
|
Restforce.configure do |config|
|
66
68
|
config.send("#{attr}=", 'foobar')
|
data/spec/unit/document_spec.rb
CHANGED
data/spec/unit/mash_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Mash do
|
@@ -6,7 +8,7 @@ describe Restforce::Mash do
|
|
6
8
|
|
7
9
|
context 'when array' do
|
8
10
|
let(:input) { [{ foo: 'hello' }, { bar: 'world' }] }
|
9
|
-
it { should
|
11
|
+
it { should(be_all { |obj| expect(obj).to be_a Restforce::Mash }) }
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Middleware::Authentication do
|
@@ -58,7 +60,7 @@ describe Restforce::Middleware::Authentication do
|
|
58
60
|
should include FaradayMiddleware::ParseJson,
|
59
61
|
Faraday::Adapter::NetHttp
|
60
62
|
}
|
61
|
-
its(:handlers) { should_not include Restforce::Middleware::Logger
|
63
|
+
its(:handlers) { should_not include Restforce::Middleware::Logger }
|
62
64
|
end
|
63
65
|
|
64
66
|
context 'with logging enabled' do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Middleware::CustomHeaders do
|
@@ -13,7 +15,7 @@ describe Restforce::Middleware::CustomHeaders do
|
|
13
15
|
context 'when :request_headers are not a Hash' do
|
14
16
|
let(:options) { { request_headers: 'bad header' } }
|
15
17
|
|
16
|
-
it { should_not
|
18
|
+
it { should_not(change { env[:request_headers] }) }
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Middleware::Gzip do
|
@@ -29,7 +31,7 @@ describe Restforce::Middleware::Gzip do
|
|
29
31
|
end
|
30
32
|
|
31
33
|
context 'when :compress is false' do
|
32
|
-
it { should_not
|
34
|
+
it { should_not(change { env[:request_headers]['Accept-Encoding'] }) }
|
33
35
|
end
|
34
36
|
|
35
37
|
context 'when :compress is true' do
|
@@ -37,7 +39,7 @@ describe Restforce::Middleware::Gzip do
|
|
37
39
|
options[:compress] = true
|
38
40
|
end
|
39
41
|
|
40
|
-
it { should
|
42
|
+
it { should(change { env[:request_headers]['Accept-Encoding'] }.to('gzip')) }
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Middleware::Mashify do
|
4
6
|
let(:env) { { body: JSON.parse(fixture('sobject/query_success_response')) } }
|
5
7
|
subject(:middleware) {
|
6
|
-
described_class.new(lambda {|env|
|
8
|
+
described_class.new(lambda { |env|
|
7
9
|
Faraday::Response.new(env)
|
8
10
|
}, client, options)
|
9
11
|
}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Restforce::Middleware::RaiseError do
|
@@ -11,34 +13,46 @@ describe Restforce::Middleware::RaiseError do
|
|
11
13
|
context 'when the status code is 404' do
|
12
14
|
let(:status) { 404 }
|
13
15
|
|
14
|
-
it
|
15
|
-
expect { on_complete }.to raise_error
|
16
|
+
it 'raises Restforce::NotFoundError' do
|
17
|
+
expect { on_complete }.to raise_error Restforce::NotFoundError,
|
16
18
|
'INVALID_FIELD: error_message'
|
17
19
|
end
|
20
|
+
|
21
|
+
it 'raises an error that inherits from Faraday::Error::ResourceNotFound' do
|
22
|
+
expect { on_complete }.to raise_error Faraday::Error::ResourceNotFound
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
context 'when the status code is 300' do
|
21
27
|
let(:status) { 300 }
|
22
28
|
|
23
|
-
it
|
24
|
-
expect { on_complete }.to raise_error
|
29
|
+
it 'raises Restforce::MatchesMultipleError' do
|
30
|
+
expect { on_complete }.to raise_error Restforce::MatchesMultipleError,
|
25
31
|
/300: The external ID provided/
|
26
32
|
end
|
33
|
+
|
34
|
+
it 'raises an error that inherits from Faraday::Error::ClientError' do
|
35
|
+
expect { on_complete }.to raise_error Faraday::Error::ClientError
|
36
|
+
end
|
27
37
|
end
|
28
38
|
|
29
39
|
context 'when the status code is 400' do
|
30
40
|
let(:status) { 400 }
|
31
41
|
|
32
|
-
it "raises an error" do
|
33
|
-
expect { on_complete }.to raise_error
|
42
|
+
it "raises an error derived from the response's errorCode" do
|
43
|
+
expect { on_complete }.to raise_error Restforce::ErrorCode::InvalidField,
|
34
44
|
'INVALID_FIELD: error_message'
|
35
45
|
end
|
46
|
+
|
47
|
+
it 'raises an error that inherits from Faraday::Error::ClientError' do
|
48
|
+
expect { on_complete }.to raise_error Faraday::Error::ClientError
|
49
|
+
end
|
36
50
|
end
|
37
51
|
|
38
52
|
context 'when the status code is 401' do
|
39
53
|
let(:status) { 401 }
|
40
54
|
|
41
|
-
it
|
55
|
+
it 'raises Restforce::UnauthorizedError' do
|
42
56
|
expect { on_complete }.to raise_error Restforce::UnauthorizedError,
|
43
57
|
'INVALID_FIELD: error_message'
|
44
58
|
end
|
@@ -47,17 +61,26 @@ describe Restforce::Middleware::RaiseError do
|
|
47
61
|
context 'when the status code is 413' do
|
48
62
|
let(:status) { 413 }
|
49
63
|
|
50
|
-
it
|
51
|
-
expect { on_complete }.to raise_error
|
64
|
+
it 'raises Restforce::EntityTooLargeError' do
|
65
|
+
expect { on_complete }.to raise_error Restforce::EntityTooLargeError,
|
52
66
|
'413: Request Entity Too Large'
|
53
67
|
end
|
68
|
+
|
69
|
+
it 'raises an error that inherits from Faraday::Error::ClientError' do
|
70
|
+
expect { on_complete }.to raise_error Faraday::Error::ClientError
|
71
|
+
end
|
54
72
|
end
|
55
73
|
|
56
74
|
context 'when status is 400+ and body is a string' do
|
57
75
|
let(:body) { 'An error occured' }
|
58
|
-
let(:status) {
|
76
|
+
let(:status) { 400 }
|
77
|
+
|
78
|
+
it 'raises a generic Restforce::ResponseError' do
|
79
|
+
expect { on_complete }.to raise_error Restforce::ResponseError,
|
80
|
+
"(error code missing): #{body}"
|
81
|
+
end
|
59
82
|
|
60
|
-
it 'raises an error
|
83
|
+
it 'raises an error that inherits from Faraday::Error::ClientError' do
|
61
84
|
expect { on_complete }.to raise_error Faraday::Error::ClientError,
|
62
85
|
"(error code missing): #{body}"
|
63
86
|
end
|