feat: Ability to search conversation with message content (#1265)

- API end point which takes message content as search parameter
- end point supports additional filtering with labels and inbox ids
- swagger doc
This commit is contained in:
Sojan Jose 2020-09-23 15:27:41 +05:30 committed by GitHub
parent bc8becf49c
commit 532331edb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 9 deletions

View file

@ -15,6 +15,12 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
@conversations_count = result[:count] @conversations_count = result[:count]
end end
def search
result = conversation_finder.perform
@conversations = result[:conversations]
@conversations_count = result[:count]
end
def create def create
@conversation = ::Conversation.create!(conversation_params) @conversation = ::Conversation.create!(conversation_params)
end end

View file

@ -27,6 +27,7 @@ class ConversationFinder
find_all_conversations find_all_conversations
filter_by_status filter_by_status
filter_by_labels if params[:labels] filter_by_labels if params[:labels]
filter_by_query if params[:q]
mine_count, unassigned_count, all_count = set_count_for_all_conversations mine_count, unassigned_count, all_count = set_count_for_all_conversations
@ -76,6 +77,12 @@ class ConversationFinder
@conversations @conversations
end end
def filter_by_query
@conversations = @conversations.joins(:messages).where('messages.content LIKE :search',
search: "%#{params[:q]}%").includes(:messages).where('messages.content LIKE :search',
search: "%#{params[:q]}%")
end
def filter_by_status def filter_by_status
@conversations = @conversations.where(status: params[:status] || DEFAULT_STATUS) @conversations = @conversations.where(status: params[:status] || DEFAULT_STATUS)
end end

View file

@ -0,0 +1,22 @@
json.data do
json.meta do
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.inbox_id conversation.inbox_id
json.messages conversation.messages
json.status conversation.status
json.muted conversation.muted?
json.can_reply conversation.can_reply?
json.timestamp conversation.messages.last.try(:created_at).try(:to_i)
json.contact_last_seen_at conversation.contact_last_seen_at.to_i
json.agent_last_seen_at conversation.agent_last_seen_at.to_i
json.additional_attributes conversation.additional_attributes
json.account_id conversation.account_id
json.labels conversation.label_list
end
end
end

View file

@ -47,6 +47,7 @@ Rails.application.routes.draw do
end end
resources :conversations, only: [:index, :create, :show] do resources :conversations, only: [:index, :create, :show] do
get 'meta', on: :collection get 'meta', on: :collection
get 'search', on: :collection
scope module: :conversations do scope module: :conversations do
resources :messages, only: [:index, :create] resources :messages, only: [:index, :create]
resources :assignments, only: [:create] resources :assignments, only: [:create]

View file

@ -59,6 +59,39 @@ RSpec.describe 'Conversations API', type: :request do
end end
end end
describe 'GET /api/v1/accounts/{account.id}/conversations/search' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
get "/api/v1/accounts/#{account.id}/conversations/search", params: { q: 'test' }
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
let(:agent) { create(:user, account: account, role: :agent) }
before do
conversation = create(:conversation, account: account)
create(:message, conversation: conversation, account: account, content: 'test1')
create(:message, conversation: conversation, account: account, content: 'test2')
create(:inbox_member, user: agent, inbox: conversation.inbox)
end
it 'returns all conversations with messages containing the search query' do
get "/api/v1/accounts/#{account.id}/conversations/search",
headers: agent.create_new_auth_token,
params: { q: 'test1' },
as: :json
expect(response).to have_http_status(:success)
response_data = JSON.parse(response.body, symbolize_names: true)[:data]
expect(response_data[:meta][:all_count]).to eq(1)
expect(response_data[:payload].first[:messages].first[:content]).to eq 'test1'
end
end
end
describe 'GET /api/v1/accounts/{account.id}/conversations/:id' do describe 'GET /api/v1/accounts/{account.id}/conversations/:id' do
let(:conversation) { create(:conversation, account: account) } let(:conversation) { create(:conversation, account: account) }

View file

@ -39,6 +39,48 @@ get:
$ref: '#/definitions/bad_request_error' $ref: '#/definitions/bad_request_error'
description: Access denied description: Access denied
get:
tags:
- Conversation
operationId: conversationSearch
description: Search for conversations containing a messages with the query string
summary: Conversations Search
parameters:
- name: q
in: query
type: string
- name: assignee_type
in: query
type: string
enum: ['me', 'unassigned', 'all']
- name: status
in: query
type: string
enum: ['open', 'resolved', 'bot']
- name: page
in: query
type: integer
- name: inbox_id
in: query
type: integer
- name: labels
in: query
type: array
items:
type: string
responses:
200:
description: Success
schema:
$ref: '#/definitions/conversation_list'
400:
description: Bad Request Error
schema:
$ref: '#/definitions/bad_request_error'
description: Access denied
post: post:
tags: tags:
- Conversation - Conversation

View file

@ -283,10 +283,15 @@
"tags": [ "tags": [
"Conversation" "Conversation"
], ],
"operationId": "conversationList", "operationId": "conversationSearch",
"description": "List all the conversations with pagination", "description": "Search for conversations containing a messages with the query string",
"summary": "Conversations List", "summary": "Conversations Search",
"parameters": [ "parameters": [
{
"name": "q",
"in": "query",
"type": "string"
},
{ {
"name": "assignee_type", "name": "assignee_type",
"in": "query", "in": "query",
@ -295,8 +300,7 @@
"me", "me",
"unassigned", "unassigned",
"all" "all"
], ]
"required": true
}, },
{ {
"name": "status", "name": "status",
@ -306,14 +310,12 @@
"open", "open",
"resolved", "resolved",
"bot" "bot"
], ]
"required": true
}, },
{ {
"name": "page", "name": "page",
"in": "query", "in": "query",
"type": "integer", "type": "integer"
"required": true
}, },
{ {
"name": "inbox_id", "name": "inbox_id",