sfdc 3.0.0 → 3.0.1
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/CHANGELOG.md +96 -0
- data/Gemfile +11 -0
- data/Guardfile +11 -0
- data/LICENSE +22 -0
- data/README.md +88 -98
- data/Rakefile +10 -0
- data/lib/sfdc.rb +1 -0
- data/lib/sfdc/abstract_client.rb +1 -1
- data/lib/sfdc/attachment.rb +0 -2
- data/lib/sfdc/concerns/api.rb +1 -2
- data/lib/sfdc/concerns/authentication.rb +1 -2
- data/lib/sfdc/concerns/base.rb +5 -7
- data/lib/sfdc/concerns/caching.rb +1 -3
- data/lib/sfdc/concerns/canvas.rb +0 -2
- data/lib/sfdc/concerns/connection.rb +2 -3
- data/lib/sfdc/concerns/picklists.rb +1 -4
- data/lib/sfdc/concerns/streaming.rb +2 -2
- data/lib/sfdc/concerns/verbs.rb +0 -2
- data/lib/sfdc/config.rb +1 -0
- data/lib/sfdc/middleware/authentication.rb +4 -2
- data/lib/sfdc/middleware/authentication/password.rb +2 -5
- data/lib/sfdc/middleware/authorization.rb +1 -5
- data/lib/sfdc/middleware/caching.rb +0 -2
- data/lib/sfdc/middleware/instance_url.rb +0 -4
- data/lib/sfdc/middleware/mashify.rb +1 -3
- data/lib/sfdc/middleware/multipart.rb +6 -4
- data/lib/sfdc/signed_request.rb +1 -1
- data/lib/sfdc/tooling/client.rb +4 -6
- data/lib/sfdc/version.rb +2 -2
- data/sfdc.gemspec +27 -0
- data/spec/fixtures/auth_error_response.json +4 -0
- data/spec/fixtures/auth_success_response.json +7 -0
- data/spec/fixtures/blob.jpg +0 -0
- data/spec/fixtures/expired_session_response.json +6 -0
- data/spec/fixtures/reauth_success_response.json +7 -0
- data/spec/fixtures/refresh_error_response.json +4 -0
- data/spec/fixtures/refresh_success_response.json +7 -0
- data/spec/fixtures/services_data_success_response.json +12 -0
- data/spec/fixtures/sobject/create_success_response.json +5 -0
- data/spec/fixtures/sobject/delete_error_response.json +1 -0
- data/spec/fixtures/sobject/describe_sobjects_success_response.json +31 -0
- data/spec/fixtures/sobject/list_sobjects_success_response.json +31 -0
- data/spec/fixtures/sobject/org_query_response.json +11 -0
- data/spec/fixtures/sobject/query_aggregate_success_response.json +23 -0
- data/spec/fixtures/sobject/query_empty_response.json +5 -0
- data/spec/fixtures/sobject/query_error_response.json +6 -0
- data/spec/fixtures/sobject/query_paginated_first_page_response.json +14 -0
- data/spec/fixtures/sobject/query_paginated_last_page_response.json +13 -0
- data/spec/fixtures/sobject/query_success_response.json +38 -0
- data/spec/fixtures/sobject/recent_success_response.json +18 -0
- data/spec/fixtures/sobject/search_error_response.json +6 -0
- data/spec/fixtures/sobject/search_success_response.json +16 -0
- data/spec/fixtures/sobject/sobject_describe_error_response.json +6 -0
- data/spec/fixtures/sobject/sobject_describe_success_response.json +1429 -0
- data/spec/fixtures/sobject/sobject_find_error_response.json +6 -0
- data/spec/fixtures/sobject/sobject_find_success_response.json +29 -0
- data/spec/fixtures/sobject/upsert_created_success_response.json +5 -0
- data/spec/fixtures/sobject/upsert_error_response.json +6 -0
- data/spec/fixtures/sobject/upsert_multiple_error_response.json +4 -0
- data/spec/fixtures/sobject/upsert_updated_success_response.json +0 -0
- data/spec/fixtures/sobject/write_error_response.json +6 -0
- data/spec/integration/abstract_client_spec.rb +306 -0
- data/spec/integration/data/client_spec.rb +90 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/client_integration.rb +45 -0
- data/spec/support/concerns.rb +18 -0
- data/spec/support/event_machine.rb +14 -0
- data/spec/support/fixture_helpers.rb +45 -0
- data/spec/support/matchers.rb +11 -0
- data/spec/support/middleware.rb +76 -0
- data/spec/support/mock_cache.rb +13 -0
- data/spec/unit/abstract_client_spec.rb +11 -0
- data/spec/unit/attachment_spec.rb +15 -0
- data/spec/unit/collection_spec.rb +52 -0
- data/spec/unit/concerns/api_spec.rb +244 -0
- data/spec/unit/concerns/authentication_spec.rb +98 -0
- data/spec/unit/concerns/base_spec.rb +42 -0
- data/spec/unit/concerns/caching_spec.rb +29 -0
- data/spec/unit/concerns/canvas_spec.rb +30 -0
- data/spec/unit/concerns/connection_spec.rb +22 -0
- data/spec/unit/config_spec.rb +99 -0
- data/spec/unit/data/client_spec.rb +10 -0
- data/spec/unit/mash_spec.rb +36 -0
- data/spec/unit/middleware/authentication/password_spec.rb +31 -0
- data/spec/unit/middleware/authentication/token_spec.rb +24 -0
- data/spec/unit/middleware/authentication_spec.rb +67 -0
- data/spec/unit/middleware/authorization_spec.rb +11 -0
- data/spec/unit/middleware/gzip_spec.rb +66 -0
- data/spec/unit/middleware/instance_url_spec.rb +24 -0
- data/spec/unit/middleware/logger_spec.rb +19 -0
- data/spec/unit/middleware/mashify_spec.rb +11 -0
- data/spec/unit/middleware/raise_error_spec.rb +32 -0
- data/spec/unit/signed_request_spec.rb +24 -0
- data/spec/unit/sobject_spec.rb +86 -0
- data/spec/unit/tooling/client_spec.rb +7 -0
- metadata +225 -65
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler.require :default, :test
|
6
|
+
require 'faye' unless RUBY_PLATFORM == 'java'
|
7
|
+
|
8
|
+
require 'webmock/rspec'
|
9
|
+
|
10
|
+
WebMock.disable_net_connect!
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.order = 'random'
|
14
|
+
config.filter_run :focus => true
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
19
|
+
# in spec/support/ and its subdirectories.
|
20
|
+
Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each {|f| require f}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ClientIntegrationExampleGroup
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval do
|
4
|
+
let(:oauth_token) { '00Dx0000000BV7z!AR8AQAxo9UfVkh8AlV0Gomt9Czx9LjHnSSpwBMmbRcgKFmxOtvxjTrKW19ye6PE3Ds1eQz3z8jr3W7_VbWmEu4Q8TVGSTHxs' }
|
5
|
+
let(:refresh_token) { 'refresh' }
|
6
|
+
let(:instance_url) { 'https://na1.salesforce.com' }
|
7
|
+
let(:username) { 'foo' }
|
8
|
+
let(:password) { 'bar' }
|
9
|
+
let(:security_token) { 'security_token' }
|
10
|
+
let(:client_id) { 'client_id' }
|
11
|
+
let(:client_secret) { 'client_secret' }
|
12
|
+
let(:cache) { nil }
|
13
|
+
|
14
|
+
let(:base_options) do
|
15
|
+
{
|
16
|
+
:oauth_token => oauth_token,
|
17
|
+
:refresh_token => refresh_token,
|
18
|
+
:instance_url => instance_url,
|
19
|
+
:username => username,
|
20
|
+
:password => password,
|
21
|
+
:security_token => security_token,
|
22
|
+
:client_id => client_id,
|
23
|
+
:client_secret => client_secret,
|
24
|
+
:cache => cache
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:client_options) { base_options }
|
29
|
+
|
30
|
+
subject(:client) { described_class.new client_options }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec.configure do |config|
|
35
|
+
config.include self,
|
36
|
+
:example_group => {
|
37
|
+
:describes => lambda { |described| described <= Sfdc::AbstractClient },
|
38
|
+
:file_path => %r{spec/integration}
|
39
|
+
}
|
40
|
+
|
41
|
+
config.before :mashify => false do
|
42
|
+
client.middleware.delete(Sfdc::Middleware::Mashify)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ConcernsExampleGroup
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval do
|
4
|
+
let(:klass) do
|
5
|
+
context = self
|
6
|
+
Class.new { include context.described_class }
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:client) { klass.new }
|
10
|
+
|
11
|
+
subject { client }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.include self, :example_group => { :file_path => %r{spec/unit/concerns} }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before do
|
3
|
+
EventMachine.stub(:connect) if defined?(EventMachine)
|
4
|
+
end
|
5
|
+
|
6
|
+
config.filter_run_excluding :event_machine => true if RUBY_PLATFORM == 'java'
|
7
|
+
|
8
|
+
config.around :event_machine => true do |example|
|
9
|
+
EM.run {
|
10
|
+
example.run
|
11
|
+
EM.stop
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module FixtureHelpers
|
2
|
+
module InstanceMethods
|
3
|
+
|
4
|
+
def stub_api_request(endpoint, options={})
|
5
|
+
options = {
|
6
|
+
:method => :get,
|
7
|
+
:status => 200,
|
8
|
+
:api_version => Sfdc.configuration.api_version
|
9
|
+
}.merge(options)
|
10
|
+
|
11
|
+
stub = stub_request(options[:method], %r{/services/data/v#{options[:api_version]}/#{endpoint}})
|
12
|
+
stub = stub.with(:body => options[:with_body]) if options[:with_body] && !RUBY_VERSION.match(/^1.8/)
|
13
|
+
stub = stub.to_return(:status => options[:status], :body => fixture(options[:fixture]), :headers => { 'Content-Type' => 'application/json'}) if options[:fixture]
|
14
|
+
stub
|
15
|
+
end
|
16
|
+
|
17
|
+
def stub_login_request(options={})
|
18
|
+
stub = stub_request(:post, "https://login.salesforce.com/services/oauth2/token")
|
19
|
+
stub = stub.with(:body => options[:with_body]) if options[:with_body] && !RUBY_VERSION.match(/^1.8/)
|
20
|
+
stub
|
21
|
+
end
|
22
|
+
|
23
|
+
def fixture(f)
|
24
|
+
File.read(File.expand_path("../../fixtures/#{f}.json", __FILE__))
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def requests(endpoint, options={})
|
31
|
+
before do
|
32
|
+
(@requests ||= []) << stub_api_request(endpoint, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
after do
|
36
|
+
@requests.each { |request| expect(request).to have_been_requested }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
RSpec.configure do |config|
|
43
|
+
config.include FixtureHelpers::InstanceMethods
|
44
|
+
config.extend FixtureHelpers::ClassMethods
|
45
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
RSpec::Matchers.define :include_picklist_values do |expected|
|
2
|
+
match do |actual|
|
3
|
+
actual.all? { |picklist_value| expected.include? picklist_value['value'] }
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
RSpec::Matchers.define :have_client do |expected|
|
8
|
+
match do |actual|
|
9
|
+
actual.instance_variable_get(:@client) == expected
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module MiddlewareExampleGroup
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval do
|
4
|
+
let(:app) { double('@app', :call => nil) }
|
5
|
+
let(:env) { { :request_headers => {}, :response_headers => {} } }
|
6
|
+
let(:retries) { 3 }
|
7
|
+
let(:options) { { } }
|
8
|
+
let(:client) { double(Sfdc::AbstractClient) }
|
9
|
+
let(:auth_callback) { double(Proc) }
|
10
|
+
let(:success_response) { Sfdc::Mash.new(JSON.parse(fixture(:auth_success_response))) }
|
11
|
+
subject(:middleware) { described_class.new app, client, options }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.include self,
|
17
|
+
:example_group => { :file_path => %r{spec/unit/middleware} }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
shared_examples_for 'authentication middleware' do
|
22
|
+
describe '.authenticate!' do
|
23
|
+
after do
|
24
|
+
request.should have_been_requested
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when successful' do
|
28
|
+
let!(:request) { success_request }
|
29
|
+
|
30
|
+
describe '@options' do
|
31
|
+
subject { options }
|
32
|
+
|
33
|
+
before do
|
34
|
+
middleware.authenticate!
|
35
|
+
end
|
36
|
+
|
37
|
+
its([:instance_url]) { should eq 'https://na1.salesforce.com' }
|
38
|
+
its([:oauth_token]) { should eq '00Dx0000000BV7z!AR8AQAxo9UfVkh8AlV0Gomt9Czx9LjHnSSpwBMmbRcgKFmxOtvxjTrKW19ye6PE3Ds1eQz3z8jr3W7_VbWmEu4Q8TVGSTHxs' }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when an authentication_callback is specified' do
|
42
|
+
before(:each) do
|
43
|
+
options.merge!(:authentication_callback => auth_callback)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'calls the authentication callback with the response body' do
|
47
|
+
auth_callback.should_receive(:call).with(success_response)
|
48
|
+
middleware.authenticate!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when unsuccessful' do
|
54
|
+
let!(:request) { fail_request }
|
55
|
+
|
56
|
+
it 'raises an exception' do
|
57
|
+
expect {
|
58
|
+
middleware.authenticate!
|
59
|
+
}.to raise_error Sfdc::AuthenticationError, /^invalid_grant: .*/
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when an authentication_callback is specified' do
|
63
|
+
before(:each) do
|
64
|
+
options.merge!(:authentication_callback => auth_callback)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not call the authentication callback' do
|
68
|
+
auth_callback.should_not_receive(:call)
|
69
|
+
expect do
|
70
|
+
middleware.authenticate!
|
71
|
+
end.to raise_error
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sfdc::AbstractClient do
|
4
|
+
subject { described_class }
|
5
|
+
|
6
|
+
it { should < Sfdc::Concerns::Base }
|
7
|
+
it { should < Sfdc::Concerns::Connection }
|
8
|
+
it { should < Sfdc::Concerns::Authentication }
|
9
|
+
it { should < Sfdc::Concerns::Caching }
|
10
|
+
it { should < Sfdc::Concerns::API }
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sfdc::Attachment do
|
4
|
+
let(:client) { double(Sfdc::AbstractClient) }
|
5
|
+
let(:body_url) { '/services/data/v26.0/sobjects/Attachment/00PG0000006Hll5MAC/Body' }
|
6
|
+
let(:hash) { { 'Id' => '1234', 'Body' => body_url } }
|
7
|
+
let(:sobject) { described_class.new(hash, client) }
|
8
|
+
|
9
|
+
describe '.Body' do
|
10
|
+
it 'requests the body' do
|
11
|
+
client.should_receive(:get).with(body_url).and_return(double('response').as_null_object)
|
12
|
+
sobject.Body
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sfdc::Collection do
|
4
|
+
let(:client) { double(Sfdc::AbstractClient) }
|
5
|
+
|
6
|
+
describe '#new' do
|
7
|
+
context 'without pagination' do
|
8
|
+
subject(:collection) do
|
9
|
+
described_class.new(JSON.parse(fixture('sobject/query_success_response')), client)
|
10
|
+
end
|
11
|
+
|
12
|
+
it { should respond_to :each }
|
13
|
+
its(:size) { should eq 1 }
|
14
|
+
its(:has_next_page?) { should be_false }
|
15
|
+
it { should have_client client }
|
16
|
+
|
17
|
+
describe 'each record' do
|
18
|
+
it { should be_all { |record| expect(record).to be_a Sfdc::SObject } }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with pagination' do
|
23
|
+
let(:first_page) { JSON.parse(fixture('sobject/query_paginated_first_page_response')) }
|
24
|
+
let(:next_page) { JSON.parse(fixture('sobject/query_paginated_last_page_response')) }
|
25
|
+
subject(:collection) { described_class.new(first_page, client) }
|
26
|
+
|
27
|
+
it { should respond_to :each }
|
28
|
+
it { should have_client client }
|
29
|
+
|
30
|
+
context 'when only values from the first page are being requested' do
|
31
|
+
before { client.should_receive(:get).never }
|
32
|
+
|
33
|
+
its(:size) { should eq 2 }
|
34
|
+
its(:first) { should be_a Sfdc::SObject }
|
35
|
+
its(:current_page) { should be_a Array }
|
36
|
+
its(:current_page) { should have(1).element }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when all of the values are being requested' do
|
40
|
+
before do
|
41
|
+
client.stub(:get).
|
42
|
+
and_return(double(:body => Sfdc::Collection.new(next_page, client)))
|
43
|
+
end
|
44
|
+
|
45
|
+
its(:pages) { should be_all { |page| expect(page).to be_a Sfdc::Collection } }
|
46
|
+
its(:has_next_page?) { should be_true }
|
47
|
+
it { should be_all { |record| expect(record).to be_a Sfdc::SObject } }
|
48
|
+
its(:next_page) { should be_a Sfdc::Collection }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sfdc::Concerns::API do
|
4
|
+
let(:response) { double('Faraday::Response', :body => double('Body')) }
|
5
|
+
|
6
|
+
describe '.list_sobjects' do
|
7
|
+
subject { client.list_sobjects }
|
8
|
+
|
9
|
+
before do
|
10
|
+
client.stub :describe => [ { 'name' => 'foo' } ]
|
11
|
+
end
|
12
|
+
|
13
|
+
it { should eq ['foo'] }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.describe' do
|
17
|
+
subject(:describe) { client.describe }
|
18
|
+
|
19
|
+
it 'returns the global describe' do
|
20
|
+
sobjects = double('sobjects')
|
21
|
+
response.body.stub(:[]).with('sobjects').and_return(sobjects)
|
22
|
+
client.should_receive(:api_get).
|
23
|
+
with('sobjects').
|
24
|
+
and_return(response)
|
25
|
+
expect(describe).to eq sobjects
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when given the name of an sobject' do
|
29
|
+
subject(:describe) { client.describe('Whizbang') }
|
30
|
+
|
31
|
+
it 'returns the full describe' do
|
32
|
+
client.should_receive(:api_get).
|
33
|
+
with('sobjects/Whizbang/describe').
|
34
|
+
and_return(response)
|
35
|
+
expect(describe).to eq response.body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.describe_layouts' do
|
41
|
+
subject(:describe_layouts) { client.describe_layouts('Whizbang') }
|
42
|
+
|
43
|
+
it 'returns the layouts for the sobject' do
|
44
|
+
client.should_receive(:api_get).
|
45
|
+
with('sobjects/Whizbang/describe/layouts').
|
46
|
+
and_return(response)
|
47
|
+
expect(describe_layouts).to eq response.body
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when given the id of a layout' do
|
51
|
+
subject(:describe_layouts) { client.describe_layouts('Whizbang', '012E0000000RHEp') }
|
52
|
+
|
53
|
+
it 'returns the describe for the specified layout' do
|
54
|
+
client.should_receive(:api_get).
|
55
|
+
with('sobjects/Whizbang/describe/layouts/012E0000000RHEp').
|
56
|
+
and_return(response)
|
57
|
+
expect(describe_layouts).to eq response.body
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.org_id' do
|
63
|
+
subject(:org_id) { client.org_id }
|
64
|
+
|
65
|
+
it 'returns the organization id' do
|
66
|
+
organizations = [ { 'Id' => 'foo' } ]
|
67
|
+
client.should_receive(:query).
|
68
|
+
with('select id from Organization').
|
69
|
+
and_return(organizations)
|
70
|
+
expect(org_id).to eq 'foo'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.query' do
|
75
|
+
let(:soql) { 'Select Id from Account' }
|
76
|
+
subject(:results) { client.query(soql) }
|
77
|
+
|
78
|
+
context 'with mashify middleware' do
|
79
|
+
before do
|
80
|
+
client.stub :mashify? => true
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns the body' do
|
84
|
+
client.should_receive(:api_get).
|
85
|
+
with('query', :q => soql).
|
86
|
+
and_return(response)
|
87
|
+
expect(results).to eq response.body
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'without mashify middleware' do
|
92
|
+
before do
|
93
|
+
client.stub :mashify? => false
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'returns the records attribute of the body' do
|
97
|
+
records = double('records')
|
98
|
+
response.body.stub(:[]).
|
99
|
+
with('records').
|
100
|
+
and_return(records)
|
101
|
+
client.should_receive(:api_get).
|
102
|
+
with('query', :q => soql).
|
103
|
+
and_return(response)
|
104
|
+
expect(results).to eq records
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '.search' do
|
110
|
+
let(:sosl) { 'FIND {bar}' }
|
111
|
+
subject(:results) { client.search(sosl) }
|
112
|
+
|
113
|
+
it 'performs a sosl search' do
|
114
|
+
client.should_receive(:api_get).
|
115
|
+
with('search', :q => sosl).
|
116
|
+
and_return(response)
|
117
|
+
expect(results).to eq response.body
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
[:create, :update, :upsert, :destroy].each do |method|
|
122
|
+
describe ".#{method}" do
|
123
|
+
let(:args) { [] }
|
124
|
+
subject(:result) { client.send(method, *args) }
|
125
|
+
|
126
|
+
it "delegates to :#{method}!" do
|
127
|
+
client.should_receive(:"#{method}!").
|
128
|
+
with(*args).
|
129
|
+
and_return(response)
|
130
|
+
expect(result).to eq response
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'rescues exceptions' do
|
134
|
+
[Faraday::Error::ClientError].each do |exception_klass|
|
135
|
+
client.should_receive(:"#{method}!").
|
136
|
+
with(*args).
|
137
|
+
and_raise(exception_klass.new(nil))
|
138
|
+
expect(result).to eq false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '.create!' do
|
145
|
+
let(:sobject) { 'Whizbang' }
|
146
|
+
let(:attrs) { Hash.new }
|
147
|
+
subject(:result) { client.create!(sobject, attrs) }
|
148
|
+
|
149
|
+
it 'send an HTTP POST, and returns the id of the record' do
|
150
|
+
response.body.stub(:[]).with('id').and_return('1234')
|
151
|
+
client.should_receive(:api_post).
|
152
|
+
with('sobjects/Whizbang', attrs).
|
153
|
+
and_return(response)
|
154
|
+
expect(result).to eq '1234'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '.update!' do
|
159
|
+
let(:sobject) { 'Whizbang' }
|
160
|
+
let(:attrs) { Hash.new }
|
161
|
+
subject(:result) { client.update!(sobject, attrs) }
|
162
|
+
|
163
|
+
context 'when the id field is present' do
|
164
|
+
let(:attrs) { { :id => '1234' } }
|
165
|
+
|
166
|
+
it 'sends an HTTP PATCH, and returns true' do
|
167
|
+
client.should_receive(:api_patch).
|
168
|
+
with('sobjects/Whizbang/1234', attrs)
|
169
|
+
expect(result).to be_true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when the id field is missing from the attrs' do
|
174
|
+
subject { lambda { result }}
|
175
|
+
it { should raise_error ArgumentError, 'Id field missing from attrs.' }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '.upsert!' do
|
180
|
+
let(:sobject) { 'Whizbang' }
|
181
|
+
let(:field) { :External_ID__c }
|
182
|
+
let(:attrs) { { 'External_ID__c' => '1234' } }
|
183
|
+
subject(:result) { client.upsert!(sobject, field, attrs) }
|
184
|
+
|
185
|
+
context 'when the record is found and updated' do
|
186
|
+
it 'returns true' do
|
187
|
+
response.body.stub :[]
|
188
|
+
client.should_receive(:api_patch).
|
189
|
+
with('sobjects/Whizbang/External_ID__c/1234', {}).
|
190
|
+
and_return(response)
|
191
|
+
expect(result).to be_true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when the record is found and created' do
|
196
|
+
it 'returns the id of the record' do
|
197
|
+
response.body.stub(:[]).with('id').and_return('4321')
|
198
|
+
client.should_receive(:api_patch).
|
199
|
+
with('sobjects/Whizbang/External_ID__c/1234', {}).
|
200
|
+
and_return(response)
|
201
|
+
expect(result).to eq '4321'
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe '.destroy!' do
|
207
|
+
let(:id) { '1234' }
|
208
|
+
let(:sobject) { 'Whizbang' }
|
209
|
+
subject(:result) { client.destroy!(sobject, id) }
|
210
|
+
|
211
|
+
it 'sends and HTTP delete, and returns true' do
|
212
|
+
client.should_receive(:api_delete).
|
213
|
+
with('sobjects/Whizbang/1234')
|
214
|
+
expect(result).to be_true
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe '.find' do
|
219
|
+
let(:sobject) { 'Whizbang' }
|
220
|
+
let(:id) { '1234' }
|
221
|
+
let(:field) { nil }
|
222
|
+
subject(:result) { client.find(sobject, id, field) }
|
223
|
+
|
224
|
+
context 'when no external id is specified' do
|
225
|
+
it 'returns the full representation of the object' do
|
226
|
+
client.should_receive(:api_get).
|
227
|
+
with('sobjects/Whizbang/1234').
|
228
|
+
and_return(response)
|
229
|
+
expect(result).to eq response.body
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'when an external id is specified' do
|
234
|
+
let(:field) { :External_ID__c }
|
235
|
+
|
236
|
+
it 'returns the full representation of the object' do
|
237
|
+
client.should_receive(:api_get).
|
238
|
+
with('sobjects/Whizbang/External_ID__c/1234').
|
239
|
+
and_return(response)
|
240
|
+
expect(result).to eq response.body
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|