restforce 3.0.1 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +9 -9
  3. data/.rubocop.yml +10 -12
  4. data/.rubocop_todo.yml +128 -81
  5. data/CHANGELOG.md +30 -1
  6. data/Gemfile +2 -1
  7. data/README.md +124 -12
  8. data/lib/restforce.rb +22 -1
  9. data/lib/restforce/abstract_client.rb +1 -0
  10. data/lib/restforce/attachment.rb +1 -0
  11. data/lib/restforce/concerns/api.rb +9 -6
  12. data/lib/restforce/concerns/authentication.rb +10 -0
  13. data/lib/restforce/concerns/base.rb +2 -0
  14. data/lib/restforce/concerns/batch_api.rb +87 -0
  15. data/lib/restforce/concerns/canvas.rb +1 -0
  16. data/lib/restforce/concerns/picklists.rb +2 -1
  17. data/lib/restforce/concerns/streaming.rb +75 -3
  18. data/lib/restforce/config.rb +4 -0
  19. data/lib/restforce/document.rb +1 -0
  20. data/lib/restforce/middleware/authentication.rb +3 -2
  21. data/lib/restforce/middleware/authentication/jwt_bearer.rb +38 -0
  22. data/lib/restforce/middleware/multipart.rb +1 -0
  23. data/lib/restforce/middleware/raise_error.rb +24 -8
  24. data/lib/restforce/signed_request.rb +1 -0
  25. data/lib/restforce/sobject.rb +1 -0
  26. data/lib/restforce/tooling/client.rb +3 -3
  27. data/lib/restforce/version.rb +1 -1
  28. data/restforce.gemspec +8 -7
  29. data/spec/fixtures/test_private.key +27 -0
  30. data/spec/integration/abstract_client_spec.rb +42 -1
  31. data/spec/support/fixture_helpers.rb +2 -2
  32. data/spec/unit/concerns/authentication_spec.rb +35 -0
  33. data/spec/unit/concerns/batch_api_spec.rb +107 -0
  34. data/spec/unit/concerns/streaming_spec.rb +144 -4
  35. data/spec/unit/middleware/authentication/jwt_bearer_spec.rb +62 -0
  36. data/spec/unit/middleware/raise_error_spec.rb +32 -11
  37. metadata +53 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 96f8f9560cf8a66a733f7755fd374d741191b7f8
4
- data.tar.gz: bbcfa722aefa2285b4f632b98e4109fdb585041c
2
+ SHA256:
3
+ metadata.gz: d1eb25f3f88951d1770c5850c41a9d673d3b0406a76d0c35e54288d52ee5dc00
4
+ data.tar.gz: 51af3a7a4a1e701a4f5e8a79c21452ca5417581aa889188e7f5b5e010fb8f39a
5
5
  SHA512:
6
- metadata.gz: 207ba956da9189b584ae3e6362f0f46fe7cb877105d28b4550eb6c071f38aca8f8f326b04e4c7082ce129a719e2b56cb0870ae715e95eb3aee2b7ee0d027eaf0
7
- data.tar.gz: fe5cf08c7d393d8720324a8aeceb80456b0ceda886df6cb1a185ab4b35516fe2cf89bacb0697af78fbe6da45c979320e4569d41f842f8d7c392f9fbe38f1d2e3
6
+ metadata.gz: eabc94739d7bbdb2e88fcf3b2ff4bfb3c0b4272f74f50d6fef3f549ca559cd52ab212b5fbd6988da0237257aef7d092b4f654e275a797beb28947b268263f07d
7
+ data.tar.gz: 56ac8e739182f941a45cffe4c8b28364d8b99243128c8c7864820e15e752785af3938c8f99377e3428dcc9a61ac98a72703850675455cc88effe5b68bdca454b
@@ -34,23 +34,23 @@ references:
34
34
  destination: test-results
35
35
 
36
36
  jobs:
