restforce 1.3.0 → 1.4.0

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 (63) hide show
  1. data/.travis.yml +1 -0
  2. data/CHANGELOG.md +7 -1
  3. data/Gemfile +4 -0
  4. data/Guardfile +11 -0
  5. data/README.md +19 -8
  6. data/lib/restforce.rb +44 -14
  7. data/lib/restforce/abstract_client.rb +9 -0
  8. data/lib/restforce/client.rb +1 -95
  9. data/lib/restforce/{client → concerns}/api.rb +9 -9
  10. data/lib/restforce/{client → concerns}/authentication.rb +9 -9
  11. data/lib/restforce/concerns/base.rb +58 -0
  12. data/lib/restforce/{client → concerns}/caching.rb +4 -4
  13. data/lib/restforce/concerns/canvas.rb +12 -0
  14. data/lib/restforce/{client → concerns}/connection.rb +13 -12
  15. data/lib/restforce/{client → concerns}/picklists.rb +1 -1
  16. data/lib/restforce/{client → concerns}/streaming.rb +3 -3
  17. data/lib/restforce/{client → concerns}/verbs.rb +4 -4
  18. data/lib/restforce/config.rb +40 -10
  19. data/lib/restforce/data/client.rb +18 -0
  20. data/lib/restforce/middleware/authentication.rb +9 -2
  21. data/lib/restforce/sobject.rb +1 -1
  22. data/lib/restforce/tooling/client.rb +13 -0
  23. data/lib/restforce/version.rb +1 -1
  24. data/spec/{lib/client_spec.rb → integration/abstract_client_spec.rb} +21 -214
  25. data/spec/integration/data/client_spec.rb +90 -0
  26. data/spec/spec_helper.rb +0 -14
  27. data/spec/support/client_integration.rb +45 -0
  28. data/spec/support/concerns.rb +18 -0
  29. data/spec/support/event_machine.rb +14 -0
  30. data/spec/support/middleware.rb +18 -1
  31. data/spec/unit/abstract_client_spec.rb +11 -0
  32. data/spec/{lib → unit}/attachment_spec.rb +3 -6
  33. data/spec/unit/collection_spec.rb +50 -0
  34. data/spec/unit/concerns/api_spec.rb +222 -0
  35. data/spec/unit/concerns/authentication_spec.rb +98 -0
  36. data/spec/unit/concerns/base_spec.rb +50 -0
  37. data/spec/unit/concerns/caching_spec.rb +29 -0
  38. data/spec/unit/concerns/canvas_spec.rb +30 -0
  39. data/spec/unit/concerns/connection_spec.rb +14 -0
  40. data/spec/{lib → unit}/config_spec.rb +13 -23
  41. data/spec/unit/data/client_spec.rb +10 -0
  42. data/spec/{lib → unit}/mash_spec.rb +0 -0
  43. data/spec/{lib → unit}/middleware/authentication/password_spec.rb +0 -4
  44. data/spec/{lib → unit}/middleware/authentication/token_spec.rb +0 -4
  45. data/spec/unit/middleware/authentication_spec.rb +67 -0
  46. data/spec/unit/middleware/authorization_spec.rb +11 -0
  47. data/spec/{lib → unit}/middleware/gzip_spec.rb +15 -30
  48. data/spec/unit/middleware/instance_url_spec.rb +24 -0
  49. data/spec/{lib → unit}/middleware/logger_spec.rb +4 -7
  50. data/spec/unit/middleware/mashify_spec.rb +11 -0
  51. data/spec/{lib → unit}/middleware/raise_error_spec.rb +4 -5
  52. data/spec/{lib → unit}/signed_request_spec.rb +0 -0
  53. data/spec/unit/sobject_spec.rb +68 -0
  54. data/spec/unit/tooling/client_spec.rb +7 -0
  55. metadata +75 -46
  56. data/lib/restforce/client/canvas.rb +0 -12
  57. data/spec/lib/collection_spec.rb +0 -52
  58. data/spec/lib/middleware/authentication_spec.rb +0 -69
  59. data/spec/lib/middleware/authorization_spec.rb +0 -17
  60. data/spec/lib/middleware/instance_url_spec.rb +0 -31
  61. data/spec/lib/middleware/mashify_spec.rb +0 -28
  62. data/spec/lib/sobject_spec.rb +0 -122
  63. data/spec/support/basic_client.rb +0 -37
@@ -3,6 +3,7 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.2
5
5
  - 1.9.3
6
+ - 2.0.0
6
7
  - jruby-18mode
7
8
  - jruby-19mode
8
9
  - ree
