Class: Databasedotcom::Client
- Inherits:
-
Object
- Object
- Databasedotcom::Client
- Defined in:
- lib/databasedotcom/client.rb
Overview
Interface for operating the Force.com REST API
Instance Attribute Summary collapse
-
#client_id ⇒ Object
The client id (aka “Consumer Key”) to use for OAuth2 authentication.
-
#client_secret ⇒ Object
The client secret (aka “Consumer Secret” to use for OAuth2 authentication).
-
#debugging ⇒ Object
If true, print API debugging information to stdout.
-
#host ⇒ Object
The host to use for OAuth2 authentication.
-
#instance_url ⇒ Object
The base URL to the authenticated user’s SalesForce instance.
-
#oauth_token ⇒ Object
The OAuth access token in use by the client.
-
#password ⇒ Object
The SalesForce password.
-
#sobject_module ⇒ Object
A Module in which to materialize Sobject classes.
-
#user_id ⇒ Object
readonly
The SalesForce user id of the authenticated user.
-
#username ⇒ Object
The SalesForce username.
-
#version ⇒ Object
The API version the client is using.
Instance Method Summary collapse
-
#authenticate(options = nil) ⇒ Object
Authenticate to the Force.com API.
-
#create(class_or_classname, object_attrs) ⇒ Object
Returns a new instance of class_or_classname (which can be passed in as either a String or a Class) with the specified attributes.
-
#delete(class_or_classname, record_id) ⇒ Object
Deletes the record of type class_or_classname with id of record_id.
-
#describe_sobject(class_name) ⇒ Object
Returns a description of the Sobject specified by class_name.
-
#find(class_or_classname, record_id) ⇒ Object
Returns an instance of the Sobject specified by class_or_classname (which can be either a String or a Class) populated with the values of the Force.com record specified by record_id.
-
#http_delete(path, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP DELETE request to the specified path (relative to self.instance_url).
-
#http_get(path, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP GET request to the specified path (relative to self.instance_url).
-
#http_multipart_post(path, parts, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP POST request to the specified path (relative to self.instance_url), using Content-Type multiplart/form-data.
-
#http_patch(path, data = nil, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP PATCH request to the specified path (relative to self.instance_url).
-
#http_post(path, data = nil, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP POST request to the specified path (relative to self.instance_url).
-
#initialize(options = {}) ⇒ Client
constructor
Returns a new client object.
-
#list_sobjects ⇒ Object
Returns an Array of Strings listing the class names for every type of Sobject in the database.
-
#materialize(classnames) ⇒ Object
Dynamically defines classes for Force.com class names.
-
#next_page(path) ⇒ Object
Used by Collection objects.
-
#previous_page(path) ⇒ Object
Used by Collection objects.
-
#query(soql_expr) ⇒ Object
Returns a Collection of Sobjects of the class specified in the soql_expr, which is a valid SOQL expression.
-
#recent ⇒ Object
Returns a Collection of recently touched items.
-
#search(sosl_expr) ⇒ Object
Returns a Collection of Sobject instances form the results of the SOSL search.
-
#trending_topics ⇒ Object
Returns an array of trending topic names.
-
#update(class_or_classname, record_id, new_attrs) ⇒ Object
Updates the attributes of the record of type class_or_classname and specified by record_id with the values of new_attrs in the Force.com database.
-
#upsert(class_or_classname, field, value, attrs) ⇒ Object
Attempts to find the record on Force.com of type class_or_classname with attribute field set as value.
Constructor Details
#initialize(options = {}) ⇒ Client
Returns a new client object. options can be one of the following
-
A String containing the name of a YAML file formatted like:
--- client_id: <your_salesforce_client_id> client_secret: <your_salesforce_client_secret> host: login.salesforce.com debugging: true version: 23.0 sobject_module: My::Module
-
A Hash containing the following keys:
client_id client_secret host debugging version sobject_module
If the environment variables DATABASEDOTCOM_CLIENT_ID, DATABASEDOTCOM_CLIENT_SECRET, DATABASEDOTCOM_HOST, DATABASEDOTCOM_DEBUGGING, DATABASEDOTCOM_VERSION, and/or DATABASEDOTCOM_SOBJECT_MODULE are present, they override any other values provided
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/databasedotcom/client.rb', line 51 def initialize( = {}) if .is_a?(String) @options = YAML.load_file() else @options = end @options.symbolize_keys! if ENV['DATABASE_COM_URL'] url = URI.parse(ENV['DATABASE_COM_URL']) = Hash[url.query.split("&").map{|q| q.split("=")}].symbolize_keys! self.host = url.host self.client_id = [:oauth_key] self.client_secret = [:oauth_secret] self.username = [:user] self.password = [:password] else self.client_id = ENV['DATABASEDOTCOM_CLIENT_ID'] || @options[:client_id] self.client_secret = ENV['DATABASEDOTCOM_CLIENT_SECRET'] || @options[:client_secret] self.host = ENV['DATABASEDOTCOM_HOST'] || @options[:host] || "login.salesforce.com" end self.debugging = ENV['DATABASEDOTCOM_DEBUGGING'] || @options[:debugging] self.version = ENV['DATABASEDOTCOM_VERSION'] || @options[:version] self.version = self.version.to_s if self.version self.sobject_module = ENV['DATABASEDOTCOM_SOBJECT_MODULE'] || @options[:sobject_module] end |
Instance Attribute Details
#client_id ⇒ Object
The client id (aka “Consumer Key”) to use for OAuth2 authentication
9 10 11 |
# File 'lib/databasedotcom/client.rb', line 9 def client_id @client_id end |
#client_secret ⇒ Object
The client secret (aka “Consumer Secret” to use for OAuth2 authentication)
11 12 13 |
# File 'lib/databasedotcom/client.rb', line 11 def client_secret @client_secret end |
#debugging ⇒ Object
If true, print API debugging information to stdout. Defaults to false.
17 18 19 |
# File 'lib/databasedotcom/client.rb', line 17 def debugging @debugging end |
#host ⇒ Object
The host to use for OAuth2 authentication. Defaults to login.salesforce.com
19 20 21 |
# File 'lib/databasedotcom/client.rb', line 19 def host @host end |
#instance_url ⇒ Object
The base URL to the authenticated user’s SalesForce instance
15 16 17 |
# File 'lib/databasedotcom/client.rb', line 15 def instance_url @instance_url end |
#oauth_token ⇒ Object
The OAuth access token in use by the client
13 14 15 |
# File 'lib/databasedotcom/client.rb', line 13 def oauth_token @oauth_token end |
#password ⇒ Object
The SalesForce password
29 30 31 |
# File 'lib/databasedotcom/client.rb', line 29 def password @password end |
#sobject_module ⇒ Object
A Module in which to materialize Sobject classes. Defaults to the global module (Object)
23 24 25 |
# File 'lib/databasedotcom/client.rb', line 23 def sobject_module @sobject_module end |
#user_id ⇒ Object (readonly)
The SalesForce user id of the authenticated user
25 26 27 |
# File 'lib/databasedotcom/client.rb', line 25 def user_id @user_id end |
#username ⇒ Object
The SalesForce username
27 28 29 |
# File 'lib/databasedotcom/client.rb', line 27 def username @username end |
#version ⇒ Object
The API version the client is using. Defaults to 23.0
21 22 23 |
# File 'lib/databasedotcom/client.rb', line 21 def version @version end |
Instance Method Details
#authenticate(options = nil) ⇒ Object
Authenticate to the Force.com API. options is a Hash, interpreted as follows:
-
If options contains the keys
:username
and:password
, those credentials are used to authenticate. In this case, the value of:password
may need to include a concatenated security token, if required by your Salesforce org -
If options contains the key
:provider
, it is assumed to be the hash returned by Omniauth from a successful web-based OAuth2 authentication -
If options contains the keys
:token
and:instance_url
, those are assumed to be a valid OAuth2 token and instance URL for a Salesforce account, obtained from an external source
Raises SalesForceError if an error occurs
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/databasedotcom/client.rb', line 85 def authenticate( = nil) if user_and_pass?() req = Net::HTTP.new(self.host, 443) req.use_ssl=true user = self.username || [:username] pass = self.password || [:password] path = "/services/oauth2/token?grant_type=password&client_id=#{self.client_id}&client_secret=#{client_secret}&username=#{user}&password=#{pass}" log_request("https://#{self.host}/#{path}") result = req.post(path, "") log_response(result) raise SalesForceError.new(result) unless result.is_a?(Net::HTTPOK) json = JSON.parse(result.body) @user_id = json["id"].match(/\/([^\/]+)$/)[1] rescue nil self.instance_url = json["instance_url"] self.oauth_token = json["access_token"] elsif .is_a?(Hash) if .has_key?("provider") @user_id = ["extra"]["user_hash"]["user_id"] rescue nil self.instance_url = ["credentials"]["instance_url"] self.oauth_token = ["credentials"]["token"] else raise ArgumentError unless .has_key?(:token) && .has_key?(:instance_url) self.instance_url = [:instance_url] self.oauth_token = [:token] end end self.version = "23.0" unless self.version self.oauth_token end |
#create(class_or_classname, object_attrs) ⇒ Object
Returns a new instance of class_or_classname (which can be passed in as either a String or a Class) with the specified attributes.
client.create("Car", {"Color" => "Blue", "Year" => "2011"}) #=> #<Car @Id="recordid", @Color="Blue", @Year="2011">
202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/databasedotcom/client.rb', line 202 def create(class_or_classname, object_attrs) class_or_classname = find_or_materialize(class_or_classname) json_for_assignment = coerced_json(object_attrs, class_or_classname) result = http_post("/services/data/v#{self.version}/sobjects/#{class_or_classname.sobject_name}", json_for_assignment) new_object = class_or_classname.new JSON.parse(json_for_assignment).each do |property, value| set_value(new_object, property, value, class_or_classname.type_map[property][:type]) end id = JSON.parse(result.body)["id"] set_value(new_object, "Id", id, "id") new_object end |
#delete(class_or_classname, record_id) ⇒ Object
Deletes the record of type class_or_classname with id of record_id. class_or_classname can be a String or a Class.
client.delete(Car, "rid")
237 238 239 240 |
# File 'lib/databasedotcom/client.rb', line 237 def delete(class_or_classname, record_id) clazz = find_or_materialize(class_or_classname) http_delete("/services/data/v#{self.version}/sobjects/#{clazz.sobject_name}/#{record_id}") end |
#describe_sobject(class_name) ⇒ Object
Returns a description of the Sobject specified by class_name. The description includes all fields and their properties for the Sobject.
151 152 153 154 |
# File 'lib/databasedotcom/client.rb', line 151 def describe_sobject(class_name) result = http_get("/services/data/v#{self.version}/sobjects/#{class_name}/describe") JSON.parse(result.body) end |
#find(class_or_classname, record_id) ⇒ Object
Returns an instance of the Sobject specified by class_or_classname (which can be either a String or a Class) populated with the values of the Force.com record specified by record_id. If given a Class that is not defined, it will attempt to materialize the class on demand.
client.find(Account, "recordid") #=> #<Account @Id="recordid", ...>
160 161 162 163 164 165 166 167 168 169 |
# File 'lib/databasedotcom/client.rb', line 160 def find(class_or_classname, record_id) class_or_classname = find_or_materialize(class_or_classname) result = http_get("/services/data/v#{self.version}/sobjects/#{class_or_classname.sobject_name}/#{record_id}") response = JSON.parse(result.body) new_record = class_or_classname.new class_or_classname.description["fields"].each do |field| set_value(new_record, field["name"], response[key_from_label(field["label"])] || response[field["name"]], field["type"]) end new_record end |
#http_delete(path, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP DELETE request to the specified path (relative to self.instance_url). Query parameters are included from parameters. The required Authorization
header is automatically included, as are any additional headers specified in headers. Returns the HTTPResult if it is of type HTTPSuccess- raises SalesForceError otherwise.
274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/databasedotcom/client.rb', line 274 def http_delete(path, parameters={}, headers={}) req = Net::HTTP.new(URI.parse(self.instance_url).host, 443) req.use_ssl = true path_parameters = (parameters || {}).collect { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join('&') encoded_path = [URI.escape(path), path_parameters.empty? ? nil : path_parameters].compact.join('?') log_request(encoded_path) result = req.delete(encoded_path, {"Authorization" => "OAuth #{self.oauth_token}"}.merge(headers)) log_response(result) raise SalesForceError.new(result) unless result.is_a?(Net::HTTPNoContent) result end |
#http_get(path, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP GET request to the specified path (relative to self.instance_url). Query parameters are included from parameters. The required Authorization
header is automatically included, as are any additional headers specified in headers. Returns the HTTPResult if it is of type HTTPSuccess- raises SalesForceError otherwise.
258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/databasedotcom/client.rb', line 258 def http_get(path, parameters={}, headers={}) req = Net::HTTP.new(URI.parse(self.instance_url).host, 443) req.use_ssl = true path_parameters = (parameters || {}).collect { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join('&') encoded_path = [URI.escape(path), path_parameters.empty? ? nil : path_parameters].compact.join('?') log_request(encoded_path) result = req.get(encoded_path, {"Authorization" => "OAuth #{self.oauth_token}"}.merge(headers)) log_response(result) raise SalesForceError.new(result) unless result.is_a?(Net::HTTPSuccess) result end |
#http_multipart_post(path, parts, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP POST request to the specified path (relative to self.instance_url), using Content-Type multiplart/form-data. The parts of the body of the request are taken from parts_. Query parameters are included from parameters. The required Authorization
header is automatically included, as are any additional headers specified in headers. Returns the HTTPResult if it is of type HTTPSuccess- raises SalesForceError otherwise.
320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/databasedotcom/client.rb', line 320 def http_multipart_post(path, parts, parameters={}, headers={}) req = Net::HTTP.new(URI.parse(self.instance_url).host, 443) req.use_ssl = true path_parameters = (parameters || {}).collect { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join('&') encoded_path = [URI.escape(path), path_parameters.empty? ? nil : path_parameters].compact.join('?') log_request(encoded_path) result = req.request(Net::HTTP::Post::Multipart.new(encoded_path, parts, {"Authorization" => "OAuth #{self.oauth_token}"}.merge(headers))) log_response(result) raise SalesForceError.new(result) unless result.is_a?(Net::HTTPSuccess) result end |
#http_patch(path, data = nil, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP PATCH request to the specified path (relative to self.instance_url). The body of the request is taken from data. Query parameters are included from parameters. The required Authorization
header is automatically included, as are any additional headers specified in headers. Returns the HTTPResult if it is of type HTTPSuccess- raises SalesForceError otherwise.
304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/databasedotcom/client.rb', line 304 def http_patch(path, data=nil, parameters={}, headers={}) req = Net::HTTP.new(URI.parse(self.instance_url).host, 443) req.use_ssl = true path_parameters = (parameters || {}).collect { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join('&') encoded_path = [URI.escape(path), path_parameters.empty? ? nil : path_parameters].compact.join('?') log_request(encoded_path, data) result = req.send_request("PATCH", encoded_path, data, {"Content-Type" => data ? "application/json" : "text/plain", "Authorization" => "OAuth #{self.oauth_token}"}.merge(headers)) log_response(result) raise SalesForceError.new(result) unless result.is_a?(Net::HTTPSuccess) result end |
#http_post(path, data = nil, parameters = {}, headers = {}) ⇒ Object
Performs an HTTP POST request to the specified path (relative to self.instance_url). The body of the request is taken from data. Query parameters are included from parameters. The required Authorization
header is automatically included, as are any additional headers specified in headers. Returns the HTTPResult if it is of type HTTPSuccess- raises SalesForceError otherwise.
289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/databasedotcom/client.rb', line 289 def http_post(path, data=nil, parameters={}, headers={}) req = Net::HTTP.new(URI.parse(self.instance_url).host, 443) req.use_ssl = true path_parameters = (parameters || {}).collect { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join('&') encoded_path = [URI.escape(path), path_parameters.empty? ? nil : path_parameters].compact.join('?') log_request(encoded_path, data) result = req.post(encoded_path, data, {"Content-Type" => data ? "application/json" : "text/plain", "Authorization" => "OAuth #{self.oauth_token}"}.merge(headers)) log_response(result) raise SalesForceError.new(result) unless result.is_a?(Net::HTTPSuccess) result end |
#list_sobjects ⇒ Object
Returns an Array of Strings listing the class names for every type of Sobject in the database. Raises SalesForceError if an error occurs.
118 119 120 121 122 123 124 125 |
# File 'lib/databasedotcom/client.rb', line 118 def list_sobjects result = http_get("/services/data/v#{self.version}/sobjects") if result.is_a?(Net::HTTPOK) JSON.parse(result.body)["sobjects"].collect { |sobject| sobject["name"] } elsif result.is_a?(Net::HTTPBadRequest) raise SalesForceError.new(result) end end |
#materialize(classnames) ⇒ Object
Dynamically defines classes for Force.com class names. classnames can be a single String or an Array of Strings. Returns the class or Array of classes defined.
client.materialize("Contact") #=> Contact
client.materialize(%w(Contact Company)) #=> [Contact, Company]
The classes defined by materialize derive from Sobject, and have getters and setters defined for all the attributes defined by the associated Force.com Sobject.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/databasedotcom/client.rb', line 133 def materialize(classnames) classes = (classnames.is_a?(Array) ? classnames : [classnames]).collect do |clazz| original_classname = clazz clazz = original_classname[0].capitalize + original_classname[1..-1] unless module_namespace.const_defined?(clazz) new_class = module_namespace.const_set(clazz, Class.new(Databasedotcom::Sobject::Sobject)) new_class.client = self new_class.materialize(original_classname) new_class else module_namespace.const_get(clazz) end end classes.length == 1 ? classes.first : classes end |
#next_page(path) ⇒ Object
Used by Collection objects. Returns a Collection of Sobjects from the specified URL path that represents the next page of paginated results.
188 189 190 191 |
# File 'lib/databasedotcom/client.rb', line 188 def next_page(path) result = http_get(path) collection_from(result.body) end |
#previous_page(path) ⇒ Object
Used by Collection objects. Returns a Collection of Sobjects from the specified URL path that represents the previous page of paginated results.
194 195 196 197 |
# File 'lib/databasedotcom/client.rb', line 194 def previous_page(path) result = http_get(path) collection_from(result.body) end |
#query(soql_expr) ⇒ Object
Returns a Collection of Sobjects of the class specified in the soql_expr, which is a valid SOQL expression. The objects will only be populated with the values of attributes specified in the query.
client.query("SELECT Name FROM Account") #=> [#<Account @Id=nil, @Name="Foo", ...>, #<Account @Id=nil, @Name="Bar", ...> ...]
174 175 176 177 |
# File 'lib/databasedotcom/client.rb', line 174 def query(soql_expr) result = http_get("/services/data/v#{self.version}/query?q=#{soql_expr}") collection_from(result.body) end |
#recent ⇒ Object
Returns a Collection of recently touched items. The Collection contains Sobject instances that are fully populated with their correct values.
243 244 245 246 |
# File 'lib/databasedotcom/client.rb', line 243 def recent result = http_get("/services/data/v#{self.version}/recent") collection_from(result.body) end |
#search(sosl_expr) ⇒ Object
Returns a Collection of Sobject instances form the results of the SOSL search.
client.search("FIND {bar}") #=> [#<Account @Name="foobar", ...>, #<Account @Name="barfoo", ...> ...]
182 183 184 185 |
# File 'lib/databasedotcom/client.rb', line 182 def search(sosl_expr) result = http_get("/services/data/v#{self.version}/search?q=#{sosl_expr}") collection_from(result.body) end |
#trending_topics ⇒ Object
Returns an array of trending topic names.
249 250 251 252 253 |
# File 'lib/databasedotcom/client.rb', line 249 def trending_topics result = http_get("/services/data/v#{self.version}/chatter/topics/trending") result = JSON.parse(result.body) result["topics"].collect { |topic| topic["name"] } end |
#update(class_or_classname, record_id, new_attrs) ⇒ Object
Updates the attributes of the record of type class_or_classname and specified by record_id with the values of new_attrs in the Force.com database. new_attrs is a hash of attribute => value
client.update("Car", "rid", {"Color" => "Red"})
218 219 220 221 222 |
# File 'lib/databasedotcom/client.rb', line 218 def update(class_or_classname, record_id, new_attrs) class_or_classname = find_or_materialize(class_or_classname) json_for_update = coerced_json(new_attrs, class_or_classname) http_patch("/services/data/v#{self.version}/sobjects/#{class_or_classname.sobject_name}/#{record_id}", json_for_update) end |
#upsert(class_or_classname, field, value, attrs) ⇒ Object
Attempts to find the record on Force.com of type class_or_classname with attribute field set as value. If found, it will update the record with the attrs hash. If not found, it will create a new record with attrs.
client.upsert(Car, "Color", "Blue", {"Year" => "2012"})
228 229 230 231 232 |
# File 'lib/databasedotcom/client.rb', line 228 def upsert(class_or_classname, field, value, attrs) clazz = find_or_materialize(class_or_classname) json_for_update = coerced_json(attrs, clazz) http_patch("/services/data/v#{self.version}/sobjects/#{clazz.sobject_name}/#{field}/#{value}", json_for_update) end |