37
- build-ruby251:
37
+ build-ruby265:
38
38
  docker:
39
- - image: circleci/ruby:2.5.1
39
+ - image: circleci/ruby:2.6.5
40
40
  steps: *steps
41
- build-ruby244:
41
+ build-ruby257:
42
42
  docker:
43
- - image: circleci/ruby:2.4.4
43
+ - image: circleci/ruby:2.5.7
44
44
  steps: *steps
45
- build-ruby237:
45
+ build-ruby249:
46
46
  docker:
47
- - image: circleci/ruby:2.3.7
47
+ - image: circleci/ruby:2.4.9
48
48
  steps: *steps
49
49
 
50
50
  workflows:
51
51
  version: 2
52
52
  tests:
53
53
  jobs:
54
- - build-ruby251
55
- - build-ruby244
56
- - build-ruby237
54
+ - build-ruby265
55
+ - build-ruby257
56
+ - build-ruby249
@@ -4,46 +4,44 @@ inherit_from: .rubocop_todo.yml
4
4
 
5
5
  AllCops:
6
6
  DisplayCopNames: true
7
- Include:
8
- - Rakefile
9
7
  Exclude:
10
8
  - .*/**/*
11
9
  - vendor/**/*
12
- TargetRubyVersion: 2.3
10
+ TargetRubyVersion: 2.4
13
11
 
14
12
  # Limit lines to 80 characters.
15
- LineLength:
13
+ Metrics/LineLength:
16
14
  Max: 90
17
15
 
18
- ClassLength:
16
+ Metrics/ClassLength:
19
17
  Enabled: false
20
18
 
21
- ModuleLength:
19
+ Metrics/ModuleLength:
22
20
  Enabled: false
23
21
 
24
22
  # Avoid methods longer than 30 lines of code
25
- MethodLength:
23
+ Metrics/MethodLength:
26
24
  CountComments: false # count full line comments?
27
25
  Max: 87
28
26
 
29
27
  # Avoid single-line methods.
30
- SingleLineMethods:
28
+ Style/SingleLineMethods:
31
29
  AllowIfMethodIsEmpty: true
32
30
 
33
- StringLiterals:
31
+ Style/StringLiterals:
34
32
  Enabled: false
35
33
 
36
- GlobalVars:
34
+ Style/GlobalVars:
37
35
  Enabled: false # We use them Redis + StatsD (though maybe we shouldn't?)
38
36
 
39
37
  # Wants underscores in all large numbers. Pain in the ass for things like
40
38
  # unix timestamps.
41
- NumericLiterals:
39
+ Style/NumericLiterals:
42
40
  Enabled: false
43
41
 
44
42
  # Wants you to use the same argument names for every reduce. This seems kinda
45
43
  # naff compared to naming them semantically
46
- SingleLineBlockParams:
44
+ Style/SingleLineBlockParams:
47
45
  Enabled: false
48
46
 
49
47
  Style/SignalException:
@@ -1,117 +1,164 @@
1
- # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-06-06 22:47:07 +0100 using RuboCop version 0.32.0.
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2019-10-09 21:39:40 +0100 using RuboCop version 0.75.0.
3
4
  # The point is for the user to remove these configuration records
4
5
  # one by one as the offenses are removed from the code base.
5
6
  # Note that changes in the inspected code, or installation of new
6
7
  # versions of RuboCop, may require this file to be generated again.
7
8
 
8
- # Offense count: 50
9
+ # Offense count: 17
9
10
  # Cop supports --auto-correct.
