restforce 3.0.0 → 3.2.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 +5 -5
- data/.rubocop.yml +9 -11
- data/.rubocop_todo.yml +128 -81
- data/CHANGELOG.md +25 -0
- data/Gemfile +1 -1
- data/README.md +99 -4
- data/lib/restforce/abstract_client.rb +1 -0
- data/lib/restforce/attachment.rb +1 -0
- data/lib/restforce/concerns/api.rb +10 -7
- data/lib/restforce/concerns/batch_api.rb +87 -0
- data/lib/restforce/concerns/canvas.rb +1 -0
- data/lib/restforce/concerns/picklists.rb +1 -0
- data/lib/restforce/concerns/streaming.rb +58 -1
- data/lib/restforce/config.rb +1 -0
- data/lib/restforce/document.rb +1 -0
- data/lib/restforce/middleware/multipart.rb +1 -0
- data/lib/restforce/middleware/raise_error.rb +24 -8
- data/lib/restforce/signed_request.rb +1 -0
- data/lib/restforce/sobject.rb +1 -0
- data/lib/restforce/tooling/client.rb +3 -3
- data/lib/restforce/version.rb +1 -1
- data/lib/restforce.rb +21 -1
- data/restforce.gemspec +6 -6
- data/spec/integration/abstract_client_spec.rb +42 -1
- data/spec/support/fixture_helpers.rb +2 -2
- data/spec/unit/concerns/api_spec.rb +10 -0
- data/spec/unit/concerns/batch_api_spec.rb +107 -0
- data/spec/unit/concerns/streaming_spec.rb +112 -0
- data/spec/unit/middleware/raise_error_spec.rb +32 -11
- metadata +32 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fa2491edb7d7f6b3d48a866be6d9c896e886987a995ee3b1cd6cfd10732cf99b
|
4
|
+
data.tar.gz: 33ea3530dc17a98ee4b26b93636499c491352d0000fc8ac35ae81bd0f4e0426b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ff50cc0872b5491f2a183886f857d381c9c854bcd1932ee4e92e2231dab367d16c0a6a28c4c9525a4ce807acea255ffd3d4d9070766379ae29f51117c9cb024
|
7
|
+
data.tar.gz: 82750aac34ffe679439ea478043da95d4fe9b3847704b65ad241a8aaa4bc65092da5321b69c1443296bd3b6bf682fb3f0c907be1356f95ccf2ad3a460979fedf
|
data/.rubocop.yml
CHANGED
@@ -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
10
|
TargetRubyVersion: 2.3
|
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:
|
data/.rubocop_todo.yml
CHANGED
@@ -1,117 +1,164 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
#
|
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:
|
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
|
-
|
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:
|
47
|
+
# Offense count: 9
|
14
48
|
Metrics/AbcSize:
|
15
|
-
Max:
|
49
|
+
Max: 38
|
16
50
|
|
17
|
-
# Offense count:
|
51
|
+
# Offense count: 1
|
18
52
|
Metrics/CyclomaticComplexity:
|
19
|
-
Max:
|
53
|
+
Max: 8
|
20
54
|
|
21
|
-
# Offense count:
|
55
|
+
# Offense count: 1
|
22
56
|
Metrics/PerceivedComplexity:
|
23
|
-
Max:
|
57
|
+
Max: 9
|
24
58
|
|
25
|
-
# Offense count:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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:
|
79
|
+
# Offense count: 12
|
36
80
|
# Cop supports --auto-correct.
|
37
|
-
# Configuration parameters: EnforcedStyle,
|
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
|
-
|
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:
|
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:
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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:
|
121
|
+
# Offense count: 3
|
62
122
|
Style/DoubleNegation:
|
63
|
-
|
123
|
+
Exclude:
|
124
|
+
- 'lib/restforce/concerns/picklists.rb'
|
125
|
+
- 'lib/restforce/middleware/instance_url.rb'
|
64
126
|
|
65
|
-
# Offense count:
|
127
|
+
# Offense count: 1
|
66
128
|
# Configuration parameters: MinBodyLength.
|
67
129
|
Style/GuardClause:
|
68
|
-
|
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:
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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:
|
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
|
-
|
154
|
+
Exclude:
|
155
|
+
- 'lib/restforce/mash.rb'
|
156
|
+
- 'lib/restforce/sobject.rb'
|
101
157
|
|
102
|
-
# Offense count:
|
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
|
-
|
163
|
+
Exclude:
|
164
|
+
- 'lib/restforce/middleware.rb'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
## 3.2.1 (Dec 8, 2021)
|
2
|
+
|
3
|
+
* Update the gemspec to reflect that `faraday` `v1.0.0` is not supported (@timrogers)
|
4
|
+
|
5
|
+
## 3.2.0 (Oct 9, 2019)
|
6
|
+
|
7
|
+
* Add support for the Batch API (@gaiottino, @teoulas)
|
8
|
+
* 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)
|
9
|
+
* Expose the full response in exceptions' messages to make debugging easier (@boblail)
|
10
|
+
* Properly escape IDs with spaces in them when working with existing records (@pushups)
|
11
|
+
|
12
|
+
## 3.1.0 (Aug 16, 2018)
|
13
|
+
|
14
|
+
* Add support for replaying missed messages when using the Salesforce Streaming API (@andreimaxim, @drteeth, @panozzaj)
|
15
|
+
|
16
|
+
## 3.0.1 (Aug 4, 2018)
|
17
|
+
|
18
|
+
* Fix `NoMethodError` when upserting an existing record (@opti)
|
19
|
+
|
1
20
|
## 3.0.0 (Aug 2, 2018)
|
2
21
|
|
3
22
|
* __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.)
|
@@ -7,6 +26,12 @@
|
|
7
26
|
* Fix `get_updated` and `get_deleted` API calls by removing the erroneous leading forward slash from the path (@scottolsen)
|
8
27
|
* Fix unpacking of dependent picklist options (@parkm)
|
9
28
|
|
29
|
+
## 2.5.4 (May 15, 2019)
|
30
|
+
|
31
|
+
See the [`v2`](https://github.com/restforce/restforce/tree/v2) branch for this release.
|
32
|
+
|
33
|
+
* Escape record IDs passed in to the client to identify records to find, delete, etc. (@jmdx, @apanzerj)
|
34
|
+
|
10
35
|
## 2.5.3 (Apr 25, 2017)
|
11
36
|
|
12
37
|
* 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
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.
|
28
|
+
gem 'restforce', '~> 3.2.1'
|
29
29
|
|
30
30
|
And then execute:
|
31
31
|
|
@@ -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
|
|
@@ -523,7 +523,7 @@ require 'faye'
|
|
523
523
|
# Initialize a client with your username/password/oauth token/etc.
|
524
524
|
client = Restforce.new(username: 'foo',
|
525
525
|
password: 'bar',
|
526
|
-
security_token: 'security token'
|
526
|
+
security_token: 'security token',
|
527
527
|
client_id: 'client_id',
|
528
528
|
client_secret: 'client_secret')
|
529
529
|
|
@@ -547,7 +547,102 @@ end
|
|
547
547
|
Boom, you're now receiving push notifications when Accounts are
|
548
548
|
created/updated.
|
549
549
|
|
550
|
-
|
550
|
+
#### Replaying Events
|
551
|
+
|
552
|
+
Since API version 37.0, Salesforce stores events for 24 hours and they can be
|
553
|
+
replayed if your application experienced some downtime.
|
554
|
+
|
555
|
+
In order to replay past events, all you need to do is specify the last known
|
556
|
+
event ID when subscribing and you will receive all events that happened since
|
557
|
+
that event ID:
|
558
|
+
|
559
|
+
```ruby
|
560
|
+
EM.run {
|
561
|
+
# Subscribe to the PushTopic.
|
562
|
+
client.subscribe 'AllAccounts', replay: 10 do |message|
|
563
|
+
puts message.inspect
|
564
|
+
end
|
565
|
+
}
|
566
|
+
```
|
567
|
+
|
568
|
+
In this specific case you will see events with replay ID 11, 12 and so on.
|
569
|
+
|
570
|
+
There are two magic values for the replay ID accepted by Salesforce:
|
571
|
+
|
572
|
+
* `-2`, for getting all the events that appeared in the last 24 hours
|
573
|
+
* `-1`, for getting only newer events
|
574
|
+
|
575
|
+
**Warning**: Only use a replay ID of a event from the last 24 hours otherwise
|
576
|
+
Salesforce will not send anything, including newer events. If in doubt, use one
|
577
|
+
of the two magic replay IDs mentioned above.
|
578
|
+
|
579
|
+
You might want to store the replay ID in some sort of datastore so you can
|
580
|
+
access it, for example between application restarts. In that case, there is the
|
581
|
+
option of passing a custom replay handler which responds to `[]` and `[]=`.
|
582
|
+
|
583
|
+
Below is a sample replay handler that stores the replay ID for each channel in
|
584
|
+
memory using a Hash, stores a timestamp and has some rudimentary logic that
|
585
|
+
will use one of the magic IDs depending on the value of the timestamp:
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
class SimpleReplayHandler
|
589
|
+
|
590
|
+
MAX_AGE = 86_400 # 24 hours
|
591
|
+
|
592
|
+
INIT_REPLAY_ID = -1
|
593
|
+
DEFAULT_REPLAY_ID = -2
|
594
|
+
|
595
|
+
def initialize
|
596
|
+
@channels = {}
|
597
|
+
@last_modified = nil
|
598
|
+
end
|
599
|
+
|
600
|
+
# This method is called during the initial subscribe phase
|
601
|
+
# in order to send the correct replay ID.
|
602
|
+
def [](channel)
|
603
|
+
if @last_modified.nil?
|
604
|
+
puts "[#{channel}] No timestamp defined, sending magic replay ID #{INIT_REPLAY_ID}"
|
605
|
+
|
606
|
+
INIT_REPLAY_ID
|
607
|
+
elsif old_replay_id?
|
608
|
+
puts "[#{channel}] Old timestamp, sending magic replay ID #{DEFAULT_REPLAY_ID}"
|
609
|
+
|
610
|
+
DEFAULT_REPLAY_ID
|
611
|
+
else
|
612
|
+
@channels[channel]
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
def []=(channel, replay_id)
|
617
|
+
puts "[#{channel}] Writing replay ID: #{replay_id}"
|
618
|
+
|
619
|
+
@last_modified = Time.now
|
620
|
+
@channels[channel] = replay_id
|
621
|
+
end
|
622
|
+
|
623
|
+
def old_replay_id?
|
624
|
+
@last_modified.is_a?(Time) && Time.now - @last_modified > MAX_AGE
|
625
|
+
end
|
626
|
+
end
|
627
|
+
```
|
628
|
+
|
629
|
+
In order to use it, simply pass the object as the value of the `replay` option
|
630
|
+
of the subscription:
|
631
|
+
|
632
|
+
```ruby
|
633
|
+
EM.run {
|
634
|
+
# Subscribe to the PushTopic and use the custom replay handler to store any
|
635
|
+
# received replay ID.
|
636
|
+
client.subscribe 'AllAccounts', replay: SimpleReplayHandler.new do |message|
|
637
|
+
puts message.inspect
|
638
|
+
end
|
639
|
+
}
|
640
|
+
```
|
641
|
+
|
642
|
+
_See also_:
|
643
|
+
|
644
|
+
* [Force.com Streaming API docs](http://www.salesforce.com/us/developer/docs/api_streaming/index.htm)
|
645
|
+
* [Message Durability docs](https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/using_streaming_api_durability.htm)
|
551
646
|
|
552
647
|
*Note:* Restforce's streaming implementation is known to be compatible with version `0.8.9` of the faye gem.
|
553
648
|
|
data/lib/restforce/attachment.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'erb'
|
3
4
|
require 'uri'
|
4
5
|
require 'restforce/concerns/verbs'
|
5
6
|
|
@@ -320,6 +321,7 @@ module Restforce
|
|
320
321
|
def update!(sobject, attrs)
|
321
322
|
id = attrs.fetch(attrs.keys.find { |k, v| k.to_s.casecmp('id').zero? }, nil)
|
322
323
|
raise ArgumentError, 'ID field missing from provided attributes' unless id
|
324
|
+
|
323
325
|
attrs_without_id = attrs.reject { |k, v| k.to_s.casecmp("id").zero? }
|
324
326
|
api_patch "sobjects/#{sobject}/#{CGI.escape(id)}", attrs_without_id
|
325
327
|
true
|
@@ -377,10 +379,11 @@ module Restforce
|
|
377
379
|
api_post "sobjects/#{sobject}/#{field}", attrs
|
378
380
|
end
|
379
381
|
else
|
380
|
-
api_patch "sobjects/#{sobject}/#{field}
|
382
|
+
api_patch "sobjects/#{sobject}/#{field}/" \
|
383
|
+
"#{ERB::Util.url_encode(external_id)}", attrs
|
381
384
|
end
|
382
385
|
|
383
|
-
response
|
386
|
+
response.body.respond_to?(:fetch) ? response.body.fetch('id', true) : true
|
384
387
|
end
|
385
388
|
|
386
389
|
# Public: Delete a record.
|
@@ -414,7 +417,7 @@ module Restforce
|
|
414
417
|
# Returns true of the sobject was successfully deleted.
|
415
418
|
# Raises an exception if an error is returned from Salesforce.
|
416
419
|
def destroy!(sobject, id)
|
417
|
-
api_delete "sobjects/#{sobject}/#{
|
420
|
+
api_delete "sobjects/#{sobject}/#{ERB::Util.url_encode(id)}"
|
418
421
|
true
|
419
422
|
end
|
420
423
|
|
@@ -428,9 +431,9 @@ module Restforce
|
|
428
431
|
# Returns the Restforce::SObject sobject record.
|
429
432
|
def find(sobject, id, field = nil)
|
430
433
|
url = if field
|
431
|
-
"sobjects/#{sobject}/#{field}/#{
|
434
|
+
"sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
|
432
435
|
else
|
433
|
-
"sobjects/#{sobject}/#{
|
436
|
+
"sobjects/#{sobject}/#{ERB::Util.url_encode(id)}"
|
434
437
|
end
|
435
438
|
api_get(url).body
|
436
439
|
end
|
@@ -446,9 +449,9 @@ module Restforce
|
|
446
449
|
#
|
447
450
|
def select(sobject, id, select, field = nil)
|
448
451
|
path = if field
|
449
|
-
"sobjects/#{sobject}/#{field}/#{
|
452
|
+
"sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
|
450
453
|
else
|
451
|
-
"sobjects/#{sobject}/#{
|
454
|
+
"sobjects/#{sobject}/#{ERB::Util.url_encode(id)}"
|
452
455
|
end
|
453
456
|
|
454
457
|
path = "#{path}?fields=#{select.join(',')}" if select&.any?
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'restforce/concerns/verbs'
|
4
|
+
|
5
|
+
module Restforce
|
6
|
+
module Concerns
|
7
|
+
module BatchAPI
|
8
|
+
extend Restforce::Concerns::Verbs
|
9
|
+
|
10
|
+
define_verbs :post
|
11
|
+
|
12
|
+
def batch(halt_on_error: false)
|
13
|
+
subrequests = Subrequests.new(options)
|
14
|
+
yield(subrequests)
|
15
|
+
subrequests.requests.each_slice(25).map do |requests|
|
16
|
+
properties = {
|
17
|
+
batchRequests: requests,
|
18
|
+
haltOnError: halt_on_error
|
19
|
+
}
|
20
|
+
response = api_post('composite/batch', properties.to_json)
|
21
|
+
body = response.body
|
22
|
+
results = body['results']
|
23
|
+
if halt_on_error && body['hasErrors']
|
24
|
+
last_error_index = results.rindex { |result| result['statusCode'] != 412 }
|
25
|
+
last_error = results[last_error_index]
|
26
|
+
raise BatchAPIError, last_error['result'][0]['errorCode']
|
27
|
+
end
|
28
|
+
results.map(&:compact)
|
29
|
+
end.flatten
|
30
|
+
end
|
31
|
+
|
32
|
+
def batch!(&block)
|
33
|
+
batch(halt_on_error: true, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Subrequests
|
37
|
+
def initialize(options)
|
38
|
+
@options = options
|
39
|
+
@requests = []
|
40
|
+
end
|
41
|
+
attr_reader :options, :requests
|
42
|
+
|
43
|
+
def create(sobject, attrs)
|
44
|
+
requests << { method: 'POST', url: batch_api_path(sobject), richInput: attrs }
|
45
|
+
end
|
46
|
+
|
47
|
+
def update(sobject, attrs)
|
48
|
+
id = attrs.fetch(attrs.keys.find { |k, v| k.to_s.casecmp?('id') }, nil)
|
49
|
+
raise ArgumentError, 'Id field missing from attrs.' unless id
|
50
|
+
|
51
|
+
attrs_without_id = attrs.reject { |k, v| k.to_s.casecmp?('id') }
|
52
|
+
requests << {
|
53
|
+
method: 'PATCH',
|
54
|
+
url: batch_api_path("#{sobject}/#{id}"),
|
55
|
+
richInput: attrs_without_id
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def destroy(sobject, id)
|
60
|
+
requests << { method: 'DELETE', url: batch_api_path("#{sobject}/#{id}") }
|
61
|
+
end
|
62
|
+
|
63
|
+
def upsert(sobject, ext_field, attrs)
|
64
|
+
raise ArgumentError, 'External id field missing.' unless ext_field
|
65
|
+
|
66
|
+
ext_id = attrs.fetch(attrs.keys.find { |k, v|
|
67
|
+
k.to_s.casecmp?(ext_field.to_s)
|
68
|
+
}, nil)
|
69
|
+
raise ArgumentError, 'External id missing from attrs.' unless ext_id
|
70
|
+
|
71
|
+
attrs_without_ext_id = attrs.reject { |k, v| k.to_s.casecmp?(ext_field) }
|
72
|
+
requests << {
|
73
|
+
method: 'PATCH',
|
74
|
+
url: batch_api_path("#{sobject}/#{ext_field}/#{ext_id}"),
|
75
|
+
richInput: attrs_without_ext_id
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def batch_api_path(path)
|
82
|
+
"v#{options[:api_version]}/sobjects/#{path}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|