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.

Files changed (69) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +22 -0
  6. data/README.md +161 -0
  7. data/Rakefile +10 -0
  8. data/lib/restforce.rb +18 -0
  9. data/lib/restforce/client.rb +287 -0
  10. data/lib/restforce/collection.rb +23 -0
  11. data/lib/restforce/config.rb +68 -0
  12. data/lib/restforce/mash.rb +61 -0
  13. data/lib/restforce/middleware.rb +30 -0
  14. data/lib/restforce/middleware/authentication.rb +32 -0
  15. data/lib/restforce/middleware/authentication/oauth.rb +22 -0
  16. data/lib/restforce/middleware/authentication/password.rb +27 -0
  17. data/lib/restforce/middleware/authorization.rb +19 -0
  18. data/lib/restforce/middleware/instance_url.rb +27 -0
  19. data/lib/restforce/middleware/mashify.rb +18 -0
  20. data/lib/restforce/middleware/raise_error.rb +18 -0
  21. data/lib/restforce/sobject.rb +41 -0
  22. data/lib/restforce/version.rb +3 -0
  23. data/restforce.gemspec +28 -0
  24. data/spec/fixtures/auth_error_response.json +1 -0
  25. data/spec/fixtures/auth_success_response.json +1 -0
  26. data/spec/fixtures/expired_session_response.json +1 -0
  27. data/spec/fixtures/reauth_success_response.json +1 -0
  28. data/spec/fixtures/refresh_error_response.json +1 -0
  29. data/spec/fixtures/refresh_success_response.json +7 -0
  30. data/spec/fixtures/services_data_success_response.json +12 -0
  31. data/spec/fixtures/sobject/create_success_response.json +5 -0
  32. data/spec/fixtures/sobject/delete_error_response.json +1 -0
  33. data/spec/fixtures/sobject/describe_sobjects_success_response.json +31 -0
  34. data/spec/fixtures/sobject/list_sobjects_success_response.json +31 -0
  35. data/spec/fixtures/sobject/org_query_response.json +11 -0
  36. data/spec/fixtures/sobject/query_aggregate_success_response.json +23 -0
  37. data/spec/fixtures/sobject/query_empty_response.json +5 -0
  38. data/spec/fixtures/sobject/query_error_response.json +4 -0
  39. data/spec/fixtures/sobject/query_paginated_first_page_response.json +12 -0
  40. data/spec/fixtures/sobject/query_paginated_last_page_response.json +11 -0
  41. data/spec/fixtures/sobject/query_success_response.json +36 -0
  42. data/spec/fixtures/sobject/recent_success_response.json +18 -0
  43. data/spec/fixtures/sobject/search_error_response.json +4 -0
  44. data/spec/fixtures/sobject/search_success_response.json +16 -0
  45. data/spec/fixtures/sobject/sobject_describe_error_response.json +4 -0
  46. data/spec/fixtures/sobject/sobject_describe_success_response.json +1304 -0
  47. data/spec/fixtures/sobject/sobject_find_error_response.json +4 -0
  48. data/spec/fixtures/sobject/sobject_find_success_response.json +29 -0
  49. data/spec/fixtures/sobject/upsert_created_success_response.json +2 -0
  50. data/spec/fixtures/sobject/upsert_error_response.json +1 -0
  51. data/spec/fixtures/sobject/upsert_multiple_error_response.json +1 -0
  52. data/spec/fixtures/sobject/upsert_updated_success_response.json +0 -0
  53. data/spec/fixtures/sobject/write_error_response.json +6 -0
  54. data/spec/lib/client_spec.rb +214 -0
  55. data/spec/lib/collection_spec.rb +50 -0
  56. data/spec/lib/config_spec.rb +70 -0
  57. data/spec/lib/middleware/authentication/oauth_spec.rb +30 -0
  58. data/spec/lib/middleware/authentication/password_spec.rb +37 -0
  59. data/spec/lib/middleware/authentication_spec.rb +67 -0
  60. data/spec/lib/middleware/authorization_spec.rb +17 -0
  61. data/spec/lib/middleware/instance_url_spec.rb +48 -0
  62. data/spec/lib/middleware/mashify_spec.rb +28 -0
  63. data/spec/lib/middleware/raise_error_spec.rb +27 -0
  64. data/spec/lib/sobject_spec.rb +93 -0
  65. data/spec/spec_helper.rb +17 -0
  66. data/spec/support/basic_client.rb +35 -0
  67. data/spec/support/fixture_helpers.rb +20 -0
  68. data/spec/support/middleware.rb +33 -0
  69. metadata +257 -0
@@ -0,0 +1,4 @@
1
+ [ {
2
+ "message" : "Provided external ID field does not exist or is not accessible: 23foo",
3
+ "errorCode" : "NOT_FOUND"
4
+ } ]
@@ -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,2 @@
1
+ {"id":"foo" +
2
+ "","errors":[],"success":true}
@@ -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"]
@@ -0,0 +1,6 @@
1
+ [
2
+ {
3
+ "message":"No such column 'foo' on sobject of type Bar",
4
+ "errorCode":"INVALID_FIELD"
5
+ }
6
+ ]
@@ -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