feat: Attribute changed filter for automations (#4621)

This commit is contained in:
Tejaswini Chile 2022-06-07 13:01:01 +05:30 committed by GitHub
parent 267252d13a
commit 56f668db6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 6 deletions

View file

@ -9,6 +9,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
def create def create
@automation_rule = Current.account.automation_rules.new(automation_rules_permit) @automation_rule = Current.account.automation_rules.new(automation_rules_permit)
@automation_rule.actions = params[:actions] @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? 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 def update
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@automation_rule.update!(automation_rules_permit) automation_rule_update
@automation_rule.actions = params[:actions] if params[:actions]
@automation_rule.save!
process_attachments process_attachments
rescue StandardError => e rescue StandardError => e
@ -67,6 +66,13 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
private 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 def automation_rules_permit
params.permit( params.permit(
:name, :description, :event_name, :account_id, :active, :name, :description, :event_name, :account_id, :active,

View file

@ -4,11 +4,12 @@ class AutomationRuleListener < BaseListener
conversation = event_obj.data[:conversation] conversation = event_obj.data[:conversation]
account = conversation.account account = conversation.account
changed_attributes = event_obj.data[:changed_attributes]
return unless rule_present?('conversation_updated', account) return unless rule_present?('conversation_updated', account)
@rules.each do |rule| @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? AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
end end
end end
@ -18,11 +19,12 @@ class AutomationRuleListener < BaseListener
conversation = event_obj.data[:conversation] conversation = event_obj.data[:conversation]
account = conversation.account account = conversation.account
changed_attributes = event_obj.data[:changed_attributes]
return unless rule_present?('conversation_created', account) return unless rule_present?('conversation_created', account)
@rules.each do |rule| @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? ::AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
end end
end end
@ -32,11 +34,13 @@ class AutomationRuleListener < BaseListener
message = event_obj.data[:message] message = event_obj.data[:message]
account = message.try(:account) account = message.try(:account)
changed_attributes = event_obj.data[:changed_attributes]
return unless rule_present?('message_created', account) return unless rule_present?('message_created', account)
@rules.each do |rule| @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? ::AutomationRules::ActionService.new(rule, account, message.conversation).perform if conditions_match.present?
end end
end end

View file

@ -11,18 +11,24 @@ class AutomationRules::ConditionsFilterService < FilterService
file = File.read('./lib/filters/filter_keys.json') file = File.read('./lib/filters/filter_keys.json')
@filters = JSON.parse(file) @filters = JSON.parse(file)
@options = options @options = options
@changed_attributes = options[:changed_attributes]
end end
def perform def perform
@conversation_filters = @filters['conversations'] @conversation_filters = @filters['conversations']
@contact_filters = @filters['contacts'] @contact_filters = @filters['contacts']
@message_filters = @filters['messages'] @message_filters = @filters['messages']
@attribute_changed_query_filter = []
@rule.conditions.each_with_index do |query_hash, current_index| @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) apply_filter(query_hash, current_index)
end end
records = base_relation.where(@query_string, @filter_values.with_indifferent_access) 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? records.any?
end end
@ -43,6 +49,37 @@ class AutomationRules::ConditionsFilterService < FilterService
end end
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) def message_query_string(current_filter, query_hash, current_index)
attribute_key = query_hash['attribute_key'] attribute_key = query_hash['attribute_key']
query_operator = query_hash['query_operator'] query_operator = query_hash['query_operator']

View file

@ -259,6 +259,101 @@ describe AutomationRuleListener do
expect(conversation.messages.first.content).to eq('Send this message.') expect(conversation.messages.first.content).to eq('Send this message.')
end end
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 end
describe '#message_created' do describe '#message_created' do