Skip to content
Draft
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
20 changes: 20 additions & 0 deletions spec/api/http_api_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,26 @@ describe LavinMQ::HTTP::Server do
end
end

it "should sort by column with nil values" do
with_http_server do |http, s|
vhost = s.vhosts["/"]
vhost.declare_exchange("no-policy-ex", "direct", durable: false, auto_delete: false)
vhost.declare_exchange("has-policy-ex", "direct", durable: false, auto_delete: false)
definitions = {"federation-upstream" => JSON::Any.new("test")} of String => JSON::Any
vhost.add_policy("test-policy", "^has-policy", "exchanges", definitions, 0_i8)

response = http.get("/api/exchanges/%2F?page=1&sort=policy")
response.status_code.should eq 200
items = JSON.parse(response.body).as_h["items"].as_a
policies = items.map { |i| i["policy"]?.try(&.as_s?) }
non_nil = policies.compact
non_nil.should eq non_nil.sort

response = http.get("/api/exchanges/%2F?page=1&sort=policy&sort_reverse=true")
response.status_code.should eq 200
end
end

it "should sort results by nested keys" do
stats_interval = LavinMQ::Config.instance.stats_interval
LavinMQ::Config.instance.stats_interval = 1000
Expand Down
35 changes: 22 additions & 13 deletions src/lavinmq/http/controller.cr
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,7 @@ module LavinMQ
if sort_by = context.request.query_params.fetch("sort", nil).try &.split(".")
sorted_items = all_items.to_a
begin
if first_element = sorted_items.first?
case v = dig(first_element, sort_by)
when Number
sorted_items.sort_by! { |i| dig(i, sort_by).as(Number) }
when String
sorted_items.sort_by! { |i| dig(i, sort_by).as(String).downcase }
when QueueState
sorted_items.sort_by! { |i| dig(i, sort_by).as(QueueState) }
else
bad_request(context, "Can't sort on type #{v.class}")
end
end
sort_by_key!(sorted_items, sort_by, context)
rescue KeyError | TypeCastError
bad_request(context, "Sort key #{sort_by.join(".")} is not valid")
end
Expand All @@ -85,12 +74,32 @@ module LavinMQ
end
end

private def sort_by_key!(sorted_items, sort_by, context)
# Find first non-nil value to determine the sort type
sample_value = sorted_items.each do |item|
v = dig(item, sort_by)
break v unless v.nil?
end
case sample_value
when Number
sorted_items.sort_by! { |i| dig(i, sort_by).as?(Number) || 0 }
when String
sorted_items.sort_by! { |i| (dig(i, sort_by).as?(String) || "").downcase }
when QueueState
sorted_items.sort_by! { |i| dig(i, sort_by).as?(QueueState) || QueueState::Closed }
when Nil
nil # all values are nil, nothing to sort
else
bad_request(context, "Can't sort on type #{sample_value.class}")
end
end

private def dig(tuple : NamedTuple, keys : Array(String))
if keys.size > 1
nt = tuple[keys.first].as?(NamedTuple) || raise KeyError.new("'#{keys.first}' is not a nested tuple")
dig(nt, keys[1..])
else
tuple[keys.first]? || 0
tuple[keys.first]?
end
end

Expand Down
Loading