class Insights::API::Common::Filter
Constants
- ALL_COMPARISON_KEYWORDS
- INTEGER_COMPARISON_KEYWORDS
- STRING_COMPARISON_KEYWORDS
Attributes
api_doc_definition[R]
apply[R]
arel_table[R]
extra_filterable_attributes[R]
model[R]
query[W]
Public Class Methods
association_attribute_properties(api_doc_definitions, raw_filter)
click to toggle source
# File lib/insights/api/common/filter.rb, line 88 def self.association_attribute_properties(api_doc_definitions, raw_filter) return {} if raw_filter.blank? association_attributes = compact_filter(raw_filter).keys.select { |key| key.include?('.') }.compact.uniq return {} if association_attributes.blank? association_attributes.each_with_object({}) do |key, hash| association, attr = key.split('.') hash[key] = api_doc_definitions[association.singularize.classify].properties[attr.to_s] end end
compact_filter(filter)
click to toggle source
Compact filters to support association filtering
Input: {"source_type"=>{"name"=>{"eq"=>"rhev"}}} Output: {"source_type.name"=>{"eq"=>"rhev"}} Input: {"source_type"=>{"name"=>{"eq"=>["openstack", "openshift"]}}} Output: {"source_type.name"=>{"eq"=>["openstack", "openshift"]}}
# File lib/insights/api/common/filter.rb, line 65 def self.compact_filter(filter) result = {} return result if filter.blank? return filter unless filter.kind_of?(Hash) || filter.kind_of?(ActionController::Parameters) filter = Hash(filter.permit!) if filter.kind_of?(ActionController::Parameters) filter.each do |ak, av| if av.kind_of?(Hash) av.each do |atk, atv| if !ALL_COMPARISON_KEYWORDS.include?(atk) result["#{ak}.#{atk}"] = atv else result[ak] = av end end else result[ak] = av end end result end
new(model, raw_filter, api_doc_definition, extra_filterable_attributes = {})
click to toggle source
Instantiates a new Filter
object
Parameters:¶ ↑
- model
-
An AR model that acts as the base collection to be filtered
- raw_filter
-
The filter from the request query string
api_doc_definition
-
The documented object definition from the OpenAPI doc
extra_filterable_attributes
-
Attributes that can be used for filtering but are not documented in the OpenAPI doc. Something like `{“undocumented_column” => {“type” => “string”}}`
Returns:¶ ↑
A new Filter
object, call apply
to get the filtered set of results.
# File lib/insights/api/common/filter.rb, line 26 def initialize(model, raw_filter, api_doc_definition, extra_filterable_attributes = {}) @raw_filter = raw_filter @api_doc_definition = api_doc_definition @arel_table = model.arel_table @extra_filterable_attributes = extra_filterable_attributes @model = model end
Private Class Methods
build_filtered_scope(scope, api_version, klass_name, filter)
click to toggle source
# File lib/insights/api/common/filter.rb, line 180 def self.build_filtered_scope(scope, api_version, klass_name, filter) return scope unless filter openapi_doc = ::Insights::API::Common::OpenApi::Docs.instance[api_version] openapi_schema_name, = ::Insights::API::Common::GraphQL::Generator.openapi_schema(openapi_doc, klass_name) action_parameters = ActionController::Parameters.new(filter) definitions = openapi_doc.definitions association_attribute_properties = association_attribute_properties(definitions, action_parameters) new(scope, action_parameters, definitions[openapi_schema_name], association_attribute_properties).apply end
Public Instance Methods
query()
click to toggle source
# File lib/insights/api/common/filter.rb, line 34 def query @query ||= filter_associations.present? ? model.left_outer_joins(filter_associations) : model end
Private Instance Methods
add_filter(requested_comparator, allowed_comparators, key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 165 def add_filter(requested_comparator, allowed_comparators, key, value) return unless attribute = attribute_for_key(key) type = determine_string_attribute_type(attribute) if requested_comparator.in?(["nil", "not_nil"]) send("comparator_#{requested_comparator}", key, value) elsif requested_comparator.in?(allowed_comparators) value = parse_datetime(value) if type == :datetime return if value.nil? send("comparator_#{requested_comparator}", key, value) else errors << "unsupported #{type} comparator: #{requested_comparator}" end end
arel_lower(key)
click to toggle source
# File lib/insights/api/common/filter.rb, line 223 def arel_lower(key) Arel::Nodes::NamedFunction.new("LOWER", [model_arel_attribute(key)]) end
attribute_for_key(key)
click to toggle source
# File lib/insights/api/common/filter.rb, line 137 def attribute_for_key(key) attribute = api_doc_definition.properties[key.to_s] attribute ||= extra_filterable_attributes[key.to_s] return attribute if attribute errors << "found unpermitted parameter: #{key}" nil end
comparator_contains(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 227 def comparator_contains(key, value) return value.each { |v| comparator_contains(key, v) } if value.kind_of?(Array) self.query = query.where(model_arel_attribute(key).matches("%#{query.sanitize_sql_like(value)}%", nil, true)) end
comparator_contains_i(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 233 def comparator_contains_i(key, value) return value.each { |v| comparator_contains_i(key, v) } if value.kind_of?(Array) self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches("%#{query.sanitize_sql_like(value.downcase)}%", nil, true))) end
comparator_ends_with(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 247 def comparator_ends_with(key, value) self.query = query.where(model_arel_attribute(key).matches("%#{query.sanitize_sql_like(value)}", nil, true)) end
comparator_ends_with_i(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 251 def comparator_ends_with_i(key, value) self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches("%#{query.sanitize_sql_like(value.downcase)}", nil, true))) end
comparator_eq(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 255 def comparator_eq(key, value) self.query = query.where(model_arel_attribute(key).eq_any(Array(value))) end
comparator_eq_i(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 269 def comparator_eq_i(key, value) values = Array(value).map { |v| query.sanitize_sql_like(v.downcase) } self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches_any(values))) end
comparator_gt(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 275 def comparator_gt(key, value) self.query = query.where(model_arel_attribute(key).gt(value)) end
comparator_gte(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 279 def comparator_gte(key, value) self.query = query.where(model_arel_attribute(key).gteq(value)) end
comparator_lt(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 283 def comparator_lt(key, value) self.query = query.where(model_arel_attribute(key).lt(value)) end
comparator_lte(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 287 def comparator_lte(key, value) self.query = query.where(model_arel_attribute(key).lteq(value)) end
comparator_nil(key, _value = nil)
click to toggle source
# File lib/insights/api/common/filter.rb, line 291 def comparator_nil(key, _value = nil) self.query = query.where(model_arel_attribute(key).eq(nil)) end
comparator_not_eq(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 259 def comparator_not_eq(key, value) self.query = query.where.not(model_arel_attribute(key).eq_any(Array(value))) end
comparator_not_eq_i(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 263 def comparator_not_eq_i(key, value) values = Array(value).map { |v| query.sanitize_sql_like(v.downcase) } self.query = query.where.not(model_arel_table(key).grouping(arel_lower(key).matches_any(values))) end
comparator_not_nil(key, _value = nil)
click to toggle source
# File lib/insights/api/common/filter.rb, line 295 def comparator_not_nil(key, _value = nil) self.query = query.where.not(model_arel_attribute(key).eq(nil)) end
comparator_starts_with(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 239 def comparator_starts_with(key, value) self.query = query.where(model_arel_attribute(key).matches("#{query.sanitize_sql_like(value)}%", nil, true)) end
comparator_starts_with_i(key, value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 243 def comparator_starts_with_i(key, value) self.query = query.where(model_arel_table(key).grouping(arel_lower(key).matches("#{query.sanitize_sql_like(value.downcase)}%", nil, true))) end
determine_string_attribute_type(attribute)
click to toggle source
# File lib/insights/api/common/filter.rb, line 145 def determine_string_attribute_type(attribute) return :timestamp if attribute["format"] == "date-time" return :integer if attribute["pattern"] == /^\d+$/ :string end
errors()
click to toggle source
# File lib/insights/api/common/filter.rb, line 151 def errors @errors ||= [] end
filter_associations()
click to toggle source
# File lib/insights/api/common/filter.rb, line 125 def filter_associations return nil if @raw_filter.blank? @filter_associations ||= begin self.class.compact_filter(@raw_filter).keys.collect do |key| next unless key.include?('.') key.split('.').first.to_sym end.compact.uniq end end
integer(k, val)
click to toggle source
# File lib/insights/api/common/filter.rb, line 213 def integer(k, val) if val.kind_of?(Hash) val.each do |comparator, value| add_filter(comparator, INTEGER_COMPARISON_KEYWORDS, k, value) end else add_filter("eq", INTEGER_COMPARISON_KEYWORDS, k, val) end end
key_model_attribute(key)
click to toggle source
# File lib/insights/api/common/filter.rb, line 106 def key_model_attribute(key) if key.include?('.') association, attr = key.split('.') [association.classify.constantize, attr] else [model, key] end end
model_arel_attribute(key)
click to toggle source
# File lib/insights/api/common/filter.rb, line 115 def model_arel_attribute(key) key_model, attr = key_model_attribute(key) key_model.arel_attribute(attr) end
model_arel_table(key)
click to toggle source
# File lib/insights/api/common/filter.rb, line 120 def model_arel_table(key) key_model, attr = key_model_attribute(key) key_model.arel_table end
parse_datetime(value)
click to toggle source
# File lib/insights/api/common/filter.rb, line 204 def parse_datetime(value) return value.collect { |i| parse_datetime(i, ) } if value.kind_of?(Array) DateTime.parse(value) rescue ArgumentError errors << "invalid timestamp: #{value}" return nil end
string(k, val)
click to toggle source
# File lib/insights/api/common/filter.rb, line 155 def string(k, val) if val.kind_of?(Hash) val.each do |comparator, value| add_filter(comparator, STRING_COMPARISON_KEYWORDS, k, value) end else add_filter("eq", STRING_COMPARISON_KEYWORDS, k, val) end end
timestamp(k, val)
click to toggle source
# File lib/insights/api/common/filter.rb, line 194 def timestamp(k, val) if val.kind_of?(Hash) val.each do |comparator, value| add_filter(comparator, INTEGER_COMPARISON_KEYWORDS, k, value) end else add_filter("eq", INTEGER_COMPARISON_KEYWORDS, k, val) end end