sfdc 0.0.1 → 1.0.0

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.
Files changed (47) hide show
  1. checksums.yaml +8 -8
  2. data/lib/sfdc.rb +39 -1
  3. data/lib/sfdc/attachment.rb +23 -0
  4. data/lib/sfdc/chatter/comment.rb +10 -0
  5. data/lib/sfdc/chatter/conversation.rb +100 -0
  6. data/lib/sfdc/chatter/feed.rb +64 -0
  7. data/lib/sfdc/chatter/feed_item.rb +40 -0
  8. data/lib/sfdc/chatter/feeds.rb +5 -0
  9. data/lib/sfdc/chatter/filter_feed.rb +14 -0
  10. data/lib/sfdc/chatter/group.rb +45 -0
  11. data/lib/sfdc/chatter/group_membership.rb +9 -0
  12. data/lib/sfdc/chatter/like.rb +9 -0
  13. data/lib/sfdc/chatter/message.rb +29 -0
  14. data/lib/sfdc/chatter/photo_methods.rb +55 -0
  15. data/lib/sfdc/chatter/record.rb +122 -0
  16. data/lib/sfdc/chatter/subscription.rb +9 -0
  17. data/lib/sfdc/chatter/user.rb +153 -0
  18. data/lib/sfdc/client.rb +98 -0
  19. data/lib/sfdc/client/api.rb +320 -0
  20. data/lib/sfdc/client/authentication.rb +40 -0
  21. data/lib/sfdc/client/caching.rb +26 -0
  22. data/lib/sfdc/client/canvas.rb +12 -0
  23. data/lib/sfdc/client/connection.rb +74 -0
  24. data/lib/sfdc/client/picklists.rb +90 -0
  25. data/lib/sfdc/client/streaming.rb +31 -0
  26. data/lib/sfdc/client/verbs.rb +68 -0
  27. data/lib/sfdc/collection.rb +40 -0
  28. data/lib/sfdc/config.rb +136 -0
  29. data/lib/sfdc/mash.rb +65 -0
  30. data/lib/sfdc/middleware.rb +29 -0
  31. data/lib/sfdc/middleware/authentication.rb +63 -0
  32. data/lib/sfdc/middleware/authentication/password.rb +20 -0
  33. data/lib/sfdc/middleware/authentication/token.rb +15 -0
  34. data/lib/sfdc/middleware/authorization.rb +19 -0
  35. data/lib/sfdc/middleware/caching.rb +24 -0
  36. data/lib/sfdc/middleware/gzip.rb +31 -0
  37. data/lib/sfdc/middleware/instance_url.rb +18 -0
  38. data/lib/sfdc/middleware/logger.rb +40 -0
  39. data/lib/sfdc/middleware/mashify.rb +18 -0
  40. data/lib/sfdc/middleware/multipart.rb +53 -0
  41. data/lib/sfdc/middleware/raise_error.rb +23 -0
  42. data/lib/sfdc/signed_request.rb +48 -0
  43. data/lib/sfdc/sobject.rb +64 -0
  44. data/lib/sfdc/upload_io.rb +20 -0
  45. data/lib/sfdc/version.rb +1 -1
  46. metadata +59 -4
  47. data/lib/sfdc/sfdc.rb +0 -0
