Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions lib/restforce/concerns/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,125 @@ def update!(sobject, attrs)
true
end

# Public: Insert collection of records.
#
# attrs_collection - Collection of attributes for new records.
# all_or_none - Fail all collection if one record creation fails.
#
# Examples
#
# # Add new accounts
# client.create_collection([{Name: 'Foo Inc.'}, {Name: 'Bar Corp.'}])
# # => [{ 'id' => '0016000000MRatd', 'success' => true, 'errors' => [] },
# # { 'id' => '0016000000MRert', 'success' => true, 'errors' => [] }]
#
# Returns array of result objects.
# Returns false if something bad happens.
def create_collection(*args)
create_collection!(*args)
rescue *exceptions
false
end

# Public: Insert collection of records.
#
# attrs_collection - Collection of attributes for new records.
# all_or_none - Fail all collection if one record creation fails.
#
# Examples
#
# # Add new accounts
# client.create_collection!([{Name: 'Foo Inc.'}, {Name: 'Bar Corp.'}])
# # => [{ 'id' => '1', 'success' => true, 'errors' => [] },
# # { 'id' => '2', 'success' => true, 'errors' => [] }]
#
# Returns array of result objects.
# Raises exceptions if an error is returned from Salesforce.
def create_collection!(attrs_collection, all_or_none = false)
raise ArgumentError,
'Amount of records to create is limited to 200' if attrs_collection.size > 200

api_post('composite/sobjects', {records: attrs_collection, allOrNone: all_or_none}).body
end

# Public: Update collection of records.
#
# attrs_collection - Collection of attributes for existing records.
# all_or_none - Fail all collection if one record creation fails.
#
# Examples
#
# # Update accounts
# client.update_collection([{Id: '1', Name: 'Foo Inc.'}, {Id: '2', Name: 'Bar Corp.'}])
# # => [{ 'id' => '1', 'success' => true, 'errors' => [] },
# # { 'id' => '2', 'success' => true, 'errors' => [] }]
#
# Returns array of result objects.
# Returns false if something bad happens.
def update_collection(*args)
update_collection!(*args)
rescue *exceptions
false
end

# Public: Update collection of records.
#
# attrs_collection - Collection of attributes for existing records.
# all_or_none - Fail all collection if one record creation fails.
#
# Examples
#
# # Update accounts
# client.update_collection!([{Id: '1', Name: 'Foo Inc.'}, {Id: '2', Name: 'Bar Corp.'}])
# # => [{ 'id' => '1', 'success' => true, 'errors' => [] },
# # { 'id' => '2', 'success' => true, 'errors' => [] }]
#
# Returns array of result objects.
# Raises exceptions if an error is returned from Salesforce.
def update_collection!(attrs_collection, all_or_none = false)
raise ArgumentError,
'Amount of records to update is limited to 200' if attrs_collection.size > 200

api_patch('composite/sobjects', {records: attrs_collection, allOrNone: all_or_none}).body
end

# Public: Delete collection of records.
#
# ids - List of IDs of objects to be deleted.
# all_or_none - Roll back the entire request when the deletion of any object fails.
#
# Examples
#
# # Delete collection of records
# client.destroy_collection('0016000000MRatd', '0016000000ERats')
#
# Returns array of result objects.
# Returns false if an error is returned from Salesforce.
def destroy_collection(*args)
destroy_collection!(*args)
rescue *exceptions
false
end

# Public: Delete collection of records.
#
# ids - List of IDs of objects to be deleted.
# all_or_none - Roll back the entire request when the deletion of any object fails.
#
# Examples
#
# # Delete collection of records
# client.destroy_collection!('0016000000MRatd', '0016000000ERats')
#
# Returns array of result objects.
# Raises exceptions if an error is returned from Salesforce.
def destroy_collection!(ids, all_or_none = false)
raise ArgumentError,
'Amount of records to delete is limited to 200' if ids.size > 200

api_delete("composite/sobjects?ids=#{ids.join(',')}&allOrNone=#{all_or_none}").body
end

# Public: Update or create a record based on an external ID
#
# sobject - The name of the sobject to created.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"message" : "At least 1 Id is required.",
"errorCode" : "REQUIRED_FIELD_MISSING"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"id" : "Foo",
"success" : true,
"errors" : [ ]
}
]
4 changes: 4 additions & 0 deletions spec/fixtures/sobject/composite_sobjects_error_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"message": "Json Deserialization failed on token 'null' and has left off in the middle of parsing a row. Will go to end of row to begin parsing the next row",
"errorCode": "INVALID_FIELD"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"id": "some_id",
"errors": [ ],
"success": true
}
]
144 changes: 144 additions & 0 deletions spec/integration/abstract_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,150 @@
end
end

describe '.create_collection!' do
context 'with valid params' do
requests 'composite/sobjects',
method: :post,
with_body: {
allOrNone: false,
records: [{Name: 'Foobar'}]
},
fixture: 'sobject/composite_sobjects_success_response'

subject { client.create_collection!([{Name: 'Foobar'}]) }

it { should eq [{'id' => 'some_id', 'errors' => [], 'success' => true}] }
end

context 'with invalid params' do
requests 'composite/sobjects',
method: :post,
status: 400,
with_body: {
allOrNone: false,
records: ['Foo']
},
fixture: 'sobject/composite_sobjects_error_response'

