diff --git a/lib/resque_bus/server.rb b/lib/resque_bus/server.rb index d26eb62..9d01bad 100644 --- a/lib/resque_bus/server.rb +++ b/lib/resque_bus/server.rb @@ -22,6 +22,125 @@ def self.included(base) } end + + class Helpers + class << self + def parse_query(query_string) + query_string = query_string.to_s.strip + has_open_brace = query_string.include?("{") + has_close_brace = query_string.include?("}") + has_multiple_lines = query_string.include?("\n") + has_colon = query_string.include?(":") + has_comma = query_string.include?(",") + has_quote = query_string.include?("\"") + + exception = nil + + # first let's see if it parses + begin + query_attributes = JSON.parse(query_string) + raise "Not a JSON Object" unless query_attributes.is_a?(Hash) + rescue StandardError => e + exception = e + end + return query_attributes unless exception + + if query_attributes + # it parsed but it's something else + if query_attributes.is_a?(Array) && query_attributes.length == 1 + # maybe it's pasted from the inputs in the web UI like queues/bus_incoming + # this is an array (of job arguments) and the first one is a JSON string + json_string = query_attributes.first + fixed = JSON.parse(json_string) rescue nil + return fixed if fixed + end + + # something else? + raise exception + end + + if !has_open_brace && !has_close_brace + # maybe they just forgot the braces + fixed = JSON.parse("{ #{query_string} }") rescue nil + return fixed if fixed + end + + if !has_open_brace + # maybe they just forgot the braces + fixed = JSON.parse("{ #{query_string}") rescue nil + return fixed if fixed + end + + if !has_close_brace + # maybe they just forgot the braces + fixed = JSON.parse("#{query_string} }") rescue nil + return fixed if fixed + end + + if !has_multiple_lines && !has_colon && !has_open_brace && !has_close_brace + # we say they just put a bus_event type here, so help them out + return {"bus_event_type" => query_string, "more_here" => true} + end + + if has_colon && !has_quote + # maybe it's some search syntax like this: field: value other: true, etc + # maybe use something like this later: https://github.com/dxwcyber/search-query-parser + + # quote all the strings, (simply) tries to avoid integers + test_query = query_string.gsub(/([a-zA-z]\w*)/,'"\0"') + if !has_comma + test_query.gsub!("\n", ",\n") + end + if !has_open_brace && !has_close_brace + test_query = "{ #{test_query} }" + end + + fixed = JSON.parse(test_query) rescue nil + return fixed if fixed + end + + if has_open_brace && has_close_brace + # maybe the whole thing is a hash output from a hash.inspect log + ruby_hash_text = query_string.clone + # https://stackoverflow.com/questions/1667630/how-do-i-convert-a-string-object-into-a-hash-object + # Transform object string symbols to quoted strings + ruby_hash_text.gsub!(/([{,]\s*):([^>\s]+)\s*=>/, '\1"\2"=>') + # Transform object string numbers to quoted strings + ruby_hash_text.gsub!(/([{,]\s*)([0-9]+\.?[0-9]*)\s*=>/, '\1"\2"=>') + # Transform object value symbols to quotes strings + ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>\s*:([^,}\s]+\s*)/, '\1\2=>"\3"') + # Transform array value symbols to quotes strings + ruby_hash_text.gsub!(/([\[,]\s*):([^,\]\s]+)/, '\1"\2"') + # fix up nil situation + ruby_hash_text.gsub!(/=>nil/, '=>null') + # Transform object string object value delimiter to colon delimiter + ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>/, '\1\2:') + fixed = JSON.parse(ruby_hash_text) rescue nil + return fixed if fixed + end + + raise exception + end + + def sort_query(query_attributes) + query_attributes.each do |key, value| + if value.is_a?(Hash) + query_attributes[key] = sort_query(value) + end + end + query_attributes.sort_by { |key| key }.to_h + end + + def query_subscriptions(app, query_attributes) + # TODO: all of this can move to method in queue-bus to replace event_display_tuples + if query_attributes + subscriptions = app.subscription_matches(query_attributes) + else + subscriptions = app.send(:subscriptions).all + end + end + end + end end end diff --git a/lib/resque_bus/server/views/bus.erb b/lib/resque_bus/server/views/bus.erb index 312b691..aba5b9b 100644 --- a/lib/resque_bus/server/views/bus.erb +++ b/lib/resque_bus/server/views/bus.erb @@ -9,6 +9,13 @@ if (agree) else return false ; } + +function setSample() { + var text = document.getElementById("query_attributes").textContent; + var textArea = document.getElementById('querytext'); + textArea.value = text; + return false; +} // --> @@ -16,27 +23,50 @@ else app_hash = {} class_hash = {} event_hash = {} + query_string = params[:query].to_s.strip + + query_attributes = nil + query_error = nil + if query_string.length > 0 + begin + query_attributes = ::ResqueBus::Server::Helpers.parse_query(query_string) + raise "Not a JSON Object" unless query_attributes.is_a?(Hash) + rescue Exception => e + query_attributes = nil + query_error = e.message + end + + if query_attributes + # sort keys for display + query_attributes = ::ResqueBus::Server::Helpers.sort_query(query_attributes) + end + end # collect each differently ::QueueBus::Application.all.each do |app| app_key = app.app_key - app_hash[app_key] ||= [] - app.event_display_tuples.each do |tuple| - class_name, queue, filters = tuple + subscriptions = ::ResqueBus::Server::Helpers.query_subscriptions(app, query_attributes) + subscriptions.each do |sub| + class_name = sub.class_name + queue = sub.queue_name + filters = sub.matcher.filters + sub_key = sub.key + if filters["bus_event_type"] event = filters["bus_event_type"] else event = "see filter" end - app_hash[app_key] << [event, class_name, queue, filters] + app_hash[app_key] ||= [] + app_hash[app_key] << [sub_key, event, class_name, queue, filters] class_hash[class_name] ||= [] - class_hash[class_name] << [app_key, event, queue, filters] + class_hash[class_name] << [app_key, sub_key, event, queue, filters] event_hash[event] ||= [] - event_hash[event] << [app_key, class_name, queue, filters] + event_hash[event] << [app_key, sub_key, class_name, queue, filters] end end @@ -53,14 +83,14 @@ else form = "" if button text, url = button - form = "
" + form = "" end if !val out = "Enter JSON of an event to see applicable subscriptions.
++<% end %> +<% if query_attributes %> +<%= + h(query_error.strip) + %>
+<%= + h(JSON.pretty_generate(query_attributes).strip) + %>
The apps below have registered the given classes and queues.
| App Key | +Subscription Key | Event Type | Class Name | Queue | Filters |
|---|
@@ -108,6 +167,7 @@ else