restforce 3.1.0 → 3.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3352202fbc4460b9f83a50b93f32dce58142b7ad
4
- data.tar.gz: a2680033dec036183f9fcb767cb4935f17ecad27
2
+ SHA256:
3
+ metadata.gz: 41d61a57996195a4809acd8e3d16d79dcf1ff9b1637ddd0e4c8b12cb8f967f5c
4
+ data.tar.gz: 41a090599ea254b58142ffdd0ed4fae33bad9d99fce472260d187018768a0cac
5
5
  SHA512:
6
- metadata.gz: 8ef0d7d4a741961ce17a76fb5b93e3906f216bbe7bea5dbe2598eead312571e859392e4a54f32414fda1cffb0ffca9a4a3e8283f9eb333baea58529e51d9f86a
7
- data.tar.gz: b6f445ee78e3e217b9279b7958744281e9d16dcb5cb7e1606c1a1cdc7d6f462afc8c7278a1de4ce751fd929a8372d5fbdd22c19148bc504b64f6790f1e52cfdb
6
+ metadata.gz: 7be41c73c7e8aa79f1d5246e339e320e567cfddee5f317dc8286279d3fba0cbcf0bf631524330c2b4e2fad6ba71c6da6c88fa63f0af871fad04e9c5e43a0aaed
7
+ data.tar.gz: 42fe28bb527485254263015de5c5877a6371e59f14eb2aa6048fd3821356d17c143801bf389603632100eae860559f644c816727dcd0dc651d97aad30839d34a
@@ -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:
@@ -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,3 +1,10 @@
1
+ ## 3.2.0 (Oct 9, 2019)
2
+
3
+ * Add support for the Batch API (@gaiottino, @teoulas)
4
+ * 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)
5
+ * Expose the full response in exceptions' messages to make debugging easier (@boblail)
6
+ * Properly escape IDs with spaces in them when working with existing records (@pushups)
7
+
1
8
  ## 3.1.0 (Aug 16, 2018)
2
9
 
3
10
  * Add support for replaying missed messages when using the Salesforce Streaming API (@andreimaxim, @drteeth, @panozzaj)
@@ -8,13 +15,19 @@
8
15
 
9
16
  ## 3.0.0 (Aug 2, 2018)
10
17
 
11
- * __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.)
18
+ * __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.)
12
19
  * Fix `NoMethodError` when trying to upsert a record using a `Fixnum` as the external ID (@AlexandruCD)
13
20
  * Escape record IDs passed in to the client to identify records to find, delete, etc. (@jmdx)
14
21
  * 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)
15
22
  * Fix `get_updated` and `get_deleted` API calls by removing the erroneous leading forward slash from the path (@scottolsen)
16
23
  * Fix unpacking of dependent picklist options (@parkm)
17
24
 