subject {
lambda do
client.create_collection!(['Foo'])
end
}

it { should raise_error(Faraday::ClientError) }
end
end

describe '.create_collection' do
context 'with invalid params' do
requests 'composite/sobjects',
method: :post,
status: 400,
with_body: {
allOrNone: false,
records: ['Foo']
},
fixture: 'sobject/composite_sobjects_error_response'

subject { client.create_collection(['Foo']) }

it { should eq false }
end
end

describe '.update_collection!' do
context 'with valid params' do
requests 'composite/sobjects',
method: :patch,
with_body: {
allOrNone: false,
records: [{Name: 'Foobar'}]
},
fixture: 'sobject/composite_sobjects_success_response'

subject { client.update_collection!([{Name: 'Foobar'}]) }

it { should eq [{'id' => 'some_id', 'errors' => [], 'success' => true}] }
end

context 'with invalid params' do
requests 'composite/sobjects',
method: :patch,
status: 400,
with_body: {
allOrNone: false,
records: ['Foo']
},
fixture: 'sobject/composite_sobjects_error_response'

subject {
lambda do
client.update_collection!(['Foo'])
end
}

it { should raise_error(Faraday::ClientError) }
end
end

describe '.update_collection' do
context 'with invalid params' do
requests 'composite/sobjects',
method: :patch,
status: 400,
with_body: {
allOrNone: false,
records: ['Foo']
},
fixture: 'sobject/composite_sobjects_error_response'

subject { client.update_collection(['Foo']) }

it { should eq false }
end
end

describe '.destroy_collection!' do
context 'with valid params' do
requests 'composite/sobjects\?allOrNone=false&ids=Foo',
method: :delete,
fixture: 'sobject/composite_sobjects_destroy_success_response'

subject { client.destroy_collection!(['Foo']) }

it { should eq [{'id' => 'Foo', 'errors' => [], 'success' => true}] }
end

context 'with invalid params' do
requests 'composite/sobjects\?allOrNone=false&ids=',
method: :delete,
status: 400,
fixture: 'sobject/composite_sobjects_destroy_bad_request_response'

subject {
lambda do
client.destroy_collection!([])
end
}

it { should raise_error(Faraday::ClientError) }
end
end

describe '.destroy_collection' do
context 'with invalid params' do
requests 'composite/sobjects\?allOrNone=false&ids=',
method: :delete,
status: 400,
fixture: 'sobject/composite_sobjects_destroy_bad_request_response'

subject { client.destroy_collection([]) }

it { should eq false }
end
end

describe '.upsert!' do
context 'when updated' do
requests 'sobjects/Account/External__c/foobar',
Expand Down
66 changes: 66 additions & 0 deletions spec/unit/concerns/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,72 @@
end
end

describe '.create_collection!' do
let(:attrs) { [{'Name' => 'Foobar'}] }
subject(:result) { client.create_collection!(attrs) }

it 'sends an HTTP POST, and returns list of result objects' do
response.stub(:body).and_return([{'id' => '1', 'success' => true, 'errors' => []}])
client.should_receive(:api_post).
with('composite/sobjects', {records: attrs, allOrNone: false}).
and_return(response)
expect(result[0]).to eq({'id' => '1', 'success' => true, 'errors' => []})
end

context 'when attributes collection size is more than 200' do
let(:attrs) { Array.new(201) }

it "raises an error" do
expect { client.create_collection!(attrs) }.to raise_error(
ArgumentError, 'Amount of records to create is limited to 200')
end
end
end

describe '.update_collection!' do
let(:attrs) { [{'id' => '1', 'Name' => 'Foobar'}] }
subject(:result) { client.update_collection!(attrs) }

it 'sends an HTTP PATCH, and returns list of result objects' do
response.stub(:body).and_return([{'id' => '1', 'success' => true, 'errors' => []}])
client.should_receive(:api_patch).
with('composite/sobjects', {records: attrs, allOrNone: false}).
and_return(response)
expect(result[0]).to eq({'id' => '1', 'success' => true, 'errors' => []})
end

context 'when attributes collection size is more than 200' do
let(:attrs) { Array.new(201) }

it "raises an error" do
expect { client.update_collection!(attrs) }.to raise_error(
ArgumentError, 'Amount of records to update is limited to 200')
end
end
end

describe '.destroy_collection!' do
let(:attrs) { ['1'] }
subject(:result) { client.destroy_collection!(attrs) }

it 'sends an HTTP DELETE, and returns list of result objects' do
response.stub(:body).and_return([{'id' => '1', 'success' => true, 'errors' => []}])
client.should_receive(:api_delete).
with('composite/sobjects?ids=1&allOrNone=false').
and_return(response)
expect(result[0]).to eq({'id' => '1', 'success' => true, 'errors' => []})
end

context 'when ids collection size is more than 200' do
let(:attrs) { Array.new(201) }

it "raises an error" do
expect { client.destroy_collection!(attrs) }.to raise_error(
ArgumentError, 'Amount of records to delete is limited to 200')
end
end
end

describe '.upsert!' do
let(:sobject) { 'Whizbang' }
let(:field) { :External_ID__c }
Expand Down