Merge branch 'release/1.2.1'

This commit is contained in:
Pranav Raj Sreepuram 2020-03-08 22:15:04 +05:30
commit 1273ea64d3
46 changed files with 367 additions and 145 deletions

4
.gitignore vendored
View file

@ -52,4 +52,6 @@ coverage
# ignore packages
node_modules
package-lock.json
package-lock.json
*.dump

View file

@ -293,7 +293,7 @@ GEM
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.3)
puma (4.3.2)
puma (4.3.3)
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)

View file

@ -1,8 +1,18 @@
class Api::V1::Conversations::MessagesController < Api::BaseController
before_action :set_conversation, only: [:create]
before_action :set_conversation, only: [:index, :create]
def index
@messages = message_finder.perform
end
def create
mb = Messages::Outgoing::NormalBuilder.new(current_user, @conversation, params)
@message = mb.perform
end
private
def message_finder
@message_finder ||= MessageFinder.new(@conversation, params)
end
end

View file

@ -7,9 +7,7 @@ class Api::V1::ConversationsController < Api::BaseController
@conversations_count = result[:count]
end
def show
@messages = messages_finder.perform
end
def show; end
def toggle_status
@status = @conversation.toggle_status
@ -34,8 +32,4 @@ class Api::V1::ConversationsController < Api::BaseController
def conversation_finder
@conversation_finder ||= ConversationFinder.new(current_user, params)
end
def messages_finder
@message_finder ||= MessageFinder.new(@conversation, params)
end
end

View file

