Skip to content

Commit

Permalink
chore: Support multiple values for automation message content (#7871)
Browse files Browse the repository at this point in the history
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
  • Loading branch information
3 people committed Sep 19, 2023
1 parent 0dd5104 commit 9ba5adf
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 21 deletions.
31 changes: 22 additions & 9 deletions app/javascript/dashboard/helper/filterQueryGenerator.js
@@ -1,21 +1,34 @@
const setArrayValues = item => {
return item.values[0]?.id ? item.values.map(val => val.id) : item.values;
};

const generateValues = item => {
if (item.attribute_key === 'content') {
const values = item.values || '';
return values.split(',');
}
if (Array.isArray(item.values)) {
return setArrayValues(item);
}
if (typeof item.values === 'object') {
return [item.values.id];
}
if (!item.values) {
return [];
}
return [item.values];
};

const generatePayload = data => {
// Make a copy of data to avoid vue data reactivity issues
const filters = JSON.parse(JSON.stringify(data));
let payload = filters.map(item => {
if (Array.isArray(item.values)) {
item.values = setArrayValues(item);
} else if (typeof item.values === 'object') {
item.values = [item.values.id];
} else if (!item.values) {
item.values = [];
} else {
item.values = [item.values];
}
// If item key is content, we will split it using comma and return as array
// FIX ME: Make this generic option instead of using the key directly here
item.values = generateValues(item);
return item;
});

// For every query added, the query_operator is set default to and so the
// last query will have an extra query_operator, this would break the api.
// Setting this to null for all query payload
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/dashboard/mixins/automations/methodsMixin.js
Expand Up @@ -199,6 +199,12 @@ export default {
values: condition.values[0],
};
}
if (inputType === 'comma_separated_plain_text') {
return {
...condition,
values: condition.values.join(','),
};
}
return {
...condition,
query_operator: condition.query_operator || 'and',
Expand Down
Expand Up @@ -52,7 +52,7 @@
{{ $t('AUTOMATION.ADD.FORM.CONDITIONS.LABEL') }}
</label>
<div
class="w-full w-full p-4 bg-slate-25 dark:bg-slate-700 rounded-lg border border-solid border-slate-50 dark:border-slate-700 mb-4"
class="w-full p-4 bg-slate-25 dark:bg-slate-700 rounded-lg border border-solid border-slate-50 dark:border-slate-700 mb-4"
>
<filter-input-box
v-for="(condition, i) in automation.conditions"
Expand Down Expand Up @@ -94,7 +94,7 @@
{{ $t('AUTOMATION.ADD.FORM.ACTIONS.LABEL') }}
</label>
<div
class="w-full w-full p-4 bg-slate-25 dark:bg-slate-700 rounded-lg border border-solid border-slate-50 dark:border-slate-700 mb-4"
class="w-full p-4 bg-slate-25 dark:bg-slate-700 rounded-lg border border-solid border-slate-50 dark:border-slate-700 mb-4"
>
<automation-action-input
v-for="(action, i) in automation.actions"
Expand Down
Expand Up @@ -19,7 +19,7 @@ export const AUTOMATIONS = {
key: 'content',
name: 'Message Content',
attributeI18nKey: 'MESSAGE_CONTAINS',
inputType: 'plain_text',
inputType: 'comma_separated_plain_text',
filterOperators: OPERATOR_TYPES_2,
},
{
Expand Down
21 changes: 17 additions & 4 deletions app/services/filter_service.rb
Expand Up @@ -23,8 +23,8 @@ def filter_operation(query_hash, current_index)
@filter_values["value_#{current_index}"] = filter_values(query_hash)
equals_to_filter_string(query_hash[:filter_operator], current_index)
when 'contains', 'does_not_contain'
@filter_values["value_#{current_index}"] = "%#{string_filter_values(query_hash)}%"
like_filter_string(query_hash[:filter_operator], current_index)
@filter_values["value_#{current_index}"] = values_for_ilike(query_hash)
ilike_filter_string(query_hash[:filter_operator], current_index)
when 'is_present'
@filter_values["value_#{current_index}"] = 'IS NOT NULL'
when 'is_not_present'
Expand All @@ -47,8 +47,6 @@ def filter_values(query_hash)
query_hash['values'].map { |x| Conversation.statuses[x.to_sym] }
when 'message_type'
query_hash['values'].map { |x| Message.message_types[x.to_sym] }
when 'content'
string_filter_values(query_hash)
else
case_insensitive_values(query_hash)
end
Expand All @@ -62,6 +60,15 @@ def case_insensitive_values(query_hash)
end
end

def values_for_ilike(query_hash)
if query_hash['values'].is_a?(Array)
query_hash['values']
.map { |item| "%#{item.strip}%" }
else
["%#{query_hash['values'].strip}%"]
end
end

def string_filter_values(query_hash)
return query_hash['values'][0].downcase if query_hash['values'].is_a?(Array)

Expand Down Expand Up @@ -149,6 +156,12 @@ def equals_to_filter_string(filter_operator, current_index)
"NOT IN (:value_#{current_index})"
end

def ilike_filter_string(filter_operator, current_index)
return "ILIKE ANY (ARRAY[:value_#{current_index}])" if %w[contains].include?(filter_operator)

"NOT ILIKE ALL (ARRAY[:value_#{current_index}])"
end

def like_filter_string(filter_operator, current_index)
return "LIKE :value_#{current_index}" if %w[contains starts_with].include?(filter_operator)

Expand Down
47 changes: 42 additions & 5 deletions spec/services/conversations/filter_service_spec.rb
Expand Up @@ -10,7 +10,6 @@
let!(:campaign_2) { create(:campaign, title: 'Campaign', account: account) }
let!(:inbox) { create(:inbox, account: account, enable_auto_assignment: false) }

let!(:unassigned_conversation) { create(:conversation, account: account, inbox: inbox) }
let!(:user_2_assigned_conversation) { create(:conversation, account: account, inbox: inbox, assignee: user_2) }
let!(:en_conversation_1) do
create(:conversation, account: account, inbox: inbox, assignee: user_1, campaign_id: campaign_1.id,
Expand Down Expand Up @@ -75,15 +74,53 @@
params[:payload] = payload
result = filter_service.new(params, user_1).perform
conversations = Conversation.where("additional_attributes ->> 'browser_language' IN (?) AND status IN (?)", ['en'], [1, 2])
expect(result.length).to be conversations.count
expect(result[:count][:all_count]).to be conversations.count
end

it 'filter conversations by additional_attributes and status with pagination' do
params[:payload] = payload
params[:page] = 2
result = filter_service.new(params, user_1).perform
conversations = Conversation.where("additional_attributes ->> 'browser_language' IN (?) AND status IN (?)", ['en'], [1, 2])
expect(result.length).to be conversations.count
expect(result[:count][:all_count]).to be conversations.count
end

it 'filters items with contains filter_operator with values being an array' do
params[:payload] = [{
attribute_key: 'browser_language',
filter_operator: 'contains',
values: %w[tr fr],
query_operator: '',
custom_attribute_type: ''
}.with_indifferent_access]

create(:conversation, account: account, inbox: inbox, assignee: user_1, campaign_id: campaign_1.id,
status: 'pending', additional_attributes: { 'browser_language': 'fr' })
create(:conversation, account: account, inbox: inbox, assignee: user_1, campaign_id: campaign_1.id,
status: 'pending', additional_attributes: { 'browser_language': 'tr' })

result = filter_service.new(params, user_1).perform
expect(result[:count][:all_count]).to be 2
end

it 'filters items with does not contain filter operator with values being an array' do
params[:payload] = [{
attribute_key: 'browser_language',
filter_operator: 'does_not_contain',
values: %w[tr en],
query_operator: '',
custom_attribute_type: ''
}.with_indifferent_access]

create(:conversation, account: account, inbox: inbox, assignee: user_1, campaign_id: campaign_1.id,
status: 'pending', additional_attributes: { 'browser_language': 'fr' })
create(:conversation, account: account, inbox: inbox, assignee: user_1, campaign_id: campaign_1.id,
status: 'pending', additional_attributes: { 'browser_language': 'tr' })

result = filter_service.new(params, user_1).perform

expect(result[:count][:all_count]).to be 1
expect(result[:conversations].first.additional_attributes['browser_language']).to eq 'fr'
end

it 'filter conversations by additional_attributes with NOT_IN filter' do
Expand All @@ -98,7 +135,7 @@
end

it 'filter conversations by tags' do
unassigned_conversation.update_labels('support')
user_2_assigned_conversation.update_labels('support')
params[:payload] = [
{
attribute_key: 'assignee_id',
Expand All @@ -119,7 +156,7 @@
}.with_indifferent_access
]
result = filter_service.new(params, user_1).perform
expect(result.length).to be 2
expect(result[:count][:all_count]).to be 1
end

it 'filter conversations by is_present filter_operator' do
Expand Down

0 comments on commit 9ba5adf

Please sign in to comment.