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,122 @@
1
+ require 'json'
2
+
3
+ module Sfdc
4
+ module Chatter
5
+ # Superclasses all Chatter resources except feeds. Some methods may not be supported by the Force.com API for certain subclasses.
6
+ class Record
7
+ attr_reader :raw_hash, :name, :id, :url, :type, :client
8
+
9
+ # Create a new record from the returned JSON response of an API request. Sets the client, name, id, url, and type attributes. Saves the raw response as +raw_hash+.
10
+ def initialize(client, response)
11
+ @client = client
12
+ @raw_hash = response.is_a?(Hash) ? response : JSON.parse(response)
13
+ @name = @raw_hash["name"]
14
+ @id = @raw_hash["id"]
15
+ @url = @raw_hash["url"]
16
+ @type = @raw_hash["type"]
17
+ end
18
+
19
+ # Find a single Record or a Collection of records by id. _resource_id_ can be a single id or a list of ids.
20
+ def self.find(client, resource_id, parameters={})
21
+ if resource_id.is_a?(Array)
22
+ resource_ids = resource_id.join(',')
23
+ url = "/services/data/v#{client.version}/chatter/#{self.resource_name}/batch/#{resource_ids}"
24
+ response = JSON.parse(client.http_get(url, parameters).body)
25
+ good_results = response["results"].select { |r| r["statusCode"] == 200 }
26
+ collection = Sfdc::Collection.new(client, good_results.length)
27
+ good_results.each do |result|
28
+ collection << self.new(client, result["result"])
29
+ end
30
+ collection
31
+ else
32
+ path_components = ["/services/data/v#{client.version}/chatter"]
33
+ if parameters.has_key?(:user_id)
34
+ path_components << "users/#{parameters[:user_id]}"
35
+ parameters.delete(:user_id)
36
+ end
37
+ path_components << "#{self.resource_name}/#{resource_id}"
38
+ url = path_components.join('/')
39
+ response = JSON.parse(client.http_get(url, parameters).body)
40
+ self.new(client, response)
41
+ end
42
+ end
43
+
44
+ # Return a Collection of records that match the _query_.
45
+ def self.search(client, query, parameters={})
46
+ self.all(client, parameters.merge(self.search_parameter_name => query))
47
+ end
48
+
49
+ # Return a Collection of all records.
50
+ def self.all(client, parameters={})
51
+ path_components = ["/services/data/v#{client.version}/chatter"]
52
+ if parameters.has_key?(:user_id)
53
+ path_components << "users/#{parameters[:user_id]}"
54
+ parameters.delete(:user_id)
55
+ end
56
+ path_components << self.resource_name
57
+ url = path_components.join('/')
58
+ result = client.http_get(url, parameters)
59
+ response = JSON.parse(result.body)
60
+ collection = Sfdc::Collection.new(client, self.total_size_of_collection(response), response["nextPageUrl"], response["previousPageUrl"], response["currentPageUrl"])
61
+ self.collection_from_response(response).each do |resource|
62
+ collection << self.new(client, resource)
63
+ end
64
+ collection
65
+ end
66
+
67
+ # Delete the Record identified by _resource_id_.
68
+ def self.delete(client, resource_id, parameters={})
69
+ path_components = ["/services/data/v#{client.version}/chatter"]
70
+ if parameters.has_key?(:user_id)
71
+ path_components << "users/#{parameters[:user_id]}"
72
+ parameters.delete(:user_id)
73
+ end
74
+ path_components << self.resource_name
75
+ path_components << resource_id
76
+ path = path_components.join('/')
77
+ client.http_delete(path, parameters)
78
+ end
79
+
80
+ # A Hash representation of the User that created this Record.
81
+ def user
82
+ self.raw_hash["user"]
83
+ end
84
+
85
+ # A Hash representation of the entity that is the parent of this Record.
86
+ def parent
87
+ self.raw_hash["parent"]
88
+ end
89
+
90
+ # Delete this record.
91
+ def delete(parameters={})
92
+ self.class.delete(self.client, self.id, parameters)
93
+ end
94
+
95
+ # Reload this record.
96
+ def reload
97
+ self.class.find(self.client, self.id)
98
+ end
99
+
100
+ # The REST resource name of this Record.
101
+ #
102
+ # GroupMembership.resource_name #=> group-memberships
103
+ def self.resource_name
104
+ (self.name.split('::').last).resourcerize + "s"
105
+ end
106
+
107
+ protected
108
+
109
+ def self.total_size_of_collection(response)
110
+ response["total"] || response["totalMemberCount"]
111
+ end
112
+
113
+ def self.collection_from_response(response)
114
+ response[self.resource_name]
115
+ end
116
+
117
+ def self.search_parameter_name
118
+ :q
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,9 @@
1
+ require 'Sfdc/chatter/record'
2
+
3
+ module Sfdc
4
+ module Chatter
5
+ # A representation of a user "following" some other entity.
6
+ class Subscription < Record
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,153 @@
1
+ require 'Sfdc/chatter/record'
2
+ require 'Sfdc/chatter/photo_methods'
3
+
4
+ module Sfdc
5
+ module Chatter
6
+ # Defines a User in your org.
7
+ class User < Record
8
+ include PhotoMethods
9
+
10
+ # Returns a Collection of Subscription objects that represents all followers of the User identified by _subject_id_.
11
+ def self.followers(client, subject_id="me")
12
+ url = "/services/data/v#{client.version}/chatter/users/#{subject_id}/followers"
13
+ result = client.http_get(url)
14
+ response = JSON.parse(result.body)
15
+ collection = Sfdc::Collection.new(client, response["total"], response["nextPageUrl"], response["previousPageUrl"], response["currentPageUrl"])
16
+ response["followers"].each do |subscription|
17
+ collection << Subscription.new(client, subscription)
18
+ end
19
+ collection
20
+ end
21
+
22
+ # Returns a Collection of Subscription objects that represent all entities that the User identified by _subject_id_ is following.
23
+ def self.following(client, subject_id="me")
24
+ url = "/services/data/v#{client.version}/chatter/users/#{subject_id}/following"
25
+ result = client.http_get(url)
26
+ response = JSON.parse(result.body)
27
+ collection = Sfdc::Collection.new(client, response["total"], response["nextPageUrl"], response["previousPageUrl"], response["currentPageUrl"])
28
+ response["following"].each do |subscription|
29
+ collection << Subscription.new(client, subscription)
30
+ end
31
+ collection
32
+ end
33
+
34
+ # Returns a Collection of Group objects that represent all the groups that the User identified by _subject_id_ is a part of.
35
+ def self.groups(client, subject_id="me")
36
+ url = "/services/data/v#{client.version}/chatter/users/#{subject_id}/groups"
37
+ result = client.http_get(url)
38
+ response = JSON.parse(result.body)
39
+ collection = Sfdc::Collection.new(client, response["total"], response["nextPageUrl"], response["previousPageUrl"], response["currentPageUrl"])
40
+ response["groups"].each do |group|
41
+ collection << Group.new(client, group)
42
+ end
43
+ collection
44
+ end
45
+
46
+ # Returns the current status of the User identified by _subject_id_.
47
+ def self.status(client, subject_id="me")
48
+ url = "/services/data/v#{client.version}/chatter/users/#{subject_id}/status"
49
+ result = client.http_get(url)
50
+ JSON.parse(result.body)
51
+ end
52
+
53
+ # Posts a status update as the User identified by _subject_id_ with content _text_.
54
+ def self.post_status(client, subject_id, text)
55
+ url = "/services/data/v#{client.version}/chatter/users/#{subject_id}/status"
56
+ result = client.http_post(url, nil, :text => text)
57
+ JSON.parse(result.body)
58
+ end
59
+
60
+ # Deletes the status of User identified by _subject_id_.
61
+ def self.delete_status(client, subject_id="me")
62
+ client.http_delete "/services/data/v#{client.version}/chatter/users/#{subject_id}/status"
63
+ end
64
+
65
+ # Creates and returns a new Subscription object that represents the User identified by _subject_id_ following the resource identified by _resource_id_.
66
+ def self.follow(client, subject_id, resource_id)
67
+ response = client.http_post("/services/data/v#{client.version}/chatter/users/#{subject_id}/following", nil, :subjectId => resource_id)
68
+ Subscription.new(client, response.body)
69
+ end
70
+
71
+ # Returns a Collection of conversations that belong to the User identified by _subject_id_.
72
+ def self.conversations(client, subject_id)
73
+ Conversation.all(client, :user_id => subject_id)
74
+ end
75
+
76
+ # Returns a Collection of private messages that belong to the User identified by _subject_id_.
77
+ def self.messages(client, subject_id)
78
+ Message.all(client, :user_id => subject_id)
79
+ end
80
+
81
+ # Get a Collection of Subscription objects for this User. Always makes a call to the server.
82
+ def followers!
83
+ self.class.followers(self.client, self.id)
84
+ end
85
+
86
+ # Get a Collection of Subscription objects for this User. Returns cached data if it has been called before.
87
+ def followers
88
+ @followers ||= followers!
89
+ end
90
+
91
+ # Get a Collection of Subscription objects that represents all resources that this User is following. Always makes a call to the server.
92
+ def following!
93
+ self.class.following(self.client, self.id)
94
+ end
95
+
96
+ # Get a Collection of Subscription objects that represents all resources that this User is following. Returns cached data if it has been called before.
97
+ def following
98
+ @following ||= following!
99
+ end
100
+
101
+ # Returns this current status of this User.
102
+ def status
103
+ self.raw_hash["currentStatus"]
104
+ end
105
+
106
+ # Posts a new status with content _text_ for this User.
107
+ def post_status(text)
108
+ self.class.post_status(self.client, self.id, text)
109
+ end
110
+
111
+ # Deletes the current status of this User. Returns the deleted status.
112
+ def delete_status
113
+ self.class.delete_status(self.client, self.id)
114
+ status
115
+ end
116
+
117
+ # Get a Collection of Group objects that represents all groups that this User is in. Always makes a call to the server.
118
+ def groups!
119
+ self.class.groups(self.client, self.id)
120
+ end
121
+
122
+ # Get a Collection of Group objects that represents all groups that this User is in. Returns cached data if it has been called before.
123
+ def groups
124
+ @groups ||= groups!
125
+ end
126
+
127
+ # Creates a new Subscription that represents this User following the resource with id _record_id_.
128
+ def follow(record_id)
129
+ self.class.follow(self.client, self.id, record_id)
130
+ end
131
+
132
+ # Get a Collection of Conversation objects that represents the conversations for this User. Always makes a call to the server.
133
+ def conversations!
134
+ self.class.conversations(self.client, self.id)
135
+ end
136
+
137
+ # Get a Collection of Conversation objects that represents the conversations for this User. Returns cached data if it has been called before.
138
+ def conversations
139
+ @conversations ||= conversations!
140
+ end
141
+
142
+ # Get a Collection of Message objects that represents the messages for this User. Always makes a call to the server.
143
+ def messages!
144
+ self.class.messages(self.client, self.id)
145
+ end
146
+
147
+ # Get a Collection of Message objects that represents the messages for this User. Returns cached data if it has been called before.
148
+ def messages
149
+ @messages ||= messages!
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,98 @@
1
+ require 'sfdc/client/connection'
2
+ require 'sfdc/client/authentication'
3
+ require 'sfdc/client/streaming'
4
+ require 'sfdc/client/picklists'
5
+ require 'sfdc/client/caching'
6
+ require 'sfdc/client/canvas'
7
+ require 'sfdc/client/api'
8
+
9
+ module Sfdc
10
+ class Client
11
+ include Sfdc::Client::Connection
12
+ include Sfdc::Client::Authentication
13
+ include Sfdc::Client::Streaming
14
+ include Sfdc::Client::Picklists
15
+ include Sfdc::Client::Caching
16
+ include Sfdc::Client::Canvas
17
+ include Sfdc::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
+ # Sfdc::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
+ # Sfdc::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
+ # Sfdc::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[Sfdc.configuration.options.map { |option| [option, Sfdc.configuration.send(option)] }]
76
+ @options.merge! opts
77
+ yield builder if block_given?
78
+ end
79
+
80
+ def instance_url
81
+ authenticate! unless @options[:instance_url]
82
+ @options[:instance_url]
83
+ end
84
+
85
+ # Public: Returns a url to the resource.
86
+ #
87
+ # resource - A record that responds to to_sparam or a String/Fixnum.
88
+ #
89
+ # Returns the url to the resource.
90
+ def url(resource)
91
+ "#{instance_url}/#{(resource.respond_to?(:to_sparam) ? resource.to_sparam : resource)}"
92
+ end
93
+
94
+ def inspect
95
+ "#<#{self.class} @options=#{@options.inspect}>"
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,320 @@
1
+ require 'sfdc/client/verbs'
2
+ require 'sfdc/client/authentication'
3
+
4
+ module Sfdc
5
+ class Client
6
+ module API
7
+ extend Sfdc::Client::Verbs
8
+
9
+ # Public: Helper methods for performing arbitrary actions against the API using
10
+ # various HTTP verbs.
11
+ #
12
+ # Examples
13
+ #
14
+ # # Perform a get request
15
+ # client.get '/services/data/v24.0/sobjects'
16
+ # client.api_get 'sobjects'
17
+ #
18
+ # # Perform a post request
19
+ # client.post '/services/data/v24.0/sobjects/Account', { ... }
20
+ # client.api_post 'sobjects/Account', { ... }
21
+ #
22
+ # # Perform a put request
23
+ # client.put '/services/data/v24.0/sobjects/Account/001D000000INjVe', { ... }
24
+ # client.api_put 'sobjects/Account/001D000000INjVe', { ... }
25
+ #
26
+ # # Perform a delete request
27
+ # client.delete '/services/data/v24.0/sobjects/Account/001D000000INjVe'
28
+ # client.api_delete 'sobjects/Account/001D000000INjVe'
29
+ #
30
+ # Returns the Faraday::Response.
31
+ define_verbs :get, :post, :put, :delete, :patch, :head
32
+
33
+ # Public: Get the names of all sobjects on the org.
34
+ #
35
+ # Examples
36
+ #
37
+ # # get the names of all sobjects on the org
38
+ # client.list_sobjects
39
+ # # => ['Account', 'Lead', ... ]
40
+ #
41
+ # Returns an Array of String names for each SObject.
42
+ def list_sobjects
43
+ describe.collect { |sobject| sobject['name'] }
44
+ end
45
+
46
+ # Public: Returns a detailed describe result for the specified sobject
47
+ #
48
+ # sobject - Stringish name of the sobject (default: nil).
49
+ #
50
+ # Examples
51
+ #
52
+ # # get the global describe for all sobjects
53
+ # client.describe
54
+ # # => { ... }
55
+ #
56
+ # # get the describe for the Account object
57
+ # client.describe('Account')
58
+ # # => { ... }
59
+ #
60
+ # Returns the Hash representation of the describe call.
61
+ def describe(sobject = nil)
62
+ if sobject
63
+ api_get("sobjects/#{sobject.to_s}/describe").body
64
+ else
65
+ api_get('sobjects').body['sobjects']
66
+ end
67
+ end
68
+
69
+ # Public: Get the current organization's Id.
70
+ #
71
+ # Examples
72
+ #
73
+ # client.org_id
74
+ # # => '00Dx0000000BV7z'
75
+ #
76
+ # Returns the String organization Id
77
+ def org_id
78
+ query('select id from Organization').first['Id']
79
+ end
80
+
81
+ # Public: Get the current user's Id.
82
+ # client.user_id
83
+ def user_id
84
+ response = authenticate!
85
+ info = get(response.id).body
86
+ user_id = info.user_id
87
+ return user_id
88
+ end
89
+
90
+ # Public: Executs a SOQL query and returns the result.
91
+ #
92
+ # soql - A SOQL expression.
93
+ #
94
+ # Examples
95
+ #
96
+ # # Find the names of all Accounts
97
+ # client.query('select Name from Account').map(&:Name)
98
+ # # => ['Foo Bar Inc.', 'Whizbang Corp']
99
+ #
100
+ # Returns a Sfdc::Collection if Sfdc.configuration.mashify is true.
101
+ # Returns an Array of Hash for each record in the result if Sfdc.configuration.mashify is false.
102
+ def query(soql)
103
+ response = api_get 'query', :q => soql
104
+ mashify? ? response.body : response.body['records']
105
+ end
106
+
107
+ # Returns SFDC recent items Id.
108
+ def recent
109
+ response = api_get 'recent'
110
+ mashify? ? response.body : response.body['records']
111
+ end
112
+
113
+ # Public: Perform a SOSL search
114
+ #
115
+ # sosl - A SOSL expression.
116
+ #
117
+ # Examples
118
+ #
119
+ # # Find all occurrences of 'bar'
120
+ # client.search('FIND {bar}')
121
+ # # => #<Sfdc::Collection >
122
+ #
123
+ # # Find accounts match the term 'genepoint' and return the Name field
124
+ # client.search('FIND {genepoint} RETURNING Account (Name)').map(&:Name)
125
+ # # => ['GenePoint']
126
+ #
127
+ # Returns a Sfdc::Collection if Sfdc.configuration.mashify is true.
128
+ # Returns an Array of Hash for each record in the result if Sfdc.configuration.mashify is false.
129
+ def search(sosl)
130
+ api_get('search', :q => sosl).body
131
+ end
132
+
133
+ # Public: Insert a new record.
134
+ #
135
+ # sobject - String name of the sobject.
136
+ # attrs - Hash of attributes to set on the new record.
137
+ #
138
+ # Examples
139
+ #
140
+ # # Add a new account
141
+ # client.create('Account', Name: 'Foobar Inc.')
142
+ # # => '0016000000MRatd'
143
+ #
144
+ # Returns the String Id of the newly created sobject.
145
+ # Returns false if something bad happens.
146
+ def create(*args)
147
+ create!(*args)
148
+ rescue *exceptions
149
+ false
150
+ end
151
+ alias_method :insert, :create
152
+
153
+ # Public: Insert a new record.
154
+ #
155
+ # sobject - String name of the sobject.
156
+ # attrs - Hash of attributes to set on the new record.
157
+ #
158
+ # Examples
159
+ #
160
+ # # Add a new account
161
+ # client.create!('Account', Name: 'Foobar Inc.')
162
+ # # => '0016000000MRatd'
163
+ #
164
+ # Returns the String Id of the newly created sobject.
165
+ # Raises exceptions if an error is returned from Salesforce.
166
+ def create!(sobject, attrs)
167
+ api_post("sobjects/#{sobject}", attrs).body['id']
168
+ end
169
+ alias_method :insert!, :create!
170
+
171
+ # Public: Update a record.
172
+ #
173
+ # sobject - String name of the sobject.
174
+ # attrs - Hash of attributes to set on the record.
175
+ #
176
+ # Examples
177
+ #
178
+ # # Update the Account with Id '0016000000MRatd'
179
+ # client.update('Account', Id: '0016000000MRatd', Name: 'Whizbang Corp')
180
+ #
181
+ # Returns true if the sobject was successfully updated.
182
+ # Returns false if there was an error.
183
+ def update(*args)
184
+ update!(*args)
185
+ rescue *exceptions
186
+ false
187
+ end
188
+
189
+ # Public: Update a record.
190
+ #
191
+ # sobject - String name of the sobject.
192
+ # attrs - Hash of attributes to set on the record.
193
+ #
194
+ # Examples
195
+ #
196
+ # # Update the Account with Id '0016000000MRatd'
197
+ # client.update!('Account', Id: '0016000000MRatd', Name: 'Whizbang Corp')
198
+ #
199
+ # Returns true if the sobject was successfully updated.
200
+ # Raises an exception if an error is returned from Salesforce.
201
+ def update!(sobject, attrs)
202
+ id = attrs.delete(attrs.keys.find { |k| k.to_s.downcase == 'id' })
203
+ raise 'Id field missing.' unless id
204
+ api_patch "sobjects/#{sobject}/#{id}", attrs
205
+ true
206
+ end
207
+
208
+ # Public: Update or create a record based on an external ID
209
+ #
210
+ # sobject - The name of the sobject to created.
211
+ # field - The name of the external Id field to match against.
212
+ # attrs - Hash of attributes for the record.
213
+ #
214
+ # Examples
215
+ #
216
+ # # Update the record with external ID of 12
217
+ # client.upsert('Account', 'External__c', External__c: 12, Name: 'Foobar')
218
+ #
219
+ # Returns true if the record was found and updated.
220
+ # Returns the Id of the newly created record if the record was created.
221
+ # Returns false if something bad happens.
222
+ def upsert(*args)
223
+ upsert!(*args)
224
+ rescue *exceptions
225
+ false
226
+ end
227
+
228
+ # Public: Update or create a record based on an external ID
229
+ #
230
+ # sobject - The name of the sobject to created.
231
+ # field - The name of the external Id field to match against.
232
+ # attrs - Hash of attributes for the record.
233
+ #
234
+ # Examples
235
+ #
236
+ # # Update the record with external ID of 12
237
+ # client.upsert!('Account', 'External__c', External__c: 12, Name: 'Foobar')
238
+ #
239
+ # Returns true if the record was found and updated.
240
+ # Returns the Id of the newly created record if the record was created.
241
+ # Raises an exception if an error is returned from Salesforce.
242
+ def upsert!(sobject, field, attrs)
243
+ external_id = attrs.delete(attrs.keys.find { |k| k.to_s.downcase == field.to_s.downcase })
244
+ response = api_patch "sobjects/#{sobject}/#{field.to_s}/#{external_id}", attrs
245
+ (response.body && response.body['id']) ? response.body['id'] : true
246
+ end
247
+
248
+ # Public: Delete a record.
249
+ #
250
+ # sobject - String name of the sobject.
251
+ # id - The Salesforce ID of the record.
252
+ #
253
+ # Examples
254
+ #
255
+ # # Delete the Account with Id '0016000000MRatd'
256
+ # client.destroy('Account', '0016000000MRatd')
257
+ #
258
+ # Returns true if the sobject was successfully deleted.
259
+ # Returns false if an error is returned from Salesforce.
260
+ def destroy(*args)
261
+ destroy!(*args)
262
+ rescue *exceptions
263
+ false
264
+ end
265
+
266
+ # Public: Delete a record.
267
+ #
268
+ # sobject - String name of the sobject.
269
+ # id - The Salesforce ID of the record.
270
+ #
271
+ # Examples
272
+ #
273
+ # # Delete the Account with Id '0016000000MRatd'
274
+ # client.destroy('Account', '0016000000MRatd')
275
+ #
276
+ # Returns true of the sobject was successfully deleted.
277
+ # Raises an exception if an error is returned from Salesforce.
278
+ def destroy!(sobject, id)
279
+ api_delete "sobjects/#{sobject}/#{id}"
280
+ true
281
+ end
282
+
283
+ # Public: Finds a single record and returns all fields.
284
+ #
285
+ # sobject - The String name of the sobject.
286
+ # id - The id of the record. If field is specified, id should be the id
287
+ # of the external field.
288
+ # field - External ID field to use (default: nil).
289
+ #
290
+ # Returns the Sfdc::SObject sobject record.
291
+ def find(sobject, id, field=nil)
292
+ api_get(field ? "sobjects/#{sobject}/#{field}/#{id}" : "sobjects/#{sobject}/#{id}").body
293
+ end
294
+
295
+
296
+ #################### Chatter items ############################
297
+ def feeds
298
+ api_get("/chatter/feeds/").body
299
+ end
300
+
301
+ private
302
+
303
+ # Internal: Returns a path to an api endpoint
304
+ #
305
+ # Examples
306
+ #
307
+ # api_path('sobjects')
308
+ # # => '/services/data/v24.0/sobjects'
309
+ def api_path(path)
310
+ "/services/data/v#{@options[:api_version]}/#{path}"
311
+ end
312
+
313
+ # Internal: Errors that should be rescued from in non-bang methods
314
+ def exceptions
315
+ [Faraday::Error::ClientError]
316
+ end
317
+
318
+ end
319
+ end
320
+ end