From d979bd399f58ebfc0020260c1abbc36cf1a60801 Mon Sep 17 00:00:00 2001 From: Claire McQuin Date: Thu, 18 Dec 2014 17:10:49 -0800 Subject: Make search with filtering match partial_search. * Updated search to use argument parameters. --- lib/chef/search/query.rb | 146 +++++++++++++++++++---------------------------- 1 file changed, 60 insertions(+), 86 deletions(-) (limited to 'lib/chef/search/query.rb') diff --git a/lib/chef/search/query.rb b/lib/chef/search/query.rb index cc43efe1b1..531ad53990 100644 --- a/lib/chef/search/query.rb +++ b/lib/chef/search/query.rb @@ -16,14 +16,12 @@ # limitations under the License. # + require 'chef/config' -require 'uri' -require 'chef/rest' -require 'chef/node' -require 'chef/role' -require 'chef/data_bag' -require 'chef/data_bag_item' require 'chef/exceptions' +require 'chef/rest' + +require 'uri' class Chef class Search @@ -32,37 +30,45 @@ class Chef attr_accessor :rest def initialize(url=nil) - @rest = Chef::REST.new(url ||Chef::Config[:chef_server_url]) + @rest = Chef::REST.new(url || Chef::Config[:chef_server_url]) end - - # This search is only kept for backwards compatibility, since the results of the - # new filtered search method will be in a slightly different format - def partial_search(type, query='*:*', *args, &block) - Chef::Log.warn("DEPRECATED: The 'partial_search' api is deprecated, please use the search api with 'filter_result'") - # accept both types of args - if args.length == 1 && args[0].is_a?(Hash) - args_hash = args[0].dup - # partial_search implemented in the partial search cookbook uses the - # arg hash :keys instead of :filter_result to filter returned data - args_hash[:filter_result] = args_hash[:keys] + # Backwards compatability for cookbooks. + # This can be removed in Chef > 12. + def partial_search(type, query="*:*", *args, &block) + Chef::Log.warn(<<-WARNDEP) +DEPRECATED: The 'partial_search' API is deprecated and will be removed in +future releases. Please use 'search' with a :filter_result argument to get +partial search data. +WARNDEP + + # Users can pass either a hash or a list of arguments. + if args.length == 1 && args.first.is_a?(Hash) + args_h = args.first + rows = args_h[:rows] + start = args_h[:start] + sort = args_h[:sort] + keys = args_h[:keys] else - args_hash = {} - args_hash[:sort] = args[0] if args.length >= 1 - args_hash[:start] = args[1] if args.length >= 2 - args_hash[:rows] = args[2] if args.length >= 3 + rows = args[0] + start = args[1] + sort = args[2] + keys = args[3] end - unless block.nil? - raw_results = search(type,query,args_hash) + # Set defaults. Otherwise, search may receive nil arguments. + # We could pass nil arguments along to search, assuming that default values will be + # filled in later. However, since this is a deprecated method, it will be easier to + # do a little more work here than to change search in the future. + rows ||= 1000 + start ||= 0 + sort ||= 'X_CHEF_id_CHEF_X asc' + + unless block.nil? #@TODO: IS THIS CORRECT? THIS DOESN'T SEEM CORRECT. WHY DO WE EVEN NEED IT? + search(type, query, filter_result: keys, rows: rows, start: start, sort: sort) else - raw_results = search(type,query,args_hash,&block) - end - results = Array.new - raw_results[0].each do |r| - results << r["data"] + search(type, query, filter_result: keys.dup, rows: rows, start: start, sort: sort, &block) end - return results end # @@ -85,89 +91,57 @@ class Chef # an example of the returned json may be: # {"ip_address":"127.0.0.1", "ruby_version": "1.9.3"} # - def search(type, query='*:*', *args, &block) + def search(type, query="*:*", filter_result:nil, rows:1000, start:0, sort:'X_CHEF_id_CHEF_X asc', &block) validate_type(type) - validate_args(args) - scrubbed_args = Hash.new + query_string = create_query_string(type, query, rows, start, sort) + response = call_rest_service(query_string, filter_result) - # argify everything - if args[0].kind_of?(Hash) - scrubbed_args = args[0] + if block + response["rows"].each { |row| block.call(row) if row } + if response["start"] + response["rows"].size < response["total"] + start = response["start"] + rows + search(type, query, filter_result: filter_result, rows: rows, start: start, sort: sort, &block) + end + true else - # This api will be deprecated in a future release - scrubbed_args = { :sort => args[0], :start => args[1], :rows => args[2] } + [ + response["rows"], + response["start"], + response["total"] + ] end - - # set defaults, if they haven't been set yet. - scrubbed_args[:sort] ||= 'X_CHEF_id_CHEF_X asc' - scrubbed_args[:start] ||= 0 - scrubbed_args[:rows] ||= 1000 - - do_search(type, query, scrubbed_args, &block) - end - - def list_indexes - @rest.get_rest("search") end private def validate_type(t) unless t.kind_of?(String) || t.kind_of?(Symbol) msg = "Invalid search object type #{t.inspect} (#{t.class}), must be a String or Symbol." + - "Useage: search(:node, QUERY, [OPTIONAL_ARGS])" + + "Useage: search(:node, QUERY[, OPTIONAL_ARGS])" + " `knife search environment QUERY (options)`" raise Chef::Exceptions::InvalidSearchQuery, msg end end - def validate_args(a) - max_args = 3 - raise Chef::Exceptions::InvalidSearchQuery, "Too many arguments! (#{a.size} for <= #{max_args})" if a.size > max_args + def create_query_string(type, query, rows, start, sort) + "search/#{type}?q=#{escape(query)}&sort=#{escape(sort)}&start=#{escape(start)}&rows=#{escape(rows)}" end def escape(s) s && URI.escape(s.to_s) end - # new search api that allows for a cleaner implementation of things like return filters - # (formerly known as 'partial search'). - # Also args should never be nil, but that is required for Ruby 1.8 compatibility - def do_search(type, query="*:*", args=nil, &block) - query_string = create_query_string(type, query, args) - response = call_rest_service(query_string, args) - unless block.nil? - response["rows"].each { |rowset| block.call(rowset) unless rowset.nil?} - unless (response["start"] + response["rows"].length) >= response["total"] - args[:start] = response["start"] + args[:rows] - do_search(type, query, args, &block) - end - true + def call_rest_service(query_string, filter_result) + if filter_result + response = rest.post_rest(query_string, filter_result) + response['rows'].map! { |row| row['data'] } else - [ response["rows"], response["start"], response["total"] ] + response = rest.get_rest(query_string) end - end - - # create the full rest url string - def create_query_string(type, query, args) - # create some default variables just so we don't break backwards compatibility - sort = args[:sort] - start = args[:start] - rows = args[:rows] - return "search/#{type}?q=#{escape(query)}&sort=#{escape(sort)}&start=#{escape(start)}&rows=#{escape(rows)}" + response end - def call_rest_service(query_string, args) - if args.key?(:filter_result) - response = @rest.post_rest(query_string, args[:filter_result]) - response_rows = response['rows'].map { |row| row['data'] } - else - response = @rest.get_rest(query_string) - response_rows = response['rows'] - end - return response - end end end end -- cgit v1.2.1