refactor: extract custom attribute methods from FilterService (#13743)
- Extracted 6 custom attribute methods (`custom_attribute_query`, `attribute_model`, `attribute_data_type`, `build_custom_attr_query`, `custom_attribute`, `not_in_custom_attr_query`) into a new `Filters::CustomAttributeFilterHelper` module. - Added an inline `rubocop:disable` for the intentional `Lint/ShadowedException` in `coerce_lt_gt_value` — `Date::Error` is a subclass of `ArgumentError`, but both are listed explicitly for clarity. ## Why `app/services/filters/` The existing `Filters::FilterHelper` lives in `app/helpers/filters/`, but that location triggers `Rails/HelperInstanceVariable` for any module that uses instance variables. The extracted methods share state with `FilterService` via instance variables (`@attribute_key`, `@account`, `@custom_attribute`, etc.), so placing them in `app/helpers/` would require a cop disable. `app/services/filters/` is a better fit because: - The module is a service mixin, not a view helper — it's only included by `FilterService` and its subclasses (`Conversations::FilterService`, `Contacts::FilterService`, `AutomationRules::ConditionsFilterService`). - It sits alongside the services that use it. - No cop disables needed. --------- Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@ require 'json'
|
|||||||
|
|
||||||
class FilterService
|
class FilterService
|
||||||
include Filters::FilterHelper
|
include Filters::FilterHelper
|
||||||
|
include Filters::CustomAttributeFilterHelper
|
||||||
include CustomExceptions::CustomFilter
|
include CustomExceptions::CustomFilter
|
||||||
|
|
||||||
ATTRIBUTE_MODEL = 'conversation_attribute'.freeze
|
ATTRIBUTE_MODEL = 'conversation_attribute'.freeze
|
||||||
@@ -137,26 +138,8 @@ class FilterService
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_attribute_query(query_hash, custom_attribute_type, current_index)
|
|
||||||
@attribute_key = query_hash[:attribute_key]
|
|
||||||
@custom_attribute_type = custom_attribute_type
|
|
||||||
attribute_data_type
|
|
||||||
return '' if @custom_attribute.blank?
|
|
||||||
|
|
||||||
build_custom_attr_query(query_hash, current_index)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def attribute_model
|
|
||||||
@attribute_model = @custom_attribute_type.presence || self.class::ATTRIBUTE_MODEL
|
|
||||||
end
|
|
||||||
|
|
||||||
def attribute_data_type
|
|
||||||
attribute_type = custom_attribute(@attribute_key, @account, attribute_model).try(:attribute_display_type)
|
|
||||||
@attribute_data_type = self.class::ATTRIBUTE_TYPES[attribute_type]
|
|
||||||
end
|
|
||||||
|
|
||||||
def standard_attribute_data_type(attribute_key)
|
def standard_attribute_data_type(attribute_key)
|
||||||
@filters.each_value do |section|
|
@filters.each_value do |section|
|
||||||
return section.dig(attribute_key, 'data_type') if section.is_a?(Hash) && section.key?(attribute_key)
|
return section.dig(attribute_key, 'data_type') if section.is_a?(Hash) && section.key?(attribute_key)
|
||||||
@@ -173,44 +156,10 @@ class FilterService
|
|||||||
else
|
else
|
||||||
raise CustomExceptions::CustomFilter::InvalidValue.new(attribute_name: attribute_key)
|
raise CustomExceptions::CustomFilter::InvalidValue.new(attribute_name: attribute_key)
|
||||||
end
|
end
|
||||||
rescue Date::Error, ArgumentError, FloatDomainError, TypeError
|
rescue ArgumentError, FloatDomainError, TypeError
|
||||||
raise CustomExceptions::CustomFilter::InvalidValue.new(attribute_name: attribute_key)
|
raise CustomExceptions::CustomFilter::InvalidValue.new(attribute_name: attribute_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_custom_attr_query(query_hash, current_index)
|
|
||||||
filter_operator_value = filter_operation(query_hash, current_index)
|
|
||||||
query_operator = query_hash[:query_operator]
|
|
||||||
table_name = attribute_model == 'conversation_attribute' ? 'conversations' : 'contacts'
|
|
||||||
|
|
||||||
query = if attribute_data_type == 'text'
|
|
||||||
ActiveRecord::Base.sanitize_sql_array(
|
|
||||||
["LOWER(#{table_name}.custom_attributes ->> ?)::#{attribute_data_type} #{filter_operator_value} #{query_operator} ", @attribute_key]
|
|
||||||
)
|
|
||||||
else
|
|
||||||
ActiveRecord::Base.sanitize_sql_array(
|
|
||||||
["(#{table_name}.custom_attributes ->> ?)::#{attribute_data_type} #{filter_operator_value} #{query_operator} ", @attribute_key]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
query + not_in_custom_attr_query(table_name, query_hash, attribute_data_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
def custom_attribute(attribute_key, account, custom_attribute_type)
|
|
||||||
current_account = account || Current.account
|
|
||||||
attribute_model = custom_attribute_type.presence || self.class::ATTRIBUTE_MODEL
|
|
||||||
@custom_attribute = current_account.custom_attribute_definitions.where(
|
|
||||||
attribute_model: attribute_model
|
|
||||||
).find_by(attribute_key: attribute_key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def not_in_custom_attr_query(table_name, query_hash, attribute_data_type)
|
|
||||||
return '' unless query_hash[:filter_operator] == 'not_equal_to'
|
|
||||||
|
|
||||||
ActiveRecord::Base.sanitize_sql_array(
|
|
||||||
[" OR (#{table_name}.custom_attributes ->> ?)::#{attribute_data_type} IS NULL ", @attribute_key]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def equals_to_filter_string(filter_operator, current_index)
|
def equals_to_filter_string(filter_operator, current_index)
|
||||||
return "IN (:value_#{current_index})" if filter_operator == 'equal_to'
|
return "IN (:value_#{current_index})" if filter_operator == 'equal_to'
|
||||||
|
|
||||||
|
|||||||
55
app/services/filters/custom_attribute_filter_helper.rb
Normal file
55
app/services/filters/custom_attribute_filter_helper.rb
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
module Filters::CustomAttributeFilterHelper
|
||||||
|
def custom_attribute_query(query_hash, custom_attribute_type, current_index)
|
||||||
|
@attribute_key = query_hash[:attribute_key]
|
||||||
|
@custom_attribute_type = custom_attribute_type
|
||||||
|
attribute_data_type
|
||||||
|
return '' if @custom_attribute.blank?
|
||||||
|
|
||||||
|
build_custom_attr_query(query_hash, current_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def attribute_model
|
||||||
|
@attribute_model = @custom_attribute_type.presence || self.class::ATTRIBUTE_MODEL
|
||||||
|
end
|
||||||
|
|
||||||
|
def attribute_data_type
|
||||||
|
attribute_type = custom_attribute(@attribute_key, @account, attribute_model).try(:attribute_display_type)
|
||||||
|
@attribute_data_type = self.class::ATTRIBUTE_TYPES[attribute_type]
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_custom_attr_query(query_hash, current_index)
|
||||||
|
filter_operator_value = filter_operation(query_hash, current_index)
|
||||||
|
query_operator = query_hash[:query_operator]
|
||||||
|
table_name = attribute_model == 'conversation_attribute' ? 'conversations' : 'contacts'
|
||||||
|
|
||||||
|
query = if attribute_data_type == 'text'
|
||||||
|
ActiveRecord::Base.sanitize_sql_array(
|
||||||
|
["LOWER(#{table_name}.custom_attributes ->> ?)::#{attribute_data_type} #{filter_operator_value} #{query_operator} ", @attribute_key]
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ActiveRecord::Base.sanitize_sql_array(
|
||||||
|
["(#{table_name}.custom_attributes ->> ?)::#{attribute_data_type} #{filter_operator_value} #{query_operator} ", @attribute_key]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
query + not_in_custom_attr_query(table_name, query_hash, attribute_data_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_attribute(attribute_key, account, custom_attribute_type)
|
||||||
|
current_account = account || Current.account
|
||||||
|
attribute_model = custom_attribute_type.presence || self.class::ATTRIBUTE_MODEL
|
||||||
|
@custom_attribute = current_account.custom_attribute_definitions.where(
|
||||||
|
attribute_model: attribute_model
|
||||||
|
).find_by(attribute_key: attribute_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def not_in_custom_attr_query(table_name, query_hash, attribute_data_type)
|
||||||
|
return '' unless query_hash[:filter_operator] == 'not_equal_to'
|
||||||
|
|
||||||
|
ActiveRecord::Base.sanitize_sql_array(
|
||||||
|
[" OR (#{table_name}.custom_attributes ->> ?)::#{attribute_data_type} IS NULL ", @attribute_key]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
3
app/views/fields/confirmed_at_field/_show.html.erb
Normal file
3
app/views/fields/confirmed_at_field/_show.html.erb
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<% if field.data %>
|
||||||
|
<%= field.datetime %>
|
||||||
|
<% end %>
|
||||||
Reference in New Issue
Block a user