11
+ # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
12
+ # SupportedHashRocketStyles: key, separator, table
13
+ # SupportedColonStyles: key, separator, table
14
+ # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
15
+ Layout/AlignHash:
16
+ Exclude:
17
+ - 'lib/restforce/middleware/logger.rb'
18
+ - 'restforce.gemspec'
19
+ - 'spec/integration/abstract_client_spec.rb'
20
+ - 'spec/unit/config_spec.rb'
21
+
22
+ # Offense count: 2
23
+ # Cop supports --auto-correct.
24
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
25
+ # SupportedStyles: special_inside_parentheses, consistent, align_braces
26
+ Layout/IndentFirstHashElement:
27
+ Exclude:
28
+ - 'lib/restforce/concerns/connection.rb'
29
+
30
+ # Offense count: 1
31
+ # Cop supports --auto-correct.
32
+ # Configuration parameters: AllowForAlignment.
33
+ Layout/SpaceAroundOperators:
34
+ Exclude:
35
+ - 'lib/restforce.rb'
36
+
37
+ # Offense count: 8
38
+ # Cop supports --auto-correct.
39
+ # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
10
40
  Lint/UnusedBlockArgument:
11
- Enabled: false
41
+ Exclude:
42
+ - 'lib/restforce/concerns/api.rb'
43
+ - 'lib/restforce/concerns/batch_api.rb'
44
+ - 'lib/restforce/middleware/multipart.rb'
45
+ - 'spec/unit/config_spec.rb'
12
46
 
13
- # Offense count: 187
47
+ # Offense count: 9
14
48
  Metrics/AbcSize:
15
- Max: 61
49
+ Max: 38
16
50
 
17
- # Offense count: 26
51
+ # Offense count: 1
18
52
  Metrics/CyclomaticComplexity:
19
- Max: 10
53
+ Max: 8
20
54
 
21
- # Offense count: 11
55
+ # Offense count: 1
22
56
  Metrics/PerceivedComplexity:
23
- Max: 10
57
+ Max: 9
24
58
 
25
- # Offense count: 12
26
- Naming/AccessorMethodName:
27
- Enabled: false
28
-
29
- # Offense count: 164
30
- # Cop supports --auto-correct.
31
- # Configuration parameters: EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle, SupportedLastArgumentHashStyles.
32
- Layout/AlignHash:
33
- Enabled: false
59
+ # Offense count: 5
60
+ # Configuration parameters: EnforcedStyleForLeadingUnderscores.
61
+ # SupportedStylesForLeadingUnderscores: disallowed, required, optional
62
+ Naming/MemoizedInstanceVariableName:
63
+ Exclude:
64
+ - 'lib/restforce/concerns/picklists.rb'
65
+ - 'lib/restforce/concerns/streaming.rb'
66
+
67
+ # Offense count: 2
68
+ # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros.
69
+ # NamePrefix: is_, has_, have_
70
+ # NamePrefixBlacklist: is_, has_, have_
71
+ # NameWhitelist: is_a?
72
+ # MethodDefinitionMacros: define_method, define_singleton_method
73
+ Naming/PredicateName:
74
+ Exclude:
75
+ - 'spec/**/*'
76
+ - 'lib/restforce/collection.rb'
77
+ - 'lib/restforce/middleware/multipart.rb'
34
78
 
35
- # Offense count: 3
79
+ # Offense count: 12
36
80
  # Cop supports --auto-correct.
37
- # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
81
+ # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners.
82
+ # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
83
+ # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
84
+ # FunctionalMethods: let, let!, subject, watch
85
+ # IgnoredMethods: lambda, proc, it
38
86
  Style/BlockDelimiters:
39
- Enabled: false
87
+ Exclude:
88
+ - 'lib/restforce/concerns/batch_api.rb'
89
+ - 'spec/support/event_machine.rb'
90
+ - 'spec/support/middleware.rb'
91
+ - 'spec/unit/concerns/base_spec.rb'
92
+ - 'spec/unit/concerns/batch_api_spec.rb'
93
+ - 'spec/unit/concerns/caching_spec.rb'
94
+ - 'spec/unit/concerns/streaming_spec.rb'
95
+ - 'spec/unit/middleware/authentication_spec.rb'
96
+ - 'spec/unit/middleware/mashify_spec.rb'
40
97
 
