sfdc 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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