restforce 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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