Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raise UnknownAttributeError if .where is given invalid attribute names #80

Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Not released

- Raise `UnknownFieldError` if `.where` is given non-existent attribute names (https://github.com/Beyond-Finance/active_force/pull/80)
- Fix `.update` and `.update!`: include given `nil` valued attributes in request (https://github.com/Beyond-Finance/active_force/pull/79)
- Change `.first` to not query the API if records have already been retrieved (https://github.com/Beyond-Finance/active_force/pull/77)

Expand Down
16 changes: 13 additions & 3 deletions lib/active_force/active_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

module ActiveForce
class PreparedStatementInvalid < ArgumentError; end

class UnknownFieldError < StandardError
def initialize(object, field)
super("unknown field '#{field}' for #{object.name}")
end
end

class RecordNotFound < StandardError
attr_reader :table_name, :conditions

Expand Down Expand Up @@ -45,9 +52,9 @@ def count
sfdc_client.query(super.to_s).first.expr0
end

def sum field
def sum(field)
raise ArgumentError, 'field is required' if field.blank?
raise ArgumentError, "field '#{field}' does not exist on #{sobject}" unless mappings.key?(field.to_sym)
raise UnknownFieldError.new(sobject, field) unless mappings.key?(field.to_sym)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seemed appropriate to change this as well, since it's basically the same thing.


sfdc_client.query(super(mappings.fetch(field.to_sym)).to_s).first.expr0
end
Expand Down Expand Up @@ -167,7 +174,10 @@ def raise_if_bind_arity_mismatch(expected_var_count, actual_var_count)

def build_conditions_from_hash(hash)
hash.map do |key, value|
applicable_predicate mappings[key], value
field = mappings[key]
raise UnknownFieldError.new(sobject, key) if field.blank?

applicable_predicate(field, value)
end
end

Expand Down
32 changes: 27 additions & 5 deletions spec/active_force/active_query_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
require 'spec_helper'

class TestSObject < ActiveForce::SObject
def self.decorate(records)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a spec failing below when I changed sobject to a class_double because ActiveForce::SObject does not implement .decorate. I see that this method is supposed to be something that the consumer would implement in their subclass. This seems like a weird pattern to use with class methods. In any case, this seemed like the least intrusive workaround so that we can get a class_double here that validates that this mock matches real methods.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I never fully understood why the original authors of this library did something like that but I guess we're stuck with it for now. 🤷

records
end
end

describe ActiveForce::ActiveQuery do
let(:sobject) do
double("sobject", {
table_name: "table_name",
fields: [],
mappings: mappings
})
class_double(
TestSObject,
{ table_name: 'table_name', fields: [], mappings: mappings, name: 'TableName' }
)
end
let(:mappings){ { id: "Id", field: "Field__c", other_field: "Other_Field" } }
let(:client) { double('client', query: nil) }
Expand Down Expand Up @@ -296,6 +301,13 @@
end
end
end

context 'when given attributes Hash with fields that do not exist on the SObject' do
it 'raises UnknownFieldError' do
expect { active_query.where(xyz: 1) }
.to raise_error(ActiveForce::UnknownFieldError, /unknown field 'xyz' for #{sobject.name}/i)
end
end
end

describe '#not' do
Expand Down Expand Up @@ -329,6 +341,11 @@
new_query = active_query.find_by field: 123
expect(new_query).to be_nil
end

it 'should raise UnknownFieldError if given invalid field' do
expect { active_query.find_by(invalid: true) }
.to raise_error(ActiveForce::UnknownFieldError, /unknown field 'invalid' for #{sobject.name}/i)
end
end

describe '#find_by!' do
Expand All @@ -337,6 +354,11 @@
expect { active_query.find_by!(field: 123) }
.to raise_error(ActiveForce::RecordNotFound, "Couldn't find #{sobject.table_name} with {:field=>123}")
end

it 'should raise UnknownFieldError if given invalid field' do
expect { active_query.find_by!(invalid: true) }
.to raise_error(ActiveForce::UnknownFieldError, /unknown field 'invalid' for #{sobject.name}/i)
end
end

describe '#find!' do
Expand Down
15 changes: 13 additions & 2 deletions spec/active_force/sobject_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,9 @@ class IceCream < ActiveForce::SObject
expect { Whizbang.sum(nil) }.to raise_error(ArgumentError, 'field is required')
end

it 'raises ArgumentError if given invalid field' do
it 'raises UnknownFieldError if given invalid field' do
expect { Whizbang.sum(:invalid) }
.to raise_error(ArgumentError, /field 'invalid' does not exist on Whizbang/i)
.to raise_error(ActiveForce::UnknownFieldError, /unknown field 'invalid' for Whizbang/i)
end

it 'sends the correct query to the client' do
Expand Down Expand Up @@ -419,17 +419,28 @@ class IceCream < ActiveForce::SObject
expect(client).to receive(:query).with("SELECT #{Whizbang.fields.join ', '} FROM Whizbang__c WHERE (Id = 123) AND (Text_Label = 'foo') LIMIT 1")
Whizbang.find_by id: 123, text: "foo"
end

it 'raises UnknownFieldError if given invalid field' do
expect { Whizbang.find_by(xyz: 1) }
.to raise_error(ActiveForce::UnknownFieldError, /unknown field 'xyz' for Whizbang/)
end
end

describe "#find_by!" do
it "queries the client, with the SFDC field names and correctly enclosed values" do
expect(client).to receive(:query).with("SELECT #{Whizbang.fields.join ', '} FROM Whizbang__c WHERE (Id = 123) AND (Text_Label = 'foo') LIMIT 1").and_return([Restforce::Mash.new(Id: 123, text: 'foo')])
Whizbang.find_by! id: 123, text: "foo"
end

it "raises if nothing found" do
expect(client).to receive(:query).with("SELECT #{Whizbang.fields.join ', '} FROM Whizbang__c WHERE (Id = 123) AND (Text_Label = 'foo') LIMIT 1")
expect { Whizbang.find_by! id: 123, text: "foo" }.to raise_error(ActiveForce::RecordNotFound)
end

it 'raises UnknownFieldError if given invalid field' do
expect { Whizbang.find_by!(xyz: 1) }
.to raise_error(ActiveForce::UnknownFieldError, /unknown field 'xyz' for Whizbang/)
end
end

describe '.find!' do
Expand Down