From 7df68c6388046f94a5c886f0707abd4ef16be6af Mon Sep 17 00:00:00 2001 From: Tejaswini Chile Date: Thu, 13 Jan 2022 11:21:06 +0530 Subject: [PATCH] Feat: Automations Actions (#3564) --- .../accounts/automation_rules_controller.rb | 2 +- app/listeners/automation_rule_listener.rb | 17 ++++++- app/models/automation_rule.rb | 3 ++ .../automation_rules/action_service.rb | 2 + .../conditions_filter_service.rb | 23 +++++++++ app/services/filter_service.rb | 5 +- .../automation_rules/index.json.jbuilder | 2 +- .../partials/_automation_rule.json.jbuilder | 2 + ...0126_add_active_flag_to_automation_rule.rb | 5 ++ db/schema.rb | 1 + lib/filters/filter_keys.json | 16 ++++++ .../automation_rules_controller_spec.rb | 2 +- .../automation_rule_listener_spec.rb | 51 ++++++++++++++++++- 13 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20220110090126_add_active_flag_to_automation_rule.rb diff --git a/app/controllers/api/v1/accounts/automation_rules_controller.rb b/app/controllers/api/v1/accounts/automation_rules_controller.rb index 0ec35c765..723e587e6 100644 --- a/app/controllers/api/v1/accounts/automation_rules_controller.rb +++ b/app/controllers/api/v1/accounts/automation_rules_controller.rb @@ -2,7 +2,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont before_action :check_authorization def index - @automation_rules = Current.account.automation_rules + @automation_rules = Current.account.automation_rules.active end def create diff --git a/app/listeners/automation_rule_listener.rb b/app/listeners/automation_rule_listener.rb index 9562e4ffe..7b64a0b3a 100644 --- a/app/listeners/automation_rule_listener.rb +++ b/app/listeners/automation_rule_listener.rb @@ -14,12 +14,25 @@ class AutomationRuleListener < BaseListener return unless rule_present?('conversation_created', conversation) @rules.each do |rule| - conditions_match = AutomationRule::ConditionsFilterService.new(rule, conversation).perform - AutomationRule::ActionService.new(rule, conversation).perform if conditions_match.present? + conditions_match = ::AutomationRule::ConditionsFilterService.new(rule, conversation).perform + ::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 def rule_present?(event_name, conversation) + return if conversation.blank? + @rules = AutomationRule.where( event_name: event_name, account_id: conversation.account_id diff --git a/app/models/automation_rule.rb b/app/models/automation_rule.rb index 84ae6b51c..4f9717481 100644 --- a/app/models/automation_rule.rb +++ b/app/models/automation_rule.rb @@ -4,6 +4,7 @@ # # id :bigint not null, primary key # actions :jsonb not null +# active :boolean default(TRUE), not null # conditions :jsonb not null # description :text # event_name :string not null @@ -23,6 +24,8 @@ class AutomationRule < ApplicationRecord validate :json_conditions_format validate :json_actions_format + scope :active, -> { where(active: true) } + 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 diff --git a/app/services/automation_rules/action_service.rb b/app/services/automation_rules/action_service.rb index 28327959a..94dc7ab28 100644 --- a/app/services/automation_rules/action_service.rb +++ b/app/services/automation_rules/action_service.rb @@ -46,6 +46,8 @@ class AutomationRules::ActionService TeamNotifications::AutomationNotificationMailer.conversation_creation(@conversation, team, params[:message]) when 'conversation_updated' TeamNotifications::AutomationNotificationMailer.conversation_updated(@conversation, team, params[:message]) + when 'message_created' + TeamNotifications::AutomationNotificationMailer.message_created(@conversation, team, params[:message]) end end diff --git a/app/services/automation_rules/conditions_filter_service.rb b/app/services/automation_rules/conditions_filter_service.rb index bce3b581d..6e3d7f64f 100644 --- a/app/services/automation_rules/conditions_filter_service.rb +++ b/app/services/automation_rules/conditions_filter_service.rb @@ -21,6 +21,29 @@ class AutomationRules::ConditionsFilterService < FilterService records.any? 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) attribute_key = query_hash['attribute_key'] query_operator = query_hash['query_operator'] diff --git a/app/services/filter_service.rb b/app/services/filter_service.rb index 263a6bfeb..054060742 100644 --- a/app/services/filter_service.rb +++ b/app/services/filter_service.rb @@ -31,10 +31,13 @@ class FilterService end 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') 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 query_hash['values'] end diff --git a/app/views/api/v1/accounts/automation_rules/index.json.jbuilder b/app/views/api/v1/accounts/automation_rules/index.json.jbuilder index 266ab14b8..e4a363086 100644 --- a/app/views/api/v1/accounts/automation_rules/index.json.jbuilder +++ b/app/views/api/v1/accounts/automation_rules/index.json.jbuilder @@ -1,4 +1,4 @@ -json.data do +json.payload do json.array! @automation_rules do |automation_rule| json.partial! 'api/v1/accounts/automation_rules/partials/automation_rule.json.jbuilder', automation_rule: automation_rule end diff --git a/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder b/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder index 9799a4339..e8e596659 100644 --- a/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder +++ b/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder @@ -5,3 +5,5 @@ json.description automation_rule.description json.event_name automation_rule.event_name json.conditions automation_rule.conditions json.actions automation_rule.actions +json.created_on automation_rule.created_at +json.active automation_rule.active? diff --git a/db/migrate/20220110090126_add_active_flag_to_automation_rule.rb b/db/migrate/20220110090126_add_active_flag_to_automation_rule.rb new file mode 100644 index 000000000..cd1d73270 --- /dev/null +++ b/db/migrate/20220110090126_add_active_flag_to_automation_rule.rb @@ -0,0 +1,5 @@ +class AddActiveFlagToAutomationRule < ActiveRecord::Migration[6.1] + def change + add_column :automation_rules, :active, :boolean, default: true, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 53d72fe98..de8245d92 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -133,6 +133,7 @@ ActiveRecord::Schema.define(version: 2022_01_11_223630) do t.jsonb "actions", default: "{}", null: false t.datetime "created_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" end diff --git a/lib/filters/filter_keys.json b/lib/filters/filter_keys.json index 9412980a7..a34dad067 100644 --- a/lib/filters/filter_keys.json +++ b/lib/filters/filter_keys.json @@ -135,5 +135,21 @@ "filter_operators": ["exactly_equal_to", "contains", "does_not_contain" ], "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" + } } } diff --git a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb index d922c7f06..dda022839 100644 --- a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'Api::V1::Accounts::AutomationRulesController', type: :request do expect(response).to have_http_status(:success) 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 diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb index f14167f5f..8a5e2f8d3 100644 --- a/spec/listeners/automation_rule_listener_spec.rb +++ b/spec/listeners/automation_rule_listener_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe AutomationRuleListener do let(:listener) { described_class.instance } - let(:account) { create(:account) } + let!(:account) { create(:account) } let(:inbox) { create(:inbox, account: account) } let(:contact) { create(:contact, account: account, identifier: '123') } let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: inbox) } @@ -69,4 +69,53 @@ describe AutomationRuleListener do 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