From 532331edb6e99265fb1fb0a46159f799b6257c36 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Wed, 23 Sep 2020 15:27:41 +0530 Subject: [PATCH] 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 --- .../v1/accounts/conversations_controller.rb | 6 +++ app/finders/conversation_finder.rb | 7 ++++ .../conversations/search.json.jbuilder | 22 ++++++++++ config/routes.rb | 1 + .../accounts/conversations_controller_spec.rb | 33 +++++++++++++++ .../paths/conversation/index_or_create.yml | 42 +++++++++++++++++++ swagger/swagger.json | 20 +++++---- 7 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 app/views/api/v1/accounts/conversations/search.json.jbuilder diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index 2f1bdcbfb..e2fe7a7ed 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -15,6 +15,12 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro @conversations_count = result[:count] end + def search + result = conversation_finder.perform + @conversations = result[:conversations] + @conversations_count = result[:count] + end + def create @conversation = ::Conversation.create!(conversation_params) end diff --git a/app/finders/conversation_finder.rb b/app/finders/conversation_finder.rb index 183c9b535..f4bbcc149 100644 --- a/app/finders/conversation_finder.rb +++ b/app/finders/conversation_finder.rb @@ -27,6 +27,7 @@ class ConversationFinder find_all_conversations filter_by_status filter_by_labels if params[:labels] + filter_by_query if params[:q] mine_count, unassigned_count, all_count = set_count_for_all_conversations @@ -76,6 +77,12 @@ class ConversationFinder @conversations 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 @conversations = @conversations.where(status: params[:status] || DEFAULT_STATUS) end diff --git a/app/views/api/v1/accounts/conversations/search.json.jbuilder b/app/views/api/v1/accounts/conversations/search.json.jbuilder new file mode 100644 index 000000000..e55ae8f3e --- /dev/null +++ b/app/views/api/v1/accounts/conversations/search.json.jbuilder @@ -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 diff --git a/config/routes.rb b/config/routes.rb index e41b86f54..3cc52387e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -47,6 +47,7 @@ Rails.application.routes.draw do end resources :conversations, only: [:index, :create, :show] do get 'meta', on: :collection + get 'search', on: :collection scope module: :conversations do resources :messages, only: [:index, :create] resources :assignments, only: [:create] diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index 41fab769f..97a7cc785 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -59,6 +59,39 @@ RSpec.describe 'Conversations API', type: :request do 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 let(:conversation) { create(:conversation, account: account) } diff --git a/swagger/paths/conversation/index_or_create.yml b/swagger/paths/conversation/index_or_create.yml index 2afba74c8..e9d867005 100644 --- a/swagger/paths/conversation/index_or_create.yml +++ b/swagger/paths/conversation/index_or_create.yml @@ -39,6 +39,48 @@ get: $ref: '#/definitions/bad_request_error' 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: tags: - Conversation diff --git a/swagger/swagger.json b/swagger/swagger.json index 02e024190..a9c4efaf7 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -283,10 +283,15 @@ "tags": [ "Conversation" ], - "operationId": "conversationList", - "description": "List all the conversations with pagination", - "summary": "Conversations List", + "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", @@ -295,8 +300,7 @@ "me", "unassigned", "all" - ], - "required": true + ] }, { "name": "status", @@ -306,14 +310,12 @@ "open", "resolved", "bot" - ], - "required": true + ] }, { "name": "page", "in": "query", - "type": "integer", - "required": true + "type": "integer" }, { "name": "inbox_id",