force 0.0.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +107 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +421 -0
- data/Rakefile +10 -0
- data/coverage/assets/0.7.1/application.css +1110 -0
- data/coverage/assets/0.7.1/application.js +626 -0
- data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
- data/coverage/assets/0.7.1/favicon_green.png +0 -0
- data/coverage/assets/0.7.1/favicon_red.png +0 -0
- data/coverage/assets/0.7.1/favicon_yellow.png +0 -0
- data/coverage/assets/0.7.1/loading.gif +0 -0
- data/coverage/assets/0.7.1/magnify.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +19808 -0
- data/force.gemspec +27 -0
- data/lib/force.rb +74 -0
- data/lib/force/abstract_client.rb +9 -0
- data/lib/force/attachment.rb +21 -0
- data/lib/force/client.rb +3 -0
- data/lib/force/collection.rb +45 -0
- data/lib/force/concerns/api.rb +321 -0
- data/lib/force/concerns/authentication.rb +39 -0
- data/lib/force/concerns/base.rb +59 -0
- data/lib/force/concerns/caching.rb +24 -0
- data/lib/force/concerns/canvas.rb +10 -0
- data/lib/force/concerns/connection.rb +74 -0
- data/lib/force/concerns/picklists.rb +87 -0
- data/lib/force/concerns/streaming.rb +31 -0
- data/lib/force/concerns/verbs.rb +67 -0
- data/lib/force/config.rb +140 -0
- data/lib/force/data/client.rb +18 -0
- data/lib/force/mash.rb +66 -0
- data/lib/force/middleware.rb +27 -0
- data/lib/force/middleware/authentication.rb +73 -0
- data/lib/force/middleware/authentication/password.rb +17 -0
- data/lib/force/middleware/authentication/token.rb +15 -0
- data/lib/force/middleware/authorization.rb +15 -0
- data/lib/force/middleware/caching.rb +22 -0
- data/lib/force/middleware/gzip.rb +31 -0
- data/lib/force/middleware/instance_url.rb +14 -0
- data/lib/force/middleware/logger.rb +40 -0
- data/lib/force/middleware/mashify.rb +16 -0
- data/lib/force/middleware/multipart.rb +55 -0
- data/lib/force/middleware/raise_error.rb +25 -0
- data/lib/force/signed_request.rb +48 -0
- data/lib/force/sobject.rb +68 -0
- data/lib/force/tooling/client.rb +11 -0
- data/lib/force/upload_io.rb +20 -0
- data/lib/force/version.rb +3 -0
- data/spec/fixtures/auth_error_response.json +4 -0
- data/spec/fixtures/auth_success_response.json +7 -0
- data/spec/fixtures/blob.jpg +0 -0
- data/spec/fixtures/expired_session_response.json +6 -0
- data/spec/fixtures/reauth_success_response.json +7 -0
- data/spec/fixtures/refresh_error_response.json +4 -0
- data/spec/fixtures/refresh_success_response.json +7 -0
- data/spec/fixtures/services_data_success_response.json +12 -0
- data/spec/fixtures/sobject/create_success_response.json +5 -0
- data/spec/fixtures/sobject/delete_error_response.json +1 -0
- data/spec/fixtures/sobject/describe_sobjects_success_response.json +31 -0
- data/spec/fixtures/sobject/list_sobjects_success_response.json +31 -0
- data/spec/fixtures/sobject/org_query_response.json +11 -0
- data/spec/fixtures/sobject/query_aggregate_success_response.json +23 -0
- data/spec/fixtures/sobject/query_empty_response.json +5 -0
- data/spec/fixtures/sobject/query_error_response.json +6 -0
- data/spec/fixtures/sobject/query_paginated_first_page_response.json +14 -0
- data/spec/fixtures/sobject/query_paginated_last_page_response.json +13 -0
- data/spec/fixtures/sobject/query_success_response.json +38 -0
- data/spec/fixtures/sobject/recent_success_response.json +18 -0
- data/spec/fixtures/sobject/search_error_response.json +6 -0
- data/spec/fixtures/sobject/search_success_response.json +16 -0
- data/spec/fixtures/sobject/sobject_describe_error_response.json +6 -0
- data/spec/fixtures/sobject/sobject_describe_success_response.json +1429 -0
- data/spec/fixtures/sobject/sobject_find_error_response.json +6 -0
- data/spec/fixtures/sobject/sobject_find_success_response.json +29 -0
- data/spec/fixtures/sobject/upsert_created_success_response.json +5 -0
- data/spec/fixtures/sobject/upsert_error_response.json +6 -0
- data/spec/fixtures/sobject/upsert_multiple_error_response.json +4 -0
- data/spec/fixtures/sobject/upsert_updated_success_response.json +0 -0
- data/spec/fixtures/sobject/write_error_response.json +6 -0
- data/spec/integration/abstract_client_spec.rb +306 -0
- data/spec/integration/data/client_spec.rb +90 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/client_integration.rb +45 -0
- data/spec/support/concerns.rb +18 -0
- data/spec/support/event_machine.rb +14 -0
- data/spec/support/fixture_helpers.rb +45 -0
- data/spec/support/matchers.rb +11 -0
- data/spec/support/middleware.rb +76 -0
- data/spec/support/mock_cache.rb +13 -0
- data/spec/unit/abstract_client_spec.rb +11 -0
- data/spec/unit/attachment_spec.rb +15 -0
- data/spec/unit/collection_spec.rb +52 -0
- data/spec/unit/concerns/api_spec.rb +244 -0
- data/spec/unit/concerns/authentication_spec.rb +98 -0
- data/spec/unit/concerns/base_spec.rb +42 -0
- data/spec/unit/concerns/caching_spec.rb +29 -0
- data/spec/unit/concerns/canvas_spec.rb +30 -0
- data/spec/unit/concerns/connection_spec.rb +22 -0
- data/spec/unit/config_spec.rb +99 -0
- data/spec/unit/data/client_spec.rb +10 -0
- data/spec/unit/mash_spec.rb +36 -0
- data/spec/unit/middleware/authentication/password_spec.rb +31 -0
- data/spec/unit/middleware/authentication/token_spec.rb +24 -0
- data/spec/unit/middleware/authentication_spec.rb +67 -0
- data/spec/unit/middleware/authorization_spec.rb +11 -0
- data/spec/unit/middleware/gzip_spec.rb +66 -0
- data/spec/unit/middleware/instance_url_spec.rb +24 -0
- data/spec/unit/middleware/logger_spec.rb +19 -0
- data/spec/unit/middleware/mashify_spec.rb +11 -0
- data/spec/unit/middleware/raise_error_spec.rb +32 -0
- data/spec/unit/signed_request_spec.rb +24 -0
- data/spec/unit/sobject_spec.rb +86 -0
- data/spec/unit/tooling/client_spec.rb +7 -0
- data/tmp/rspec_guard_result +1 -0
- metadata +383 -0
data/force.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../lib/force/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = "force"
|
|
6
|
+
s.version = Force::VERSION
|
|
7
|
+
s.authors = ["Eric J. Holmes", "Mattt Thompson"]
|
|
8
|
+
s.email = ["eric@ejholmes.net", "mattt@heroku.com"]
|
|
9
|
+
s.description = "A lightweight ruby client for the Salesforce REST api."
|
|
10
|
+
s.summary = "A lightweight ruby client for the Salesforce REST api."
|
|
11
|
+
s.homepage = "https://github.com/heroku/force"
|
|
12
|
+
|
|
13
|
+
s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|example|log|pkg|script|spec|test|vendor)/ }
|
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.require_paths = ["lib"]
|
|
17
|
+
|
|
18
|
+
s.add_dependency 'faraday', '~> 0.8.4'
|
|
19
|
+
s.add_dependency 'faraday_middleware', '>= 0.8.8'
|
|
20
|
+
s.add_dependency 'json', ['>= 1.7.5', '< 1.9.0']
|
|
21
|
+
s.add_dependency 'hashie', ['>= 1.2.0', '< 2.1']
|
|
22
|
+
|
|
23
|
+
s.add_development_dependency 'rspec', '~> 2.14.0'
|
|
24
|
+
s.add_development_dependency 'webmock', '~> 1.13.0'
|
|
25
|
+
s.add_development_dependency 'simplecov', '~> 0.7.1'
|
|
26
|
+
s.add_development_dependency 'faye' unless RUBY_PLATFORM == 'java'
|
|
27
|
+
end
|
data/lib/force.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'faraday_middleware'
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
require 'force/version'
|
|
6
|
+
require 'force/config'
|
|
7
|
+
|
|
8
|
+
module Force
|
|
9
|
+
autoload :AbstractClient, 'force/abstract_client'
|
|
10
|
+
autoload :SignedRequest, 'force/signed_request'
|
|
11
|
+
autoload :Collection, 'force/collection'
|
|
12
|
+
autoload :Middleware, 'force/middleware'
|
|
13
|
+
autoload :Attachment, 'force/attachment'
|
|
14
|
+
autoload :UploadIO, 'force/upload_io'
|
|
15
|
+
autoload :SObject, 'force/sobject'
|
|
16
|
+
autoload :Client, 'force/client'
|
|
17
|
+
autoload :Mash, 'force/mash'
|
|
18
|
+
|
|
19
|
+
module Concerns
|
|
20
|
+
autoload :Authentication, 'force/concerns/authentication'
|
|
21
|
+
autoload :Connection, 'force/concerns/connection'
|
|
22
|
+
autoload :Picklists, 'force/concerns/picklists'
|
|
23
|
+
autoload :Streaming, 'force/concerns/streaming'
|
|
24
|
+
autoload :Caching, 'force/concerns/caching'
|
|
25
|
+
autoload :Canvas, 'force/concerns/canvas'
|
|
26
|
+
autoload :Verbs, 'force/concerns/verbs'
|
|
27
|
+
autoload :Base, 'force/concerns/base'
|
|
28
|
+
autoload :API, 'force/concerns/api'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module Data
|
|
32
|
+
autoload :Client, 'force/data/client'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module Tooling
|
|
36
|
+
autoload :Client, 'force/tooling/client'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Error = Class.new(StandardError)
|
|
40
|
+
AuthenticationError = Class.new(Error)
|
|
41
|
+
UnauthorizedError = Class.new(Error)
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# Alias for Force::Data::Client.new
|
|
45
|
+
#
|
|
46
|
+
# Shamelessly pulled from https://github.com/pengwynn/octokit/blob/master/lib/octokit.rb
|
|
47
|
+
def new(*args)
|
|
48
|
+
data(*args)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def data(*args)
|
|
52
|
+
Force::Data::Client.new(*args)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def tooling(*args)
|
|
56
|
+
Force::Tooling::Client.new(*args)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Helper for decoding signed requests.
|
|
60
|
+
def decode_signed_request(*args)
|
|
61
|
+
SignedRequest.decode(*args)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Add .tap method in Ruby 1.8
|
|
66
|
+
module CoreExtensions
|
|
67
|
+
def tap
|
|
68
|
+
yield self
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Object.send :include, Force::CoreExtensions unless Object.respond_to? :tap
|
|
74
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Force
|
|
2
|
+
class Attachment < Force::SObject
|
|
3
|
+
# Public: Returns the body of the attachment.
|
|
4
|
+
#
|
|
5
|
+
# Examples
|
|
6
|
+
#
|
|
7
|
+
# attachment = client.query('select Id, Name, Body from Attachment').first
|
|
8
|
+
# File.open(attachment.Name, 'wb') { |f| f.write(attachment.Body) }
|
|
9
|
+
def Body
|
|
10
|
+
ensure_id && ensure_body
|
|
11
|
+
@client.get(super).body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def ensure_body
|
|
17
|
+
return true if self.Body?
|
|
18
|
+
raise 'You need to query the Body for the record first.'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/force/client.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Force
|
|
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 Force::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 array of the elements on the current page
|
|
26
|
+
def current_page
|
|
27
|
+
first(@raw_page['records'].size)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Return the current and all of the following pages.
|
|
31
|
+
def pages
|
|
32
|
+
[self] + (has_next_page? ? next_page.pages : [])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns true if there is a pointer to the next page.
|
|
36
|
+
def has_next_page?
|
|
37
|
+
!@raw_page['nextRecordsUrl'].nil?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the next page as a Force::Collection if it's available, nil otherwise.
|
|
41
|
+
def next_page
|
|
42
|
+
@next_page ||= @client.get(@raw_page['nextRecordsUrl']).body if has_next_page?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
require 'force/concerns/verbs'
|
|
2
|
+
|
|
3
|
+
module Force
|
|
4
|
+
module Concerns
|
|
5
|
+
module API
|
|
6
|
+
extend Force::Concerns::Verbs
|
|
7
|
+
|
|
8
|
+
# Public: Helper methods for performing arbitrary actions against the API using
|
|
9
|
+
# various HTTP verbs.
|
|
10
|
+
#
|
|
11
|
+
# Examples
|
|
12
|
+
#
|
|
13
|
+
# # Perform a get request
|
|
14
|
+
# client.get '/services/data/v24.0/sobjects'
|
|
15
|
+
# client.api_get 'sobjects'
|
|
16
|
+
#
|
|
17
|
+
# # Perform a post request
|
|
18
|
+
# client.post '/services/data/v24.0/sobjects/Account', { ... }
|
|
19
|
+
# client.api_post 'sobjects/Account', { ... }
|
|
20
|
+
#
|
|
21
|
+
# # Perform a put request
|
|
22
|
+
# client.put '/services/data/v24.0/sobjects/Account/001D000000INjVe', { ... }
|
|
23
|
+
# client.api_put 'sobjects/Account/001D000000INjVe', { ... }
|
|
24
|
+
#
|
|
25
|
+
# # Perform a delete request
|
|
26
|
+
# client.delete '/services/data/v24.0/sobjects/Account/001D000000INjVe'
|
|
27
|
+
# client.api_delete 'sobjects/Account/001D000000INjVe'
|
|
28
|
+
#
|
|
29
|
+
# Returns the Faraday::Response.
|
|
30
|
+
define_verbs :get, :post, :put, :delete, :patch, :head
|
|
31
|
+
|
|
32
|
+
# Public: Get the names of all sobjects on the org.
|
|
33
|
+
#
|
|
34
|
+
# Examples
|
|
35
|
+
#
|
|
36
|
+
# # get the names of all sobjects on the org
|
|
37
|
+
# client.list_sobjects
|
|
38
|
+
# # => ['Account', 'Lead', ... ]
|
|
39
|
+
#
|
|
40
|
+
# Returns an Array of String names for each SObject.
|
|
41
|
+
def list_sobjects
|
|
42
|
+
describe.collect { |sobject| sobject['name'] }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Public: Returns a detailed describe result for the specified sobject
|
|
46
|
+
#
|
|
47
|
+
# sobject - Stringish name of the sobject (default: nil).
|
|
48
|
+
#
|
|
49
|
+
# Examples
|
|
50
|
+
#
|
|
51
|
+
# # get the global describe for all sobjects
|
|
52
|
+
# client.describe
|
|
53
|
+
# # => { ... }
|
|
54
|
+
#
|
|
55
|
+
# # get the describe for the Account object
|
|
56
|
+
# client.describe('Account')
|
|
57
|
+
# # => { ... }
|
|
58
|
+
#
|
|
59
|
+
# Returns the Hash representation of the describe call.
|
|
60
|
+
def describe(sobject = nil)
|
|
61
|
+
if sobject
|
|
62
|
+
api_get("sobjects/#{sobject.to_s}/describe").body
|
|
63
|
+
else
|
|
64
|
+
api_get('sobjects').body['sobjects']
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Public: Returns a detailed description of the Page Layout for the
|
|
69
|
+
# specified sobject type, or URIs for layouts if the sobject has
|
|
70
|
+
# multiple Record Types.
|
|
71
|
+
#
|
|
72
|
+
# This resource was introduced in version 28.0.
|
|
73
|
+
#
|
|
74
|
+
# Examples:
|
|
75
|
+
# # get the layouts for the sobject
|
|
76
|
+
# client.describe_layouts('Account')
|
|
77
|
+
# # => { ... }
|
|
78
|
+
#
|
|
79
|
+
# # get the layout for the specified Id for the sobject
|
|
80
|
+
# client.describe_layouts('Account', '012E0000000RHEp')
|
|
81
|
+
# # => { ... }
|
|
82
|
+
#
|
|
83
|
+
# Returns the Hash representation of the describe_layouts result
|
|
84
|
+
def describe_layouts(sobject, layout_id = nil)
|
|
85
|
+
if layout_id
|
|
86
|
+
api_get("sobjects/#{sobject.to_s}/describe/layouts/#{layout_id}").body
|
|
87
|
+
else
|
|
88
|
+
api_get("sobjects/#{sobject.to_s}/describe/layouts").body
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Public: Get the current organization's Id.
|
|
93
|
+
#
|
|
94
|
+
# Examples
|
|
95
|
+
#
|
|
96
|
+
# client.org_id
|
|
97
|
+
# # => '00Dx0000000BV7z'
|
|
98
|
+
#
|
|
99
|
+
# Returns the String organization Id
|
|
100
|
+
def org_id
|
|
101
|
+
query('select id from Organization').first['Id']
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Public: Executs a SOQL query and returns the result.
|
|
105
|
+
#
|
|
106
|
+
# soql - A SOQL expression.
|
|
107
|
+
#
|
|
108
|
+
# Examples
|
|
109
|
+
#
|
|
110
|
+
# # Find the names of all Accounts
|
|
111
|
+
# client.query('select Name from Account').map(&:Name)
|
|
112
|
+
# # => ['Foo Bar Inc.', 'Whizbang Corp']
|
|
113
|
+
#
|
|
114
|
+
# Returns a Force::Collection if Force.configuration.mashify is true.
|
|
115
|
+
# Returns an Array of Hash for each record in the result if Force.configuration.mashify is false.
|
|
116
|
+
def query(soql)
|
|
117
|
+
response = api_get 'query', :q => soql
|
|
118
|
+
mashify? ? response.body : response.body['records']
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Public: Perform a SOSL search
|
|
122
|
+
#
|
|
123
|
+
# sosl - A SOSL expression.
|
|
124
|
+
#
|
|
125
|
+
# Examples
|
|
126
|
+
#
|
|
127
|
+
# # Find all occurrences of 'bar'
|
|
128
|
+
# client.search('FIND {bar}')
|
|
129
|
+
# # => #<Force::Collection >
|
|
130
|
+
#
|
|
131
|
+
# # Find accounts match the term 'genepoint' and return the Name field
|
|
132
|
+
# client.search('FIND {genepoint} RETURNING Account (Name)').map(&:Name)
|
|
133
|
+
# # => ['GenePoint']
|
|
134
|
+
#
|
|
135
|
+
# Returns a Force::Collection if Force.configuration.mashify is true.
|
|
136
|
+
# Returns an Array of Hash for each record in the result if Force.configuration.mashify is false.
|
|
137
|
+
def search(sosl)
|
|
138
|
+
api_get('search', :q => sosl).body
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Public: Insert a new record.
|
|
142
|
+
#
|
|
143
|
+
# sobject - String name of the sobject.
|
|
144
|
+
# attrs - Hash of attributes to set on the new record.
|
|
145
|
+
#
|
|
146
|
+
# Examples
|
|
147
|
+
#
|
|
148
|
+
# # Add a new account
|
|
149
|
+
# client.create('Account', Name: 'Foobar Inc.')
|
|
150
|
+
# # => '0016000000MRatd'
|
|
151
|
+
#
|
|
152
|
+
# Returns the String Id of the newly created sobject.
|
|
153
|
+
# Returns false if something bad happens.
|
|
154
|
+
def create(*args)
|
|
155
|
+
create!(*args)
|
|
156
|
+
rescue *exceptions
|
|
157
|
+
false
|
|
158
|
+
end
|
|
159
|
+
alias_method :insert, :create
|
|
160
|
+
|
|
161
|
+
# Public: Insert a new record.
|
|
162
|
+
#
|
|
163
|
+
# sobject - String name of the sobject.
|
|
164
|
+
# attrs - Hash of attributes to set on the new record.
|
|
165
|
+
#
|
|
166
|
+
# Examples
|
|
167
|
+
#
|
|
168
|
+
# # Add a new account
|
|
169
|
+
# client.create!('Account', Name: 'Foobar Inc.')
|
|
170
|
+
# # => '0016000000MRatd'
|
|
171
|
+
#
|
|
172
|
+
# Returns the String Id of the newly created sobject.
|
|
173
|
+
# Raises exceptions if an error is returned from Salesforce.
|
|
174
|
+
def create!(sobject, attrs)
|
|
175
|
+
api_post("sobjects/#{sobject}", attrs).body['id']
|
|
176
|
+
end
|
|
177
|
+
alias_method :insert!, :create!
|
|
178
|
+
|
|
179
|
+
# Public: Update a record.
|
|
180
|
+
#
|
|
181
|
+
# sobject - String name of the sobject.
|
|
182
|
+
# attrs - Hash of attributes to set on the record.
|
|
183
|
+
#
|
|
184
|
+
# Examples
|
|
185
|
+
#
|
|
186
|
+
# # Update the Account with Id '0016000000MRatd'
|
|
187
|
+
# client.update('Account', Id: '0016000000MRatd', Name: 'Whizbang Corp')
|
|
188
|
+
#
|
|
189
|
+
# Returns true if the sobject was successfully updated.
|
|
190
|
+
# Returns false if there was an error.
|
|
191
|
+
def update(*args)
|
|
192
|
+
update!(*args)
|
|
193
|
+
rescue *exceptions
|
|
194
|
+
false
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Public: Update a record.
|
|
198
|
+
#
|
|
199
|
+
# sobject - String name of the sobject.
|
|
200
|
+
# attrs - Hash of attributes to set on the record.
|
|
201
|
+
#
|
|
202
|
+
# Examples
|
|
203
|
+
#
|
|
204
|
+
# # Update the Account with Id '0016000000MRatd'
|
|
205
|
+
# client.update!('Account', Id: '0016000000MRatd', Name: 'Whizbang Corp')
|
|
206
|
+
#
|
|
207
|
+
# Returns true if the sobject was successfully updated.
|
|
208
|
+
# Raises an exception if an error is returned from Salesforce.
|
|
209
|
+
def update!(sobject, attrs)
|
|
210
|
+
id = attrs.delete(attrs.keys.find { |k| k.to_s.downcase == 'id' })
|
|
211
|
+
raise ArgumentError, 'Id field missing from attrs.' unless id
|
|
212
|
+
api_patch "sobjects/#{sobject}/#{id}", attrs
|
|
213
|
+
true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Public: Update or create a record based on an external ID
|
|
217
|
+
#
|
|
218
|
+
# sobject - The name of the sobject to created.
|
|
219
|
+
# field - The name of the external Id field to match against.
|
|
220
|
+
# attrs - Hash of attributes for the record.
|
|
221
|
+
#
|
|
222
|
+
# Examples
|
|
223
|
+
#
|
|
224
|
+
# # Update the record with external ID of 12
|
|
225
|
+
# client.upsert('Account', 'External__c', External__c: 12, Name: 'Foobar')
|
|
226
|
+
#
|
|
227
|
+
# Returns true if the record was found and updated.
|
|
228
|
+
# Returns the Id of the newly created record if the record was created.
|
|
229
|
+
# Returns false if something bad happens.
|
|
230
|
+
def upsert(*args)
|
|
231
|
+
upsert!(*args)
|
|
232
|
+
rescue *exceptions
|
|
233
|
+
false
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Public: Update or create a record based on an external ID
|
|
237
|
+
#
|
|
238
|
+
# sobject - The name of the sobject to created.
|
|
239
|
+
# field - The name of the external Id field to match against.
|
|
240
|
+
# attrs - Hash of attributes for the record.
|
|
241
|
+
#
|
|
242
|
+
# Examples
|
|
243
|
+
#
|
|
244
|
+
# # Update the record with external ID of 12
|
|
245
|
+
# client.upsert!('Account', 'External__c', External__c: 12, Name: 'Foobar')
|
|
246
|
+
#
|
|
247
|
+
# Returns true if the record was found and updated.
|
|
248
|
+
# Returns the Id of the newly created record if the record was created.
|
|
249
|
+
# Raises an exception if an error is returned from Salesforce.
|
|
250
|
+
def upsert!(sobject, field, attrs)
|
|
251
|
+
external_id = attrs.delete(attrs.keys.find { |k| k.to_s.downcase == field.to_s.downcase })
|
|
252
|
+
response = api_patch "sobjects/#{sobject}/#{field.to_s}/#{external_id}", attrs
|
|
253
|
+
(response.body && response.body['id']) ? response.body['id'] : true
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Public: Delete a record.
|
|
257
|
+
#
|
|
258
|
+
# sobject - String name of the sobject.
|
|
259
|
+
# id - The Salesforce ID of the record.
|
|
260
|
+
#
|
|
261
|
+
# Examples
|
|
262
|
+
#
|
|
263
|
+
# # Delete the Account with Id '0016000000MRatd'
|
|
264
|
+
# client.destroy('Account', '0016000000MRatd')
|
|
265
|
+
#
|
|
266
|
+
# Returns true if the sobject was successfully deleted.
|
|
267
|
+
# Returns false if an error is returned from Salesforce.
|
|
268
|
+
def destroy(*args)
|
|
269
|
+
destroy!(*args)
|
|
270
|
+
rescue *exceptions
|
|
271
|
+
false
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Public: Delete a record.
|
|
275
|
+
#
|
|
276
|
+
# sobject - String name of the sobject.
|
|
277
|
+
# id - The Salesforce ID of the record.
|
|
278
|
+
#
|
|
279
|
+
# Examples
|
|
280
|
+
#
|
|
281
|
+
# # Delete the Account with Id '0016000000MRatd'
|
|
282
|
+
# client.destroy('Account', '0016000000MRatd')
|
|
283
|
+
#
|
|
284
|
+
# Returns true of the sobject was successfully deleted.
|
|
285
|
+
# Raises an exception if an error is returned from Salesforce.
|
|
286
|
+
def destroy!(sobject, id)
|
|
287
|
+
api_delete "sobjects/#{sobject}/#{id}"
|
|
288
|
+
true
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Public: Finds a single record and returns all fields.
|
|
292
|
+
#
|
|
293
|
+
# sobject - The String name of the sobject.
|
|
294
|
+
# id - The id of the record. If field is specified, id should be the id
|
|
295
|
+
# of the external field.
|
|
296
|
+
# field - External ID field to use (default: nil).
|
|
297
|
+
#
|
|
298
|
+
# Returns the Force::SObject sobject record.
|
|
299
|
+
def find(sobject, id, field=nil)
|
|
300
|
+
api_get(field ? "sobjects/#{sobject}/#{field}/#{id}" : "sobjects/#{sobject}/#{id}").body
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
private
|
|
304
|
+
|
|
305
|
+
# Internal: Returns a path to an api endpoint
|
|
306
|
+
#
|
|
307
|
+
# Examples
|
|
308
|
+
#
|
|
309
|
+
# api_path('sobjects')
|
|
310
|
+
# # => '/services/data/v24.0/sobjects'
|
|
311
|
+
def api_path(path)
|
|
312
|
+
"/services/data/v#{options[:api_version]}/#{path}"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Internal: Errors that should be rescued from in non-bang methods
|
|
316
|
+
def exceptions
|
|
317
|
+
[Faraday::Error::ClientError]
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|