41
- # Offense count: 128
42
- # Configuration parameters: EnforcedStyle, SupportedStyles.
43
- Style/ClassAndModuleChildren:
44
- Enabled: false
45
-
46
- # Offense count: 21
47
- # Cop supports --auto-correct.
48
- Layout/ClosingParenthesisIndentation:
49
- Enabled: false
50
-
51
- # Offense count: 13
98
+ # Offense count: 12
52
99
  # Cop supports --auto-correct.
53
- # Configuration parameters: Keywords.
54
- Style/CommentAnnotation:
55
- Enabled: false
56
-
57
- # Offense count: 743
100
+ # Configuration parameters: AutoCorrect, EnforcedStyle.
101
+ # SupportedStyles: nested, compact
102
+ Style/ClassAndModuleChildren:
103
+ Exclude:
104
+ - 'lib/restforce/middleware/authentication.rb'
105
+ - 'lib/restforce/middleware/authentication/password.rb'
106
+ - 'lib/restforce/middleware/authentication/token.rb'
107
+ - 'lib/restforce/middleware/authorization.rb'
108
+ - 'lib/restforce/middleware/caching.rb'
109
+ - 'lib/restforce/middleware/custom_headers.rb'
110
+ - 'lib/restforce/middleware/gzip.rb'
111
+ - 'lib/restforce/middleware/instance_url.rb'
112
+ - 'lib/restforce/middleware/logger.rb'
113
+ - 'lib/restforce/middleware/mashify.rb'
114
+ - 'lib/restforce/middleware/multipart.rb'
115
+ - 'lib/restforce/middleware/raise_error.rb'
116
+
117
+ # Offense count: 36
58
118
  Style/Documentation:
59
119
  Enabled: false
60
120
 
61
- # Offense count: 9
121
+ # Offense count: 3
62
122
  Style/DoubleNegation:
63
- Enabled: false
123
+ Exclude:
124
+ - 'lib/restforce/concerns/picklists.rb'
125
+ - 'lib/restforce/middleware/instance_url.rb'
64
126
 
65
- # Offense count: 109
127
+ # Offense count: 1
66
128
  # Configuration parameters: MinBodyLength.
67
129
  Style/GuardClause:
68
- Enabled: false
69
-
70
- # Offense count: 48
71
- # Cop supports --auto-correct.
72
- # Configuration parameters: EnforcedStyle, SupportedStyles.
73
- Layout/IndentHash:
74
- Enabled: false
130
+ Exclude:
131
+ - 'lib/restforce/middleware/authentication.rb'
75
132
 
76
- # Offense count: 70
133
+ # Offense count: 25
77
134
  # Cop supports --auto-correct.
135
+ # Configuration parameters: EnforcedStyle.
136
+ # SupportedStyles: line_count_dependent, lambda, literal
78
137
  Style/Lambda:
79
- Enabled: false
80
-
81
- # Offense count: 577
82
- # Cop supports --auto-correct.
83
- # Configuration parameters: EnforcedStyle, SupportedStyles.
84
- Layout/MultilineOperationIndentation:
85
- Enabled: false
86
-
87
- # Offense count: 21
88
- # Configuration parameters: NamePrefix, NamePrefixBlacklist.
89
- Naming/PredicateName:
90
- Enabled: false
138
+ Exclude:
139
+ - 'lib/restforce/config.rb'
140
+ - 'spec/integration/abstract_client_spec.rb'
141
+ - 'spec/unit/concerns/base_spec.rb'
142
+ - 'spec/unit/concerns/streaming_spec.rb'
143
+ - 'spec/unit/middleware/authentication_spec.rb'
144
+ - 'spec/unit/middleware/authorization_spec.rb'
145
+ - 'spec/unit/middleware/custom_headers_spec.rb'
146
+ - 'spec/unit/middleware/gzip_spec.rb'
147
+ - 'spec/unit/middleware/instance_url_spec.rb'
148
+ - 'spec/unit/middleware/logger_spec.rb'
149
+ - 'spec/unit/sobject_spec.rb'
91
150
 