@@ -0,0 +1,40 @@
1
+ module Sfdc
2
+ class Client
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
+ Sfdc::Middleware::Authentication::Password
16
+ elsif oauth_refresh?
17
+ Sfdc::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
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ module Sfdc
2
+ class Client
3
+ module Caching
4
+
5
+ # Public: Runs the block with caching disabled.
6
+ #
7
+ # block - A query/describe/etc.
8
+ #
9
+ # Returns the result of the block
10
+ def without_caching(&block)
11
+ @options[:use_cache] = false
12
+ block.call
13
+ ensure
14
+ @options.delete(:use_cache)
15
+ end
16
+
17
+ private
18
+
19
+ # Internal: Cache to use for the caching middleware
20
+ def cache
21
+ @options[:cache]
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Sfdc
2
+ class Client
3
+ module Canvas
4
+
5
+ def decode_signed_request(signed_request)
6
+ raise 'client_secret not set' unless @options[:client_secret]
7
+ SignedRequest.decode(signed_request, @options[:client_secret])
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,74 @@
1
+ module Sfdc
2
+ class Client
3
+ module Connection
4
+
5
+ # Public: The Faraday::Builder instance used for the middleware stack. This
6
+ # can be used to insert an custom middleware.
7
+ #
8
+ # Examples
9
+ #
10
+ # # Add the instrumentation middleware for Rails.
11
+ # client.middleware.use FaradayMiddleware::Instrumentation
12
+ #
13
+ # Returns the Faraday::Builder for the Faraday connection.
14
+ def middleware
15
+ connection.builder
16
+ end
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 Sfdc::Middleware::Mashify, self, @options
26
+ # Handles multipart file uploads for blobs.
27
+ builder.use Sfdc::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 Sfdc::Middleware::Authorization, self, @options
34
+ # Ensures the instance url is set.
35
+ builder.use Sfdc::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 Sfdc::Middleware::Caching, cache, @options if cache
40
+ # Follows 30x redirects.
41
+ builder.use FaradayMiddleware::FollowRedirects
42
+ # Raises errors for 40x responses.
43
+ builder.use Sfdc::Middleware::RaiseError
44
+ # Log request/responses
45
+ builder.use Sfdc::Middleware::Logger, Sfdc.configuration.logger, @options if Sfdc.log?
46
+ # Compress/Decompress the request/response
47
+ builder.use Sfdc::Middleware::Gzip, self, @options
48
+
49
+ builder.adapter adapter
50
+ end
51
+ end
52
+
53
+ def adapter
54
+ Sfdc.configuration.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
+ }
64
+ end
65
+
66
+ # Internal: Returns true if the middlware stack includes the
67
+ # Sfdc::Middleware::Mashify middleware.
68
+ def mashify?
69
+ middleware.handlers.index(Sfdc::Middleware::Mashify)
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,90 @@
1
+ module Sfdc
2
+ class Client
3
+ module Picklists
4
+
5
+ # Public: Get the available picklist values for a picklist or multipicklist field.
6
+ #
7
+ # sobject - The String name of the sobject.
8
+ # field - The String name of the picklist/multipicklist field.
9
+ # options - A hash of options. (default: {}).
10
+ # :valid_for - If specified, will only return picklist values
11
+ # that are valid for the controlling picklist
12
+ # value
13
+ #
14
+ # Examples
15
+ #
16
+ # client.picklist_values('Account', 'Type')
17
+ # # => [#<Sfdc::Mash label="Prospect" value="Prospect">]
18
+ #
19
+ # # Given a custom object named Automobile__c with picklist fields
20
+ # # Model__c and Make__c, where Model__c depends on the value of
21
+ # # Make__c.
22
+ # client.picklist_values('Automobile__c', 'Model__c', :valid_for => 'Honda')
23
+ # # => [#<Sfdc::Mash label="Civic" value="Civic">, ... ]
24
+ #
25
+ # Returns an array of Sfdc::Mash objects.
26
+ def picklist_values(sobject, field, options = {})
27
+ PicklistValues.new(describe(sobject)['fields'], field, options)
28
+ end
29
+
30
+ private
31
+
32
+ class PicklistValues < Array
33
+
34
+ def initialize(fields, field, options = {})
35
+ @fields, @field = fields, field
36
+ @valid_for = options.delete(:valid_for)
37
+ raise "#{field} is not a dependent picklist" if @valid_for && !dependent?
38
+ replace(picklist_values)
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :fields
44
+
45
+ def picklist_values
46
+ if valid_for?
47
+ field['picklistValues'].select { |picklist_entry| valid? picklist_entry }
48
+ else
49
+ field['picklistValues']
50
+ end
51
+ end
52
+
53
+ # Returns true of the given field is dependent on another field.
54
+ def dependent?
55
+ !!field['dependentPicklist']
56
+ end
57
+
58
+ def valid_for?
59
+ !!@valid_for
60
+ end
61
+
62
+ def controlling_picklist
63
+ @_controlling_picklist ||= controlling_field['picklistValues'].find { |picklist_entry| picklist_entry['value'] == @valid_for }
64
+ end
65
+
66
+ def index
67
+ @_index ||= controlling_field['picklistValues'].index(controlling_picklist)
68
+ end
69
+
70
+ def controlling_field
71
+ @_controlling_field ||= fields.find { |f| f['name'] == field['controllerName'] }
72
+ end
73
+
74
+ def field
75
+ @_field ||= fields.find { |f| f['name'] == @field }
76
+ end
77
+
78
+ # Returns true if the picklist entry is valid for the controlling picklist.
79
+ #
80
+ # See http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_describesobjects_describesobjectresult.htm
81
+ def valid?(picklist_entry)
82
+ valid_for = picklist_entry['validFor'].ljust(16, 'A').unpack('m').first.unpack('q*')
83
+ (valid_for[index >> 3] & (0x80 >> index % 8)) != 0
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,31 @@
1
+ module Sfdc
2
+ class Client
3
+ module Streaming
4
+
5
+ # Public: Subscribe to a PushTopic
6
+ #
7
+ # channels - The name of the PushTopic channel(s) to subscribe to.
8
+ # block - A block to run when a new message is received.
9
+ #
10
+ # Returns a Faye::Subscription
11
+ def subscribe(channels, &block)
12
+ faye.subscribe Array(channels).map { |channel| "/topic/#{channel}" }, &block
13
+ end
14
+
15
+ # Public: Faye client to use for subscribing to PushTopics
16
+ def faye
17
+ raise 'Instance URL missing. Call .authenticate! first.' unless @options[:instance_url]
18
+ @faye ||= Faye::Client.new("#{@options[:instance_url]}/cometd/#{@options[:api_version]}").tap do |client|
19
+ client.bind 'transport:down' do
20
+ Sfdc.log "[COMETD DOWN]"
21
+ client.set_header 'Authorization', "OAuth #{authenticate!.access_token}"
22
+ end
23
+ client.bind 'transport:up' do
24
+ Sfdc.log "[COMETD UP]"
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,68 @@
1
+ module Sfdc
2
+ class Client
3
+ module Verbs
4
+
5
+ # Internal: Define methods to handle a verb.
6
+ #
7
+ # verbs - A list of verbs to define methods for.
8
+ #
9
+ # Examples
10
+ #
11
+ # define_verbs :get, :post
12
+ #
13
+ # Returns nil.
14
+ def define_verbs(*verbs)
15
+ verbs.each do |verb|
16
+ define_verb(verb)
17
+ define_api_verb(verb)
18
+ end
19
+ end
20
+
21
+ # Internal: Defines a method to handle HTTP requests with the passed in
22
+ # verb.
23
+ #
24
+ # verb - Symbol name of the verb (e.g. :get).
25
+ #
26
+ # Examples
27
+ #
28
+ # define_verb :get
29
+ # # => get '/services/data/v24.0/sobjects'
30
+ #
31
+ # Returns nil.
32
+ def define_verb(verb)
33
+ define_method verb do |*args, &block|
34
+ retries = @options[:authentication_retries]
35
+ begin
36
+ connection.send(verb, *args, &block)
37
+ rescue Sfdc::UnauthorizedError
38
+ if retries > 0
39
+ retries -= 1
40
+ connection.url_prefix = @options[:instance_url]
41
+ retry
42
+ end
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
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,40 @@
1
+ module Sfdc
2
+ class Collection
3
+ include Enumerable
4
+
5
+ # Given a hash and client, will create an Enumerator that will lazily
6
+ # request Salesforce for the next page of results.
7
+ def initialize(hash, client)
8
+ @client = client
9
+ @raw_page = hash
10
+ end
11
+
12
+ # Yield each value on each page.
13
+ def each
14
+ @raw_page['records'].each { |record| yield Sfdc::Mash.build(record, @client) }
15
+
16
+ next_page.each { |record| yield record } if has_next_page?
17
+ end
18
+
19
+ # Return the size of the Collection without making any additional requests.
20
+ def size
21
+ @raw_page['totalSize']
22
+ end
23
+ alias_method :length, :size
24
+
25
+ # Return the current and all of the following pages.
26
+ def pages
27
+ [self] + (has_next_page? ? next_page.pages : [])
28
+ end
29
+
30
+ # Returns true if there is a pointer to the next page.
31
+ def has_next_page?
32
+ !@raw_page['nextRecordsUrl'].nil?
33
+ end
34
+
35
+ # Returns the next page as a Sfdc::Collection if it's available, nil otherwise.
36
+ def next_page
37
+ @next_page ||= @client.get(@raw_page['nextRecordsUrl']).body if has_next_page?
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,136 @@
1
+ require 'logger'
2
+
3
+ module Sfdc
4
+ class << self
5
+ attr_writer :log
6
+
7
+ # Returns the current Configuration
8
+ #
9
+ # Example
10
+ #
11
+ # Sfdc.configuration.username = "username"
12
+ # Sfdc.configuration.password = "password"
13
+ def configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ # Yields the Configuration
18
+ #
19
+ # Example
20
+ #
21
+ # Sfdc.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 Sfdc.log?
35
+ Sfdc.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
+ attr_reader :default
60
+ alias_method :default_provided?, :default
61
+
62
+ def write_attribute
63
+ configuration.send :attr_accessor, name
64
+ end
65
+
66
+ def define_method
67
+ our_default = default
68
+ our_name = name
69
+ configuration.send :define_method, our_name do
70
+ instance_variable_get(:"@#{our_name}") ||
71
+ instance_variable_set(:"@#{our_name}", our_default.respond_to?(:call) ? our_default.call : our_default)
72
+ end
73
+ end
74
+ end
75
+
76
+ class << self
77
+ attr_accessor :options
78
+
79
+ def option(*args)
80
+ option = Option.define(self, *args)
81
+ (self.options ||= []) << option.name
82
+ end
83
+ end
84
+
85
+ option :api_version, :default => '27.0'
86
+
87
+ # The username to use during login.
88
+ option :username, :default => lambda { ENV['SALESFORCE_USERNAME'] }
89
+
90
+ # The password to use during login.
91
+ option :password, :default => lambda { ENV['SALESFORCE_PASSWORD'] }
92
+
93
+ # The security token to use during login.
94
+ option :security_token, :default => lambda { ENV['SALESFORCE_SECURITY_TOKEN'] }
95
+
96
+ # The OAuth client id
97
+ option :client_id, :default => lambda { ENV['SALESFORCE_CLIENT_ID'] }
98
+
99
+ # The OAuth client secret
100
+ option :client_secret, :default => lambda { ENV['SALESFORCE_CLIENT_SECRET'] }
101
+
102
+ # Set this to true if you're authenticating with a Sandbox instance.
103
+ # Defaults to false.
104
+ option :host, :default => 'login.salesforce.com'
105
+
106
+ option :oauth_token
107
+ option :refresh_token
108
+ option :instance_url
109
+
110
+ # Set this to an object that responds to read, write and fetch and all GET
111
+ # requests will be cached.
112
+ option :cache
113
+
114
+ # The number of times reauthentication should be tried before failing.
115
+ option :authentication_retries, :default => 3
116
+
117
+ # Set to true if you want responses from Salesforce to be gzip compressed.
118
+ option :compress
119
+
120
+ # Faraday request read/open timeout.
121
+ option :timeout
122
+
123
+ # Faraday adapter to use. Defaults to Faraday.default_adapter.
124
+ option :adapter, :default => lambda { Faraday.default_adapter }
125
+
126
+ option :proxy_uri, :default => lambda { ENV['PROXY_URI'] }
127
+
128
+ def logger
129
+ @logger ||= ::Logger.new STDOUT
130
+ end
131
+
132
+ def options
133
+ self.class.options
134
+ end
135
+ end
136
+ end