@ -1,5 +1,7 @@
class Twitter::CallbacksController < Twitter::BaseController
def show
return redirect_to app_new_twitter_inbox_url if permitted_params[:denied]
@response = twitter_client.access_token(
oauth_token: permitted_params[:oauth_token],
oauth_verifier: permitted_params[:oauth_verifier]
@ -46,6 +48,6 @@ class Twitter::CallbacksController < Twitter::BaseController
end
def permitted_params
params.permit(:oauth_token, :oauth_verifier)
params.permit(:oauth_token, :oauth_verifier, :denied)
end
end

View file

@ -15,7 +15,7 @@ class MessageApi extends ApiClient {
}
getPreviousMessages({ conversationId, before }) {
return axios.get(`${this.url}/${conversationId}`, {
return axios.get(`${this.url}/${conversationId}/messages`, {
params: { before },
});
}

View file

@ -33,14 +33,18 @@
<div class="reply-box__bottom">
<ul class="tabs">
<li class="tabs-title" :class="{ 'is-active': !isPrivate }">
<a href="#" @click="makeReply">Reply</a>
<a href="#" @click="makeReply">{{
$t('CONVERSATION.REPLYBOX.REPLY')
}}</a>
</li>
<li class="tabs-title is-private" :class="{ 'is-active': isPrivate }">
<a href="#" @click="makePrivate">Private Note</a>
<a href="#" @click="makePrivate">{{
$t('CONVERSATION.REPLYBOX.PRIVATE_NOTE')
}}</a>
</li>
<li v-if="message.length" class="tabs-title message-length">
<a :class="{ 'message-error': message.length > 620 }">
{{ message.length }} / 640
<a :class="{ 'message-error': message.length > maxLength - 40 }">
{{ message.length }} / {{ maxLength }}
</a>
</li>
</ul>
@ -49,16 +53,12 @@
class="button send-button"
:disabled="disableButton()"
:class="{
disabled: message.length === 0 || message.length > 640,
disabled: message.length === 0 || message.length > maxLength,
warning: isPrivate,
}"
@click="sendMessage"
>
{{
isPrivate
? $t('CONVERSATION.REPLYBOX.CREATE')
: $t('CONVERSATION.REPLYBOX.SEND')
}}
{{ replyButtonLabel }}
<i
class="icon"
:class="{
@ -82,6 +82,10 @@ import EmojiInput from '../emoji/EmojiInput';
import CannedResponse from './CannedResponse';
export default {
components: {
EmojiInput,
CannedResponse,
},
mixins: [clickaway],
data() {
return {
@ -103,10 +107,32 @@ export default {
} = this.currentChat;
return channel;
},
},
components: {
EmojiInput,
CannedResponse,
conversationType() {
const {
additional_attributes: additionalAttributes = {},
} = this.currentChat;
return additionalAttributes.type || '';
},
maxLength() {
if (this.channelType === 'Channel::FacebookPage') {
return 640;
}
if (this.channelType === 'Channel::TwitterProfile') {
if (this.conversationType === 'tweet') {
return 280;
}
}
return 10000;
},
replyButtonLabel() {
if (this.isPrivate) {
return this.$t('CONVERSATION.REPLYBOX.CREATE');
}
if (this.conversationType === 'tweet') {
return this.$t('CONVERSATION.REPLYBOX.TWEET');
}
return this.$t('CONVERSATION.REPLYBOX.SEND');
},
},
watch: {
message(val) {

View file

@ -21,8 +21,11 @@
"PRIVATE_MSG_INPUT": "Shift + enter for new line. This will be visible only to Agents"
},
"REPLYBOX": {
"REPLY": "Reply",
"PRIVATE_NOTE": "Private Note",
"SEND": "Send",
"CREATE": "Add Note"
"CREATE": "Add Note",
"TWEET": "Tweet"
},
"VISIBLE_TO_AGENTS": "Private Note: Only visible to you and your team",
"CHANGE_STATUS": "Conversation status changed",

View file

@ -58,6 +58,7 @@ export default {
this.initialize();
this.$watch('$store.state.route', () => this.initialize());
this.$watch('chatList.length', () => {
this.fetchConversation();
this.setActiveChat();
});
},
@ -81,13 +82,28 @@ export default {
break;
default:
this.$store.dispatch('setActiveInbox', null);
this.$store.dispatch('clearSelectedState');
break;
}
},
setActiveChat() {
fetchConversation() {
if (!this.conversationId) {
return;
}
const chat = this.findConversation();
if (!chat) {
this.$store.dispatch('getConversation', this.conversationId);
}
},
findConversation() {
const conversationId = parseInt(this.conversationId, 10);
const [chat] = this.chatList.filter(c => c.id === conversationId);
return chat;
},
setActiveChat() {
const chat = this.findConversation();
if (!chat) return;
this.$store.dispatch('setActiveChat', chat).then(() => {
bus.$emit('scrollToMessage');

View file

@ -28,7 +28,7 @@ export default {
roles: ['administrator', 'agent'],
component: ConversationView,
props: route => {
return { conversationId: route.params.conversation_id };
return { inboxId: 0, conversationId: route.params.conversation_id };
},
},
{

View file

@ -39,6 +39,9 @@
<span v-if="item.channel_type === 'Channel::WebWidget'">
Website
</span>
<span v-if="item.channel_type === 'Channel::TwitterProfile'">
Twitter
</span>
</td>
<!-- Action Buttons -->

View file

@ -1,9 +1,9 @@
<template>
<div class="profile--settings--row row">
<div class="columns small-3 ">
<p class="section--title">
<h4 class="block-title">
{{ $t('PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.TITLE') }}
</p>
</h4>
<p>{{ $t('PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.NOTE') }}</p>
</div>
<div class="columns small-9">

View file

@ -3,12 +3,12 @@
<form @submit.prevent="updateUser">
<div class="small-12 row profile--settings--row">
<div class="columns small-3 ">
<p class="section--title">
<h4 class="block-title">
{{ $t('PROFILE_SETTINGS.FORM.PROFILE_SECTION.TITLE') }}
</p>
</h4>
<p>{{ $t('PROFILE_SETTINGS.FORM.PROFILE_SECTION.NOTE') }}</p>
</div>
<div class="columns small-9">
<div class="columns small-9 medium-5">
<label>
{{ $t('PROFILE_SETTINGS.FORM.PROFILE_IMAGE.LABEL') }}
<thumbnail size="80px" :src="avatarUrl"></thumbnail>
@ -48,12 +48,12 @@
</div>
<div class="profile--settings--row row">
<div class="columns small-3 ">
<p class="section--title">
<h4 class="block-title">
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.TITLE') }}
</p>
</h4>
<p>{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.NOTE') }}</p>
</div>
<div class="columns small-9">
<div class="columns small-9 medium-5">
<label :class="{ error: $v.password.$error }">
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD.LABEL') }}
<input
@ -211,16 +211,12 @@ export default {
.profile--settings--row {
@include border-normal-bottom;
padding: 16px;
padding: $space-normal;
.small-3 {
padding: 16px 16px 16px 0;
padding: $space-normal $space-medium $space-normal 0;
}
.small-9 {
padding: 16px;
padding: $space-normal;
}
}
.section--title {
color: $color-woot;
}
</style>

View file

@ -7,6 +7,15 @@ import FBChannel from '../../../api/channel/fbChannel';
// actions
const actions = {
getConversation: async ({ commit }, conversationId) => {
try {
const response = await ConversationApi.show(conversationId);
commit(types.default.ADD_CONVERSATION, response.data);
} catch (error) {
// Ignore error
}
},
fetchAllConversations: async ({ commit, dispatch }, params) => {
commit(types.default.SET_LIST_LOADING_STATUS);
try {

View file

@ -33,12 +33,13 @@ const getters = {
getChatListLoadingStatus: ({ listLoadingStatus }) => listLoadingStatus,
getAllMessagesLoaded(_state) {
const [chat] = getSelectedChatConversation(_state);
return chat.allMessagesLoaded === undefined
return !chat || chat.allMessagesLoaded === undefined
? false
: chat.allMessagesLoaded;
},
getUnreadCount(_state) {
const [chat] = getSelectedChatConversation(_state);
if (!chat) return [];
return chat.messages.filter(
chatMessage =>
chatMessage.created_at * 1000 > chat.agent_last_seen_at * 1000 &&

View file

@ -0,0 +1,24 @@
import axios from 'axios';
import actions from '../../conversations/actions';
import * as types from '../../../mutation-types';
const commit = jest.fn();
global.axios = axios;
jest.mock('axios');
describe('#actions', () => {
describe('#getConversation', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({ data: { id: 1, meta: {} } });
await actions.getConversation({ commit }, 1);
expect(commit.mock.calls).toEqual([
[types.default.ADD_CONVERSATION, { id: 1, meta: {} }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({ message: 'Incorrect header' });
await actions.getConversation({ commit });
expect(commit.mock.calls).toEqual([]);
});
});
});

View file

@ -6,6 +6,7 @@ class Conversations::EventDataPresenter < SimpleDelegator
def push_data
{
id: display_id,
additional_attributes: additional_attributes,
inbox_id: inbox_id,
messages: push_messages,
meta: push_meta,

View file

@ -11,7 +11,9 @@ class Twitter::DirectMessageParserService < Twitter::WebhooksBaseService
content: message_create_data['message_data']['text'],
account_id: @inbox.account_id,
inbox_id: @inbox.id,
message_type: outgoing_message? ? :outgoing : :incoming
message_type: outgoing_message? ? :outgoing : :incoming,
contact_id: @contact.id,
source_id: direct_message_data['id']
)
end

View file

@ -39,10 +39,16 @@ class Twitter::SendReplyService
end
def send_tweet_reply
twitter_client.send_tweet_reply(
response = twitter_client.send_tweet_reply(
reply_to_tweet_id: conversation.additional_attributes['tweet_id'],
tweet: screen_name + message.content
)
if response.status == '200'
tweet_data = response.body
message.update!(source_id: tweet_data['id_str'])
else
Rails.logger 'TWITTER_TWEET_REPLY_ERROR' + response.body
end
end
def send_reply

View file

@ -3,16 +3,9 @@ class Twitter::TweetParserService < Twitter::WebhooksBaseService
def perform
set_inbox
find_or_create_contact(user)
set_conversation
@conversation.messages.create(
account_id: @inbox.account_id,
contact_id: @contact.id,
content: tweet_text,
inbox_id: @inbox.id,
message_type: message_type,
source_id: tweet_data['id'].to_s
)
return if message_already_exist?
create_message
end
private
@ -37,6 +30,10 @@ class Twitter::TweetParserService < Twitter::WebhooksBaseService
tweet_data['user']
end
def tweet_id
tweet_data['id'].to_s
end
def parent_tweet_id
tweet_data['in_reply_to_status_id_str'].nil? ? tweet_data['id'].to_s : tweet_data['in_reply_to_status_id_str']
end
@ -66,4 +63,21 @@ class Twitter::TweetParserService < Twitter::WebhooksBaseService
@conversation = ::Conversation.create!(conversation_params)
end
def message_already_exist?
@inbox.messages.find_by(source_id: tweet_id)
end
def create_message
find_or_create_contact(user)
set_conversation
@conversation.messages.create(
account_id: @inbox.account_id,
contact_id: @contact.id,
content: tweet_text,
inbox_id: @inbox.id,
message_type: message_type,
source_id: tweet_id
)
end
end

View file

@ -1,26 +1,5 @@
json.payload do
json.array! @conversations do |conversation|
json.meta do
json.sender do
json.id conversation.contact.id
json.name conversation.contact.name
json.thumbnail conversation.contact.avatar_url
json.channel conversation.inbox.try(:channel_type)
end
json.assignee conversation.assignee
end
json.id conversation.display_id
if conversation.unread_incoming_messages.count.zero?
json.messages [conversation.messages.last.try(:push_event_data)]
else
json.messages conversation.unread_messages.map(&:push_event_data)
end
json.inbox_id conversation.inbox_id
json.status conversation.status
json.timestamp conversation.messages.last.try(:created_at).try(:to_i)
json.user_last_seen_at conversation.user_last_seen_at.to_i
json.agent_last_seen_at conversation.agent_last_seen_at.to_i
json.unread_count conversation.unread_incoming_messages.count
json.partial! 'api/v1/conversations/partials/conversation.json.jbuilder', conversation: conversation
end
end

View file

@ -4,31 +4,9 @@ json.data do
json.unassigned_count @conversations_count[:unassigned_count]
json.all_count @conversations_count[:all_count]
end
json.payload do
json.array! @conversations do |conversation|
json.meta do
json.sender do
json.id conversation.contact.id
json.name conversation.contact.name
json.thumbnail conversation.contact.avatar_url
json.channel conversation.inbox.try(:channel_type)
end
json.assignee conversation.assignee
end
json.id conversation.display_id
if conversation.unread_incoming_messages.count.zero?
json.messages [conversation.messages.last.try(:push_event_data)]
else
json.messages conversation.unread_messages.map(&:push_event_data)
end
json.inbox_id conversation.inbox_id
json.status conversation.status
json.timestamp conversation.messages.last.try(:created_at).try(:to_i)
json.user_last_seen_at conversation.user_last_seen_at.to_i
json.agent_last_seen_at conversation.agent_last_seen_at.to_i
json.unread_count conversation.unread_incoming_messages.count
json.partial! 'api/v1/conversations/partials/conversation.json.jbuilder', conversation: conversation
end
end
end

View file

@ -0,0 +1,20 @@
json.meta do
json.labels @conversation.label_list
json.additional_attributes @conversation.additional_attributes
json.contact_id @conversation.contact_id
end
json.payload do
json.array! @messages do |message|
json.id message.id
json.content message.content
json.inbox_id message.inbox_id
json.conversation_id message.conversation.display_id
json.message_type message.message_type_before_type_cast
json.created_at message.created_at.to_i
json.private message.private
json.source_id message.source_id
json.attachment message.attachment.push_event_data if message.attachment
json.sender message.user.push_event_data if message.user
end
end

View file

@ -0,0 +1,24 @@
json.meta do
json.sender do
json.id conversation.contact.id
json.name conversation.contact.name
json.thumbnail conversation.contact.avatar_url
json.channel conversation.inbox.try(:channel_type)
end
json.assignee conversation.assignee
end
json.id conversation.display_id
if conversation.unread_incoming_messages.count.zero?
json.messages [conversation.messages.last.try(:push_event_data)]
else
json.messages conversation.unread_messages.map(&:push_event_data)
end
json.inbox_id conversation.inbox_id
json.status conversation.status
json.timestamp conversation.messages.last.try(:created_at).try(:to_i)
json.user_last_seen_at conversation.user_last_seen_at.to_i
json.agent_last_seen_at conversation.agent_last_seen_at.to_i
json.unread_count conversation.unread_incoming_messages.count
json.additional_attributes conversation.additional_attributes

View file

@ -1,20 +1 @@
json.meta do
json.labels @conversation.label_list
json.additional_attributes @conversation.additional_attributes
json.contact_id @conversation.contact_id
end
json.payload do
json.array! @messages do |message|
json.id message.id
json.content message.content
json.inbox_id message.inbox_id
json.conversation_id message.conversation.display_id
json.message_type message.message_type_before_type_cast
json.created_at message.created_at.to_i
json.private message.private
json.source_id message.source_id
json.attachment message.attachment.push_event_data if message.attachment
json.sender message.user.push_event_data if message.user
end
end
json.partial! 'api/v1/conversations/partials/conversation.json.jbuilder', conversation: @conversation

View file

@ -73,8 +73,8 @@ Rails.application.routes.draw do
end
resources :conversations, only: [:index, :show] do
scope module: :conversations do # for nested controller
resources :messages, only: [:create]
scope module: :conversations do
resources :messages, only: [:index, :create]
resources :assignments, only: [:create]
resources :labels, only: [:create, :index]
end

View file

@ -0,0 +1,61 @@
---
path: "/docs/webhooks"
title: "Webhooks"
---
Webhooks are HTTP callbacks which can be defined for every account. They are triggered by events like message creation in Chatwoot. You can create more than one webhook for an account. Currently webhooks support only `message_created` event
**Step 1**. Click on Integrations link is settings sidebar. Click on "Configure" button.
![integrations](./images/integrations.png)
**Step 2**. You will see the list of webhooks you have already added to the account.
![configure](./images/configure.png)
**Step 3**. Click on the "Add new webhook", it will display a modal where you can input the URL to which the POST request should be sent.
![add-a-webhook](./images/add-a-webhook.png)
Chatwoot currently supports webhooks for message creation only. Once a new message is created in the any of the inboxes of the account, it will send a POST request with the following payload to the configured URLs.
### A sample webhook payload
```json
{
"event": "message_created", // The name of the event
"id": "1", // Message ID
"content": "Hi", // Content of the message
"created_at": "2020-03-03 13:05:57 UTC", // Time at which the message was sent
"message_type": "incoming", // This will have a type incoming or outgoing. Incoming messages are sent by the user from the widget, Outgoing messages are sent by the agent to the user.
"source_id": "", // This would the external id if the inbox is a Twitter or Facebook integration.
"sender": { // This would provide the details of the agent who sent this message
"id": "1",
"name": "Agent",
"email": "agent@example.com"
},
"contact": { // This would provide the details of the user who sent this message
"id": "1",
"name": "contact-name"
},
"conversation": { // This would provide the details of the conversation
"display_id": "1", // This is the ID of the conversation which you can see in the dashboard.
"additional_attributes": {
"browser": {
"device_name": "Macbook",
"browser_name": "Chrome",
"platform_name": "Macintosh",
"browser_version": "80.0.3987.122",
"platform_version": "10.15.2"
},
"referer": "http://www.chatwoot.com",
"initiated_at": "Tue Mar 03 2020 18:37:38 GMT-0700 (Mountain Standard Time)"
}
},
"account": { // This would provide the details of the account
"id": "1",
"name": "Chatwoot",
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

View file

@ -31,4 +31,29 @@ RSpec.describe 'Conversation Messages API', type: :request do
end
end
end
describe 'GET /api/v1/conversations/:id/messages' do
let(:conversation) { create(:conversation, account: account) }
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
get "/api/v1/conversations/#{conversation.display_id}/messages"
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) }
it 'shows the conversation' do
get "/api/v1/conversations/#{conversation.display_id}/messages",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(JSON.parse(response.body, symbolize_names: true)[:meta][:contact_id]).to eq(conversation.contact_id)
end
end
end
end

View file

@ -51,7 +51,7 @@ RSpec.describe 'Conversations API', type: :request do
as: :json
expect(response).to have_http_status(:success)
expect(JSON.parse(response.body, symbolize_names: true)[:meta][:contact_id]).to eq(conversation.contact_id)
expect(JSON.parse(response.body, symbolize_names: true)[:id]).to eq(conversation.display_id)
end
end
end

View file

@ -226,6 +226,7 @@ RSpec.describe Conversation, type: :model do
let(:conversation) { create(:conversation) }
let(:expected_data) do
{
additional_attributes: nil,
meta: {
sender: conversation.contact.push_event_data,
assignee: conversation.assignee

View file

@ -13,6 +13,7 @@ RSpec.describe Conversations::EventDataPresenter do
describe '#push_data' do
let(:expected_data) do
{
additional_attributes: nil,
meta: {
sender: conversation.contact.push_event_data,
assignee: conversation.assignee

View file

@ -4,6 +4,7 @@ describe Twitter::SendReplyService do
subject(:send_reply_service) { described_class.new(message: message) }
let(:twitter_client) { instance_double(::Twitty::Facade) }
let(:twitter_response) { instance_double(::Twitty::Response) }
let(:account) { create(:account) }
let(:widget_inbox) { create(:inbox, account: account) }
let(:twitter_channel) { create(:channel_twitter_profile, account: account) }
@ -32,7 +33,9 @@ describe Twitter::SendReplyService do
before do
allow(::Twitty::Facade).to receive(:new).and_return(twitter_client)
allow(twitter_client).to receive(:send_direct_message).and_return(true)
allow(twitter_client).to receive(:send_tweet_reply).and_return(true)
allow(twitter_client).to receive(:send_tweet_reply).and_return(twitter_response)
allow(twitter_response).to receive(:status).and_return('200')
allow(twitter_response).to receive(:body).and_return(JSON.parse({ id_str: '12345' }.to_json))
end
describe '#perform' do
@ -61,14 +64,15 @@ describe Twitter::SendReplyService do
context 'with reply' do
it 'if conversation is a direct message' do
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: dm_conversation)
create(:message, message_type: 'outgoing', inbox: twitter_inbox, account: account, conversation: dm_conversation)
create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: dm_conversation)
expect(twitter_client).to have_received(:send_direct_message)
end
it 'if conversation is a tweet' do
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
create(:message, message_type: 'outgoing', inbox: twitter_inbox, account: account, conversation: tweet_conversation)
tweet = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
expect(twitter_client).to have_received(:send_tweet_reply)
expect(tweet.reload.source_id).to eq '12345'
end
end
end

View file

@ -1,5 +1,7 @@
get:
tags: [Contact]
tags:
- Contact
operationId: contactConversations
summary: Conversations
parameters:
- name: id

View file

@ -1,5 +1,7 @@
get:
tags: [Contact]
tags:
- Contact
operationId: contactDetails
summary: Show Contact
parameters:
- name: id
@ -18,7 +20,9 @@ get:
description: Access denied
put:
tags: [Contact]
tags:
- Contact
operationId: contactUpdate
summary: Update Contact
parameters:
- name: id

View file

@ -1,5 +1,7 @@
get:
tags: [Contact]
tags:
- Contact
operationId: contactList
description: Listing all the contacts with pagination
summary: List Contacts
parameters:
@ -17,7 +19,9 @@ get:
$ref: '#/definitions/bad_request_error'
post:
tags: [Contact]
tags:
- Contact
operationId: contactCreate
description: Create New Contact
parameters:
- name: data

View file

@ -1,5 +1,7 @@
post:
tags: [Conversation]
tags:
- Conversation
operationId: conversationAssignment
summary: Assign Conversation
description: Assign a conversation to an agent
parameters:

View file

@ -1,5 +1,7 @@
get:
tags: [Conversation]
tags:
- Conversation
operationId: conversationDetails
summary: Conversation Details
description: Get all details regarding a conversation with all messages in the conversation
parameters:

View file

@ -1,5 +1,7 @@
get:
tags: [Conversation]
tags:
- Conversation
operationId: conversationLabelsList
summary: List Labels
description: Lists all the labels of a conversation
parameters:
@ -19,9 +21,11 @@ get:
description: Access denied
post:
tags: [Conversation]
summary: Add Label
description: Creates a new label and associates it with the conversation
tags:
- Conversation
operationId: conversationAddLabels
summary: Add Labels
description: Creates new labels and associates it with the conversation
parameters:
- name: id
in: path

View file

@ -1,5 +1,7 @@
get:
tags: [Conversation]
tags:
- Conversation
operationId: conversationList
description: List all the conversations with pagination
summary: Conversations List
parameters:

View file

@ -1,5 +1,7 @@
post:
tags: [Conversation]
tags:
- Conversation
operationId: conversationNewMessage
summary: Create New Message
description: All the agent replies are created as new messages through this endpoint
parameters:

View file

@ -1,5 +1,7 @@
post:
tags: [Conversation]
tags:
- Conversation
operationId: conversationToggleStatus
summary: Toggle Status
description: Toggles the status of the conversation between open and resolved
parameters:

View file

@ -1,5 +1,7 @@
post:
tags: [Conversation]
tags:
- Conversation
operationId: conversationUpdateLastSeen
summary: Update Last Seen
description: Updates the last seen of the conversation so that conversations will have the bubbles in the agents screen
parameters:

View file

@ -21,6 +21,7 @@
"tags": [
"Contact"
],
"operationId": "contactList",
"description": "Listing all the contacts with pagination",
"summary": "List Contacts",
"parameters": [
@ -49,6 +50,7 @@
"tags": [
"Contact"
],
"operationId": "contactCreate",
"description": "Create New Contact",
"parameters": [
{
@ -81,6 +83,7 @@
"tags": [
"Contact"
],
"operationId": "contactDetails",
"summary": "Show Contact",
"parameters": [
{
@ -110,6 +113,7 @@
"tags": [
"Contact"
],
"operationId": "contactUpdate",
"summary": "Update Contact",
"parameters": [
{
@ -149,6 +153,7 @@
"tags": [
"Contact"
],
"operationId": "contactConversations",
"summary": "Conversations",
"parameters": [
{
@ -180,6 +185,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationList",
"description": "List all the conversations with pagination",
"summary": "Conversations List",
"parameters": [
@ -210,6 +216,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationDetails",
"summary": "Conversation Details",
"description": "Get all details regarding a conversation with all messages in the conversation",
"parameters": [
@ -242,6 +249,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationToggleStatus",
"summary": "Toggle Status",
"description": "Toggles the status of the conversation between open and resolved",
"parameters": [
@ -274,6 +282,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationUpdateLastSeen",
"summary": "Update Last Seen",
"description": "Updates the last seen of the conversation so that conversations will have the bubbles in the agents screen",
"parameters": [
@ -303,6 +312,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationLabelsList",
"summary": "List Labels",
"description": "Lists all the labels of a conversation",
"parameters": [
@ -333,8 +343,9 @@
"tags": [
"Conversation"
],
"summary": "Add Label",
"description": "Creates a new label and associates it with the conversation",
"operationId": "conversationAddLabels",
"summary": "Add Labels",
"description": "Creates new labels and associates it with the conversation",
"parameters": [
{
"name": "id",
@ -382,6 +393,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationAssignment",
"summary": "Assign Conversation",
"description": "Assign a conversation to an agent",
"parameters": [
@ -427,6 +439,7 @@
"tags": [
"Conversation"
],
"operationId": "conversationNewMessage",
"summary": "Create New Message",
"description": "All the agent replies are created as new messages through this endpoint",
"parameters": [