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,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