92
- # Offense count: 4
93
- # Cop supports --auto-correct.
94
- Style/Proc:
95
- Enabled: false
96
-
97
- # Offense count: 173
151
+ # Offense count: 5
98
152
  # Cop supports --auto-correct.
99
153
  Style/RedundantSelf:
100
- Enabled: false
154
+ Exclude:
155
+ - 'lib/restforce/mash.rb'
156
+ - 'lib/restforce/sobject.rb'
101
157
 
102
- # Offense count: 28
103
- # Cop supports --auto-correct.
104
- Layout/SpaceBeforeFirstArg:
105
- Enabled: false
106
-
107
- # Offense count: 3
108
- # Cop supports --auto-correct.
109
- # Configuration parameters: MultiSpaceAllowedForOperators.
110
- Layout/SpaceAroundOperators:
111
- Enabled: false
112
-
113
- # Offense count: 5
158
+ # Offense count: 1
114
159
  # Cop supports --auto-correct.
115
160
  # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
161
+ # Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
116
162
  Style/TrivialAccessors:
117
- Enabled: false
163
+ Exclude:
164
+ - 'lib/restforce/middleware.rb'
@@ -1,16 +1,45 @@
1
+ ## 4.2.0 (Oct 23, 2019)
2
+
3
+ * Add support for platform events, CDC, generic events, etc. in the Streaming API (@nathanKramer)
4
+
5
+ ## 4.1.0 (Oct 20, 2019)
6
+
7
+ * Add support for JWT authentication (@nathanKramer, @tagCincy)
8
+
9
+ ## 4.0.0 (Oct 9, 2019)
10
+
11
+ * __Deprecate support for Ruby 2.3__, since [Ruby 2.3 reached its end-of-life](https://www.ruby-lang.org/en/news/2019/03/31/support-of-ruby-2-3-has-ended/) in March 2019. (This is the only breaking change included in this version.)
12
+
13
+ ## 3.2.0 (Oct 9, 2019)
14
+
15
+ * Add support for the Batch API (@gaiottino, @teoulas)
16
+ * Return specific exceptions for errors that might be returned from Salesforce.com - instead of getting a generic `Faraday::Error::ClientError`, you might get something like a `Restforce::EntityTooLargeError` (@boblail)
17
+ * Expose the full response in exceptions' messages to make debugging easier (@boblail)
18
+ * Properly escape IDs with spaces in them when working with existing records (@pushups)
19
+
20
+ ## 3.1.0 (Aug 16, 2018)
21
+
22
+ * Add support for replaying missed messages when using the Salesforce Streaming API (@andreimaxim, @drteeth, @panozzaj)
23
+
1
24
  ## 3.0.1 (Aug 4, 2018)
2
25
 
3
26
  * Fix `NoMethodError` when upserting an existing record (@opti)
4
27
 
5
28
  ## 3.0.0 (Aug 2, 2018)
6
29
 
7
- * __Deprecate support for Ruby 2.0, 2.1 and 2.2__, since [even Ruby 2.2 reached its end-of-life]##(https://www.ruby-lang.org/en/news/2018/06/20/support-of-ruby-2-2-has-ended/) in June 2018. (This is the only breaking change included in this version.)
30
+ * __Deprecate support for Ruby 2.0, 2.1 and 2.2__, since [even Ruby 2.2 reached its end-of-life](https://www.ruby-lang.org/en/news/2018/06/20/support-of-ruby-2-2-has-ended/) in June 2018. (This is the only breaking change included in this version.)
8
31
  * Fix `NoMethodError` when trying to upsert a record using a `Fixnum` as the external ID (@AlexandruCD)
9
32
  * Escape record IDs passed in to the client to identify records to find, delete, etc. (@jmdx)
10
33
  * Stop relying on our middleware for Gzip compression if you're using `httpclient`, since Faraday enables this automatically using `httpclient`'s built-in support (@shivanshgaur)
11
34
  * Fix `get_updated` and `get_deleted` API calls by removing the erroneous leading forward slash from the path (@scottolsen)
12
35
  * Fix unpacking of dependent picklist options (@parkm)
13
36
 
37
+ ## 2.5.4 (May 15, 2019)
38
+
39
+ See the [`v2`](https://github.com/restforce/restforce/tree/v2) branch for this release.
40
+
41
+ * Escape record IDs passed in to the client to identify records to find, delete, etc. (@jmdx, @apanzerj)
42
+
14
43
  ## 2.5.3 (Apr 25, 2017)
15
44
 
16
45
  * Raise an error where a custom external ID field name is supplied to `upsert` and `upsert!`, but it is missing from the provided attributes (@velveret)
data/Gemfile CHANGED
@@ -3,8 +3,9 @@
3
3
  source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
- gem 'faraday', '~> 0.15.0'
6
+ gem 'faraday', '~> 0.17.0'
7
7
  gem 'jruby-openssl', platforms: :jruby
8
+ gem 'jwt'
8
9
  gem 'rake'
9
10
 
10
11
  group :development do
data/README.md CHANGED
@@ -25,7 +25,7 @@ Features include:
25
25
 
26
26
  Add this line to your application's Gemfile:
27
27
 
28
- gem 'restforce', '~> 3.0.1'
28
+ gem 'restforce', '~> 4.2.0'
29
29
 
30
30
  And then execute:
31
31
 
@@ -35,7 +35,7 @@ Or install it yourself as:
35
35
 
36
36
  $ gem install restforce
37
37
 
38
- __As of [version 3.0.0](https://github.com/restforce/restforce/blob/master/CHANGELOG.md#300-aug-2-2018), this gem is only compatible with Ruby 2.3.0 and later.__ You'll need to use version 2.5.3 or earlier if you're running on Ruby 2.2, 2.1 or 2.0. For Ruby 1.9.3, you'll need to manually specify that you wish to use version 2.4.2.
38
+ __As of [version 4.0.0](https://github.com/restforce/restforce/blob/master/CHANGELOG.md#400-oct-9-2019), this gem is only compatible with Ruby 2.4.0 and later.__ You'll need to use version 3.2.0 or earlier if you're running on Ruby 2.3. If you're running on Ruby 2.2, 2.1 or 2.0, use version 2.5.3 or earlier. For Ruby 1.9.3, you'll need to manually specify that you wish to use version 2.4.2.
39
39
 
40
40
  This gem is versioned using [Semantic Versioning](http://semver.org/), so you can be confident when updating that there will not be breaking changes outside of a major version (following format MAJOR.MINOR.PATCH, so for instance moving from 3.1.0 to 4.0.0 would be allowed to include incompatible API changes). See the [changelog](https://github.com/restforce/restforce/tree/master/CHANGELOG.md) for details on what has changed in each version.
41
41
 
@@ -43,7 +43,7 @@ This gem is versioned using [Semantic Versioning](http://semver.org/), so you ca
43
43
 
44
44
  Restforce is designed with flexibility and ease of use in mind. By default, all API calls will
45
45
  return [Hashie::Mash](https://github.com/intridea/hashie/tree/v1.2.0) objects,
46
- so you can do things like `client.query('select Id, (select Name from Children__r) from Account').Children__r.first.Name`.
46
+ so you can do things like `client.query('select Id, (select Name from Children__r) from Account').first.Children__r.first.Name`.
47
47
 
48
48
  ### Initialization
49
49
 
@@ -111,6 +111,21 @@ client = Restforce.new(username: 'foo',
111
111
  api_version: '41.0')
112
112
  ```
113
113
 
114
+ #### JWT Bearer Token
115
+
116
+ If you prefer to use a [JWT Bearer Token](https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com#Obtaining_an_Access_Token_using_a_JWT_Bearer_Token) to authenticate:
117
+
118
+ ```ruby
119
+ client = Restforce.new(username: 'foo',
120
+ client_id: 'client_id',
121
+ instance_url: 'instance_url',
122
+ jwt_key: 'certificate_private_key',
123
+ api_version: '38.0')
124
+ ```
125
+
126
+ The `jwt_key` option is the private key of the certificate uploaded to your Connected App in Salesforce.
127
+ Choose "use digital signatures" in the Connected App configuration screen to upload your certificate.
128
+
114
129
  You can also set the username, password, security token, client ID, client
115
130
  secret and API version in environment variables:
116
131
 
@@ -473,11 +488,11 @@ document = client.query('select Id, Name, Body from Document').first
473
488
  File.open(document.Name, 'wb') { |f| f.write(document.Body) }
474
489
  ```
475
490
 
476
- **Note:** The example above is only applicable if your SOQL query returns a single Document record. If more than one record is returned,
491
+ **Note:** The example above is only applicable if your SOQL query returns a single Document record. If more than one record is returned,
477
492
  the Body field contains an URL to retrieve the BLOB content for the first 2000 records returned. Subsequent records contain the BLOB content
478
- in the Body field. This is confusing and hard to debug. See notes in [Issue #301](https://github.com/restforce/restforce/issues/301#issuecomment-298972959) explaining this detail.
479
- **Executive Summary:** Don't retrieve the Body field in a SOQL query; instead, use the BLOB retrieval URL documented
480
- in [SObject BLOB Retrieve](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_blob_retrieve.htm)
493
+ in the Body field. This is confusing and hard to debug. See notes in [Issue #301](https://github.com/restforce/restforce/issues/301#issuecomment-298972959) explaining this detail.
494
+ **Executive Summary:** Don't retrieve the Body field in a SOQL query; instead, use the BLOB retrieval URL documented
495
+ in [SObject BLOB Retrieve](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_blob_retrieve.htm)
481
496
 
482
497
  * * *
483
498
 
@@ -513,8 +528,10 @@ client.get('/services/apexrest/FieldCase', company: 'GenePoint')
513
528
 
514
529
  ### Streaming
515
530
 
516
- Restforce supports the [Streaming API](http://wiki.developerforce.com/page/Getting_Started_with_the_Force.com_Streaming_API), and makes implementing
517
- pub/sub with Salesforce a trivial task:
531
+ Restforce supports the [Streaming API](https://trailhead.salesforce.com/en/content/learn/modules/api_basics/api_basics_streaming), and makes implementing
532
+ pub/sub with Salesforce a trivial task.
533
+
534
+ Here is an example of creating and subscribing to a `PushTopic`:
518
535
 
519
536
  ```ruby
520
537
  # Restforce uses faye as the underlying implementation for CometD.
@@ -523,7 +540,7 @@ require 'faye'
523
540
  # Initialize a client with your username/password/oauth token/etc.
524
541
  client = Restforce.new(username: 'foo',
525
542
  password: 'bar',
526
- security_token: 'security token'
543
+ security_token: 'security token',
527
544
  client_id: 'client_id',
528
545
  client_secret: 'client_secret')
529
546
 
@@ -538,7 +555,7 @@ client.create!('PushTopic',
538
555
 
539
556
  EM.run do
540
557
  # Subscribe to the PushTopic.
541
- client.subscribe 'AllAccounts' do |message|
558
+ client.subscription '/topic/AllAccounts' do |message|
542
559
  puts message.inspect
543
560
  end
544
561
  end
@@ -547,7 +564,102 @@ end
547
564
  Boom, you're now receiving push notifications when Accounts are
548
565
  created/updated.
549
566
 
550
- _See also: [Force.com Streaming API docs](http://www.salesforce.com/us/developer/docs/api_streaming/index.htm)_
567
+ #### Replaying Events
568
+
569
+ Since API version 37.0, Salesforce stores events for 24 hours and they can be
570
+ replayed if your application experienced some downtime.
571
+
572
+ In order to replay past events, all you need to do is specify the last known
573
+ event ID when subscribing and you will receive all events that happened since
574
+ that event ID:
575
+
576
+ ```ruby
577
+ EM.run {
578
+ # Subscribe to the PushTopic.
579
+ client.subscription '/topic/AllAccounts', replay: 10 do |message|
580
+ puts message.inspect
581
+ end
582
+ }
583
+ ```
584
+
585
+ In this specific case you will see events with replay ID 11, 12 and so on.
586
+
587
+ There are two magic values for the replay ID accepted by Salesforce:
588
+
589
+ * `-2`, for getting all the events that appeared in the last 24 hours
590
+ * `-1`, for getting only newer events
591
+
592
+ **Warning**: Only use a replay ID of a event from the last 24 hours otherwise
593
+ Salesforce will not send anything, including newer events. If in doubt, use one
594
+ of the two magic replay IDs mentioned above.
595
+
596
+ You might want to store the replay ID in some sort of datastore so you can
597
+ access it, for example between application restarts. In that case, there is the
598
+ option of passing a custom replay handler which responds to `[]` and `[]=`.
599
+
600
+ Below is a sample replay handler that stores the replay ID for each channel in
601
+ memory using a Hash, stores a timestamp and has some rudimentary logic that
602
+ will use one of the magic IDs depending on the value of the timestamp:
603
+
604
+ ```ruby
605
+ class SimpleReplayHandler
606
+
607
+ MAX_AGE = 86_400 # 24 hours
608
+
609
+ INIT_REPLAY_ID = -1
610
+ DEFAULT_REPLAY_ID = -2
611
+
612
+ def initialize
613
+ @channels = {}
614
+ @last_modified = nil
615
+ end
616
+
617
+ # This method is called during the initial subscribe phase
618
+ # in order to send the correct replay ID.
619
+ def [](channel)
620
+ if @last_modified.nil?
621
+ puts "[#{channel}] No timestamp defined, sending magic replay ID #{INIT_REPLAY_ID}"
622
+
623
+ INIT_REPLAY_ID
624
+ elsif old_replay_id?
625
+ puts "[#{channel}] Old timestamp, sending magic replay ID #{DEFAULT_REPLAY_ID}"
626
+
627
+ DEFAULT_REPLAY_ID
628
+ else
629
+ @channels[channel]
630
+ end
631
+ end
632
+
633
+ def []=(channel, replay_id)
634
+ puts "[#{channel}] Writing replay ID: #{replay_id}"
635
+
636
+ @last_modified = Time.now
637
+ @channels[channel] = replay_id
638
+ end
639
+
640
+ def old_replay_id?
641
+ @last_modified.is_a?(Time) && Time.now - @last_modified > MAX_AGE
642
+ end
643
+ end
644
+ ```
645
+
646
+ In order to use it, simply pass the object as the value of the `replay` option
647
+ of the subscription:
648
+
649
+ ```ruby
650
+ EM.run {
651
+ # Subscribe to the PushTopic and use the custom replay handler to store any
652
+ # received replay ID.
653
+ client.subscription '/topic/AllAccounts', replay: SimpleReplayHandler.new do |message|
654
+ puts message.inspect
655
+ end
656
+ }
657
+ ```
658
+
659
+ _See also_:
660
+
661
+ * [Force.com Streaming API docs](http://www.salesforce.com/us/developer/docs/api_streaming/index.htm)
662
+ * [Message Durability docs](https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/using_streaming_api_durability.htm)
551
663
 
552
664
  *Note:* Restforce's streaming implementation is known to be compatible with version `0.8.9` of the faye gem.
553
665