active_force 0.25.0 → 0.26.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 +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/active_force/active_query.rb +8 -1
- data/lib/active_force/composite_batch_query.rb +42 -0
- data/lib/active_force/version.rb +1 -1
- data/lib/active_force.rb +9 -1
- data/spec/active_force/active_query_spec.rb +20 -0
- data/spec/active_force/composite_batch_query_spec.rb +101 -0
- data/spec/active_force_spec.rb +20 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 657c5d9e979b06641cae8f6734a9a0371669dcc3fca816e89c0a23fca20537fb
|
|
4
|
+
data.tar.gz: c6e3646ea293a22d459faca15f8fbf9b16cbaf4482b7c38dc4e135dac22da6e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9a1fb7822ad1950f32fc86e20e9e00cf29a5f471b2792eda9fe8ae9aae1db6f2b1817ac8fcf6fc9b0ec74ee351e62befb0aab8a6e83a7a5ec09a3761e656f85
|
|
7
|
+
data.tar.gz: 7f1cb6448305cef1fd185cce72571f1eff55aa3115444edf215dd031711fe6027b51922832392eb02a1e60a82b9d77616bffe271aad638344686122bdeae91c6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'active_support/all'
|
|
2
2
|
require 'active_force/query'
|
|
3
3
|
require 'active_force/select_builder'
|
|
4
|
+
require 'active_force/composite_batch_query'
|
|
4
5
|
require 'forwardable'
|
|
5
6
|
|
|
6
7
|
module ActiveForce
|
|
@@ -245,7 +246,13 @@ module ActiveForce
|
|
|
245
246
|
end
|
|
246
247
|
|
|
247
248
|
def result
|
|
248
|
-
|
|
249
|
+
soql = self.to_s
|
|
250
|
+
|
|
251
|
+
if soql.length >= ActiveForce.composite_batch_query_threshold
|
|
252
|
+
CompositeBatchQuery.call(soql, sfdc_client)
|
|
253
|
+
else
|
|
254
|
+
sfdc_client.query(soql)
|
|
255
|
+
end
|
|
249
256
|
end
|
|
250
257
|
|
|
251
258
|
def build_order_by(args)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'active_support/all'
|
|
2
|
+
|
|
3
|
+
module ActiveForce
|
|
4
|
+
class CompositeBatchQuery
|
|
5
|
+
def self.call(soql, sfdc_client = ActiveForce.sfdc_client)
|
|
6
|
+
new(soql, sfdc_client).call
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :sfdc_client, :soql
|
|
10
|
+
|
|
11
|
+
def initialize(soql, sfdc_client)
|
|
12
|
+
@sfdc_client = sfdc_client
|
|
13
|
+
@soql = soql
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
results = sfdc_client.batch do |subrequests|
|
|
18
|
+
subrequests.requests << {
|
|
19
|
+
method: "GET",
|
|
20
|
+
url: "v#{subrequests.options[:api_version]}/query?" + {q: soql}.to_query
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
r = results.first
|
|
25
|
+
|
|
26
|
+
process_composite_batch_error_response(r) if r.statusCode >= 300
|
|
27
|
+
|
|
28
|
+
r.result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def process_composite_batch_error_response(r)
|
|
34
|
+
response_value = {status: r.statusCode, body: r.result.as_json}
|
|
35
|
+
error_code = r.result.dig(0, "errorCode")
|
|
36
|
+
message = "#{error_code}: #{r.result.dig(0, "message")}"
|
|
37
|
+
message << "\nRESPONSE: #{r.result.to_json}"
|
|
38
|
+
|
|
39
|
+
raise Restforce::ErrorCode.get_exception_class(error_code).new(message, response_value)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/active_force/version.rb
CHANGED
data/lib/active_force.rb
CHANGED
|
@@ -9,8 +9,16 @@ module ActiveForce
|
|
|
9
9
|
|
|
10
10
|
class << self
|
|
11
11
|
attr_accessor :sfdc_client
|
|
12
|
+
attr_writer :composite_batch_query_threshold
|
|
13
|
+
|
|
14
|
+
def composite_batch_query_threshold
|
|
15
|
+
@composite_batch_query_threshold ||= 100_000
|
|
16
|
+
|
|
17
|
+
return @composite_batch_query_threshold.call if @composite_batch_query_threshold.respond_to?(:call)
|
|
18
|
+
|
|
19
|
+
@composite_batch_query_threshold
|
|
20
|
+
end
|
|
12
21
|
end
|
|
13
22
|
|
|
14
23
|
self.sfdc_client = Restforce.new
|
|
15
|
-
|
|
16
24
|
end
|
|
@@ -557,6 +557,26 @@ describe ActiveForce::ActiveQuery do
|
|
|
557
557
|
end
|
|
558
558
|
end
|
|
559
559
|
|
|
560
|
+
describe '#result routing' do
|
|
561
|
+
after do
|
|
562
|
+
ActiveForce.instance_variable_set(:@composite_batch_query_threshold, nil)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
it 'uses sfdc_client.query when SOQL is at or below the threshold' do
|
|
566
|
+
ActiveForce.composite_batch_query_threshold = 100_000
|
|
567
|
+
expect(client).to receive(:query).and_return([])
|
|
568
|
+
expect(ActiveForce::CompositeBatchQuery).not_to receive(:call)
|
|
569
|
+
active_query.where("Id = 'foo'").to_a
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
it 'uses CompositeBatchQuery.call when SOQL exceeds the threshold' do
|
|
573
|
+
ActiveForce.composite_batch_query_threshold = 0
|
|
574
|
+
expect(ActiveForce::CompositeBatchQuery).to receive(:call).and_return([])
|
|
575
|
+
expect(client).not_to receive(:query)
|
|
576
|
+
active_query.where("Id = 'foo'").to_a
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
560
580
|
describe "#order" do
|
|
561
581
|
context 'when it is symbol' do
|
|
562
582
|
it "should add an order condition with actual SF field name" do
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ActiveForce::CompositeBatchQuery do
|
|
4
|
+
let(:soql) { "SELECT Id FROM Account WHERE Name = 'Test'" }
|
|
5
|
+
let(:api_version) { '58.0' }
|
|
6
|
+
let(:sfdc_client) { double(Restforce) }
|
|
7
|
+
let(:subrequests) do
|
|
8
|
+
double('subrequests', requests: [], options: { api_version: api_version })
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe '.call' do
|
|
12
|
+
it 'delegates to new(...).call' do
|
|
13
|
+
result_record = double('result_record')
|
|
14
|
+
instance = instance_double(described_class)
|
|
15
|
+
allow(described_class).to receive(:new).with(soql, sfdc_client).and_return(instance)
|
|
16
|
+
allow(instance).to receive(:call).and_return(result_record)
|
|
17
|
+
|
|
18
|
+
expect(described_class.call(soql, sfdc_client)).to eq(result_record)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'defaults to ActiveForce.sfdc_client when no client is provided' do
|
|
22
|
+
default_client = double(Restforce)
|
|
23
|
+
allow(ActiveForce).to receive(:sfdc_client).and_return(default_client)
|
|
24
|
+
|
|
25
|
+
instance = instance_double(described_class)
|
|
26
|
+
allow(described_class).to receive(:new).with(soql, default_client).and_return(instance)
|
|
27
|
+
allow(instance).to receive(:call).and_return(double('result'))
|
|
28
|
+
|
|
29
|
+
expect { described_class.call(soql) }.not_to raise_error
|
|
30
|
+
expect(described_class).to have_received(:new).with(soql, default_client)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#call' do
|
|
35
|
+
context 'when the response is successful' do
|
|
36
|
+
let(:result_record) { double(Restforce::Collection) }
|
|
37
|
+
let(:batch_result) do
|
|
38
|
+
double(Restforce::Mash, statusCode: 200, result: result_record)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
before do
|
|
42
|
+
allow(sfdc_client).to receive(:batch).and_yield(subrequests).and_return([batch_result])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'sends a GET batch subrequest with the correct URL' do
|
|
46
|
+
described_class.call(soql, sfdc_client)
|
|
47
|
+
|
|
48
|
+
expected_url = "v#{api_version}/query?" + { q: soql }.to_query
|
|
49
|
+
expect(subrequests.requests).to include(
|
|
50
|
+
hash_including(method: 'GET', url: expected_url)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'returns the result from the batch response' do
|
|
55
|
+
expect(described_class.call(soql, sfdc_client)).to eq(result_record)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context 'when the response statusCode is exactly 300' do
|
|
60
|
+
let(:error_body) do
|
|
61
|
+
[{ 'errorCode' => 'MULTIPLE_CHOICES', 'message' => 'multiple records found' }]
|
|
62
|
+
end
|
|
63
|
+
let(:batch_result) do
|
|
64
|
+
double('batch_result', statusCode: 300, result: error_body)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
before do
|
|
68
|
+
allow(sfdc_client).to receive(:batch).and_yield(subrequests).and_return([batch_result])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'treats it as an error' do
|
|
72
|
+
expect {
|
|
73
|
+
described_class.call(soql, sfdc_client)
|
|
74
|
+
}.to raise_error(Restforce::ResponseError)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'when the response is an error' do
|
|
79
|
+
let(:error_body) do
|
|
80
|
+
[{ 'errorCode' => 'MALFORMED_QUERY', 'message' => 'unexpected token' }]
|
|
81
|
+
end
|
|
82
|
+
let(:batch_result) do
|
|
83
|
+
double(Restforce::Mash, statusCode: 400, result: error_body)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
before do
|
|
87
|
+
allow(sfdc_client).to receive(:batch).and_yield(subrequests).and_return([batch_result])
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'raises a Restforce error with the correct error code and message' do
|
|
91
|
+
expect {
|
|
92
|
+
described_class.call(soql, sfdc_client)
|
|
93
|
+
}.to raise_error(Restforce::ErrorCode::MalformedQuery) do |error|
|
|
94
|
+
expect(error.message).to include('MALFORMED_QUERY')
|
|
95
|
+
expect(error.message).to include('unexpected token')
|
|
96
|
+
expect(error.message).to include('RESPONSE:')
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/spec/active_force_spec.rb
CHANGED
|
@@ -4,4 +4,24 @@ describe ActiveForce do
|
|
|
4
4
|
it 'should have a version number' do
|
|
5
5
|
expect(ActiveForce::VERSION).to_not be_nil
|
|
6
6
|
end
|
|
7
|
+
|
|
8
|
+
describe '.composite_batch_query_threshold' do
|
|
9
|
+
after do
|
|
10
|
+
ActiveForce.instance_variable_set(:@composite_batch_query_threshold, nil)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'returns 100_000 by default' do
|
|
14
|
+
expect(ActiveForce.composite_batch_query_threshold).to eq(100_000)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'allows setting a custom integer value' do
|
|
18
|
+
ActiveForce.composite_batch_query_threshold = 25_000
|
|
19
|
+
expect(ActiveForce.composite_batch_query_threshold).to eq(25_000)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'supports callable values (proc/lambda)' do
|
|
23
|
+
ActiveForce.composite_batch_query_threshold = -> { 50_000 }
|
|
24
|
+
expect(ActiveForce.composite_batch_query_threshold).to eq(50_000)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
7
27
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_force
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.26.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eloy Espinaco
|
|
8
8
|
- Pablo Oldani
|
|
9
9
|
- Armando Andini
|
|
10
10
|
- José Piccioni
|
|
11
|
-
autorequire:
|
|
12
11
|
bindir: bin
|
|
13
12
|
cert_chain: []
|
|
14
|
-
date:
|
|
13
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
15
14
|
dependencies:
|
|
16
15
|
- !ruby/object:Gem::Dependency
|
|
17
16
|
name: activemodel
|
|
@@ -173,6 +172,7 @@ files:
|
|
|
173
172
|
- lib/active_force/bulk/job.rb
|
|
174
173
|
- lib/active_force/bulk/job_result.rb
|
|
175
174
|
- lib/active_force/bulk/records.rb
|
|
175
|
+
- lib/active_force/composite_batch_query.rb
|
|
176
176
|
- lib/active_force/field.rb
|
|
177
177
|
- lib/active_force/mapping.rb
|
|
178
178
|
- lib/active_force/query.rb
|
|
@@ -195,6 +195,7 @@ files:
|
|
|
195
195
|
- spec/active_force/bulk/record_spec.rb
|
|
196
196
|
- spec/active_force/bulk_spec.rb
|
|
197
197
|
- spec/active_force/callbacks_spec.rb
|
|
198
|
+
- spec/active_force/composite_batch_query_spec.rb
|
|
198
199
|
- spec/active_force/field_spec.rb
|
|
199
200
|
- spec/active_force/mapping_spec.rb
|
|
200
201
|
- spec/active_force/query_spec.rb
|
|
@@ -218,7 +219,6 @@ metadata:
|
|
|
218
219
|
bug_tracker_uri: https://github.com/Beyond-Finance/active_force/issues
|
|
219
220
|
changelog_uri: https://github.com/Beyond-Finance/active_force/blob/main/CHANGELOG.md
|
|
220
221
|
source_code_uri: https://github.com/Beyond-Finance/active_force
|
|
221
|
-
post_install_message:
|
|
222
222
|
rdoc_options: []
|
|
223
223
|
require_paths:
|
|
224
224
|
- lib
|
|
@@ -233,8 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
233
233
|
- !ruby/object:Gem::Version
|
|
234
234
|
version: '0'
|
|
235
235
|
requirements: []
|
|
236
|
-
rubygems_version: 3.
|
|
237
|
-
signing_key:
|
|
236
|
+
rubygems_version: 3.6.9
|
|
238
237
|
specification_version: 4
|
|
239
238
|
summary: Help you implement models persisting on Sales Force within Rails using RESTForce
|
|
240
239
|
test_files:
|
|
@@ -246,6 +245,7 @@ test_files:
|
|
|
246
245
|
- spec/active_force/bulk/record_spec.rb
|
|
247
246
|
- spec/active_force/bulk_spec.rb
|
|
248
247
|
- spec/active_force/callbacks_spec.rb
|
|
248
|
+
- spec/active_force/composite_batch_query_spec.rb
|
|
249
249
|
- spec/active_force/field_spec.rb
|
|
250
250
|
- spec/active_force/mapping_spec.rb
|
|
251
251
|
- spec/active_force/query_spec.rb
|