feat: Add filter APIs for Contacts and Conversations (#3264)
This commit is contained in:
@@ -51,7 +51,10 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def filter
|
def filter
|
||||||
@contacts = Current.account.contacts.limit(10)
|
result = ::Contacts::FilterService.new(params.permit!, current_user).perform
|
||||||
|
contacts = result[:contacts]
|
||||||
|
@contacts_count = result[:count]
|
||||||
|
@contacts = fetch_contacts_with_conversation_count(contacts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def contactable_inboxes
|
def contactable_inboxes
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
|||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def filter
|
def filter
|
||||||
@conversations = Current.account.conversations.limit(10)
|
result = ::Conversations::FilterService.new(params.permit!, current_user).perform
|
||||||
|
@conversations = result[:conversations]
|
||||||
|
@conversations_count = result[:count]
|
||||||
end
|
end
|
||||||
|
|
||||||
def mute
|
def mute
|
||||||
|
|||||||
44
app/services/contacts/filter_service.rb
Normal file
44
app/services/contacts/filter_service.rb
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
class Contacts::FilterService < FilterService
|
||||||
|
def perform
|
||||||
|
@contacts = contact_query_builder
|
||||||
|
|
||||||
|
{
|
||||||
|
contacts: @contacts,
|
||||||
|
count: {
|
||||||
|
all_count: @contacts.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_query_builder
|
||||||
|
contact_filters = @filters['contacts']
|
||||||
|
|
||||||
|
@params[:payload].each_with_index do |query_hash, current_index|
|
||||||
|
current_filter = contact_filters[query_hash['attribute_key']]
|
||||||
|
@query_string += contact_query_string(current_filter, query_hash, current_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_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 'additional_attributes'
|
||||||
|
" contacts.additional_attributes ->> '#{attribute_key}' #{filter_operator_value} #{query_operator} "
|
||||||
|
when 'standard'
|
||||||
|
if attribute_key == 'labels'
|
||||||
|
" tags.id #{filter_operator_value} #{query_operator} "
|
||||||
|
else
|
||||||
|
" contacts.#{attribute_key} #{filter_operator_value} #{query_operator} "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_relation
|
||||||
|
Current.account.contacts.left_outer_joins(:labels)
|
||||||
|
end
|
||||||
|
end
|
||||||
59
app/services/conversations/filter_service.rb
Normal file
59
app/services/conversations/filter_service.rb
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
class Conversations::FilterService < FilterService
|
||||||
|
def perform
|
||||||
|
@conversations = conversation_query_builder
|
||||||
|
mine_count, unassigned_count, all_count, = set_count_for_all_conversations
|
||||||
|
assigned_count = all_count - unassigned_count
|
||||||
|
|
||||||
|
{
|
||||||
|
conversations: conversations,
|
||||||
|
count: {
|
||||||
|
mine_count: mine_count,
|
||||||
|
assigned_count: assigned_count,
|
||||||
|
unassigned_count: unassigned_count,
|
||||||
|
all_count: all_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_query_builder
|
||||||
|
conversation_filters = @filters['conversations']
|
||||||
|
@params[:payload].each_with_index do |query_hash, current_index|
|
||||||
|
current_filter = conversation_filters[query_hash['attribute_key']]
|
||||||
|
@query_string += conversation_query_string(current_filter, query_hash, current_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_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 'additional_attributes'
|
||||||
|
" conversations.additional_attributes ->> '#{attribute_key}' #{filter_operator_value} #{query_operator} "
|
||||||
|
when 'standard'
|
||||||
|
if attribute_key == 'labels'
|
||||||
|
" tags.id #{filter_operator_value} #{query_operator} "
|
||||||
|
else
|
||||||
|
" conversations.#{attribute_key} #{filter_operator_value} #{query_operator} "
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_relation
|
||||||
|
Current.account.conversations.left_outer_joins(:labels)
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_page
|
||||||
|
@params[:page] || 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversations
|
||||||
|
@conversations = @conversations.includes(
|
||||||
|
:taggings, :inbox, { assignee: { avatar_attachment: [:blob] } }, { contact: { avatar_attachment: [:blob] } }, :team
|
||||||
|
)
|
||||||
|
@conversations.latest.page(current_page)
|
||||||
|
end
|
||||||
|
end
|
||||||
50
app/services/filter_service.rb
Normal file
50
app/services/filter_service.rb
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
require 'json'
|
||||||
|
|
||||||
|
class FilterService
|
||||||
|
def initialize(params, user)
|
||||||
|
@params = params
|
||||||
|
@user = user
|
||||||
|
file = File.read('./lib/filters/filter_keys.json')
|
||||||
|
@filters = JSON.parse(file)
|
||||||
|
@query_string = ''
|
||||||
|
@filter_values = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform; end
|
||||||
|
|
||||||
|
def filter_operation(query_hash, current_index)
|
||||||
|
case query_hash[:filter_operator]
|
||||||
|
when 'equal_to'
|
||||||
|
@filter_values["value_#{current_index}"] = filter_values(query_hash)
|
||||||
|
"IN (:value_#{current_index})"
|
||||||
|
when 'not_equal_to'
|
||||||
|
@filter_values["value_#{current_index}"] = filter_values(query_hash)
|
||||||
|
"NOT IN (:value_#{current_index})"
|
||||||
|
when 'contains'
|
||||||
|
@filter_values["value_#{current_index}"] = "%#{filter_values(query_hash)}%"
|
||||||
|
"LIKE :value_#{current_index}"
|
||||||
|
when 'does_not_contain'
|
||||||
|
@filter_values["value_#{current_index}"] = "%#{filter_values(query_hash)}%"
|
||||||
|
"NOT LIKE :value_#{current_index}"
|
||||||
|
else
|
||||||
|
@filter_values["value_#{current_index}"] = filter_values(query_hash).to_s
|
||||||
|
"= :value_#{current_index}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_values(query_hash)
|
||||||
|
if query_hash['attribute_key'] == 'status'
|
||||||
|
query_hash['values'].map { |x| Conversation.statuses[x.to_sym] }
|
||||||
|
else
|
||||||
|
query_hash['values']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_count_for_all_conversations
|
||||||
|
[
|
||||||
|
@conversations.assigned_to(@user).count,
|
||||||
|
@conversations.unassigned.count,
|
||||||
|
@conversations.count
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
json.array! @conversations do |conversation|
|
json.meta do
|
||||||
json.partial! 'api/v1/models/conversation.json.jbuilder', conversation: conversation
|
json.mine_count @conversations_count[:mine_count]
|
||||||
|
json.unassigned_count @conversations_count[:unassigned_count]
|
||||||
|
json.all_count @conversations_count[:all_count]
|
||||||
|
end
|
||||||
|
json.payload do
|
||||||
|
json.array! @conversations do |conversation|
|
||||||
|
json.partial! 'api/v1/conversations/partials/conversation.json.jbuilder', conversation: conversation
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Rails.application.routes.draw do
|
|||||||
collection do
|
collection do
|
||||||
get :meta
|
get :meta
|
||||||
get :search
|
get :search
|
||||||
get :filter
|
post :filter
|
||||||
end
|
end
|
||||||
scope module: :conversations do
|
scope module: :conversations do
|
||||||
resources :messages, only: [:index, :create, :destroy]
|
resources :messages, only: [:index, :create, :destroy]
|
||||||
@@ -83,7 +83,7 @@ Rails.application.routes.draw do
|
|||||||
collection do
|
collection do
|
||||||
get :active
|
get :active
|
||||||
get :search
|
get :search
|
||||||
get :filter
|
post :filter
|
||||||
post :import
|
post :import
|
||||||
end
|
end
|
||||||
member do
|
member do
|
||||||
|
|||||||
@@ -65,8 +65,8 @@
|
|||||||
"attribute_type": "standard"
|
"attribute_type": "standard"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attribute_key": "browser",
|
"attribute_key": "browser_language",
|
||||||
"attribute_name": "browser",
|
"attribute_name": "Browser Language",
|
||||||
"input_type": "textbox",
|
"input_type": "textbox",
|
||||||
"data_type": "text",
|
"data_type": "text",
|
||||||
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
||||||
|
|||||||
153
lib/filters/filter_keys.json
Normal file
153
lib/filters/filter_keys.json
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
{
|
||||||
|
"conversations": {
|
||||||
|
"status": {
|
||||||
|
"attribute_name": "Status",
|
||||||
|
"input_type": "multi_select",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"assignee_id": {
|
||||||
|
"attribute_name": "Assignee Name",
|
||||||
|
"input_type": "search_box with name tags/plain text",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"contact_id": {
|
||||||
|
"attribute_name": "Contact Name",
|
||||||
|
"input_type": "plain_text",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"inbox_id": {
|
||||||
|
"attribute_name": "Inbox Name",
|
||||||
|
"input_type": "search_box",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"team_id": {
|
||||||
|
"attribute_name": "Team Name",
|
||||||
|
"input_type": "search_box",
|
||||||
|
"data_type": "number",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"attribute_name": "Conversation Identifier",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "Number",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"campaign_id": {
|
||||||
|
"attribute_name": "Campaign Name",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "Number",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"attribute_name": "Labels",
|
||||||
|
"input_type": "tags",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": ["exactly_equal_to", "contains", "does_not_contain" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"browser_language": {
|
||||||
|
"attribute_name": "Browser Language",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
||||||
|
"attribute_type": "additional_attributes"
|
||||||
|
},
|
||||||
|
"country_code": {
|
||||||
|
"attribute_name": "Country Name",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "present", "is_not_present" ],
|
||||||
|
"attribute_type": "additional_attributes"
|
||||||
|
},
|
||||||
|
"referer": {
|
||||||
|
"attribute_name": "Referer link",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "link",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "present", "is_not_present" ],
|
||||||
|
"attribute_type": "additional_attributes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contacts": {
|
||||||
|
"assignee_id": {
|
||||||
|
"attribute_name": "Assignee Name",
|
||||||
|
"input_type": "search_box with name tags/plain text",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"contact_id": {
|
||||||
|
"attribute_name": "Contact Name",
|
||||||
|
"input_type": "plain_text",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"inbox_id": {
|
||||||
|
"attribute_name": "Inbox Name",
|
||||||
|
"input_type": "search_box",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"team_id": {
|
||||||
|
"attribute_name": "Team Name",
|
||||||
|
"input_type": "search_box",
|
||||||
|
"data_type": "number",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"attribute_name": "Conversation Identifier",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "Number",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"campaign_id": {
|
||||||
|
"attribute_name": "Campaign Name",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "Number",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"attribute_name": "Labels",
|
||||||
|
"input_type": "tags",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": ["exactly_equal_to", "contains", "does_not_contain" ],
|
||||||
|
"attribute_type": "standard"
|
||||||
|
},
|
||||||
|
"browser_language": {
|
||||||
|
"attribute_name": "Browser Language",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
||||||
|
"attribute_type": "additional_attributes"
|
||||||
|
},
|
||||||
|
"country_code": {
|
||||||
|
"attribute_name": "Country Name",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "text",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "present", "is_not_present" ],
|
||||||
|
"attribute_type": "additional_attributes"
|
||||||
|
},
|
||||||
|
"referer": {
|
||||||
|
"attribute_name": "Referer link",
|
||||||
|
"input_type": "textbox",
|
||||||
|
"data_type": "link",
|
||||||
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "present", "is_not_present" ],
|
||||||
|
"attribute_type": "additional_attributes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -233,10 +233,12 @@ RSpec.describe 'Contacts API', type: :request do
|
|||||||
let!(:contact2) { create(:contact, :with_email, name: 'testcontact', account: account, email: 'test@test.com') }
|
let!(:contact2) { create(:contact, :with_email, name: 'testcontact', account: account, email: 'test@test.com') }
|
||||||
|
|
||||||
it 'returns all contacts when query is empty' do
|
it 'returns all contacts when query is empty' do
|
||||||
get "/api/v1/accounts/#{account.id}/contacts/filter",
|
post "/api/v1/accounts/#{account.id}/contacts/filter",
|
||||||
params: { q: [] },
|
params: {
|
||||||
headers: admin.create_new_auth_token,
|
payload: []
|
||||||
as: :json
|
},
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(contact2.email)
|
expect(response.body).to include(contact2.email)
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ RSpec.describe 'Conversations API', type: :request do
|
|||||||
describe 'GET /api/v1/accounts/{account.id}/conversations/filter' do
|
describe 'GET /api/v1/accounts/{account.id}/conversations/filter' do
|
||||||
context 'when it is an unauthenticated user' do
|
context 'when it is an unauthenticated user' do
|
||||||
it 'returns unauthorized' do
|
it 'returns unauthorized' do
|
||||||
get "/api/v1/accounts/#{account.id}/conversations/filter", params: { q: 'test' }
|
post "/api/v1/accounts/#{account.id}/conversations/filter", params: { q: 'test' }
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
@@ -129,16 +129,15 @@ RSpec.describe 'Conversations API', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'returns all conversations with empty query' do
|
it 'returns all conversations with empty query' do
|
||||||
get "/api/v1/accounts/#{account.id}/conversations/filter",
|
post "/api/v1/accounts/#{account.id}/conversations/filter",
|
||||||
headers: agent.create_new_auth_token,
|
headers: agent.create_new_auth_token,
|
||||||
params: { q: 'test1' },
|
params: { payload: [] },
|
||||||
as: :json
|
as: :json
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
response_data = JSON.parse(response.body, symbolize_names: true)
|
response_data = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
|
||||||
expect(response_data.count).to eq(1)
|
expect(response_data.count).to eq(2)
|
||||||
expect(response_data[0][:messages][0][:content]).to include(Message.first.content)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
56
spec/services/contacts/filter_service_spec.rb
Normal file
56
spec/services/contacts/filter_service_spec.rb
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ::Contacts::FilterService do
|
||||||
|
subject(:filter_service) { described_class }
|
||||||
|
|
||||||
|
let!(:account) { create(:account) }
|
||||||
|
let!(:user_1) { create(:user, account: account) }
|
||||||
|
let!(:user_2) { create(:user, account: account) }
|
||||||
|
let!(:inbox) { create(:inbox, account: account, enable_auto_assignment: false) }
|
||||||
|
let!(:contact) { create(:contact, account: account, additional_attributes: { 'browser_language': 'en' }) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: user_1, inbox: inbox)
|
||||||
|
create(:inbox_member, user: user_2, inbox: inbox)
|
||||||
|
create(:conversation, account: account, inbox: inbox, assignee: user_1, contact: contact)
|
||||||
|
create(:conversation, account: account, inbox: inbox)
|
||||||
|
Current.account = account
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#perform' do
|
||||||
|
context 'with query present' do
|
||||||
|
let!(:params) { { payload: [], page: 1 } }
|
||||||
|
let(:payload) do
|
||||||
|
[
|
||||||
|
{
|
||||||
|
attribute_key: 'browser_language',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: ['en'],
|
||||||
|
query_operator: nil
|
||||||
|
}.with_indifferent_access
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filter contacts by additional_attributes' do
|
||||||
|
params[:payload] = payload
|
||||||
|
result = filter_service.new(params, user_1).perform
|
||||||
|
expect(result.length).to be 2
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filter conversations by tags' do
|
||||||
|
Contact.last.update_labels('support')
|
||||||
|
params[:payload] = [
|
||||||
|
{
|
||||||
|
attribute_key: 'labels',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [1],
|
||||||
|
query_operator: nil
|
||||||
|
}.with_indifferent_access
|
||||||
|
]
|
||||||
|
|
||||||
|
result = filter_service.new(params, user_1).perform
|
||||||
|
expect(result.length).to be 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
76
spec/services/conversations/filter_service_spec.rb
Normal file
76
spec/services/conversations/filter_service_spec.rb
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ::Conversations::FilterService do
|
||||||
|
subject(:filter_service) { described_class }
|
||||||
|
|
||||||
|
let!(:account) { create(:account) }
|
||||||
|
let!(:user_1) { create(:user, account: account) }
|
||||||
|
let!(:user_2) { create(:user, account: account) }
|
||||||
|
let!(:inbox) { create(:inbox, account: account, enable_auto_assignment: false) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: user_1, inbox: inbox)
|
||||||
|
create(:inbox_member, user: user_2, inbox: inbox)
|
||||||
|
create(:conversation, account: account, inbox: inbox, assignee: user_1)
|
||||||
|
create(:conversation, account: account, inbox: inbox, assignee: user_1,
|
||||||
|
status: 'pending', additional_attributes: { 'browser_language': 'en' })
|
||||||
|
create(:conversation, account: account, inbox: inbox, assignee: user_1,
|
||||||
|
status: 'pending', additional_attributes: { 'browser_language': 'en' })
|
||||||
|
create(:conversation, account: account, inbox: inbox, assignee: user_2)
|
||||||
|
# unassigned conversation
|
||||||
|
create(:conversation, account: account, inbox: inbox)
|
||||||
|
Current.account = account
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#perform' do
|
||||||
|
context 'with query present' do
|
||||||
|
let!(:params) { { payload: [], page: 1 } }
|
||||||
|
let(:payload) do
|
||||||
|
[
|
||||||
|
{
|
||||||
|
attribute_key: 'browser_language',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: ['en'],
|
||||||
|
query_operator: 'AND'
|
||||||
|
}.with_indifferent_access,
|
||||||
|
{
|
||||||
|
attribute_key: 'status',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: %w[open pending],
|
||||||
|
query_operator: nil
|
||||||
|
}.with_indifferent_access
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filter conversations by custom_attributes and status' do
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'filter conversations by tags' do
|
||||||
|
Conversation.last.update_labels('support')
|
||||||
|
params[:payload] = [
|
||||||
|
{
|
||||||
|
attribute_key: 'assignee_id',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [
|
||||||
|
user_1.id,
|
||||||
|
user_2.id
|
||||||
|
],
|
||||||
|
query_operator: 'AND'
|
||||||
|
}.with_indifferent_access,
|
||||||
|
{
|
||||||
|
attribute_key: 'labels',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: [1],
|
||||||
|
query_operator: nil
|
||||||
|
}.with_indifferent_access
|
||||||
|
]
|
||||||
|
result = filter_service.new(params, user_1).perform
|
||||||
|
expect(result.length).to be 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user