Skip to content

Commit

Permalink
feat: Add ability to resolve API channel conversations (#8348)
Browse files Browse the repository at this point in the history
- 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>
  • Loading branch information
CristianDuta and Cristian Duta committed Feb 21, 2024
1 parent cc47cca commit ebae547
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 4 deletions.
24 changes: 22 additions & 2 deletions app/controllers/public/api/v1/inboxes/conversations_controller.rb
@@ -1,15 +1,31 @@
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
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
@conversations = @contact_inbox.hmac_verified? ? @contact.conversations : @contact_inbox.conversations
end

def show; end

def create
@conversation = create_conversation
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
case params[:typing_status]
when 'on'
Expand All @@ -30,7 +46,11 @@ def update_last_seen
private

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

def create_conversation
Expand Down
@@ -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
3 changes: 2 additions & 1 deletion config/routes.rb
Expand Up @@ -366,8 +366,9 @@
resources :inboxes do
scope module: :inboxes do
resources :contacts, only: [:create, :show, :update] do
resources :conversations, only: [:index, :create] do
resources :conversations, only: [:index, :create, :show] do
member do
post :toggle_status
post :toggle_typing
post :update_last_seen
end
Expand Down
Expand Up @@ -36,6 +36,45 @@
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
it 'creates a conversation for that contact' do
post "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations"
Expand Down
33 changes: 32 additions & 1 deletion swagger/paths/index.yml
Expand Up @@ -94,7 +94,6 @@
patch:
$ref: ./public/inboxes/contacts/update.yml


/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations:
parameters:
- $ref: '#/parameters/public_inbox_identifier'
Expand All @@ -104,6 +103,38 @@
get:
$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:
parameters:
- $ref: '#/parameters/public_inbox_identifier'
Expand Down
14 changes: 14 additions & 0 deletions swagger/paths/public/inboxes/conversations/show.yml
@@ -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 changes: 14 additions & 0 deletions swagger/paths/public/inboxes/conversations/toggle_status.yml
@@ -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 changes: 18 additions & 0 deletions swagger/paths/public/inboxes/conversations/toggle_typing.yml
@@ -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
12 changes: 12 additions & 0 deletions swagger/paths/public/inboxes/conversations/update_last_seen.yml
@@ -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
143 changes: 143 additions & 0 deletions swagger/swagger.json
Expand Up @@ -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": {
"parameters": [
{
Expand Down

0 comments on commit ebae547

Please sign in to comment.