Feat: Automations Actions (#3564)
This commit is contained in:
@@ -2,7 +2,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
|||||||
before_action :check_authorization
|
before_action :check_authorization
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@automation_rules = Current.account.automation_rules
|
@automation_rules = Current.account.automation_rules.active
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|||||||
@@ -14,12 +14,25 @@ class AutomationRuleListener < BaseListener
|
|||||||
return unless rule_present?('conversation_created', conversation)
|
return unless rule_present?('conversation_created', conversation)
|
||||||
|
|
||||||
@rules.each do |rule|
|
@rules.each do |rule|
|
||||||
conditions_match = AutomationRule::ConditionsFilterService.new(rule, conversation).perform
|
conditions_match = ::AutomationRule::ConditionsFilterService.new(rule, conversation).perform
|
||||||
AutomationRule::ActionService.new(rule, conversation).perform if conditions_match.present?
|
::AutomationRules::ActionService.new(rule, conversation).perform if conditions_match.present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_created(event_obj)
|
||||||
|
conversation = event_obj.data[:conversation]
|
||||||
|
message = event_obj.data[:message]
|
||||||
|
return unless rule_present?('message_created', conversation)
|
||||||
|
|
||||||
|
@rules.each do |rule|
|
||||||
|
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation).message_conditions(message)
|
||||||
|
::AutomationRules::ActionService.new(rule, conversation).perform if conditions_match.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rule_present?(event_name, conversation)
|
def rule_present?(event_name, conversation)
|
||||||
|
return if conversation.blank?
|
||||||
|
|
||||||
@rules = AutomationRule.where(
|
@rules = AutomationRule.where(
|
||||||
event_name: event_name,
|
event_name: event_name,
|
||||||
account_id: conversation.account_id
|
account_id: conversation.account_id
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# actions :jsonb not null
|
# actions :jsonb not null
|
||||||
|
# active :boolean default(TRUE), not null
|
||||||
# conditions :jsonb not null
|
# conditions :jsonb not null
|
||||||
# description :text
|
# description :text
|
||||||
# event_name :string not null
|
# event_name :string not null
|
||||||
@@ -23,6 +24,8 @@ class AutomationRule < ApplicationRecord
|
|||||||
validate :json_conditions_format
|
validate :json_conditions_format
|
||||||
validate :json_actions_format
|
validate :json_actions_format
|
||||||
|
|
||||||
|
scope :active, -> { where(active: true) }
|
||||||
|
|
||||||
CONDITIONS_ATTRS = %w[country_code status browser_language assignee_id team_id referer].freeze
|
CONDITIONS_ATTRS = %w[country_code status browser_language assignee_id team_id referer].freeze
|
||||||
ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agents].freeze
|
ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agents].freeze
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class AutomationRules::ActionService
|
|||||||
TeamNotifications::AutomationNotificationMailer.conversation_creation(@conversation, team, params[:message])
|
TeamNotifications::AutomationNotificationMailer.conversation_creation(@conversation, team, params[:message])
|
||||||
when 'conversation_updated'
|
when 'conversation_updated'
|
||||||
TeamNotifications::AutomationNotificationMailer.conversation_updated(@conversation, team, params[:message])
|
TeamNotifications::AutomationNotificationMailer.conversation_updated(@conversation, team, params[:message])
|
||||||
|
when 'message_created'
|
||||||
|
TeamNotifications::AutomationNotificationMailer.message_created(@conversation, team, params[:message])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,29 @@ class AutomationRules::ConditionsFilterService < FilterService
|
|||||||
records.any?
|
records.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def message_conditions(_message)
|
||||||
|
message_filters = @filters['messages']
|
||||||
|
|
||||||
|
@rule.conditions.each_with_index do |query_hash, current_index|
|
||||||
|
current_filter = message_filters[query_hash['attribute_key']]
|
||||||
|
@query_string += message_query_string(current_filter, query_hash.with_indifferent_access, current_index)
|
||||||
|
end
|
||||||
|
records = Message.where(conversation: @conversation).where(@query_string, @filter_values.with_indifferent_access)
|
||||||
|
records.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_query_string(current_filter, query_hash, current_index)
|
||||||
|
attribute_key = query_hash['attribute_key']
|
||||||
|
query_operator = query_hash['query_operator']
|
||||||
|
|
||||||
|
filter_operator_value = filter_operation(query_hash, current_index)
|
||||||
|
|
||||||
|
case current_filter['attribute_type']
|
||||||
|
when 'standard'
|
||||||
|
" messages.#{attribute_key} #{filter_operator_value} #{query_operator} "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def conversation_query_string(current_filter, query_hash, current_index)
|
def conversation_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']
|
||||||
|
|||||||
@@ -31,10 +31,13 @@ class FilterService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def filter_values(query_hash)
|
def filter_values(query_hash)
|
||||||
if query_hash['attribute_key'] == 'status'
|
case query_hash['attribute_key']
|
||||||
|
when 'status'
|
||||||
return Conversation.statuses.values if query_hash['values'].include?('all')
|
return Conversation.statuses.values if query_hash['values'].include?('all')
|
||||||
|
|
||||||
query_hash['values'].map { |x| Conversation.statuses[x.to_sym] }
|
query_hash['values'].map { |x| Conversation.statuses[x.to_sym] }
|
||||||
|
when 'message_type'
|
||||||
|
query_hash['values'].map { |x| Message.message_types[x.to_sym] }
|
||||||
else
|
else
|
||||||
query_hash['values']
|
query_hash['values']
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
json.data do
|
json.payload do
|
||||||
json.array! @automation_rules do |automation_rule|
|
json.array! @automation_rules do |automation_rule|
|
||||||
json.partial! 'api/v1/accounts/automation_rules/partials/automation_rule.json.jbuilder', automation_rule: automation_rule
|
json.partial! 'api/v1/accounts/automation_rules/partials/automation_rule.json.jbuilder', automation_rule: automation_rule
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ json.description automation_rule.description
|
|||||||
json.event_name automation_rule.event_name
|
json.event_name automation_rule.event_name
|
||||||
json.conditions automation_rule.conditions
|
json.conditions automation_rule.conditions
|
||||||
json.actions automation_rule.actions
|
json.actions automation_rule.actions
|
||||||
|
json.created_on automation_rule.created_at
|
||||||
|
json.active automation_rule.active?
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddActiveFlagToAutomationRule < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :automation_rules, :active, :boolean, default: true, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -133,6 +133,7 @@ ActiveRecord::Schema.define(version: 2022_01_11_223630) do
|
|||||||
t.jsonb "actions", default: "{}", null: false
|
t.jsonb "actions", default: "{}", null: false
|
||||||
t.datetime "created_at", precision: 6, null: false
|
t.datetime "created_at", precision: 6, null: false
|
||||||
t.datetime "updated_at", precision: 6, null: false
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.boolean "active", default: true, null: false
|
||||||
t.index ["account_id"], name: "index_automation_rules_on_account_id"
|
t.index ["account_id"], name: "index_automation_rules_on_account_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -135,5 +135,21 @@
|
|||||||
"filter_operators": ["exactly_equal_to", "contains", "does_not_contain" ],
|
"filter_operators": ["exactly_equal_to", "contains", "does_not_contain" ],
|
||||||
"attribute_type": "standard"
|
"attribute_type": "standard"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"message_type": {
|
||||||
|
"attribute_name": "Message Type",
|
||||||
|
"input_type": "search_box with name tags/plain text",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"attribute_name": "Message Type",
|
||||||
|
"input_type": "search_box with name tags/plain text",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ RSpec.describe 'Api::V1::Accounts::AutomationRulesController', type: :request do
|
|||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
body = JSON.parse(response.body, symbolize_names: true)
|
body = JSON.parse(response.body, symbolize_names: true)
|
||||||
expect(body[:data].first[:id]).to eq(automation_rule.id)
|
expect(body[:payload].first[:id]).to eq(automation_rule.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
describe AutomationRuleListener do
|
describe AutomationRuleListener do
|
||||||
let(:listener) { described_class.instance }
|
let(:listener) { described_class.instance }
|
||||||
let(:account) { create(:account) }
|
let!(:account) { create(:account) }
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
let(:contact) { create(:contact, account: account, identifier: '123') }
|
let(:contact) { create(:contact, account: account, identifier: '123') }
|
||||||
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: inbox) }
|
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: inbox) }
|
||||||
@@ -69,4 +69,53 @@ describe AutomationRuleListener do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#message_created' do
|
||||||
|
before do
|
||||||
|
automation_rule.update!(
|
||||||
|
event_name: 'message_created',
|
||||||
|
name: 'Call actions message created',
|
||||||
|
description: 'Add labels, assign team after message created',
|
||||||
|
conditions: [{ 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' }]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:message) { create(:message, account: account, conversation: conversation, message_type: 'incoming') }
|
||||||
|
let!(:event) do
|
||||||
|
Events::Base.new('message_created', Time.zone.now, { conversation: conversation, message: message })
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rule matches' do
|
||||||
|
it 'triggers automation rule to assign team' do
|
||||||
|
expect(conversation.team_id).not_to eq(team.id)
|
||||||
|
|
||||||
|
automation_rule
|
||||||
|
listener.message_created(event)
|
||||||
|
|
||||||
|
conversation.reload
|
||||||
|
expect(conversation.team_id).to eq(team.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'triggers automation rule to add label' do
|
||||||
|
expect(conversation.labels).to eq([])
|
||||||
|
|
||||||
|
automation_rule
|
||||||
|
listener.message_created(event)
|
||||||
|
|
||||||
|
conversation.reload
|
||||||
|
expect(conversation.labels.pluck(:name)).to eq(%w[support priority_customer])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'triggers automation rule to assign best agents' do
|
||||||
|
expect(conversation.assignee).to be_nil
|
||||||
|
|
||||||
|
automation_rule
|
||||||
|
listener.message_created(event)
|
||||||
|
|
||||||
|
conversation.reload
|
||||||
|
|
||||||
|
expect(conversation.assignee).to eq(user_1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user