@@ -1,3 +1,9 @@
1
+ ## 1.4.0 (Jun 9, 2013)
2
+
3
+ * Added support for the tooling API.
4
+ * Fixed a bug with EMSynchrony adapter.
5
+ * Added proxy support.
6
+
1
7
  ## 1.3.0 (Apr 6, 2013)
2
8
 
3
9
  * Added support for lazily traversing paginated collections #61 by @nahiluhmot.
@@ -59,7 +65,7 @@
59
65
  * The http connection read/open timeout is now configurabled.
60
66
 
61
67
  Example:
62
-
68
+
63
69
  Restforce.configure do |config|
64
70
  config.timeout = 300
65
71
  end
data/Gemfile CHANGED
@@ -5,3 +5,7 @@ gemspec
5
5
 
6
6
  gem 'rake'
7
7
  gem 'jruby-openssl', :platforms => :jruby
8
+
9
+ group :development do
10
+ gem 'guard-rspec'
11
+ end
@@ -0,0 +1,11 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', all_on_start: false, all_after_pass: false do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch('spec/spec_helper.rb') { "spec" }
7
+
8
+ watch(%r{^lib/restforce/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
9
+ watch(%r{^lib/restforce/(.+)\.rb$}) { |m| "spec/integration/#{m[1]}_spec.rb" }
10
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
11
+ end
data/README.md CHANGED
@@ -77,7 +77,7 @@ If you prefer to use a username and password to authenticate:
77
77
  ```ruby
78
78
  client = Restforce.new :username => 'foo',
79
79
  :password => 'bar',
80
- :security_token => 'security token'
80
+ :security_token => 'security token',
81
81
  :client_id => 'client_id',
82
82
  :client_secret => 'client_secret'
83
83
  ```
@@ -103,7 +103,7 @@ You can specify a http proxy using the :proxy_uri option, as follows:
103
103
  ```ruby
104
104
  client = Restforce.new :username => 'foo',
105
105
  :password => 'bar',
106
- :security_token => 'security token'
106
+ :security_token => 'security token',
107
107
  :client_id => 'client_id',
108
108
  :client_secret => 'client_secret',
109
109
  :proxy_uri => 'http://proxy.example.com:123'
@@ -118,6 +118,7 @@ You can connect to sandbox orgs by specifying a host. The default host is
118
118
  ```ruby
119
119
  client = Restforce.new :host => 'test.salesforce.com'
120
120
  ```
121
+ The host can also be set with the environment variable SALESFORCE_HOST.
121
122
 
122
123
  #### Global configuration
123
124
 
@@ -132,9 +133,9 @@ end
132
133
 
133
134
  ### Bang! methods
134
135
 
135
- All the CRUD methods (create, update, upsert, destroy) have equivalent methods with
136
+ All the CRUD methods (create, update, upsert, destroy) have equivalent methods with
136
137
  a ! at the end (create!, update!, upsert!, destroy!), which can be used if you need
137
- to do some custom error handling. The bang methods will raise exceptions, while the
138
+ to do some custom error handling. The bang methods will raise exceptions, while the
138
139
  non-bang methods will return false in the event that an exception is raised. This
139
140
  works similarly to ActiveRecord.
140
141
 
@@ -146,7 +147,7 @@ works similarly to ActiveRecord.
146
147
  accounts = client.query("select Id, Something__c from Account where Id = 'someid'")
147
148
  # => #<Restforce::Collection >
148
149
 
149
- account = records.first
150
+ account = accounts.first
150
151
  # => #<Restforce::SObject >
151
152
 
152
153
  account.sobject_type
@@ -411,9 +412,10 @@ Restforce in a rails app, you can setup custom reporting to
411
412
  [Librato](https://github.com/librato/librato-rails) using ActiveSupport::Notifications:
412
413
 
413
414
  ```ruby
414
- client = Restforce.new
415
- client.middleware.insert_after Restforce::Middleware::InstanceURL,
416
- FaradayMiddleware::Instrumentation, name: 'request.salesforce'
415
+ client = Restforce.new do |builder|
416
+ builder.insert_after Restforce::Middleware::InstanceURL,
417
+ FaradayMiddleware::Instrumentation, name: 'request.salesforce'
418
+ end
417
419
 
418
420
  # config/initializers/notifications.rb
419
421
  ActiveSupport::Notifications.subscribe('request.salesforce') do |*args|
@@ -427,6 +429,15 @@ end
427
429
 
428
430
  You can use Restforce to decode signed requests from Salesforce. See [the example app](https://gist.github.com/4052312).
429
431
 
432
+ ## Tooling API
433
+
434
+ To use the [Tooling API](http://www.salesforce.com/us/developer/docs/api_toolingpre/api_tooling.pdf),
435
+ call `Restforce.tooling` instead of `Restforce.new`:
436
+
437
+ ```ruby
438
+ client = Restforce.tooling(...)
439
+ ```
440
+
430
441
  ## Contributing
431
442
 
432
443
  1. Fork it
@@ -6,24 +6,54 @@ require 'restforce/version'
6
6
  require 'restforce/config'
7
7
 
8
8
  module Restforce
9
- autoload :SignedRequest, 'restforce/signed_request'
10
- autoload :Collection, 'restforce/collection'
11
- autoload :Middleware, 'restforce/middleware'
12
- autoload :Attachment, 'restforce/attachment'
13
- autoload :UploadIO, 'restforce/upload_io'
14
- autoload :SObject, 'restforce/sobject'
15
- autoload :Client, 'restforce/client'
16
- autoload :Mash, 'restforce/mash'
17
-
18
- AuthenticationError = Class.new(StandardError)
19
- UnauthorizedError = Class.new(StandardError)
9
+ autoload :AbstractClient, 'restforce/abstract_client'
10
+ autoload :SignedRequest, 'restforce/signed_request'
11
+ autoload :Collection, 'restforce/collection'
12
+ autoload :Middleware, 'restforce/middleware'
13
+ autoload :Attachment, 'restforce/attachment'
14
+ autoload :UploadIO, 'restforce/upload_io'
15
+ autoload :SObject, 'restforce/sobject'
16
+ autoload :Client, 'restforce/client'
17
+ autoload :Mash, 'restforce/mash'
18
+
19
+ module Concerns
20
+ autoload :Authentication, 'restforce/concerns/authentication'
21
+ autoload :Connection, 'restforce/concerns/connection'
22
+ autoload :Picklists, 'restforce/concerns/picklists'
23
+ autoload :Streaming, 'restforce/concerns/streaming'
24
+ autoload :Caching, 'restforce/concerns/caching'
25
+ autoload :Canvas, 'restforce/concerns/canvas'
26
+ autoload :Verbs, 'restforce/concerns/verbs'
27
+ autoload :Base, 'restforce/concerns/base'
28
+ autoload :API, 'restforce/concerns/api'
29
+ end
30
+
31
+ module Data
32
+ autoload :Client, 'restforce/data/client'
33
+ end
34
+
35
+ module Tooling
36
+ autoload :Client, 'restforce/tooling/client'
37
+ end
38
+
39
+ Error = Class.new(StandardError)
40
+ AuthenticationError = Class.new(Error)
41
+ UnauthorizedError = Class.new(Error)
20
42
 
21
43
  class << self
22
- # Alias for Restforce::Client.new
44
+ # Alias for Restforce::Data::Client.new
23
45
  #
24
46
  # Shamelessly pulled from https://github.com/pengwynn/octokit/blob/master/lib/octokit.rb
25
- def new(options = {})
26
- Restforce::Client.new(options)
47
+ def new(*args)
48
+ data(*args)
49
+ end
50
+
51
+ def data(*args)
52
+ Restforce::Data::Client.new(*args)
53
+ end
54
+
55
+ def tooling(*args)
56
+ Restforce::Tooling::Client.new(*args)
27
57
  end
28
58
 
29
59
  # Helper for decoding signed requests.
@@ -0,0 +1,9 @@
1
+ module Restforce
2
+ class AbstractClient
3
+ include Restforce::Concerns::Base
4
+ include Restforce::Concerns::Connection
5
+ include Restforce::Concerns::Authentication
6
+ include Restforce::Concerns::Caching
7
+ include Restforce::Concerns::API
8
+ end
9
+ end
@@ -1,97 +1,3 @@
1
- require 'restforce/client/connection'
2
- require 'restforce/client/authentication'
3
- require 'restforce/client/streaming'
4
- require 'restforce/client/picklists'
5
- require 'restforce/client/caching'
6
- require 'restforce/client/canvas'
7
- require 'restforce/client/api'
8
-
9
1
  module Restforce
10
- class Client
11
- include Restforce::Client::Connection
12
- include Restforce::Client::Authentication
13
- include Restforce::Client::Streaming
14
- include Restforce::Client::Picklists
15
- include Restforce::Client::Caching
16
- include Restforce::Client::Canvas
17
- include Restforce::Client::API
18
-
19
- # Public: Creates a new client instance
20
- #
21
- # opts - A hash of options to be passed in (default: {}).
22
- # :username - The String username to use (required for password authentication).
23
- # :password - The String password to use (required for password authentication).
24
- # :security_token - The String security token to use (required for password authentication).
25
- #
26
- # :oauth_token - The String oauth access token to authenticate api
27
- # calls (required unless password
28
- # authentication is used).
29
- # :refresh_token - The String refresh token to obtain fresh
30
- # oauth access tokens (required if oauth
31
- # authentication is used).
32
- # :instance_url - The String base url for all api requests
33
- # (required if oauth authentication is used).
34
- #
35
- # :client_id - The oauth client id to use. Needed for both
36
- # password and oauth authentication
37
- # :client_secret - The oauth client secret to use.
38
- #
39
- # :host - The String hostname to use during
40
- # authentication requests (default: 'login.salesforce.com').
41
- #
42
- # :api_version - The String REST api version to use (default: '24.0')
43
- #
44
- # :authentication_retries - The number of times that client
45
- # should attempt to reauthenticate
46
- # before raising an exception (default: 3).
47
- #
48
- # :compress - Set to true to have Salesforce compress the response (default: false).
49
- # :timeout - Faraday connection request read/open timeout. (default: nil).
50
- #
51
- # :proxy_uri - Proxy URI: 'http://proxy.example.com:port' or 'http://user@pass:proxy.example.com:port'
52
- #
53
- # Examples
54
- #
55
- # # Initialize a new client using password authentication:
56
- # Restforce::Client.new :username => 'user',
57
- # :password => 'pass',
58
- # :security_token => 'security token',
59
- # :client_id => 'client id',
60
- # :client_secret => 'client secret'
61
- #
62
- # # Initialize a new client using oauth authentication:
63
- # Restforce::Client.new :oauth_token => 'access token',
64
- # :refresh_token => 'refresh token',
65
- # :instance_url => 'https://na1.salesforce.com',
66
- # :client_id => 'client id',
67
- # :client_secret => 'client secret'
68
- #
69
- # # Initialize a new client without using any authentication middleware:
70
- # Restforce::Client.new :oauth_token => 'access token',
71
- # :instance_url => 'https://na1.salesforce.com'
72
- #
73
- def initialize(opts = {})
74
- raise 'Please specify a hash of options' unless opts.is_a?(Hash)
75
- @options = Hash[Restforce.configuration.options.map { |option| [option, Restforce.configuration.send(option)] }]
76
- @options.merge! opts
77
- end
78
-
79
- def instance_url
80
- authenticate! unless @options[:instance_url]
81
- @options[:instance_url]
82
- end
83
-
84
- # Public: Returns a url to the resource.
85
- #
86
- # resource - A record that responds to to_sparam or a String/Fixnum.
87
- #
88
- # Returns the url to the resource.
89
- def url(resource)
90
- "#{instance_url}/#{(resource.respond_to?(:to_sparam) ? resource.to_sparam : resource)}"
91
- end
92
-
93
- def inspect
94
- "#<#{self.class} @options=#{@options.inspect}>"
95
- end
96
- end
2
+ Client = Data::Client
97
3
  end
@@ -1,9 +1,9 @@
1
- require 'restforce/client/verbs'
1
+ require 'restforce/concerns/verbs'
2
2
 
3
3
  module Restforce
4
- class Client
4
+ module Concerns
5
5
  module API
6
- extend Restforce::Client::Verbs
6
+ extend Restforce::Concerns::Verbs
7
7
 
8
8
  # Public: Helper methods for performing arbitrary actions against the API using
9
9
  # various HTTP verbs.
@@ -41,7 +41,7 @@ module Restforce
41
41
  def list_sobjects
42
42
  describe.collect { |sobject| sobject['name'] }
43
43
  end
44
-
44
+
45
45
  # Public: Returns a detailed describe result for the specified sobject
46
46
  #
47
47
  # sobject - Stringish name of the sobject (default: nil).
@@ -76,7 +76,7 @@ module Restforce
76
76
  def org_id
77
77
  query('select id from Organization').first['Id']
78
78
  end
79
-
79
+
80
80
  # Public: Executs a SOQL query and returns the result.
81
81
  #
82
82
  # soql - A SOQL expression.
@@ -93,7 +93,7 @@ module Restforce
93
93
  response = api_get 'query', :q => soql
94
94
  mashify? ? response.body : response.body['records']
95
95
  end
96
-
96
+
97
97
  # Public: Perform a SOSL search
98
98
  #
99
99
  # sosl - A SOSL expression.
@@ -113,7 +113,7 @@ module Restforce
113
113
  def search(sosl)
114
114
  api_get('search', :q => sosl).body
115
115
  end
116
-
116
+
117
117
  # Public: Insert a new record.
118
118
  #
119
119
  # sobject - String name of the sobject.
@@ -184,7 +184,7 @@ module Restforce
184
184
  # Raises an exception if an error is returned from Salesforce.
185
185
  def update!(sobject, attrs)
186
186
  id = attrs.delete(attrs.keys.find { |k| k.to_s.downcase == 'id' })
187
- raise 'Id field missing.' unless id
187
+ raise ArgumentError, 'Id field missing from attrs.' unless id
188
188
  api_patch "sobjects/#{sobject}/#{id}", attrs
189
189
  true
190
190
  end
@@ -285,7 +285,7 @@ module Restforce
285
285
  # api_path('sobjects')
286
286
  # # => '/services/data/v24.0/sobjects'
287
287
  def api_path(path)
288
- "/services/data/v#{@options[:api_version]}/#{path}"
288
+ "/services/data/v#{options[:api_version]}/#{path}"
289
289
  end
290
290
 
291
291
  # Internal: Errors that should be rescued from in non-bang methods
@@ -1,11 +1,11 @@
1
1
  module Restforce
2
- class Client
2
+ module Concerns
3
3
  module Authentication
4
4
 
5
5
  # Public: Force an authentication
6
6
  def authenticate!
7
7
  raise AuthenticationError, 'No authentication middleware present' unless authentication_middleware
8
- middleware = authentication_middleware.new nil, self, @options
8
+ middleware = authentication_middleware.new nil, self, options
9
9
  middleware.authenticate!
10
10
  end
11
11
 
@@ -21,18 +21,18 @@ module Restforce
21
21
  # Internal: Returns true if username/password (autonomous) flow should be used for
22
22
  # authentication.
23
23
  def username_password?
24
- @options[:username] &&
25
- @options[:password] &&
26
- @options[:client_id] &&
27
- @options[:client_secret]
24
+ options[:username] &&
25
+ options[:password] &&
26
+ options[:client_id] &&
27
+ options[:client_secret]
28
28
  end
29
29
 
30
30
  # Internal: Returns true if oauth token refresh flow should be used for
31
31
  # authentication.
32
32
  def oauth_refresh?
33
- @options[:refresh_token] &&
34
- @options[:client_id] &&
35
- @options[:client_secret]
33
+ options[:refresh_token] &&
34
+ options[:client_id] &&
35
+ options[:client_secret]
36
36
  end
37
37
 
38
38
  end
@@ -0,0 +1,58 @@
1
+ module Restforce
2
+ module Concerns
3
+ module Base
4
+
5
+ attr_reader :options
6
+
7
+ # Public: Creates a new client instance
8
+ #
9
+ # opts - A hash of options to be passed in (default: {}).
10
+ # :username - The String username to use (required for password authentication).
11
+ # :password - The String password to use (required for password authentication).
12
+ # :security_token - The String security token to use (required for password authentication).
13
+ #
14
+ # :oauth_token - The String oauth access token to authenticate api
15
+ # calls (required unless password
16
+ # authentication is used).
17
+ # :refresh_token - The String refresh token to obtain fresh
18
+ # oauth access tokens (required if oauth
19
+ # authentication is used).
20
+ # :instance_url - The String base url for all api requests
21
+ # (required if oauth authentication is used).
22
+ #
23
+ # :client_id - The oauth client id to use. Needed for both
24
+ # password and oauth authentication
25
+ # :client_secret - The oauth client secret to use.
26
+ #
27
+ # :host - The String hostname to use during
28
+ # authentication requests (default: 'login.salesforce.com').
29
+ #
30
+ # :api_version - The String REST api version to use (default: '24.0')
31
+ #
32
+ # :authentication_retries - The number of times that client
33
+ # should attempt to reauthenticate
34
+ # before raising an exception (default: 3).
35
+ #
36
+ # :compress - Set to true to have Salesforce compress the response (default: false).
37
+ # :timeout - Faraday connection request read/open timeout. (default: nil).
38
+ #
39
+ # :proxy_uri - Proxy URI: 'http://proxy.example.com:port' or 'http://user@pass:proxy.example.com:port'
40
+ def initialize(opts = {})
41
+ raise ArgumentError, 'Please specify a hash of options' unless opts.is_a?(Hash)
42
+ @options = Hash[Restforce.configuration.options.map { |option| [option, Restforce.configuration.send(option)] }]
43
+ @options.merge! opts
44
+ yield builder if block_given?
45
+ end
46
+
47
+ def instance_url
48
+ authenticate! unless options[:instance_url]
49
+ options[:instance_url]
50
+ end
51
+
52
+ def inspect
53
+ "#<#{self.class} @options=#{@options.inspect}>"
54
+ end
55
+
56
+ end
57
+ end
58
+ end