mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 03:27:52 +00:00
feat: Add ability to resolve API channel conversations (#8348)
- Create a new endpoint to fetch a single conversation in client apis - Create a new endpoint to resolve a single conversation in client apis - Update swagger API definition to include missing endpoints Fixes: #6329 Co-authored-by: Cristian Duta <Cristian.Duta@ti8m.ch>
This commit is contained in:
@@ -1,15 +1,31 @@
|
|||||||
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
|
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
|
||||||
include Events::Types
|
include Events::Types
|
||||||
before_action :set_conversation, only: [:toggle_typing, :update_last_seen]
|
before_action :set_conversation, only: [:toggle_typing, :update_last_seen, :show, :toggle_status]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@conversations = @contact_inbox.hmac_verified? ? @contact.conversations : @contact_inbox.conversations
|
@conversations = @contact_inbox.hmac_verified? ? @contact.conversations : @contact_inbox.conversations
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@conversation = create_conversation
|
@conversation = create_conversation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def toggle_status
|
||||||
|
# Check if the conversation is already resolved to prevent redundant operations
|
||||||
|
return if @conversation.resolved?
|
||||||
|
|
||||||
|
# Assign the conversation's contact as the resolver
|
||||||
|
# This step attributes the resolution action to the contact involved in the conversation
|
||||||
|
# If this assignment is not made, the system implicitly becomes the resolver by default
|
||||||
|
Current.contact = @conversation.contact
|
||||||
|
|
||||||
|
# Update the conversation's status to 'resolved' to reflect its closure
|
||||||
|
@conversation.status = :resolved
|
||||||
|
@conversation.save!
|
||||||
|
end
|
||||||
|
|
||||||
def toggle_typing
|
def toggle_typing
|
||||||
case params[:typing_status]
|
case params[:typing_status]
|
||||||
when 'on'
|
when 'on'
|
||||||
@@ -30,7 +46,11 @@ class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::Inbox
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_conversation
|
def set_conversation
|
||||||
@conversation = @contact_inbox.contact.conversations.find_by!(display_id: params[:id])
|
@conversation = if @contact_inbox.hmac_verified?
|
||||||
|
@contact_inbox.contact.conversations.find_by!(display_id: params[:id])
|
||||||
|
else
|
||||||
|
@contact_inbox.conversations.find_by!(display_id: params[:id])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_conversation
|
def create_conversation
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
json.partial! 'public/api/v1/models/conversation', formats: [:json], resource: @conversation
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
json.partial! 'public/api/v1/models/conversation', formats: [:json], resource: @conversation
|
||||||
@@ -366,8 +366,9 @@ Rails.application.routes.draw do
|
|||||||
resources :inboxes do
|
resources :inboxes do
|
||||||
scope module: :inboxes do
|
scope module: :inboxes do
|
||||||
resources :contacts, only: [:create, :show, :update] do
|
resources :contacts, only: [:create, :show, :update] do
|
||||||
resources :conversations, only: [:index, :create] do
|
resources :conversations, only: [:index, :create, :show] do
|
||||||
member do
|
member do
|
||||||
|
post :toggle_status
|
||||||
post :toggle_typing
|
post :toggle_typing
|
||||||
post :update_last_seen
|
post :update_last_seen
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -36,6 +36,45 @@ RSpec.describe 'Public Inbox Contact Conversations API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /public/api/v1/inboxes/{identifier}/contact/{source_id}/conversations/{conversation_id}' do
|
||||||
|
it 'returns the conversation that the contact has access to' do
|
||||||
|
conversation = create(:conversation, contact_inbox: contact_inbox)
|
||||||
|
create(:message, account: conversation.account, inbox: conversation.inbox, conversation: conversation, content: 'message-1')
|
||||||
|
create(:message, account: conversation.account, inbox: conversation.inbox, conversation: conversation, content: 'message-2')
|
||||||
|
|
||||||
|
get "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations/#{conversation.display_id}"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
data = response.parsed_body
|
||||||
|
expect(data['id']).to eq(conversation.display_id)
|
||||||
|
expect(data['messages']).to be_a(Array)
|
||||||
|
expect(data['messages'].length).to eq(conversation.messages.count)
|
||||||
|
expect(data['messages'].pluck('content')).to include(conversation.messages.first.content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /public/api/v1/inboxes/{identifier}/contact/{source_id}/conversations/{conversation_id}/toggle_status' do
|
||||||
|
it 'resolves the conversation' do
|
||||||
|
conversation = create(:conversation, contact_inbox: contact_inbox)
|
||||||
|
display_id = conversation.display_id
|
||||||
|
|
||||||
|
post "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations/#{display_id}/toggle_status"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(conversation.reload).to be_resolved
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not resolve a conversation that is already resolved' do
|
||||||
|
conversation = create(:conversation, contact_inbox: contact_inbox, status: :resolved)
|
||||||
|
display_id = conversation.display_id
|
||||||
|
|
||||||
|
post "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations/#{display_id}/toggle_status"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(Conversation.where(id: conversation.id, status: :resolved).count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'POST /public/api/v1/inboxes/{identifier}/contact/{source_id}/conversations' do
|
describe 'POST /public/api/v1/inboxes/{identifier}/contact/{source_id}/conversations' do
|
||||||
it 'creates a conversation for that contact' do
|
it 'creates a conversation for that contact' do
|
||||||
post "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations"
|
post "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations"
|
||||||
|
|||||||
@@ -94,7 +94,6 @@
|
|||||||
patch:
|
patch:
|
||||||
$ref: ./public/inboxes/contacts/update.yml
|
$ref: ./public/inboxes/contacts/update.yml
|
||||||
|
|
||||||
|
|
||||||
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations:
|
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/public_inbox_identifier'
|
- $ref: '#/parameters/public_inbox_identifier'
|
||||||
@@ -104,6 +103,38 @@
|
|||||||
get:
|
get:
|
||||||
$ref: ./public/inboxes/conversations/index.yml
|
$ref: ./public/inboxes/conversations/index.yml
|
||||||
|
|
||||||
|
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}:
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/public_inbox_identifier'
|
||||||
|
- $ref: '#/parameters/public_contact_identifier'
|
||||||
|
- $ref: '#/parameters/conversation_id'
|
||||||
|
get:
|
||||||
|
$ref: ./public/inboxes/conversations/show.yml
|
||||||
|
|
||||||
|
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/toggle_status:
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/public_inbox_identifier'
|
||||||
|
- $ref: '#/parameters/public_contact_identifier'
|
||||||
|
- $ref: '#/parameters/conversation_id'
|
||||||
|
post:
|
||||||
|
$ref: ./public/inboxes/conversations/toggle_status.yml
|
||||||
|
|
||||||
|
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/toggle_typing:
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/public_inbox_identifier'
|
||||||
|
- $ref: '#/parameters/public_contact_identifier'
|
||||||
|
- $ref: '#/parameters/conversation_id'
|
||||||
|
post:
|
||||||
|
$ref: ./public/inboxes/conversations/toggle_typing.yml
|
||||||
|
|
||||||
|
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/update_last_seen:
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/public_inbox_identifier'
|
||||||
|
- $ref: '#/parameters/public_contact_identifier'
|
||||||
|
- $ref: '#/parameters/conversation_id'
|
||||||
|
post:
|
||||||
|
$ref: ./public/inboxes/conversations/update_last_seen.yml
|
||||||
|
|
||||||
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/messages:
|
/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/messages:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/public_inbox_identifier'
|
- $ref: '#/parameters/public_inbox_identifier'
|
||||||
|
|||||||
14
swagger/paths/public/inboxes/conversations/show.yml
Normal file
14
swagger/paths/public/inboxes/conversations/show.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
tags:
|
||||||
|
- Conversations API
|
||||||
|
operationId: get-single-conversation
|
||||||
|
summary: Get a single conversation
|
||||||
|
description: Retrieves the details of a specific conversation
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/public_conversation'
|
||||||
|
401:
|
||||||
|
description: Unauthorized
|
||||||
|
404:
|
||||||
|
description: Conversation not found
|
||||||
14
swagger/paths/public/inboxes/conversations/toggle_status.yml
Normal file
14
swagger/paths/public/inboxes/conversations/toggle_status.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
tags:
|
||||||
|
- Conversations API
|
||||||
|
operationId: resolve-conversation
|
||||||
|
summary: Resolve a conversation
|
||||||
|
description: Marks a conversation as resolved
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Conversation resolved successfully
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/public_conversation'
|
||||||
|
401:
|
||||||
|
description: Unauthorized
|
||||||
|
404:
|
||||||
|
description: Conversation not found
|
||||||
18
swagger/paths/public/inboxes/conversations/toggle_typing.yml
Normal file
18
swagger/paths/public/inboxes/conversations/toggle_typing.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
tags:
|
||||||
|
- Conversations API
|
||||||
|
operationId: toggle-typing-status
|
||||||
|
summary: Toggle typing status
|
||||||
|
description: Toggles the typing status in a conversation
|
||||||
|
parameters:
|
||||||
|
- name: typing_status
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: Typing status, either 'on' or 'off'
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Typing status toggled successfully
|
||||||
|
401:
|
||||||
|
description: Unauthorized
|
||||||
|
404:
|
||||||
|
description: Conversation not found
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
tags:
|
||||||
|
- Conversations API
|
||||||
|
operationId: update-last-seen
|
||||||
|
summary: Update last seen
|
||||||
|
description: Updates the last seen time of the contact in a conversation
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Last seen updated successfully
|
||||||
|
401:
|
||||||
|
description: Unauthorized
|
||||||
|
404:
|
||||||
|
description: Conversation not found
|
||||||
@@ -894,6 +894,149 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_inbox_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_contact_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/conversation_id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Conversations API"
|
||||||
|
],
|
||||||
|
"operationId": "get-single-conversation",
|
||||||
|
"summary": "Get a single conversation",
|
||||||
|
"description": "Retrieves the details of a specific conversation",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/public_conversation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Conversation not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/toggle_status": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_inbox_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_contact_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/conversation_id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Conversations API"
|
||||||
|
],
|
||||||
|
"operationId": "resolve-conversation",
|
||||||
|
"summary": "Resolve a conversation",
|
||||||
|
"description": "Marks a conversation as resolved",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Conversation resolved successfully",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/public_conversation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Conversation not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/toggle_typing": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_inbox_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_contact_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/conversation_id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Conversations API"
|
||||||
|
],
|
||||||
|
"operationId": "toggle-typing-status",
|
||||||
|
"summary": "Toggle typing status",
|
||||||
|
"description": "Toggles the typing status in a conversation",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "typing_status",
|
||||||
|
"in": "query",
|
||||||
|
"required": true,
|
||||||
|
"type": "string",
|
||||||
|
"description": "Typing status, either 'on' or 'off'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Typing status toggled successfully"
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Conversation not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/update_last_seen": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_inbox_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/public_contact_identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/conversation_id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Conversations API"
|
||||||
|
],
|
||||||
|
"operationId": "update-last-seen",
|
||||||
|
"summary": "Update last seen",
|
||||||
|
"description": "Updates the last seen time of the contact in a conversation",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Last seen updated successfully"
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Conversation not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/messages": {
|
"/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/messages": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user