restforce 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of restforce might be problematic. Click here for more details.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +161 -0
- data/Rakefile +10 -0
- data/lib/restforce.rb +18 -0
- data/lib/restforce/client.rb +287 -0
- data/lib/restforce/collection.rb +23 -0
- data/lib/restforce/config.rb +68 -0
- data/lib/restforce/mash.rb +61 -0
- data/lib/restforce/middleware.rb +30 -0
- data/lib/restforce/middleware/authentication.rb +32 -0
- data/lib/restforce/middleware/authentication/oauth.rb +22 -0
- data/lib/restforce/middleware/authentication/password.rb +27 -0
- data/lib/restforce/middleware/authorization.rb +19 -0
- data/lib/restforce/middleware/instance_url.rb +27 -0
- data/lib/restforce/middleware/mashify.rb +18 -0
- data/lib/restforce/middleware/raise_error.rb +18 -0
- data/lib/restforce/sobject.rb +41 -0
- data/lib/restforce/version.rb +3 -0
- data/restforce.gemspec +28 -0
- data/spec/fixtures/auth_error_response.json +1 -0
- data/spec/fixtures/auth_success_response.json +1 -0
- data/spec/fixtures/expired_session_response.json +1 -0
- data/spec/fixtures/reauth_success_response.json +1 -0
- data/spec/fixtures/refresh_error_response.json +1 -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 +4 -0
- data/spec/fixtures/sobject/query_paginated_first_page_response.json +12 -0
- data/spec/fixtures/sobject/query_paginated_last_page_response.json +11 -0
- data/spec/fixtures/sobject/query_success_response.json +36 -0
- data/spec/fixtures/sobject/recent_success_response.json +18 -0
- data/spec/fixtures/sobject/search_error_response.json +4 -0
- data/spec/fixtures/sobject/search_success_response.json +16 -0
- data/spec/fixtures/sobject/sobject_describe_error_response.json +4 -0
- data/spec/fixtures/sobject/sobject_describe_success_response.json +1304 -0
- data/spec/fixtures/sobject/sobject_find_error_response.json +4 -0
- data/spec/fixtures/sobject/sobject_find_success_response.json +29 -0
- data/spec/fixtures/sobject/upsert_created_success_response.json +2 -0
- data/spec/fixtures/sobject/upsert_error_response.json +1 -0
- data/spec/fixtures/sobject/upsert_multiple_error_response.json +1 -0
- data/spec/fixtures/sobject/upsert_updated_success_response.json +0 -0
- data/spec/fixtures/sobject/write_error_response.json +6 -0
- data/spec/lib/client_spec.rb +214 -0
- data/spec/lib/collection_spec.rb +50 -0
- data/spec/lib/config_spec.rb +70 -0
- data/spec/lib/middleware/authentication/oauth_spec.rb +30 -0
- data/spec/lib/middleware/authentication/password_spec.rb +37 -0
- data/spec/lib/middleware/authentication_spec.rb +67 -0
- data/spec/lib/middleware/authorization_spec.rb +17 -0
- data/spec/lib/middleware/instance_url_spec.rb +48 -0
- data/spec/lib/middleware/mashify_spec.rb +28 -0
- data/spec/lib/middleware/raise_error_spec.rb +27 -0
- data/spec/lib/sobject_spec.rb +93 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/basic_client.rb +35 -0
- data/spec/support/fixture_helpers.rb +20 -0
- data/spec/support/middleware.rb +33 -0
- metadata +257 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
{
|
2
|
+
"attributes" : {
|
3
|
+
"type" : "Whizbang",
|
4
|
+
"url" : "/services/data/v20.0/sobjects/Whizbang/23foo"
|
5
|
+
},
|
6
|
+
"Id" : "23foo",
|
7
|
+
"OwnerId" : "owner_id",
|
8
|
+
"IsDeleted" : false,
|
9
|
+
"Name" : "My First Whizbang",
|
10
|
+
"CreatedById" : "created_by_id",
|
11
|
+
"LastModifiedById" : "last_modified_by_id",
|
12
|
+
"Auto_Number" : "A-1",
|
13
|
+
"Checkbox_Label" : true,
|
14
|
+
"Currency_Label" : 23.0,
|
15
|
+
"Date_Label" : "2010-01-01",
|
16
|
+
"DateTime_Label" : "2011-07-07T00:37:00.000+0000",
|
17
|
+
"OtherDateTime_Label" : null,
|
18
|
+
"Email_Label" : "danny@example.com",
|
19
|
+
"Number_Label" : 23.0,
|
20
|
+
"Percent_Label" : 33.0,
|
21
|
+
"Phone_Label" : "(415) 555-1212",
|
22
|
+
"Picklist_Label" : "one",
|
23
|
+
"Picklist_Multiselect_Label" : "four;six",
|
24
|
+
"Text_Label" : "some text",
|
25
|
+
"TextArea_Label" : "a text area",
|
26
|
+
"TextAreaLong_Label" : "a loooooooooooooong text area",
|
27
|
+
"TextAreaRich_Label" : "Rich <strong>text</strong>",
|
28
|
+
"URL_Label" : "http://pivotallabs.com"
|
29
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
[{"message":"Provided external ID field does not exist or is not accessible: Namez","errorCode":"NOT_FOUND"}]
|
@@ -0,0 +1 @@
|
|
1
|
+
["/services/data/v23.0/sobjects/Whizbang/foo","/services/data/v23.0/sobjects/Whizbang/bar"]
|
File without changes
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for 'methods' do
|
4
|
+
describe '#new' do
|
5
|
+
context 'without options passed in' do
|
6
|
+
it 'should not raise an exception' do
|
7
|
+
expect {
|
8
|
+
described_class.new
|
9
|
+
}.to_not raise_error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with a non-hash value' do
|
14
|
+
it 'should raise an exception' do
|
15
|
+
expect {
|
16
|
+
described_class.new 'foo'
|
17
|
+
}.to raise_error, 'Please specify a hash of options'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '@options' do
|
23
|
+
subject { client.instance_variable_get :@options }
|
24
|
+
|
25
|
+
its([:oauth_token]) { should eq oauth_token }
|
26
|
+
its([:refresh_token]) { should eq refresh_token }
|
27
|
+
its([:client_id]) { should eq client_id }
|
28
|
+
its([:client_secret]) { should eq client_secret }
|
29
|
+
its([:username]) { should eq username }
|
30
|
+
its([:password]) { should eq password }
|
31
|
+
its([:security_token]) { should eq security_token }
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.authentication_middleware' do
|
35
|
+
subject { client.send :authentication_middleware }
|
36
|
+
|
37
|
+
context 'without required options for authentication middleware to be provided' do
|
38
|
+
let(:client_options) { {} }
|
39
|
+
|
40
|
+
it { should be_nil }
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with username, password, security token, client id and client secret provided' do
|
44
|
+
let(:client_options) { password_options }
|
45
|
+
|
46
|
+
it { should eq Restforce::Middleware::Authentication::Password }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with oauth token, refresh token, client id and client secret provided' do
|
50
|
+
let(:client_options) { oauth_options }
|
51
|
+
|
52
|
+
it { should eq Restforce::Middleware::Authentication::OAuth }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '.describe_sobjects' do
|
57
|
+
before do
|
58
|
+
stub_api_request :sobjects, with: 'sobject/describe_sobjects_success_response'
|
59
|
+
end
|
60
|
+
|
61
|
+
subject { client.describe_sobjects }
|
62
|
+
it { should be_an Array }
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '.list_sobjects' do
|
66
|
+
before do
|
67
|
+
stub_api_request :sobjects, with: 'sobject/describe_sobjects_success_response'
|
68
|
+
end
|
69
|
+
|
70
|
+
subject { client.list_sobjects }
|
71
|
+
it { should be_an Array }
|
72
|
+
it { should eq ['Account'] }
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.describe' do
|
76
|
+
before do
|
77
|
+
stub_api_request 'sobject/Whizbang/describe', with: 'sobject/sobject_describe_success_response'
|
78
|
+
end
|
79
|
+
|
80
|
+
subject { client.describe('Whizbang') }
|
81
|
+
its(['name']) { should eq 'Whizbang' }
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.query' do
|
85
|
+
before do
|
86
|
+
stub_api_request :query, with: 'sobject/query_success_response'
|
87
|
+
end
|
88
|
+
|
89
|
+
subject { client.query('SELECT some, fields FROM object') }
|
90
|
+
it { should be_an Array }
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '.search' do
|
94
|
+
before do
|
95
|
+
stub_api_request :search, with: 'sobject/search_success_response'
|
96
|
+
end
|
97
|
+
|
98
|
+
subject { client.search('FIND {bar}') }
|
99
|
+
it { should be_an Array }
|
100
|
+
its(:size) { should eq 2 }
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '.org_id' do
|
104
|
+
before do
|
105
|
+
stub_api_request :query, with: 'sobject/org_query_response'
|
106
|
+
end
|
107
|
+
|
108
|
+
subject { client.org_id }
|
109
|
+
it { should eq '00Dx0000000BV7z' }
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '.create' do
|
113
|
+
before do
|
114
|
+
@request = stub_api_request 'sobjects/Account', with: 'sobject/create_success_response', method: :post, body: "{\"Name\":\"Foobar\"}"
|
115
|
+
end
|
116
|
+
|
117
|
+
after do
|
118
|
+
@request.should have_been_requested
|
119
|
+
end
|
120
|
+
|
121
|
+
subject { client.create('Account', Name: 'Foobar') }
|
122
|
+
it { should eq 'some_id' }
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '.update' do
|
126
|
+
pending 'with invalid Id'
|
127
|
+
pending 'with missing Id'
|
128
|
+
context 'with success' do
|
129
|
+
before do
|
130
|
+
@request = stub_api_request 'sobjects/Account/001D000000INjVe', method: :patch, body: "{\"Name\":\"Foobar\"}"
|
131
|
+
end
|
132
|
+
|
133
|
+
after do
|
134
|
+
@request.should have_been_requested
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'with symbol Id key' do
|
138
|
+
subject { client.update('Account', Id: '001D000000INjVe', Name: 'Foobar') }
|
139
|
+
it { should be_true }
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'with string Id key' do
|
143
|
+
subject { client.update('Account', 'Id' => '001D000000INjVe', 'Name' => 'Foobar') }
|
144
|
+
it { should be_true }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '.destroy' do
|
150
|
+
pending 'with invalid Id'
|
151
|
+
|
152
|
+
context 'with success' do
|
153
|
+
before do
|
154
|
+
@request = stub_api_request 'sobjects/Account/001D000000INjVe', method: :delete
|
155
|
+
end
|
156
|
+
|
157
|
+
after do
|
158
|
+
@request.should have_been_requested
|
159
|
+
end
|
160
|
+
|
161
|
+
subject { client.destroy('Account', '001D000000INjVe') }
|
162
|
+
it { should be_true }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'with mashify middleware' do
|
168
|
+
describe Restforce::Client do
|
169
|
+
include_context 'basic client'
|
170
|
+
include_examples 'methods'
|
171
|
+
|
172
|
+
describe '.mashify?' do
|
173
|
+
subject { client.send :mashify? }
|
174
|
+
|
175
|
+
it { should be_true }
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '.query' do
|
179
|
+
context 'with pagination' do
|
180
|
+
before do
|
181
|
+
@requests = [].tap do |requests|
|
182
|
+
requests << stub_api_request('query\?q', with: 'sobject/query_paginated_first_page_response')
|
183
|
+
requests << stub_api_request('query/01gD', with: 'sobject/query_paginated_last_page_response')
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
after do
|
188
|
+
@requests.each { |request| request.should have_been_requested }
|
189
|
+
end
|
190
|
+
|
191
|
+
subject { client.query('SELECT some, fields FROM object').next_page }
|
192
|
+
it { should be_a Restforce::Collection }
|
193
|
+
specify { subject.first.Text_Label.should eq 'Last Page' }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe 'without mashify middleware' do
|
200
|
+
before do
|
201
|
+
client.send(:connection).builder.delete(Restforce::Middleware::Mashify)
|
202
|
+
end
|
203
|
+
|
204
|
+
describe Restforce::Client do
|
205
|
+
include_context 'basic client'
|
206
|
+
include_examples 'methods'
|
207
|
+
|
208
|
+
describe '.mashify?' do
|
209
|
+
subject { client.send :mashify? }
|
210
|
+
|
211
|
+
it { should be_false }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Restforce::Collection do
|
4
|
+
let(:client) { double('client') }
|
5
|
+
|
6
|
+
describe '#new' do
|
7
|
+
subject { records }
|
8
|
+
|
9
|
+
context 'without pagination' do
|
10
|
+
let(:records) do
|
11
|
+
described_class.new(JSON.parse(fixture('sobject/query_success_response')), client)
|
12
|
+
end
|
13
|
+
|
14
|
+
it { should respond_to :each }
|
15
|
+
its(:size) { should eq 1 }
|
16
|
+
its(:total_size) { should eq 1 }
|
17
|
+
its(:next_page_url) { should be_nil }
|
18
|
+
specify { subject.instance_variable_get(:@client).should eq client }
|
19
|
+
|
20
|
+
describe 'each record' do
|
21
|
+
it 'should be a Restforce::SObject' do
|
22
|
+
records.each do |record|
|
23
|
+
record.should be_a Restforce::SObject
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with pagination' do
|
30
|
+
let(:records) do
|
31
|
+
described_class.new(JSON.parse(fixture('sobject/query_paginated_first_page_response')), client)
|
32
|
+
end
|
33
|
+
|
34
|
+
it { should respond_to :each }
|
35
|
+
its(:size) { should eq 1 }
|
36
|
+
its(:total_size) { should eq 2 }
|
37
|
+
its(:next_page_url) { should eq '/services/data/v24.0/query/01gD' }
|
38
|
+
specify { subject.instance_variable_get(:@client).should eq client }
|
39
|
+
|
40
|
+
describe '.next_page' do
|
41
|
+
before do
|
42
|
+
client.should_receive(:get).and_return(Faraday::Response.new(body: Restforce::Collection.new({'records' => {}}, client)))
|
43
|
+
end
|
44
|
+
|
45
|
+
subject { records.next_page }
|
46
|
+
it { should be_a Restforce::Collection }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Restforce do
|
4
|
+
after do
|
5
|
+
Restforce.instance_variable_set :@configuration, nil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#configuration' do
|
9
|
+
subject { Restforce.configuration }
|
10
|
+
|
11
|
+
it { should be_a Restforce::Configuration }
|
12
|
+
|
13
|
+
context 'by default' do
|
14
|
+
its(:api_version) { should eq '24.0' }
|
15
|
+
its(:host) { should eq 'login.salesforce.com' }
|
16
|
+
[:username, :password, :security_token, :client_id, :client_secret,
|
17
|
+
:oauth_token, :refresh_token, :instance_url].each do |attr|
|
18
|
+
its(attr) { should be_nil }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#configure' do
|
24
|
+
[:username, :password, :security_token, :client_id, :client_secret,
|
25
|
+
:oauth_token, :refresh_token, :instance_url, :api_version, :host].each do |attr|
|
26
|
+
it "allows #{attr} to be set" do
|
27
|
+
Restforce.configure do |config|
|
28
|
+
config.send("#{attr}=", 'foobar')
|
29
|
+
end
|
30
|
+
Restforce.configuration.send(attr).should eq 'foobar'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#log?' do
|
36
|
+
subject { Restforce.log? }
|
37
|
+
|
38
|
+
context 'by default' do
|
39
|
+
it { should be_false }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#log' do
|
44
|
+
after do
|
45
|
+
Restforce.log = false
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with logging disabled' do
|
49
|
+
before do
|
50
|
+
Restforce.log = false
|
51
|
+
Restforce.configuration.logger.should_not_receive(:debug)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'doesnt log anytning' do
|
55
|
+
Restforce.log 'foobar'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with logging enabled' do
|
60
|
+
before do
|
61
|
+
Restforce.log = true
|
62
|
+
Restforce.configuration.logger.should_receive(:debug).with('foobar')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'logs something' do
|
66
|
+
Restforce.log 'foobar'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Restforce::Middleware::Authentication::OAuth do
|
4
|
+
let(:app) { double('app') }
|
5
|
+
let(:env) { { } }
|
6
|
+
let(:middleware) { described_class.new app, nil, options }
|
7
|
+
|
8
|
+
let(:options) do
|
9
|
+
{ host: 'login.salesforce.com',
|
10
|
+
refresh_token: 'refresh_token',
|
11
|
+
client_id: 'client_id',
|
12
|
+
client_secret: 'client_secret' }
|
13
|
+
end
|
14
|
+
|
15
|
+
it_behaves_like 'authentication middleware' do
|
16
|
+
let(:success_request) do
|
17
|
+
stub_request(:post, "https://login.salesforce.com/services/oauth2/token").
|
18
|
+
with(:body => "grant_type=refresh_token&refresh_token=refresh_token&" \
|
19
|
+
"client_id=client_id&client_secret=client_secret").
|
20
|
+
to_return(:status => 200, :body => fixture(:auth_success_response))
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:fail_request) do
|
24
|
+
stub_request(:post, "https://login.salesforce.com/services/oauth2/token").
|
25
|
+
with(:body => "grant_type=refresh_token&refresh_token=refresh_token&" \
|
26
|
+
"client_id=client_id&client_secret=client_secret").
|
27
|
+
to_return(:status => 400, :body => fixture(:auth_success_response))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Restforce::Middleware::Authentication::Password do
|
4
|
+
let(:app) { double('app') }
|
5
|
+
let(:env) { { } }
|
6
|
+
let(:middleware) { described_class.new app, nil, options }
|
7
|
+
|
8
|
+
let(:options) do
|
9
|
+
{ host: 'login.salesforce.com',
|
10
|
+
username: 'foo',
|
11
|
+
password: 'bar',
|
12
|
+
security_token: 'security_token',
|
13
|
+
client_id: 'client_id',
|
14
|
+
client_secret: 'client_secret' }
|
15
|
+
end
|
16
|
+
|
17
|
+
it_behaves_like 'authentication middleware' do
|
18
|
+
let(:success_request) do
|
19
|
+
stub_request(:post, "https://login.salesforce.com/services/oauth2/token").
|
20
|
+
with(:body => "grant_type=password&client_id=client_id&client_secret=" \
|
21
|
+
"client_secret&username=foo&password=barsecurity_token").
|
22
|
+
to_return(:status => 200, :body => fixture(:auth_success_response))
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:fail_request) do
|
26
|
+
stub_request(:post, "https://login.salesforce.com/services/oauth2/token").
|
27
|
+
with(:body => "grant_type=password&client_id=client_id&client_secret=" \
|
28
|
+
"client_secret&username=foo&password=barsecurity_token").
|
29
|
+
to_return(:status => 400, :body => fixture(:auth_success_response))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.password' do
|
34
|
+
subject { middleware.password }
|
35
|
+
it { should eq 'barsecurity_token' }
|
36
|
+
end
|
37
|
+
end
|