force 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +107 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +421 -0
- data/Rakefile +10 -0
- data/coverage/assets/0.7.1/application.css +1110 -0
- data/coverage/assets/0.7.1/application.js +626 -0
- data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
- data/coverage/assets/0.7.1/favicon_green.png +0 -0
- data/coverage/assets/0.7.1/favicon_red.png +0 -0
- data/coverage/assets/0.7.1/favicon_yellow.png +0 -0
- data/coverage/assets/0.7.1/loading.gif +0 -0
- data/coverage/assets/0.7.1/magnify.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +19808 -0
- data/force.gemspec +27 -0
- data/lib/force.rb +74 -0
- data/lib/force/abstract_client.rb +9 -0
- data/lib/force/attachment.rb +21 -0
- data/lib/force/client.rb +3 -0
- data/lib/force/collection.rb +45 -0
- data/lib/force/concerns/api.rb +321 -0
- data/lib/force/concerns/authentication.rb +39 -0
- data/lib/force/concerns/base.rb +59 -0
- data/lib/force/concerns/caching.rb +24 -0
- data/lib/force/concerns/canvas.rb +10 -0
- data/lib/force/concerns/connection.rb +74 -0
- data/lib/force/concerns/picklists.rb +87 -0
- data/lib/force/concerns/streaming.rb +31 -0
- data/lib/force/concerns/verbs.rb +67 -0
- data/lib/force/config.rb +140 -0
- data/lib/force/data/client.rb +18 -0
- data/lib/force/mash.rb +66 -0
- data/lib/force/middleware.rb +27 -0
- data/lib/force/middleware/authentication.rb +73 -0
- data/lib/force/middleware/authentication/password.rb +17 -0
- data/lib/force/middleware/authentication/token.rb +15 -0
- data/lib/force/middleware/authorization.rb +15 -0
- data/lib/force/middleware/caching.rb +22 -0
- data/lib/force/middleware/gzip.rb +31 -0
- data/lib/force/middleware/instance_url.rb +14 -0
- data/lib/force/middleware/logger.rb +40 -0
- data/lib/force/middleware/mashify.rb +16 -0
- data/lib/force/middleware/multipart.rb +55 -0
- data/lib/force/middleware/raise_error.rb +25 -0
- data/lib/force/signed_request.rb +48 -0
- data/lib/force/sobject.rb +68 -0
- data/lib/force/tooling/client.rb +11 -0
- data/lib/force/upload_io.rb +20 -0
- data/lib/force/version.rb +3 -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
- data/tmp/rspec_guard_result +1 -0
- metadata +383 -0
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 <= Force::AbstractClient },
|
38
|
+
:file_path => %r{spec/integration}
|
39
|
+
}
|
40
|
+
|
41
|
+
config.before :mashify => false do
|
42
|
+
client.middleware.delete(Force::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 => Force.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(Force::AbstractClient) }
|
9
|
+
let(:auth_callback) { double(Proc) }
|
10
|
+
let(:success_response) { Force::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 Force::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 Force::AbstractClient do
|
4
|
+
subject { described_class }
|
5
|
+
|
6
|
+
it { should < Force::Concerns::Base }
|
7
|
+
it { should < Force::Concerns::Connection }
|
8
|
+
it { should < Force::Concerns::Authentication }
|
9
|
+
it { should < Force::Concerns::Caching }
|
10
|
+
it { should < Force::Concerns::API }
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Force::Attachment do
|
4
|
+
let(:client) { double(Force::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 Force::Collection do
|
4
|
+
let(:client) { double(Force::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 Force::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 Force::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 => Force::Collection.new(next_page, client)))
|
43
|
+
end
|
44
|
+
|
45
|
+
its(:pages) { should be_all { |page| expect(page).to be_a Force::Collection } }
|
46
|
+
its(:has_next_page?) { should be_true }
|
47
|
+
it { should be_all { |record| expect(record).to be_a Force::SObject } }
|
48
|
+
its(:next_page) { should be_a Force::Collection }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Force::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
|