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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d950e4cd2654615f569824faddc3e9c5cab80ce4
|
|
4
|
+
data.tar.gz: 5db2eece0b302ea2500940527bd14d35db0a169a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 63c2e2a59ad018d5f21cb5c8c6c72aff006b39643164a79e23dfadbfbb686e9334184c55722f038f812dc8031bee8cc447f82e36ca6a006d485fe5915c4a552f
|
|
7
|
+
data.tar.gz: 27fe7af0715d915c42352a1b9b7dd116f456cfb915242816d7c6a5e9ecba614837fcd7cfd49d87a7616f27fca7a960889a86fba77ccb0274fb305c7e1784f1b0
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
## 1.4.1 (Jun 18, 2013)
|
|
2
|
+
|
|
3
|
+
* Fixed a bug with HTTP 413 responses #75 @patronmanager
|
|
4
|
+
|
|
5
|
+
## 1.4.0 (Jun 9, 2013)
|
|
6
|
+
|
|
7
|
+
* Added support for the tooling API.
|
|
8
|
+
* Fixed a bug with EMSynchrony adapter.
|
|
9
|
+
* Added proxy support.
|
|
10
|
+
|
|
11
|
+
## 1.3.0 (Apr 6, 2013)
|
|
12
|
+
|
|
13
|
+
* Added support for lazily traversing paginated collections #61 by @nahiluhmot.
|
|
14
|
+
|
|
15
|
+
## 1.2.0 (Mar 30, 2013)
|
|
16
|
+
|
|
17
|
+
* Added support for proxies #60 by @wazoo.
|
|
18
|
+
|
|
19
|
+
## 1.1.0 (Mar 3, 2013)
|
|
20
|
+
|
|
21
|
+
* Added ability to download attachments easily.
|
|
22
|
+
|
|
23
|
+
Example
|
|
24
|
+
|
|
25
|
+
attachment = client.query('select Id, Name, Body from Attachment').first
|
|
26
|
+
File.open(attachment.Name, 'wb') { |f| f.write(attachment.Body) }
|
|
27
|
+
|
|
28
|
+
## 1.0.6 (Feb 16, 2013)
|
|
29
|
+
|
|
30
|
+
* Added `url` method.
|
|
31
|
+
|
|
32
|
+
Example
|
|
33
|
+
|
|
34
|
+
# Url to a record id
|
|
35
|
+
client.url('0013000000rRz')
|
|
36
|
+
# => https://na1.salesforce.com/0013000000rRz
|
|
37
|
+
|
|
38
|
+
# Url to an object that responds to `to_sparam`
|
|
39
|
+
record = Struct.new(:to_sparam).new('0013000000rRz')
|
|
40
|
+
client.url('0013000000rRz')
|
|
41
|
+
# => https://na1.salesforce.com/0013000000rRz
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## 1.0.5 (Jan 11, 2013)
|
|
45
|
+
|
|
46
|
+
* Added `picklist_values` method.
|
|
47
|
+
|
|
48
|
+
Example
|
|
49
|
+
|
|
50
|
+
client.picklist_values('Account', 'Type')
|
|
51
|
+
|
|
52
|
+
client.picklist_values('Automobile__c', 'Model__c', :valid_for => 'Honda')
|
|
53
|
+
|
|
54
|
+
* Added CHANGELOG.md
|
|
55
|
+
|
|
56
|
+
## 1.0.4 (Jan 8, 2013)
|
|
57
|
+
|
|
58
|
+
* `Force::Client#inspect` now only prints out the options and not the
|
|
59
|
+
Faraday connection.
|
|
60
|
+
|
|
61
|
+
* The Faraday adapter is now configurabled:
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
|
|
65
|
+
Force.configure do |config|
|
|
66
|
+
config.adapter = :excon
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
* The http connection read/open timeout is now configurabled.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
|
|
73
|
+
Force.configure do |config|
|
|
74
|
+
config.timeout = 300
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
## 1.0.3 (Jan 7, 2013)
|
|
78
|
+
|
|
79
|
+
* Fixed typo in method call.
|
|
80
|
+
|
|
81
|
+
## 1.0.2 (Jan 7, 2013)
|
|
82
|
+
|
|
83
|
+
* Minor cleanup.
|
|
84
|
+
* Moved decoding of signed requests into it's own class.
|
|
85
|
+
|
|
86
|
+
## 1.0.1 (Dec 31, 2012)
|
|
87
|
+
|
|
88
|
+
* `username`, `password`, `security_token`, `client_id` and `client_secret`
|
|
89
|
+
options now obtain defaults from environment variables.
|
|
90
|
+
* Add `head` verb.
|
|
91
|
+
|
|
92
|
+
## 1.0.0 (Dec 23, 2012)
|
|
93
|
+
|
|
94
|
+
* Default api version changed from 24.0 to 26.0.
|
|
95
|
+
* Fixed tests for streaming api to work with latest versions of faye.
|
|
96
|
+
* Added .find method to obtain all fields from an sobject.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
force (0.0.1)
|
|
5
|
+
faraday (~> 0.8.4)
|
|
6
|
+
faraday_middleware (>= 0.8.8)
|
|
7
|
+
hashie (>= 1.2.0, < 2.1)
|
|
8
|
+
json (>= 1.7.5, < 1.9.0)
|
|
9
|
+
|
|
10
|
+
GEM
|
|
11
|
+
remote: https://rubygems.org/
|
|
12
|
+
specs:
|
|
13
|
+
addressable (2.3.5)
|
|
14
|
+
coderay (1.0.9)
|
|
15
|
+
cookiejar (0.3.0)
|
|
16
|
+
crack (0.4.1)
|
|
17
|
+
safe_yaml (~> 0.9.0)
|
|
18
|
+
diff-lcs (1.2.4)
|
|
19
|
+
em-http-request (1.1.1)
|
|
20
|
+
addressable (>= 2.3.4)
|
|
21
|
+
cookiejar
|
|
22
|
+
em-socksify (>= 0.3)
|
|
23
|
+
eventmachine (>= 1.0.3)
|
|
24
|
+
http_parser.rb (>= 0.6.0.beta.2)
|
|
25
|
+
em-socksify (0.3.0)
|
|
26
|
+
eventmachine (>= 1.0.0.beta.4)
|
|
27
|
+
eventmachine (1.0.3)
|
|
28
|
+
faraday (0.8.8)
|
|
29
|
+
multipart-post (~> 1.2.0)
|
|
30
|
+
faraday_middleware (0.9.0)
|
|
31
|
+
faraday (>= 0.7.4, < 0.9)
|
|
32
|
+
faye (1.0.0)
|
|
33
|
+
cookiejar (>= 0.3.0)
|
|
34
|
+
em-http-request (>= 0.3.0)
|
|
35
|
+
eventmachine (>= 0.12.0)
|
|
36
|
+
faye-websocket (>= 0.7.0)
|
|
37
|
+
multi_json (>= 1.0.0)
|
|
38
|
+
rack (>= 1.0.0)
|
|
39
|
+
websocket-driver (>= 0.3.0)
|
|
40
|
+
faye-websocket (0.7.0)
|
|
41
|
+
eventmachine (>= 0.12.0)
|
|
42
|
+
websocket-driver (>= 0.3.0)
|
|
43
|
+
ffi (1.9.0)
|
|
44
|
+
formatador (0.2.4)
|
|
45
|
+
guard (1.8.3)
|
|
46
|
+
formatador (>= 0.2.4)
|
|
47
|
+
listen (~> 1.3)
|
|
48
|
+
lumberjack (>= 1.0.2)
|
|
49
|
+
pry (>= 0.9.10)
|
|
50
|
+
thor (>= 0.14.6)
|
|
51
|
+
guard-rspec (3.1.0)
|
|
52
|
+
guard (>= 1.8)
|
|
53
|
+
rspec (~> 2.13)
|
|
54
|
+
hashie (2.0.5)
|
|
55
|
+
http_parser.rb (0.6.0.beta.2)
|
|
56
|
+
json (1.8.0)
|
|
57
|
+
listen (1.3.1)
|
|
58
|
+
rb-fsevent (>= 0.9.3)
|
|
59
|
+
rb-inotify (>= 0.9)
|
|
60
|
+
rb-kqueue (>= 0.2)
|
|
61
|
+
lumberjack (1.0.4)
|
|
62
|
+
method_source (0.8.2)
|
|
63
|
+
multi_json (1.8.0)
|
|
64
|
+
multipart-post (1.2.0)
|
|
65
|
+
pry (0.9.12.2)
|
|
66
|
+
coderay (~> 1.0.5)
|
|
67
|
+
method_source (~> 0.8)
|
|
68
|
+
slop (~> 3.4)
|
|
69
|
+
rack (1.5.2)
|
|
70
|
+
rake (10.1.0)
|
|
71
|
+
rb-fsevent (0.9.3)
|
|
72
|
+
rb-inotify (0.9.2)
|
|
73
|
+
ffi (>= 0.5.0)
|
|
74
|
+
rb-kqueue (0.2.0)
|
|
75
|
+
ffi (>= 0.5.0)
|
|
76
|
+
rspec (2.14.1)
|
|
77
|
+
rspec-core (~> 2.14.0)
|
|
78
|
+
rspec-expectations (~> 2.14.0)
|
|
79
|
+
rspec-mocks (~> 2.14.0)
|
|
80
|
+
rspec-core (2.14.5)
|
|
81
|
+
rspec-expectations (2.14.3)
|
|
82
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
|
83
|
+
rspec-mocks (2.14.3)
|
|
84
|
+
safe_yaml (0.9.7)
|
|
85
|
+
simplecov (0.7.1)
|
|
86
|
+
multi_json (~> 1.0)
|
|
87
|
+
simplecov-html (~> 0.7.1)
|
|
88
|
+
simplecov-html (0.7.1)
|
|
89
|
+
slop (3.4.6)
|
|
90
|
+
thor (0.18.1)
|
|
91
|
+
webmock (1.13.0)
|
|
92
|
+
addressable (>= 2.2.7)
|
|
93
|
+
crack (>= 0.3.2)
|
|
94
|
+
websocket-driver (0.3.0)
|
|
95
|
+
|
|
96
|
+
PLATFORMS
|
|
97
|
+
ruby
|
|
98
|
+
|
|
99
|
+
DEPENDENCIES
|
|
100
|
+
faye
|
|
101
|
+
force!
|
|
102
|
+
guard-rspec
|
|
103
|
+
jruby-openssl
|
|
104
|
+
rake
|
|
105
|
+
rspec (~> 2.14.0)
|
|
106
|
+
simplecov (~> 0.7.1)
|
|
107
|
+
webmock (~> 1.13.0)
|
data/Guardfile
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
guard 'rspec', all_on_start: false, all_after_pass: false do
|
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
|
3
|
+
watch('spec/spec_helper.rb') { "spec" }
|
|
4
|
+
|
|
5
|
+
watch(%r{^lib/force/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
|
6
|
+
watch(%r{^lib/force/(.+)\.rb$}) { |m| "spec/integration/#{m[1]}_spec.rb" }
|
|
7
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
|
8
|
+
end
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2012 Eric J. Holmes
|
|
2
|
+
|
|
3
|
+
With portions Copyright (c) 2013 Heroku (http://heroku.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# Force
|
|
2
|
+
|
|
3
|
+
_A ruby gem for the [Salesforce REST api](http://www.salesforce.com/us/developer/docs/api_rest/index.htm)._
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- A clean and modular architecture using [Faraday middleware](https://github.com/technoweenie/faraday) and [Hashie::Mash](https://github.com/intridea/hashie/tree/v1.2.0)'d responses.
|
|
8
|
+
- Support for interacting with multiple users from different orgs.
|
|
9
|
+
- Support for parent-to-child relationships.
|
|
10
|
+
- Support for aggregate queries.
|
|
11
|
+
- Support for the [Streaming API](#streaming)
|
|
12
|
+
- Support for blob data types.
|
|
13
|
+
- Support for GZIP compression.
|
|
14
|
+
- Support for [custom Apex REST endpoints](#custom-apex-rest-endpoints).
|
|
15
|
+
- Support for dependent picklists.
|
|
16
|
+
- Support for decoding [Force.com Canvas](http://www.salesforce.com/us/developer/docs/platform_connectpre/canvas_framework.pdf) signed requests. (NEW!)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Add this line to your application's Gemfile:
|
|
21
|
+
|
|
22
|
+
gem 'force'
|
|
23
|
+
|
|
24
|
+
And then execute:
|
|
25
|
+
|
|
26
|
+
$ bundle
|
|
27
|
+
|
|
28
|
+
Or install it yourself as:
|
|
29
|
+
|
|
30
|
+
$ gem install force
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
Force is designed with flexibility and ease of use in mind. By default, all api calls will
|
|
35
|
+
return [Hashie::Mash](https://github.com/intridea/hashie/tree/v1.2.0) objects,
|
|
36
|
+
so you can do things like `client.query('select Id, (select Name from Children__r) from Account').Children__r.first.Name`.
|
|
37
|
+
|
|
38
|
+
### Initialization
|
|
39
|
+
|
|
40
|
+
Which authentication method you use really depends on your use case. If you're
|
|
41
|
+
building an application where many users from different orgs are authenticated
|
|
42
|
+
through oauth and you need to interact with data in their org on their behalf,
|
|
43
|
+
you should use the OAuth token authentication method.
|
|
44
|
+
|
|
45
|
+
If you're using the gem to interact with a single org (maybe you're building some
|
|
46
|
+
salesforce integration internally?) then you should use the username/password
|
|
47
|
+
authentication method.
|
|
48
|
+
|
|
49
|
+
#### OAuth Token Authentication
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
client = Force.new :instance_url => 'xx.salesforce.com',
|
|
53
|
+
:oauth_token => '...'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Although the above will work, you'll probably want to take advantage of the
|
|
57
|
+
(re)authentication middleware by specifying a refresh token, client id and client secret:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
client = Force.new :instance_url => 'xx.salesforce.com',
|
|
61
|
+
:oauth_token => '...',
|
|
62
|
+
:refresh_token => '...',
|
|
63
|
+
:client_id => '...',
|
|
64
|
+
:client_secret => '...'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Username/Password authentication
|
|
68
|
+
|
|
69
|
+
If you prefer to use a username and password to authenticate:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
client = Force.new :username => 'user@example.com',
|
|
73
|
+
:password => '...',
|
|
74
|
+
:security_token => '...',
|
|
75
|
+
:client_id => '...',
|
|
76
|
+
:client_secret => '...'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
You can also set the username, password, security token, client id and client
|
|
80
|
+
secret in environment variables:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
export SALESFORCE_USERNAME="username"
|
|
84
|
+
export SALESFORCE_PASSWORD="password"
|
|
85
|
+
export SALESFORCE_SECURITY_TOKEN="security token"
|
|
86
|
+
export SALESFORCE_CLIENT_ID="client id"
|
|
87
|
+
export SALESFORCE_CLIENT_SECRET="client secret"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
client = Force.new
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Proxy Support
|
|
95
|
+
|
|
96
|
+
You can specify a http proxy using the :proxy_uri option, as follows:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
client = Force.new :proxy_uri => 'http://proxy.example.com:123'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This paramter also will accept `http://user@password:proxy.example.com:123` or using the environemnt variable `PROXY_URI`.
|
|
103
|
+
|
|
104
|
+
#### Sandbox Orgs
|
|
105
|
+
|
|
106
|
+
You can connect to sandbox orgs by specifying a host. The default host is
|
|
107
|
+
`login.salesforce.com`:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
client = Force.new :host => 'test.salesforce.com'
|
|
111
|
+
```
|
|
112
|
+
The host can also be set with the environment variable `SALESFORCE_HOST`.
|
|
113
|
+
|
|
114
|
+
#### Global Configuration
|
|
115
|
+
|
|
116
|
+
You can set any of the options passed into Force.new globally:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
Force.configure do |config|
|
|
120
|
+
config.client_id = 'foo'
|
|
121
|
+
config.client_secret = 'bar'
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### query
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
accounts = client.query("select Id, Something__c from Account where Id = 'someid'")
|
|
131
|
+
# => #<Force::Collection >
|
|
132
|
+
|
|
133
|
+
account = accounts.first
|
|
134
|
+
# => #<Force::SObject >
|
|
135
|
+
|
|
136
|
+
account.sobject_type
|
|
137
|
+
# => 'Account'
|
|
138
|
+
|
|
139
|
+
account.Id
|
|
140
|
+
# => "someid"
|
|
141
|
+
|
|
142
|
+
account.Name = 'Foobar'
|
|
143
|
+
account.save
|
|
144
|
+
# => true
|
|
145
|
+
|
|
146
|
+
account.destroy
|
|
147
|
+
# => true
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### find
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
client.find('Account', '001D000000INjVe')
|
|
154
|
+
# => #<Force::SObject Id="001D000000INjVe" Name="Test" LastModifiedBy="005G0000002f8FHIAY" ... >
|
|
155
|
+
|
|
156
|
+
client.find('Account', '1234', 'Some_External_Id_Field__c')
|
|
157
|
+
# => #<Force::SObject Id="001D000000INjVe" Name="Test" LastModifiedBy="005G0000002f8FHIAY" ... >
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### search
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
# Find all occurrences of 'bar'
|
|
164
|
+
client.search('FIND {bar}')
|
|
165
|
+
# => #<Force::Collection >
|
|
166
|
+
|
|
167
|
+
# Find accounts match the term 'genepoint' and return the Name field
|
|
168
|
+
client.search('FIND {genepoint} RETURNING Account (Name)').map(&:Name)
|
|
169
|
+
# => ['GenePoint']
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### create
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
client.create('Account', Name: 'Foobar Inc.') # => '0016000000MRatd'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### update
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
client.update('Account', Id: '0016000000MRatd', Name: 'Whizbang Corp') # => true
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### upsert
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
client.upsert('Account', 'External__c', External__c: 12, Name: 'Foobar') # => true
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### destroy
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
client.destroy('Account', '0016000000MRatd') # => true
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
> All the CRUD methods (`create`, `update`, `upsert`, `destroy`) have equivalent methods with a ! at the end (`create!`, `update!`, `upsert!`, `destroy!`), which can be used if you need to do some custom error handling. The bang methods will raise exceptions, while the
|
|
197
|
+
non-bang methods will return false in the event that an exception is raised.
|
|
198
|
+
|
|
199
|
+
### describe
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
client.describe # => { ... }
|
|
203
|
+
client.describe('Account') # => { ... }
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### describe_layouts
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
client.describe_layout('Account') # => { ... }
|
|
210
|
+
client.describe_layouts('Account', '012E0000000RHEp') # => { ... }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### picklist_values
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
client.picklist_values('Account', 'Type') # => [#<Force::Mash label="Prospect" value="Prospect">]
|
|
217
|
+
|
|
218
|
+
# Given a custom object named Automobile__c
|
|
219
|
+
# with picklist fields Model__c and Make__c,
|
|
220
|
+
# where Model__c depends on the value of Make__c.
|
|
221
|
+
client.picklist_values('Automobile__c', 'Model__c', :valid_for => 'Honda')
|
|
222
|
+
# => [#<Force::Mash label="Civic" value="Civic">, ... ]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### authenticate!
|
|
228
|
+
|
|
229
|
+
Performs an authentication and returns the response. In general, calling this
|
|
230
|
+
directly shouldn't be required, since the client will handle authentication for
|
|
231
|
+
you automatically. This should only be used if you want to force
|
|
232
|
+
an authentication before using the streaming api, or you want to get some
|
|
233
|
+
information about the user.
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
response = client.authenticate!
|
|
237
|
+
# => #<Force::Mash access_token="..." id="https://login.salesforce.com/id/00DE0000000cOGcMAM/005E0000001eM4LIAU" instance_url="https://na9.salesforce.com" issued_at="1348465359751" scope="api refresh_token" signature="3fW0pC/TEY2cjK5FCBFOZdjRtCfAuEbK1U74H/eF+Ho=">
|
|
238
|
+
|
|
239
|
+
# Get the user information
|
|
240
|
+
info = client.get(response.id).body
|
|
241
|
+
info.user_id
|
|
242
|
+
# => '005E0000001eM4LIAU'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### File Uploads
|
|
246
|
+
|
|
247
|
+
Using the new [Blob Data](http://www.salesforce.com/us/developer/docs/api_rest/Content/dome_sobject_insert_update_blob.htm) api feature (500mb limit):
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
image = Force::UploadIO.new(File.expand_path('image.jpg', __FILE__), 'image/jpeg')
|
|
251
|
+
client.create 'Document', FolderId: '00lE0000000FJ6H',
|
|
252
|
+
Description: 'Document test',
|
|
253
|
+
Name: 'My image',
|
|
254
|
+
Body: image)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Using base64-encoded data _(37.5mb limit)_:
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
data = Base64::encode64(File.read('image.jpg')
|
|
261
|
+
client.create 'Document', FolderId: '00lE0000000FJ6H',
|
|
262
|
+
Description: 'Document test',
|
|
263
|
+
Name: 'My image',
|
|
264
|
+
Body: data)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
> See also: http://www.salesforce.com/us/developer/docs/api_rest/Content/dome_sobject_insert_update_blob.htm
|
|
268
|
+
|
|
269
|
+
### Downloading Attachments
|
|
270
|
+
|
|
271
|
+
Force also makes it incredibly easy to download Attachments:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
attachment = client.query('select Id, Name, Body from Attachment').first
|
|
275
|
+
File.open(attachment.Name, 'wb') { |f| f.write(attachment.Body) }
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Custom Apex REST endpoints
|
|
279
|
+
|
|
280
|
+
You can use Force to interact with your custom REST endpoints, by using
|
|
281
|
+
`.get`, `.put`, `.patch`, `.post`, and `.delete`.
|
|
282
|
+
|
|
283
|
+
For example, if you had the following Apex REST endpoint on Salesforce:
|
|
284
|
+
|
|
285
|
+
```apex
|
|
286
|
+
@RestResource(urlMapping='/FieldCase/*')
|
|
287
|
+
global class RESTCaseController {
|
|
288
|
+
@HttpGet
|
|
289
|
+
global static List<Case> getOpenCases() {
|
|
290
|
+
String companyName = RestContext.request.params.get('company');
|
|
291
|
+
Account company = [ Select ID, Name, Email__c, BillingState from Account where Name = :companyName];
|
|
292
|
+
|
|
293
|
+
List<Case> cases = [SELECT Id, Subject, Status, OwnerId, Owner.Name from Case WHERE AccountId = :company.Id];
|
|
294
|
+
return cases;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
...then you could query the cases using Force:
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
client.get '/services/apexrest/FieldCase', :company => 'GenePoint'
|
|
303
|
+
# => #<Force::Collection ...>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
* * *
|
|
307
|
+
|
|
308
|
+
### Streaming
|
|
309
|
+
|
|
310
|
+
Force supports the [Streaming API](http://wiki.developerforce.com/page/Getting_Started_with_the_Force.com_Streaming_API), and makes implementing
|
|
311
|
+
pub/sub with Salesforce a trivial task:
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
# Force uses faye as the underlying implementation for CometD.
|
|
315
|
+
require 'faye'
|
|
316
|
+
|
|
317
|
+
# Initialize a client with your username/password/oauth token/etc.
|
|
318
|
+
client = Force.new :username => 'foo',
|
|
319
|
+
:password => 'bar',
|
|
320
|
+
:security_token => 'security token'
|
|
321
|
+
:client_id => 'client_id',
|
|
322
|
+
:client_secret => 'client_secret'
|
|
323
|
+
|
|
324
|
+
# Create a PushTopic for subscribing to Account changes.
|
|
325
|
+
client.create! 'PushTopic', {
|
|
326
|
+
ApiVersion: '23.0',
|
|
327
|
+
Name: 'AllAccounts',
|
|
328
|
+
Description: 'All account records',
|
|
329
|
+
NotifyForOperations: 'All',
|
|
330
|
+
NotifyForFields: 'All',
|
|
331
|
+
Query: "select Id from Account"
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
EM.run {
|
|
335
|
+
# Subscribe to the PushTopic.
|
|
336
|
+
client.subscribe 'AllAccounts' do |message|
|
|
337
|
+
puts message.inspect
|
|
338
|
+
end
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
_See also: http://www.salesforce.com/us/developer/docs/api_streaming/index.htm_
|
|
343
|
+
|
|
344
|
+
* * *
|
|
345
|
+
|
|
346
|
+
### Caching
|
|
347
|
+
|
|
348
|
+
The gem supports easy caching of GET requests (e.g. queries):
|
|
349
|
+
|
|
350
|
+
```ruby
|
|
351
|
+
# rails example:
|
|
352
|
+
client = Force.new cache: Rails.cache
|
|
353
|
+
|
|
354
|
+
# or
|
|
355
|
+
Force.configure do |config|
|
|
356
|
+
config.cache = Rails.cache
|
|
357
|
+
end
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
If you enable caching, you can disable caching on a per-request basis by using
|
|
361
|
+
.without_caching:
|
|
362
|
+
|
|
363
|
+
```ruby
|
|
364
|
+
client.without_caching do
|
|
365
|
+
client.query('select Id from Account')
|
|
366
|
+
end
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Logging / Debugging / Instrumenting
|
|
370
|
+
|
|
371
|
+
You can inspect what Force is sending/receiving by setting
|
|
372
|
+
`Force.log = true`.
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
Force.log = true
|
|
376
|
+
client = Force.new.query('select Id, Name from Account')
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Another awesome feature about force is that, because it is based on Faraday, you can insert your own middleware.
|
|
380
|
+
|
|
381
|
+
For example, if you were using Force in a Rails app, you can setup custom reporting to [Librato](https://github.com/librato/librato-rails) using ActiveSupport::Notifications:
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
client = Force.new do |builder|
|
|
385
|
+
builder.insert_after Force::Middleware::InstanceURL,
|
|
386
|
+
FaradayMiddleware::Instrumentation, name: 'request.salesforce'
|
|
387
|
+
end
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
#### config/initializers/notifications.rb
|
|
391
|
+
|
|
392
|
+
```ruby
|
|
393
|
+
ActiveSupport::Notifications.subscribe('request.salesforce') do |*args|
|
|
394
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
395
|
+
Librato.increment 'api.salesforce.request.total'
|
|
396
|
+
Librato.timing 'api.salesforce.request.time', event.duration
|
|
397
|
+
end
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Force.com Canvas
|
|
401
|
+
|
|
402
|
+
You can use Force to decode signed requests from Salesforce. See [the example app](https://gist.github.com/4052312).
|
|
403
|
+
|
|
404
|
+
## Tooling API
|
|
405
|
+
|
|
406
|
+
To use the [Tooling API](http://www.salesforce.com/us/developer/docs/api_toolingpre/api_tooling.pdf),
|
|
407
|
+
call `Force.tooling` instead of `Force.new`:
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
client = Force.tooling(...)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Contact
|
|
416
|
+
|
|
417
|
+
- Mattt Thompson <mattt@heroku.com>
|
|
418
|
+
|
|
419
|
+
## License
|
|
420
|
+
|
|
421
|
+
Force is available under the MIT license. See the LICENSE file for more info.
|