force 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|