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
@@ -0,0 +1,39 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Authentication
|
4
|
+
|
5
|
+
# Public: Force an authentication
|
6
|
+
def authenticate!
|
7
|
+
raise AuthenticationError, 'No authentication middleware present' unless authentication_middleware
|
8
|
+
middleware = authentication_middleware.new nil, self, options
|
9
|
+
middleware.authenticate!
|
10
|
+
end
|
11
|
+
|
12
|
+
# Internal: Determines what middleware will be used based on the options provided
|
13
|
+
def authentication_middleware
|
14
|
+
if username_password?
|
15
|
+
Force::Middleware::Authentication::Password
|
16
|
+
elsif oauth_refresh?
|
17
|
+
Force::Middleware::Authentication::Token
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Internal: Returns true if username/password (autonomous) flow should be used for
|
22
|
+
# authentication.
|
23
|
+
def username_password?
|
24
|
+
options[:username] &&
|
25
|
+
options[:password] &&
|
26
|
+
options[:client_id] &&
|
27
|
+
options[:client_secret]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Internal: Returns true if oauth token refresh flow should be used for
|
31
|
+
# authentication.
|
32
|
+
def oauth_refresh?
|
33
|
+
options[:refresh_token] &&
|
34
|
+
options[:client_id] &&
|
35
|
+
options[:client_secret]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Base
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
# Public: Creates a new client instance
|
7
|
+
#
|
8
|
+
# options - A hash of options to be passed in (default: {}).
|
9
|
+
# :username - The String username to use (required for password authentication).
|
10
|
+
# :password - The String password to use (required for password authentication).
|
11
|
+
# :security_token - The String security token to use (required for password authentication).
|
12
|
+
#
|
13
|
+
# :oauth_token - The String oauth access token to authenticate api
|
14
|
+
# calls (required unless password
|
15
|
+
# authentication is used).
|
16
|
+
# :refresh_token - The String refresh token to obtain fresh
|
17
|
+
# oauth access tokens (required if oauth
|
18
|
+
# authentication is used).
|
19
|
+
# :instance_url - The String base url for all api requests
|
20
|
+
# (required if oauth authentication is used).
|
21
|
+
#
|
22
|
+
# :client_id - The oauth client id to use. Needed for both
|
23
|
+
# password and oauth authentication
|
24
|
+
# :client_secret - The oauth client secret to use.
|
25
|
+
#
|
26
|
+
# :host - The String hostname to use during
|
27
|
+
# authentication requests (default: 'login.salesforce.com').
|
28
|
+
#
|
29
|
+
# :api_version - The String REST api version to use (default: '24.0')
|
30
|
+
#
|
31
|
+
# :authentication_retries - The number of times that client
|
32
|
+
# should attempt to reauthenticate
|
33
|
+
# before raising an exception (default: 3).
|
34
|
+
#
|
35
|
+
# :compress - Set to true to have Salesforce compress the response (default: false).
|
36
|
+
# :timeout - Faraday connection request read/open timeout. (default: nil).
|
37
|
+
#
|
38
|
+
# :proxy_uri - Proxy URI: 'http://proxy.example.com:port' or 'http://user@pass:proxy.example.com:port'
|
39
|
+
#
|
40
|
+
# :authentication_callback
|
41
|
+
# - A Proc that is called with the response body after a successful authentication.
|
42
|
+
def initialize(options = {})
|
43
|
+
@options = Hash[Force.configuration.options.map { |option| [option, Force.configuration.send(option)] }]
|
44
|
+
@options.merge!(options)
|
45
|
+
|
46
|
+
yield builder if block_given?
|
47
|
+
end
|
48
|
+
|
49
|
+
def instance_url
|
50
|
+
authenticate! unless options[:instance_url]
|
51
|
+
options[:instance_url]
|
52
|
+
end
|
53
|
+
|
54
|
+
def inspect
|
55
|
+
"#<#{self.class} @options=#{@options.inspect}>"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Caching
|
4
|
+
# Public: Runs the block with caching disabled.
|
5
|
+
#
|
6
|
+
# block - A query/describe/etc.
|
7
|
+
#
|
8
|
+
# Returns the result of the block
|
9
|
+
def without_caching(&block)
|
10
|
+
options[:use_cache] = false
|
11
|
+
block.call
|
12
|
+
ensure
|
13
|
+
options.delete(:use_cache)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Internal: Cache to use for the caching middleware
|
19
|
+
def cache
|
20
|
+
options[:cache]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Connection
|
4
|
+
# Public: The Faraday::Builder instance used for the middleware stack. This
|
5
|
+
# can be used to insert an custom middleware.
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# # Add the instrumentation middleware for Rails.
|
10
|
+
# client.middleware.use FaradayMiddleware::Instrumentation
|
11
|
+
#
|
12
|
+
# Returns the Faraday::Builder for the Faraday connection.
|
13
|
+
def middleware
|
14
|
+
connection.builder
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :builder, :middleware
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Internal: Internal faraday connection where all requests go through
|
22
|
+
def connection
|
23
|
+
@connection ||= Faraday.new(options[:instance_url], connection_options) do |builder|
|
24
|
+
# Parses JSON into Hashie::Mash structures.
|
25
|
+
builder.use Force::Middleware::Mashify, self, options
|
26
|
+
# Handles multipart file uploads for blobs.
|
27
|
+
builder.use Force::Middleware::Multipart
|
28
|
+
# Converts the request into JSON.
|
29
|
+
builder.request :json
|
30
|
+
# Handles reauthentication for 403 responses.
|
31
|
+
builder.use authentication_middleware, self, options if authentication_middleware
|
32
|
+
# Sets the oauth token in the headers.
|
33
|
+
builder.use Force::Middleware::Authorization, self, options
|
34
|
+
# Ensures the instance url is set.
|
35
|
+
builder.use Force::Middleware::InstanceURL, self, options
|
36
|
+
# Parses returned JSON response into a hash.
|
37
|
+
builder.response :json, :content_type => /\bjson$/
|
38
|
+
# Caches GET requests.
|
39
|
+
builder.use Force::Middleware::Caching, cache, options if cache
|
40
|
+
# Follows 30x redirects.
|
41
|
+
builder.use FaradayMiddleware::FollowRedirects
|
42
|
+
# Raises errors for 40x responses.
|
43
|
+
builder.use Force::Middleware::RaiseError
|
44
|
+
# Log request/responses
|
45
|
+
builder.use Force::Middleware::Logger, Force.configuration.logger, options if Force.log?
|
46
|
+
# Compress/Decompress the request/response
|
47
|
+
builder.use Force::Middleware::Gzip, self, options
|
48
|
+
|
49
|
+
builder.adapter adapter
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def adapter
|
54
|
+
options[:adapter]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Internal: Faraday Connection options
|
58
|
+
def connection_options
|
59
|
+
{ :request => {
|
60
|
+
:timeout => options[:timeout],
|
61
|
+
:open_timeout => options[:timeout] },
|
62
|
+
:proxy => options[:proxy_uri],
|
63
|
+
:ssl => {:verify => false}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Internal: Returns true if the middlware stack includes the
|
68
|
+
# Force::Middleware::Mashify middleware.
|
69
|
+
def mashify?
|
70
|
+
middleware.handlers.index(Force::Middleware::Mashify)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Picklists
|
4
|
+
# Public: Get the available picklist values for a picklist or multipicklist field.
|
5
|
+
#
|
6
|
+
# sobject - The String name of the sobject.
|
7
|
+
# field - The String name of the picklist/multipicklist field.
|
8
|
+
# options - A hash of options. (default: {}).
|
9
|
+
# :valid_for - If specified, will only return picklist values
|
10
|
+
# that are valid for the controlling picklist
|
11
|
+
# value
|
12
|
+
#
|
13
|
+
# Examples
|
14
|
+
#
|
15
|
+
# client.picklist_values('Account', 'Type')
|
16
|
+
# # => [#<Force::Mash label="Prospect" value="Prospect">]
|
17
|
+
#
|
18
|
+
# # Given a custom object named Automobile__c with picklist fields
|
19
|
+
# # Model__c and Make__c, where Model__c depends on the value of
|
20
|
+
# # Make__c.
|
21
|
+
# client.picklist_values('Automobile__c', 'Model__c', :valid_for => 'Honda')
|
22
|
+
# # => [#<Force::Mash label="Civic" value="Civic">, ... ]
|
23
|
+
#
|
24
|
+
# Returns an array of Force::Mash objects.
|
25
|
+
def picklist_values(sobject, field, options = {})
|
26
|
+
PicklistValues.new(describe(sobject)['fields'], field, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
class PicklistValues < Array
|
32
|
+
|
33
|
+
def initialize(fields, field, options = {})
|
34
|
+
@fields, @field = fields, field
|
35
|
+
@valid_for = options.delete(:valid_for)
|
36
|
+
raise "#{field} is not a dependent picklist" if @valid_for && !dependent?
|
37
|
+
replace(picklist_values)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :fields
|
43
|
+
|
44
|
+
def picklist_values
|
45
|
+
if valid_for?
|
46
|
+
field['picklistValues'].select { |picklist_entry| valid? picklist_entry }
|
47
|
+
else
|
48
|
+
field['picklistValues']
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns true of the given field is dependent on another field.
|
53
|
+
def dependent?
|
54
|
+
!!field['dependentPicklist']
|
55
|
+
end
|
56
|
+
|
57
|
+
def valid_for?
|
58
|
+
!!@valid_for
|
59
|
+
end
|
60
|
+
|
61
|
+
def controlling_picklist
|
62
|
+
@_controlling_picklist ||= controlling_field['picklistValues'].find { |picklist_entry| picklist_entry['value'] == @valid_for }
|
63
|
+
end
|
64
|
+
|
65
|
+
def index
|
66
|
+
@_index ||= controlling_field['picklistValues'].index(controlling_picklist)
|
67
|
+
end
|
68
|
+
|
69
|
+
def controlling_field
|
70
|
+
@_controlling_field ||= fields.find { |f| f['name'] == field['controllerName'] }
|
71
|
+
end
|
72
|
+
|
73
|
+
def field
|
74
|
+
@_field ||= fields.find { |f| f['name'] == @field }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns true if the picklist entry is valid for the controlling picklist.
|
78
|
+
#
|
79
|
+
# See http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_describesobjects_describesobjectresult.htm
|
80
|
+
def valid?(picklist_entry)
|
81
|
+
valid_for = picklist_entry['validFor'].ljust(16, 'A').unpack('m').first.unpack('q*')
|
82
|
+
(valid_for[index >> 3] & (0x80 >> index % 8)) != 0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Streaming
|
4
|
+
# Public: Subscribe to a PushTopic
|
5
|
+
#
|
6
|
+
# channels - The name of the PushTopic channel(s) to subscribe to.
|
7
|
+
# block - A block to run when a new message is received.
|
8
|
+
#
|
9
|
+
# Returns a Faye::Subscription
|
10
|
+
def subscribe(channels, &block)
|
11
|
+
faye.subscribe Array(channels).map { |channel| "/topic/#{channel}" }, &block
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Faye client to use for subscribing to PushTopics
|
15
|
+
def faye
|
16
|
+
raise 'Instance URL missing. Call .authenticate! first.' unless options[:instance_url]
|
17
|
+
|
18
|
+
@faye ||= Faye::Client.new("#{options[:instance_url]}/cometd/#{options[:api_version]}").tap do |client|
|
19
|
+
client.bind 'transport:down' do
|
20
|
+
Force.log "[COMETD DOWN]"
|
21
|
+
client.set_header 'Authorization', "OAuth #{authenticate!.access_token}"
|
22
|
+
end
|
23
|
+
|
24
|
+
client.bind 'transport:up' do
|
25
|
+
Force.log "[COMETD UP]"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Force
|
2
|
+
module Concerns
|
3
|
+
module Verbs
|
4
|
+
# Internal: Define methods to handle a verb.
|
5
|
+
#
|
6
|
+
# verbs - A list of verbs to define methods for.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# define_verbs :get, :post
|
11
|
+
#
|
12
|
+
# Returns nil.
|
13
|
+
def define_verbs(*verbs)
|
14
|
+
verbs.each do |verb|
|
15
|
+
define_verb(verb)
|
16
|
+
define_api_verb(verb)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Internal: Defines a method to handle HTTP requests with the passed in
|
21
|
+
# verb.
|
22
|
+
#
|
23
|
+
# verb - Symbol name of the verb (e.g. :get).
|
24
|
+
#
|
25
|
+
# Examples
|
26
|
+
#
|
27
|
+
# define_verb :get
|
28
|
+
# # => get '/services/data/v24.0/sobjects'
|
29
|
+
#
|
30
|
+
# Returns nil.
|
31
|
+
def define_verb(verb)
|
32
|
+
define_method verb do |*args, &block|
|
33
|
+
retries = options[:authentication_retries]
|
34
|
+
begin
|
35
|
+
connection.send(verb, *args, &block)
|
36
|
+
rescue Force::UnauthorizedError
|
37
|
+
if retries > 0
|
38
|
+
retries -= 1
|
39
|
+
connection.url_prefix = options[:instance_url]
|
40
|
+
retry
|
41
|
+
end
|
42
|
+
|
43
|
+
raise
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Internal: Defines a method to handle HTTP requests with the passed in
|
49
|
+
# verb to a salesforce api endpoint.
|
50
|
+
#
|
51
|
+
# verb - Symbol name of the verb (e.g. :get).
|
52
|
+
#
|
53
|
+
# Examples
|
54
|
+
#
|
55
|
+
# define_api_verb :get
|
56
|
+
# # => api_get 'sobjects'
|
57
|
+
#
|
58
|
+
# Returns nil.
|
59
|
+
def define_api_verb(verb)
|
60
|
+
define_method :"api_#{verb}" do |*args, &block|
|
61
|
+
args[0] = api_path(args[0])
|
62
|
+
send(verb, *args, &block)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/force/config.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Force
|
4
|
+
class << self
|
5
|
+
attr_writer :log
|
6
|
+
|
7
|
+
# Returns the current Configuration
|
8
|
+
#
|
9
|
+
# Example
|
10
|
+
#
|
11
|
+
# Force.configuration.username = "username"
|
12
|
+
# Force.configuration.password = "password"
|
13
|
+
def configuration
|
14
|
+
@configuration ||= Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Yields the Configuration
|
18
|
+
#
|
19
|
+
# Example
|
20
|
+
#
|
21
|
+
# Force.configure do |config|
|
22
|
+
# config.username = "username"
|
23
|
+
# config.password = "password"
|
24
|
+
# end
|
25
|
+
def configure
|
26
|
+
yield configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
def log?
|
30
|
+
@log ||= false
|
31
|
+
end
|
32
|
+
|
33
|
+
def log(message)
|
34
|
+
return unless Force.log?
|
35
|
+
Force.configuration.logger.send :debug, message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Configuration
|
40
|
+
class Option
|
41
|
+
attr_reader :configuration, :name, :options
|
42
|
+
|
43
|
+
def self.define(*args)
|
44
|
+
new(*args).define
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(configuration, name, options = {})
|
48
|
+
@configuration, @name, @options = configuration, name, options
|
49
|
+
@default = options.fetch(:default, nil)
|
50
|
+
end
|
51
|
+
|
52
|
+
def define
|
53
|
+
write_attribute
|
54
|
+
define_method if default_provided?
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
attr_reader :default
|
61
|
+
alias_method :default_provided?, :default
|
62
|
+
|
63
|
+
def write_attribute
|
64
|
+
configuration.send :attr_accessor, name
|
65
|
+
end
|
66
|
+
|
67
|
+
def define_method
|
68
|
+
our_default = default
|
69
|
+
our_name = name
|
70
|
+
configuration.send :define_method, our_name do
|
71
|
+
instance_variable_get(:"@#{our_name}") ||
|
72
|
+
instance_variable_set(:"@#{our_name}", our_default.respond_to?(:call) ? our_default.call : our_default)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class << self
|
78
|
+
attr_accessor :options
|
79
|
+
|
80
|
+
def option(*args)
|
81
|
+
option = Option.define(self, *args)
|
82
|
+
(self.options ||= []) << option.name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
option :api_version, :default => '26.0'
|
87
|
+
|
88
|
+
# The username to use during login.
|
89
|
+
option :username, :default => lambda { ENV['SALESFORCE_USERNAME'] }
|
90
|
+
|
91
|
+
# The password to use during login.
|
92
|
+
option :password, :default => lambda { ENV['SALESFORCE_PASSWORD'] }
|
93
|
+
|
94
|
+
# The security token to use during login.
|
95
|
+
option :security_token, :default => lambda { ENV['SALESFORCE_SECURITY_TOKEN'] }
|
96
|
+
|
97
|
+
# The OAuth client id
|
98
|
+
option :client_id, :default => lambda { ENV['SALESFORCE_CLIENT_ID'] }
|
99
|
+
|
100
|
+
# The OAuth client secret
|
101
|
+
option :client_secret, :default => lambda { ENV['SALESFORCE_CLIENT_SECRET'] }
|
102
|
+
|
103
|
+
# Set this to true if you're authenticating with a Sandbox instance.
|
104
|
+
# Defaults to false.
|
105
|
+
option :host, :default => lambda { ENV['SALESFORCE_HOST'] || 'login.salesforce.com' }
|
106
|
+
|
107
|
+
option :oauth_token
|
108
|
+
option :refresh_token
|
109
|
+
option :instance_url
|
110
|
+
|
111
|
+
# Set this to an object that responds to read, write and fetch and all GET
|
112
|
+
# requests will be cached.
|
113
|
+
option :cache
|
114
|
+
|
115
|
+
# The number of times reauthentication should be tried before failing.
|
116
|
+
option :authentication_retries, :default => 3
|
117
|
+
|
118
|
+
# Set to true if you want responses from Salesforce to be gzip compressed.
|
119
|
+
option :compress
|
120
|
+
|
121
|
+
# Faraday request read/open timeout.
|
122
|
+
option :timeout
|
123
|
+
|
124
|
+
# Faraday adapter to use. Defaults to Faraday.default_adapter.
|
125
|
+
option :adapter, :default => lambda { Faraday.default_adapter }
|
126
|
+
|
127
|
+
option :proxy_uri, :default => lambda { ENV['PROXY_URI'] }
|
128
|
+
|
129
|
+
# A Proc that is called with the response body after a successful authentication.
|
130
|
+
option :authentication_callback
|
131
|
+
|
132
|
+
def logger
|
133
|
+
@logger ||= ::Logger.new STDOUT
|
134
|
+
end
|
135
|
+
|
136
|
+
def options
|
137
|
+
self.class.options
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|