25
+ ## 2.5.4 (May 15, 2019)
26
+
27
+ See the [`v2`](https://github.com/restforce/restforce/tree/v2) branch for this release.
28
+
29
+ * Escape record IDs passed in to the client to identify records to find, delete, etc. (@jmdx, @apanzerj)
30
+
18
31
  ## 2.5.3 (Apr 25, 2017)
19
32
 
20
33
  * 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,7 +3,7 @@
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
8
  gem 'rake'
9
9
 
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.1.0'
28
+ gem 'restforce', '~> 3.2.0'
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
 
@@ -585,7 +585,7 @@ memory using a Hash, stores a timestamp and has some rudimentary logic that
585
585
  will use one of the magic IDs depending on the value of the timestamp:
586
586
 
587
587
  ```ruby
588
- class SimpleReplayHander
588
+ class SimpleReplayHandler
589
589
 
590
590
  MAX_AGE = 86_400 # 24 hours
591
591
 
@@ -29,6 +29,7 @@ module Restforce
29
29
  autoload :Verbs, 'restforce/concerns/verbs'
30
30
  autoload :Base, 'restforce/concerns/base'
31
31
  autoload :API, 'restforce/concerns/api'
32
+ autoload :BatchAPI, 'restforce/concerns/batch_api'
32
33
  end
33
34
 
34
35
  module Data
@@ -44,6 +45,25 @@ module Restforce
44
45
  AuthenticationError = Class.new(Error)
45
46
  UnauthorizedError = Class.new(Error)
46
47
  APIVersionError = Class.new(Error)
48
+ BatchAPIError = Class.new(Error)
49
+
50
+ # Inherit from Faraday::Error::ResourceNotFound for backwards-compatibility
51
+ # Consumers of this library that rescue and handle Faraday::Error::ResourceNotFound
52
+ # can continue to do so.
53
+ NotFoundError = Class.new(Faraday::Error::ResourceNotFound)
54
+
55
+ # Inherit from Faraday::Error::ClientError for backwards-compatibility
56
+ # Consumers of this library that rescue and handle Faraday::Error::ClientError
57
+ # can continue to do so.
58
+ ResponseError = Class.new(Faraday::Error::ClientError)
59
+ MatchesMultipleError= Class.new(ResponseError)
60
+ EntityTooLargeError = Class.new(ResponseError)
61
+
62
+ module ErrorCode
63
+ def self.const_missing(constant_name)
64
+ const_set constant_name, Class.new(ResponseError)
65
+ end
66
+ end
47
67
 
48
68
  class << self
49
69
  # Alias for Restforce::Data::Client.new
@@ -74,7 +94,7 @@ module Restforce
74
94
  self
75
95
  end
76
96
  end
77
- Object.send :include, Restforce::CoreExtensions unless Object.respond_to? :tap
97
+ Object.include Restforce::CoreExtensions unless Object.respond_to? :tap
78
98
  end
79
99
 
80
100
  if ENV['PROXY_URI']
@@ -7,5 +7,6 @@ module Restforce
7
7
  include Restforce::Concerns::Authentication
8
8
  include Restforce::Concerns::Caching
9
9
  include Restforce::Concerns::API
10
+ include Restforce::Concerns::BatchAPI
10
11
  end
11
12
  end
@@ -17,6 +17,7 @@ module Restforce
17
17
 
18
18
  def ensure_body
19
19
  return true if self.Body?
20
+
20
21
  raise 'You need to query the Body for the record first.'
21
22
  end
22
23
  end
@@ -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,7 +379,8 @@ module Restforce
377
379
  api_post "sobjects/#{sobject}/#{field}", attrs
378
380
  end
379
381
  else
380
- api_patch "sobjects/#{sobject}/#{field}/#{CGI.escape(external_id)}", attrs
382
+ api_patch "sobjects/#{sobject}/#{field}/" \
383
+ "#{ERB::Util.url_encode(external_id)}", attrs
381
384
  end
382
385
 
383
386
  response.body.respond_to?(:fetch) ? response.body.fetch('id', true) : true
@@ -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}/#{CGI.escape(id)}"
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}/#{CGI.escape(id)}"
434
+ "sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
432
435
  else
433
- "sobjects/#{sobject}/#{CGI.escape(id)}"
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}/#{CGI.escape(id)}"
452
+ "sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
450
453
  else
451
- "sobjects/#{sobject}/#{CGI.escape(id)}"
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
@@ -5,6 +5,7 @@ module Restforce
5
5
  module Canvas
6
6
  def decode_signed_request(signed_request)
7
7
  raise 'client_secret not set.' unless options[:client_secret]
8
+
8
9
  SignedRequest.decode(signed_request, options[:client_secret])
9
10
  end
10
11
  end
@@ -34,6 +34,7 @@ module Restforce
34
34
  @field = field
35
35
  @valid_for = options.delete(:valid_for)
36
36
  raise "#{field} is not a dependent picklist" if @valid_for && !dependent?
37
+
37
38
  replace(picklist_values)
38
39
  end
39
40
 
@@ -62,9 +62,7 @@ module Restforce
62
62
 
63
63
  def outgoing(message, callback)
64
64
  # Leave non-subscribe messages alone
65
- unless message['channel'] == '/meta/subscribe'
66
- return callback.call(message)
67
- end
65
+ return callback.call(message) unless message['channel'] == '/meta/subscribe'
68
66
 
69
67
  channel = message['subscription'].gsub('/topic/', '')
70
68
 
@@ -34,6 +34,7 @@ module Restforce
34
34
 
35
35
  def log(message)
36
36
  return unless Restforce.log?
37
+
37
38
  configuration.logger.send(configuration.log_level, message)
38
39
  end
39
40
  end
@@ -17,6 +17,7 @@ module Restforce
17
17
 
18
18
  def ensure_body
19
19
  return true if self.Body?
20
+
20
21
  raise 'You need to query the Body for the record first.'
21
22
  end
22
23
  end
@@ -49,6 +49,7 @@ module Restforce
49
49
  # Files
50
50
  params.each do |k, v|
51
51
  next unless v.respond_to? :content_type
52
+
52
53
  parts << Faraday::Parts::Part.new(boundary,
53
54
  k.to_s,
54
55
  v)
@@ -6,23 +6,30 @@ module Restforce
6
6
  @env = env
7
7
  case env[:status]
8
8
  when 300
9
- raise Faraday::Error::ClientError.new("300: The external ID provided matches " \
10
- "more than one record",
11
- response_values)
9
+ raise Restforce::MatchesMultipleError.new(
10
+ "300: The external ID provided matches more than one record",
11
+ response_values
12
+ )
12
13
  when 401
13
14
  raise Restforce::UnauthorizedError, message
14
15
  when 404
15
- raise Faraday::Error::ResourceNotFound, message
16
+ raise Restforce::NotFoundError, message
16
17
  when 413
17
- raise Faraday::Error::ClientError.new("413: Request Entity Too Large",
18
- response_values)
18
+ raise Restforce::EntityTooLargeError.new(
19
+ "413: Request Entity Too Large",
20
+ response_values
21
+ )
19
22
  when 400...600
20
- raise Faraday::Error::ClientError.new(message, response_values)
23
+ klass = exception_class_for_error_code(body['errorCode'])
24
+ raise klass.new(message, response_values)
21
25
  end
22
26
  end
23
27
 
24
28
  def message
25
- "#{body['errorCode']}: #{body['message']}"
29
+ message = "#{body['errorCode']}: #{body['message']}"
30
+ message << "\nRESPONSE: #{JSON.dump(@env[:body])}"
31
+ rescue StandardError
32
+ message # if JSON.dump fails, return message without extra detail
26
33
  end
27
34
 
28
35
  def body
@@ -43,5 +50,14 @@ module Restforce
43
50
  body: @env[:body]
44
51
  }
45
52
  end
53
+
54
+ ERROR_CODE_MATCHER = /\A[A-Z_]+\z/.freeze
55
+
56
+ def exception_class_for_error_code(error_code)
57
+ return Restforce::ResponseError unless ERROR_CODE_MATCHER.match?(error_code)
58
+
59
+ constant_name = error_code.split('_').map(&:capitalize).join.to_sym
60
+ Restforce::ErrorCode.const_get(constant_name)
61
+ end
46
62
  end
47
63
  end
@@ -27,6 +27,7 @@ module Restforce
27
27
  # Returns nil if the signed request is invalid.
28
28
  def decode
29
29
  return nil if signature != hmac
30
+
30
31
  JSON.parse(Base64.decode64(payload))
31
32
  end
32
33
 
@@ -63,6 +63,7 @@ module Restforce
63
63
 
64
64
  def ensure_id
65
65
  return true if self.Id?
66
+
66
67
  raise ArgumentError, 'You need to query the Id for the record first.'
67
68
  end
68
69
  end
@@ -5,9 +5,9 @@ module Restforce
5
5
  class Client < AbstractClient
6
6
  private
7
7
 
8
- def api_path(path)
9
- super("tooling/#{path}")
10
- end
8
+ def api_path(path)
9
+ super("tooling/#{path}")
10
+ end
11
11
  end
12
12
  end
13
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restforce
4
- VERSION = '3.1.0'
4
+ VERSION = '3.2.0'
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../lib/restforce/version', __FILE__)
3
+ require File.expand_path('lib/restforce/version', __dir__)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.authors = ["Eric J. Holmes", "Tim Rogers"]
@@ -31,10 +31,10 @@ Gem::Specification.new do |gem|
31
31
 
32
32
  gem.add_dependency 'hashie', ['>= 1.2.0', '< 4.0']
33
33
 
34
+ gem.add_development_dependency 'faye' unless RUBY_PLATFORM == 'java'
34
35
  gem.add_development_dependency 'rspec', '~> 2.14.0'
35
- gem.add_development_dependency 'webmock', '~> 3.4.0'
36
- gem.add_development_dependency 'simplecov', '~> 0.15.0'
37
- gem.add_development_dependency 'rubocop', '~> 0.50.0'
38
36
  gem.add_development_dependency 'rspec_junit_formatter', '~> 0.3.0'
39
- gem.add_development_dependency 'faye' unless RUBY_PLATFORM == 'java'
37
+ gem.add_development_dependency 'rubocop', '~> 0.75.0'
38
+ gem.add_development_dependency 'simplecov', '~> 0.17.1'
39
+ gem.add_development_dependency 'webmock', '~> 3.7.6'
40
40
  end
@@ -96,7 +96,7 @@ shared_examples_for Restforce::AbstractClient do
96
96
  subject do
97
97
  client.create('Account', Name: 'Foobar',
98
98
  Blob: Restforce::UploadIO.new(
99
- File.expand_path('../../fixtures/blob.jpg', __FILE__),
99
+ File.expand_path('../fixtures/blob.jpg', __dir__),
100
100
  'image/jpeg'
101
101
  ))
102
102
  end
@@ -209,6 +209,24 @@ shared_examples_for Restforce::AbstractClient do
209
209
  end
210
210
  end
211
211
  end
212
+
213
+ context 'when created with a space in the id' do
214
+ requests 'sobjects/Account/External__c/foo%20bar',
215
+ method: :patch,
216
+ with_body: "{\"Name\":\"Foobar\"}",
217
+ fixture: 'sobject/upsert_created_success_response'
218
+
219
+ [:External__c, 'External__c', :external__c, 'external__c'].each do |key|
220
+ context "with #{key.inspect} as the external id" do
221
+ subject do
222
+ client.upsert!('Account', 'External__c', key => 'foo bar',
223
+ :Name => 'Foobar')
224
+ end
225
+
226
+ it { should eq 'foo' }
227
+ end
228
+ end
229
+ end
212
230
  end
213
231
 
214
232
  describe '.destroy!' do
@@ -229,6 +247,13 @@ shared_examples_for Restforce::AbstractClient do
229
247
 
230
248
  it { should be_true }
231
249
  end
250
+
251
+ context 'with a space in the id' do
252
+ subject(:destroy!) { client.destroy!('Account', '001D000000 INjVe') }
253
+ requests 'sobjects/Account/001D000000%20INjVe', method: :delete
254
+
255
+ it { should be_true }
256
+ end
232
257
  end
233
258
 
234
259
  describe '.destroy' do
@@ -266,6 +291,14 @@ shared_examples_for Restforce::AbstractClient do
266
291
  subject { client.find('Account', '1234', 'External_Field__c') }
267
292
  it { should be_a Hash }
268
293
  end
294
+
295
+ context 'with a space in an external id' do
296
+ requests 'sobjects/Account/External_Field__c/12%2034',
297
+ fixture: 'sobject/sobject_find_success_response'
298
+
299
+ subject { client.find('Account', '12 34', 'External_Field__c') }
300
+ it { should be_a Hash }
301
+ end
269
302
  end
270
303
 
271
304
  describe '.select' do
@@ -284,6 +317,14 @@ shared_examples_for Restforce::AbstractClient do
284
317
  subject { client.select('Account', '1234', ['External_Field__c']) }
285
318
  it { should be_a Hash }
286
319
  end
320
+
321
+ context 'with a space in the id' do
322
+ requests 'sobjects/Account/12%2034',
323
+ fixture: 'sobject/sobject_select_success_response'
324
+
325
+ subject { client.select('Account', '12 34', nil, nil) }
326
+ it { should be_a Hash }
327
+ end
287
328
  end
288
329
 
289
330
  context 'when an external id is specified' do
@@ -27,8 +27,8 @@ module FixtureHelpers
27
27
  stub
28
28
  end
29
29
 
30
- def fixture(f)
31
- File.read(File.expand_path("../../fixtures/#{f}.json", __FILE__))
30
+ def fixture(filename)
31
+ File.read(File.expand_path("../../fixtures/#{filename}.json", __FILE__))
32
32
  end
33
33
  end
34
34
 
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Restforce::Concerns::BatchAPI do
6
+ let(:endpoint) { 'composite/batch' }
7
+
8
+ before do
9
+ client.should_receive(:options).and_return(api_version: 34.0)
10
+ end
11
+
12
+ shared_examples_for 'batched requests' do
13
+ it '#create' do
14
+ client.
15
+ should_receive(:api_post).
16
+ with(endpoint, { batchRequests: [
17
+ { method: 'POST', url: 'v34.0/sobjects/Object', richInput: { name: 'test' } }
18
+ ], haltOnError: halt_on_error }.to_json).
19
+ and_return(response)
20
+
21
+ client.send(method) do |subrequests|
22
+ subrequests.create('Object', name: 'test')
23
+ end
24
+ end
25
+
26
+ it '#update' do
27
+ client.
28
+ should_receive(:api_post).
29
+ with(endpoint, { batchRequests: [
30
+ { method: 'PATCH', url: "v34.0/sobjects/Object/123", richInput: {
31
+ name: 'test'
32
+ } }
33
+ ], haltOnError: halt_on_error }.to_json).
34
+ and_return(response)
35
+
36
+ client.send(method) do |subrequests|
37
+ subrequests.update('Object', id: '123', name: 'test')
38
+ end
39
+ end
40
+
41
+ it '#destroy' do
42
+ client.
43
+ should_receive(:api_post).
44
+ with(endpoint, { batchRequests: [
45
+ { method: 'DELETE', url: "v34.0/sobjects/Object/123" }
46
+ ], haltOnError: halt_on_error }.to_json).
47
+ and_return(response)
48
+
49
+ client.send(method) do |subrequests|
50
+ subrequests.destroy('Object', '123')
51
+ end
52
+ end
53
+
54
+ it '#upsert' do
55
+ client.
56
+ should_receive(:api_post).
57
+ with(endpoint, { batchRequests: [
58
+ { method: 'PATCH', url: 'v34.0/sobjects/Object/extIdField__c/456', richInput: {
59
+ name: 'test'
60
+ } }
61
+ ], haltOnError: halt_on_error }.to_json).
62
+ and_return(response)
63
+
64
+ client.send(method) do |subrequests|
65
+ subrequests.upsert('Object', 'extIdField__c',
66
+ extIdField__c: '456', name: 'test')
67
+ end
68
+ end
69
+
70
+ it 'multiple subrequests' do
71
+ client.
72
+ should_receive(:api_post).
73
+ with(endpoint, { batchRequests: [
74
+ { method: 'POST', url: 'v34.0/sobjects/Object', richInput: {
75
+ name: 'test'
76
+ } },
77
+ { method: 'PATCH', url: "v34.0/sobjects/Object/123", richInput: {
78
+ name: 'test'
79
+ } },
80
+ { method: 'DELETE', url: "v34.0/sobjects/Object/123" }
81
+ ], haltOnError: halt_on_error }.to_json).
82
+ and_return(response)
83
+
84
+ client.send(method) do |subrequests|
85
+ subrequests.create('Object', name: 'test')
86
+ subrequests.update('Object', id: '123', name: 'test')
87
+ subrequests.destroy('Object', '123')
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#batch' do
93
+ let(:method) { :batch }
94
+ let(:halt_on_error) { false }
95
+ let(:response) { double('Faraday::Response', body: { 'results' => [] }) }
96
+ it_behaves_like 'batched requests'
97
+ end
98
+
99
+ describe '#batch!' do
100
+ let(:method) { :batch! }
101
+ let(:halt_on_error) { true }
102
+ let(:response) {
103
+ double('Faraday::Response', body: { 'hasErrors' => false, 'results' => [] })
104
+ }
105
+ it_behaves_like 'batched requests'
106
+ end
107
+ end
@@ -13,34 +13,46 @@ describe Restforce::Middleware::RaiseError do
13
13
  context 'when the status code is 404' do
14
14
  let(:status) { 404 }
15
15
 
16
- it "raises an error" do
17
- expect { on_complete }.to raise_error Faraday::Error::ResourceNotFound,
16
+ it 'raises Restforce::NotFoundError' do
17
+ expect { on_complete }.to raise_error Restforce::NotFoundError,
18
18
  'INVALID_FIELD: error_message'
19
19
  end
20
+
21
+ it 'raises an error that inherits from Faraday::Error::ResourceNotFound' do
22
+ expect { on_complete }.to raise_error Faraday::Error::ResourceNotFound
23
+ end
20
24
  end
21
25
 
22
26
  context 'when the status code is 300' do
23
27
  let(:status) { 300 }
24
28
 
25
- it "raises an error" do
26
- expect { on_complete }.to raise_error Faraday::Error::ClientError,
29
+ it 'raises Restforce::MatchesMultipleError' do
30
+ expect { on_complete }.to raise_error Restforce::MatchesMultipleError,
27
31
  /300: The external ID provided/
28
32
  end
33
+
34
+ it 'raises an error that inherits from Faraday::Error::ClientError' do
35
+ expect { on_complete }.to raise_error Faraday::Error::ClientError
36
+ end
29
37
  end
30
38
 
31
39
  context 'when the status code is 400' do
32
40
  let(:status) { 400 }
33
41
 
34
- it "raises an error" do
35
- expect { on_complete }.to raise_error Faraday::Error::ClientError,
42
+ it "raises an error derived from the response's errorCode" do
43
+ expect { on_complete }.to raise_error Restforce::ErrorCode::InvalidField,
36
44
  'INVALID_FIELD: error_message'
37
45
  end
46
+
47
+ it 'raises an error that inherits from Faraday::Error::ClientError' do
48
+ expect { on_complete }.to raise_error Faraday::Error::ClientError
49
+ end
38
50
  end
39
51
 
40
52
  context 'when the status code is 401' do
41
53
  let(:status) { 401 }
42
54
 
43
- it "raises an error" do
55
+ it 'raises Restforce::UnauthorizedError' do
44
56
  expect { on_complete }.to raise_error Restforce::UnauthorizedError,
45
57
  'INVALID_FIELD: error_message'
46
58
  end
@@ -49,17 +61,26 @@ describe Restforce::Middleware::RaiseError do
49
61
  context 'when the status code is 413' do
50
62
  let(:status) { 413 }
51
63
 
52
- it "raises an error" do
53
- expect { on_complete }.to raise_error Faraday::Error::ClientError,
64
+ it 'raises Restforce::EntityTooLargeError' do
65
+ expect { on_complete }.to raise_error Restforce::EntityTooLargeError,
54
66
  '413: Request Entity Too Large'
55
67
  end
68
+
69
+ it 'raises an error that inherits from Faraday::Error::ClientError' do
70
+ expect { on_complete }.to raise_error Faraday::Error::ClientError
71
+ end
56
72
  end
57
73
 
58
74
  context 'when status is 400+ and body is a string' do
59
75
  let(:body) { 'An error occured' }
60
- let(:status) { 404 }
76
+ let(:status) { 400 }
77
+
78
+ it 'raises a generic Restforce::ResponseError' do
79
+ expect { on_complete }.to raise_error Restforce::ResponseError,
80
+ "(error code missing): #{body}"
81
+ end
61
82
 
62
- it 'raises an error with a non-existing error code' do
83
+ it 'raises an error that inherits from Faraday::Error::ClientError' do
63
84
  expect { on_complete }.to raise_error Faraday::Error::ClientError,
64
85
  "(error code missing): #{body}"
65
86
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restforce
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric J. Holmes
@@ -9,28 +9,28 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-08-16 00:00:00.000000000 Z
12
+ date: 2019-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "<="
19
- - !ruby/object:Gem::Version
20
- version: '1.0'
21
18
  - - ">="
22
19
  - !ruby/object:Gem::Version
23
20
  version: 0.9.0
21
+ - - "<="
22
+ - !ruby/object:Gem::Version
23
+ version: '1.0'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
- - - "<="
29
- - !ruby/object:Gem::Version
30
- version: '1.0'
31
28
  - - ">="
32
29
  - !ruby/object:Gem::Version
33
30
  version: 0.9.0
31
+ - - "<="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: faraday_middleware
36
36
  requirement: !ruby/object:Gem::Requirement
@@ -86,89 +86,89 @@ dependencies:
86
86
  - !ruby/object:Gem::Version
87
87
  version: '4.0'
88
88
  - !ruby/object:Gem::Dependency
89
- name: rspec
89
+ name: faye
90
90
  requirement: !ruby/object:Gem::Requirement
91
91
  requirements:
92
- - - "~>"
92
+ - - ">="
93
93
  - !ruby/object:Gem::Version
94
- version: 2.14.0
94
+ version: '0'
95
95
  type: :development
96
96
  prerelease: false
97
97
  version_requirements: !ruby/object:Gem::Requirement
98
98
  requirements:
99
- - - "~>"
99
+ - - ">="
100
100
  - !ruby/object:Gem::Version
101
- version: 2.14.0
101
+ version: '0'
102
102
  - !ruby/object:Gem::Dependency
103
- name: webmock
103
+ name: rspec
104
104
  requirement: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - "~>"
107
107
  - !ruby/object:Gem::Version
108
- version: 3.4.0
108
+ version: 2.14.0
109
109
  type: :development
110
110
  prerelease: false
111
111
  version_requirements: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: 3.4.0
115
+ version: 2.14.0
116
116
  - !ruby/object:Gem::Dependency
117
- name: simplecov
117
+ name: rspec_junit_formatter
118
118
  requirement: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: 0.15.0
122
+ version: 0.3.0
123
123
  type: :development
124
124
  prerelease: false
125
125
  version_requirements: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: 0.15.0
129
+ version: 0.3.0
130
130
  - !ruby/object:Gem::Dependency
131
131
  name: rubocop
132
132
  requirement: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: 0.50.0
136
+ version: 0.75.0
137
137
  type: :development
138
138
  prerelease: false
139
139
  version_requirements: !ruby/object:Gem::Requirement
140
140
  requirements:
141
141
  - - "~>"
142
142
  - !ruby/object:Gem::Version
143
- version: 0.50.0
143
+ version: 0.75.0
144
144
  - !ruby/object:Gem::Dependency
145
- name: rspec_junit_formatter
145
+ name: simplecov
146
146
  requirement: !ruby/object:Gem::Requirement
147
147
  requirements:
148
148
  - - "~>"
149
149
  - !ruby/object:Gem::Version
150
- version: 0.3.0
150
+ version: 0.17.1
151
151
  type: :development
152
152
  prerelease: false
153
153
  version_requirements: !ruby/object:Gem::Requirement
154
154
  requirements:
155
155
  - - "~>"
156
156
  - !ruby/object:Gem::Version
157
- version: 0.3.0
157
+ version: 0.17.1
158
158
  - !ruby/object:Gem::Dependency
159
- name: faye
159
+ name: webmock
160
160
  requirement: !ruby/object:Gem::Requirement
161
161
  requirements:
162
- - - ">="
162
+ - - "~>"
163
163
  - !ruby/object:Gem::Version
164
- version: '0'
164
+ version: 3.7.6
165
165
  type: :development
166
166
  prerelease: false
167
167
  version_requirements: !ruby/object:Gem::Requirement
168
168
  requirements:
169
- - - ">="
169
+ - - "~>"
170
170
  - !ruby/object:Gem::Version
171
- version: '0'
171
+ version: 3.7.6
172
172
  description: A lightweight ruby client for the Salesforce REST API.
173
173
  email:
174
174
  - eric@ejholmes.net
@@ -197,6 +197,7 @@ files:
197
197
  - lib/restforce/concerns/api.rb
198
198
  - lib/restforce/concerns/authentication.rb
199
199
  - lib/restforce/concerns/base.rb
200
+ - lib/restforce/concerns/batch_api.rb
200
201
  - lib/restforce/concerns/caching.rb
201
202
  - lib/restforce/concerns/canvas.rb
202
203
  - lib/restforce/concerns/connection.rb
@@ -281,6 +282,7 @@ files:
281
282
  - spec/unit/concerns/api_spec.rb
282
283
  - spec/unit/concerns/authentication_spec.rb
283
284
  - spec/unit/concerns/base_spec.rb
285
+ - spec/unit/concerns/batch_api_spec.rb
284
286
  - spec/unit/concerns/caching_spec.rb
285
287
  - spec/unit/concerns/canvas_spec.rb
286
288
  - spec/unit/concerns/connection_spec.rb
@@ -323,8 +325,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
323
325
  - !ruby/object:Gem::Version
324
326
  version: '0'
325
327
  requirements: []
326
- rubyforge_project:
327
- rubygems_version: 2.6.14.1
328
+ rubygems_version: 3.0.3
328
329
  signing_key:
329
330
  specification_version: 4
330
331
  summary: A lightweight ruby client for the Salesforce REST API.
@@ -379,6 +380,7 @@ test_files:
379
380
  - spec/unit/concerns/api_spec.rb
380
381
  - spec/unit/concerns/authentication_spec.rb
381
382
  - spec/unit/concerns/base_spec.rb
383
+ - spec/unit/concerns/batch_api_spec.rb
382
384
  - spec/unit/concerns/caching_spec.rb
383
385
  - spec/unit/concerns/canvas_spec.rb
384
386
  - spec/unit/concerns/connection_spec.rb