feat: Attribute changed filter for automations (#4621)
This commit is contained in:
parent
267252d13a
commit
56f668db6b
4 changed files with 148 additions and 6 deletions
|
@ -9,6 +9,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||
def create
|
||||
@automation_rule = Current.account.automation_rules.new(automation_rules_permit)
|
||||
@automation_rule.actions = params[:actions]
|
||||
@automation_rule.conditions = params[:conditions]
|
||||
|
||||
render json: { error: @automation_rule.errors.messages }, status: :unprocessable_entity and return unless @automation_rule.valid?
|
||||
|
||||
|
@ -31,9 +32,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||
|
||||
def update
|
||||
ActiveRecord::Base.transaction do
|
||||
@automation_rule.update!(automation_rules_permit)
|
||||
@automation_rule.actions = params[:actions] if params[:actions]
|
||||
@automation_rule.save!
|
||||
automation_rule_update
|
||||
process_attachments
|
||||
|
||||
rescue StandardError => e
|
||||
|
@ -67,6 +66,13 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||
|
||||
private
|
||||
|
||||
def automation_rule_update
|
||||
@automation_rule.update!(automation_rules_permit)
|
||||
@automation_rule.actions = params[:actions] if params[:actions]
|
||||
@automation_rule.conditions = params[:conditions] if params[:conditions]
|
||||
@automation_rule.save!
|
||||
end
|
||||
|
||||
def automation_rules_permit
|
||||
params.permit(
|
||||
:name, :description, :event_name, :account_id, :active,
|
||||
|
|
|
@ -4,11 +4,12 @@ class AutomationRuleListener < BaseListener
|
|||
|
||||
conversation = event_obj.data[:conversation]
|
||||
account = conversation.account
|
||||
changed_attributes = event_obj.data[:changed_attributes]
|
||||
|
||||
return unless rule_present?('conversation_updated', account)
|
||||
|
||||
@rules.each do |rule|
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation).perform
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
|
||||
AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
|
||||
end
|
||||
end
|
||||
|
@ -18,11 +19,12 @@ class AutomationRuleListener < BaseListener
|
|||
|
||||
conversation = event_obj.data[:conversation]
|
||||
account = conversation.account
|
||||
changed_attributes = event_obj.data[:changed_attributes]
|
||||
|
||||
return unless rule_present?('conversation_created', account)
|
||||
|
||||
@rules.each do |rule|
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation).perform
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
|
||||
::AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
|
||||
end
|
||||
end
|
||||
|
@ -32,11 +34,13 @@ class AutomationRuleListener < BaseListener
|
|||
|
||||
message = event_obj.data[:message]
|
||||
account = message.try(:account)
|
||||
changed_attributes = event_obj.data[:changed_attributes]
|
||||
|
||||
return unless rule_present?('message_created', account)
|
||||
|
||||
@rules.each do |rule|
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, message.conversation, { message: message }).perform
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, message.conversation,
|
||||
{ message: message, changed_attributes: changed_attributes }).perform
|
||||
::AutomationRules::ActionService.new(rule, account, message.conversation).perform if conditions_match.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,18 +11,24 @@ class AutomationRules::ConditionsFilterService < FilterService
|
|||
file = File.read('./lib/filters/filter_keys.json')
|
||||
@filters = JSON.parse(file)
|
||||
@options = options
|
||||
@changed_attributes = options[:changed_attributes]
|
||||
end
|
||||
|
||||
def perform
|
||||
@conversation_filters = @filters['conversations']
|
||||
@contact_filters = @filters['contacts']
|
||||
@message_filters = @filters['messages']
|
||||
@attribute_changed_query_filter = []
|
||||
|
||||
@rule.conditions.each_with_index do |query_hash, current_index|
|
||||
@attribute_changed_query_filter << query_hash and next if query_hash['filter_operator'] == 'attribute_changed'
|
||||
|
||||
apply_filter(query_hash, current_index)
|
||||
end
|
||||
|
||||
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||
records = perform_attribute_changed_filter(records) if @attribute_changed_query_filter.any?
|
||||
|
||||
records.any?
|
||||
end
|
||||
|
||||
|
@ -43,6 +49,37 @@ class AutomationRules::ConditionsFilterService < FilterService
|
|||
end
|
||||
end
|
||||
|
||||
# If attribute_changed type filter is present perform this against array
|
||||
def perform_attribute_changed_filter(records)
|
||||
@attribute_changed_records = []
|
||||
current_attribute_changed_record = base_relation
|
||||
filter_based_on_attribute_change(records, current_attribute_changed_record)
|
||||
|
||||
@attribute_changed_records.uniq
|
||||
end
|
||||
|
||||
# Loop through attribute_changed_query_filter
|
||||
def filter_based_on_attribute_change(records, current_attribute_changed_record)
|
||||
@attribute_changed_query_filter.each do |filter|
|
||||
@changed_attributes = @changed_attributes.with_indifferent_access
|
||||
changed_attribute = @changed_attributes[filter['attribute_key']].presence
|
||||
|
||||
if changed_attribute[0].in?(filter['values']['from']) && changed_attribute[1].in?(filter['values']['to'])
|
||||
@attribute_changed_records = attribute_changed_filter_query(filter, records, current_attribute_changed_record)
|
||||
end
|
||||
current_attribute_changed_record = @attribute_changed_records
|
||||
end
|
||||
end
|
||||
|
||||
# We intersect with the record if query_operator-AND is present and union if query_operator-OR is present
|
||||
def attribute_changed_filter_query(filter, records, current_attribute_changed_record)
|
||||
if filter['query_operator'] == 'AND'
|
||||
@attribute_changed_records + (current_attribute_changed_record & records)
|
||||
else
|
||||
@attribute_changed_records + (current_attribute_changed_record | records)
|
||||
end
|
||||
end
|
||||
|
||||
def message_query_string(current_filter, query_hash, current_index)
|
||||
attribute_key = query_hash['attribute_key']
|
||||
query_operator = query_hash['query_operator']
|
||||
|
|
|
@ -259,6 +259,101 @@ describe AutomationRuleListener do
|
|||
expect(conversation.messages.first.content).to eq('Send this message.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when conditions based on attribute_changed' do
|
||||
before do
|
||||
automation_rule.update!(
|
||||
event_name: 'conversation_updated',
|
||||
name: 'Call actions conversation updated when company changed from DC to Marvel',
|
||||
description: 'Add labels, assign team after conversation updated',
|
||||
conditions: [
|
||||
{
|
||||
attribute_key: 'company',
|
||||
filter_operator: 'attribute_changed',
|
||||
values: { from: ['DC'], to: ['Marvel'] },
|
||||
query_operator: 'AND'
|
||||
}.with_indifferent_access,
|
||||
{
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: ['snoozed'],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
)
|
||||
conversation.update(status: :snoozed)
|
||||
end
|
||||
|
||||
let!(:event) do
|
||||
Events::Base.new('conversation_updated', Time.zone.now, { conversation: conversation, changed_attributes: {
|
||||
company: %w[DC Marvel]
|
||||
} })
|
||||
end
|
||||
|
||||
context 'when rule matches' do
|
||||
it 'triggers automation rule to assign team' do
|
||||
expect(conversation.team_id).not_to eq(team.id)
|
||||
|
||||
listener.conversation_updated(event)
|
||||
|
||||
conversation.reload
|
||||
expect(conversation.team_id).to eq(team.id)
|
||||
end
|
||||
|
||||
it 'triggers automation rule to assign team with OR operator' do
|
||||
conversation.update(status: :open)
|
||||
automation_rule.update!(
|
||||
conditions: [
|
||||
{
|
||||
attribute_key: 'company',
|
||||
filter_operator: 'attribute_changed',
|
||||
values: { from: ['DC'], to: ['Marvel'] },
|
||||
query_operator: 'OR'
|
||||
}.with_indifferent_access,
|
||||
{
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: ['snoozed'],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
)
|
||||
|
||||
expect(conversation.team_id).not_to eq(team.id)
|
||||
|
||||
listener.conversation_updated(event)
|
||||
|
||||
conversation.reload
|
||||
expect(conversation.team_id).to eq(team.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when rule doesnt match' do
|
||||
it 'when automation rule is triggered it will not assign team' do
|
||||
conversation.update(status: :open)
|
||||
|
||||
expect(conversation.team_id).not_to eq(team.id)
|
||||
|
||||
listener.conversation_updated(event)
|
||||
|
||||
conversation.reload
|
||||
expect(conversation.team_id).not_to eq(team.id)
|
||||
end
|
||||
|
||||
it 'when automation rule is triggers, it will not assign team on attribute_changed values' do
|
||||
conversation.update(status: :snoozed)
|
||||
event = Events::Base.new('conversation_updated', Time.zone.now, { conversation: conversation,
|
||||
changed_attributes: { company: %w[Marvel DC] } })
|
||||
|
||||
expect(conversation.team_id).not_to eq(team.id)
|
||||
|
||||
listener.conversation_updated(event)
|
||||
|
||||
conversation.reload
|
||||
expect(conversation.team_id).not_to eq(team.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#message_created' do
|
||||
|
|
Loading…
Reference in a new issue