-
Notifications
You must be signed in to change notification settings - Fork 900
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
api sort on sql friendly virtual attributes #13409
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,20 +160,24 @@ def sort_params(klass) | |
orders = String(params['sort_order']).split(",") | ||
options = String(params['sort_options']).split(",") | ||
params['sort_by'].split(",").zip(orders).collect do |attr, order| | ||
if klass.attribute_method?(attr) || klass.method_defined?(attr) || attr == klass.primary_key | ||
sort_directive(attr, order, options) | ||
if klass.virtual_attribute?(attr) && !klass.attribute_supported_by_sql?(attr) | ||
raise BadRequestError, "#{klass.name} cannot be sorted by #{attr}" | ||
elsif klass.attribute_supported_by_sql?(attr) | ||
sort_directive(klass, attr, order, options) | ||
else | ||
raise BadRequestError, "#{attr} is not a valid attribute for #{klass.name}" | ||
end | ||
end.compact | ||
end | ||
|
||
def sort_directive(attr, order, options) | ||
sort_item = attr | ||
sort_item = "LOWER(#{sort_item})" if options.map(&:downcase).include?("ignore_case") | ||
sort_item << " ASC" if order && order.downcase.start_with?("asc") | ||
sort_item << " DESC" if order && order.downcase.start_with?("desc") | ||
sort_item | ||
def sort_directive(klass, attr, order, options) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whoa - now THAT is what I'm talking about |
||
if options.map(&:downcase).include?("ignore_case") && order | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit, but I noticed all three branches had the same code, and two of them contained the same condition. I don't know if something like this is any better: arel = klass.arel_attribute(attr)
if order
if options.map(&:downcase).include?("ignore_case")
arel = arel.lower
end
arel = arel.asc if order.downcase == "asc"
arel = arel.desc if order.downcase == "desc" # more verbose, but lower cognitive load than dynamic method sending
end
arel There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think So you can skip the entire line: |
||
klass.arel_attribute(attr).lower.public_send(order.downcase) | ||
elsif order | ||
klass.arel_attribute(attr).public_send(order.downcase) | ||
else | ||
klass.arel_attribute(attr) | ||
end | ||
end | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,34 @@ def create_vms_by_name(names) | |
expect_query_result(:vms, 2, 2) | ||
expect_result_resources_to_match_hash([{"name" => "redhat_vm"}, {"name" => "vmware_vm"}]) | ||
end | ||
|
||
it 'supports sql friendly virtual attributes' do | ||
host_foo = FactoryGirl.create(:host, :name => 'foo') | ||
host_bar = FactoryGirl.create(:host, :name => 'bar') | ||
vm_foo = FactoryGirl.create(:vm, :name => 'vm_foo') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you try |
||
vm_bar = FactoryGirl.create(:vm, :name => 'vm_bar') | ||
host_foo.vms << vm_foo | ||
host_bar.vms << vm_bar | ||
|
||
run_get vms_url, :sort_by => 'host_name', :sort_order => 'desc', :expand => 'resources' | ||
|
||
expect_query_result(:vms, 2, 2) | ||
expect_result_resources_to_match_hash([{'name' => 'vm_foo'}, {'name' => 'vm_bar'}]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the outside, it looks like this query returned your objects in the order you created them. I think to prove that they were ordered successfully it may be necessary to create three objects out of order, so that you can see that the result put them into some order in a way that couldn't be an accident. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jntullo can you create another PR to enhance the tests as per @imtayadeway ? Thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @abellotti will do! |
||
end | ||
|
||
it 'does not support non sql friendly virtual attributes' do | ||
FactoryGirl.create(:vm) | ||
|
||
run_get vms_url, :sort_by => 'aggressive_recommended_mem', :sort_order => 'asc' | ||
|
||
expected = { | ||
'error' => a_hash_including( | ||
'message' => 'Vm cannot be sorted by aggressive_recommended_mem' | ||
) | ||
} | ||
expect(response).to have_http_status(:bad_request) | ||
expect(response.parsed_body).to include(expected) | ||
end | ||
end | ||
|
||
describe "Filtering vms" do | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can probably drop
klass.virtual_attribute?(attr)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heck, can you just do:
(I probably missed something obvious here - e.g.: may need
attr == klass.primary_key
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kbrock would definitely be more simplistic, but I thought it might be a good idea to have both the
is not a valid attribute
andcannot be sorted by
messages separate for clarificationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
I don't think this part is necessary:
@abellotti you ok just using
klass.attribute_supported_by_sql?(attr)
instead and dropping the ever neat previous one?Did we need to support any ruby method or only fields?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the simpler path is more elegant, I like what @jntullo did to have the 2 different error messages. I'm ok with this approach here as this is low count iteration.