Chore: Inbox Members API improvements (#3008)
- New Inbox Member APIs - Return JSON errors for Platform APIs
This commit is contained in:
parent
ccd0dc39ad
commit
22d1c8baf2
@ -1,26 +1,40 @@
|
|||||||
class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseController
|
||||||
before_action :fetch_inbox, only: [:create, :show]
|
before_action :fetch_inbox
|
||||||
before_action :current_agents_ids, only: [:create]
|
before_action :current_agents_ids, only: [:update]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @inbox, :create?
|
authorize @inbox, :create?
|
||||||
begin
|
ActiveRecord::Base.transaction do
|
||||||
# update also done via same action
|
params[:user_ids].map { |user_id| @inbox.add_member(user_id) }
|
||||||
update_agents_list
|
|
||||||
head :ok
|
|
||||||
rescue StandardError => e
|
|
||||||
Rails.logger.debug { "Rescued: #{e.inspect}" }
|
|
||||||
render_could_not_create_error('Could not add agents to inbox')
|
|
||||||
end
|
end
|
||||||
|
fetch_updated_agents
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
authorize @inbox, :show?
|
authorize @inbox, :show?
|
||||||
@agents = Current.account.users.where(id: @inbox.members.select(:user_id))
|
fetch_updated_agents
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
authorize @inbox, :update?
|
||||||
|
update_agents_list
|
||||||
|
fetch_updated_agents
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
authorize @inbox, :destroy?
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
params[:user_ids].map { |user_id| @inbox.remove_member(user_id) }
|
||||||
|
end
|
||||||
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def fetch_updated_agents
|
||||||
|
@agents = Current.account.users.where(id: @inbox.members.select(:user_id))
|
||||||
|
end
|
||||||
|
|
||||||
def update_agents_list
|
def update_agents_list
|
||||||
# get all the user_ids which the inbox currently has as members.
|
# get all the user_ids which the inbox currently has as members.
|
||||||
# get the list of user_ids from params
|
# get the list of user_ids from params
|
||||||
|
|||||||
@ -43,7 +43,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
@inbox.update_working_hours(params.permit(working_hours: Inbox::OFFISABLE_ATTRS)[:working_hours]) if params[:working_hours]
|
@inbox.update_working_hours(params.permit(working_hours: Inbox::OFFISABLE_ATTRS)[:working_hours]) if params[:working_hours]
|
||||||
|
|
||||||
channel_attributes = get_channel_attributes(@inbox.channel_type)
|
channel_attributes = get_channel_attributes(@inbox.channel_type)
|
||||||
@inbox.channel.update!(permitted_params(channel_attributes)[:channel])
|
@inbox.channel.update!(permitted_params(channel_attributes)[:channel]) if permitted_params(channel_attributes)[:channel].present?
|
||||||
update_channel_feature_flags
|
update_channel_feature_flags
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
include DeviseTokenAuth::Concerns::SetUserByToken
|
include DeviseTokenAuth::Concerns::SetUserByToken
|
||||||
|
include RequestExceptionHandler
|
||||||
include Pundit
|
include Pundit
|
||||||
include SwitchLocale
|
include SwitchLocale
|
||||||
|
|
||||||
@ -9,22 +10,8 @@ class ApplicationController < ActionController::Base
|
|||||||
around_action :switch_locale
|
around_action :switch_locale
|
||||||
around_action :handle_with_exception, unless: :devise_controller?
|
around_action :handle_with_exception, unless: :devise_controller?
|
||||||
|
|
||||||
rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def handle_with_exception
|
|
||||||
yield
|
|
||||||
rescue ActiveRecord::RecordNotFound => e
|
|
||||||
Sentry.capture_exception(e)
|
|
||||||
render_not_found_error('Resource could not be found')
|
|
||||||
rescue Pundit::NotAuthorizedError
|
|
||||||
render_unauthorized('You are not authorized to do this action')
|
|
||||||
ensure
|
|
||||||
# to address the thread variable leak issues in Puma/Thin webserver
|
|
||||||
Current.reset
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_current_user
|
def set_current_user
|
||||||
@user ||= current_user
|
@user ||= current_user
|
||||||
Current.user = @user
|
Current.user = @user
|
||||||
@ -34,32 +21,6 @@ class ApplicationController < ActionController::Base
|
|||||||
@subscription ||= Current.account.subscription
|
@subscription ||= Current.account.subscription
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_unauthorized(message)
|
|
||||||
render json: { error: message }, status: :unauthorized
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_not_found_error(message)
|
|
||||||
render json: { error: message }, status: :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_could_not_create_error(message)
|
|
||||||
render json: { error: message }, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_internal_server_error(message)
|
|
||||||
render json: { error: message }, status: :internal_server_error
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_record_invalid(exception)
|
|
||||||
render json: {
|
|
||||||
message: exception.record.errors.full_messages.join(', ')
|
|
||||||
}, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_error_response(exception)
|
|
||||||
render json: exception.to_hash, status: exception.http_status
|
|
||||||
end
|
|
||||||
|
|
||||||
def pundit_user
|
def pundit_user
|
||||||
{
|
{
|
||||||
user: Current.user,
|
user: Current.user,
|
||||||
|
|||||||
47
app/controllers/concerns/request_exception_handler.rb
Normal file
47
app/controllers/concerns/request_exception_handler.rb
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
module RequestExceptionHandler
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def handle_with_exception
|
||||||
|
yield
|
||||||
|
rescue ActiveRecord::RecordNotFound => e
|
||||||
|
Sentry.capture_exception(e)
|
||||||
|
render_not_found_error('Resource could not be found')
|
||||||
|
rescue Pundit::NotAuthorizedError
|
||||||
|
render_unauthorized('You are not authorized to do this action')
|
||||||
|
ensure
|
||||||
|
# to address the thread variable leak issues in Puma/Thin webserver
|
||||||
|
Current.reset
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_unauthorized(message)
|
||||||
|
render json: { error: message }, status: :unauthorized
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_not_found_error(message)
|
||||||
|
render json: { error: message }, status: :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_could_not_create_error(message)
|
||||||
|
render json: { error: message }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_internal_server_error(message)
|
||||||
|
render json: { error: message }, status: :internal_server_error
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_record_invalid(exception)
|
||||||
|
render json: {
|
||||||
|
message: exception.record.errors.full_messages.join(', ')
|
||||||
|
}, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_error_response(exception)
|
||||||
|
render json: exception.to_hash, status: exception.http_status
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -7,8 +7,8 @@ class Platform::Api::V1::UsersController < PlatformController
|
|||||||
|
|
||||||
def create
|
def create
|
||||||
@resource = (User.find_by(email: user_params[:email]) || User.new(user_params))
|
@resource = (User.find_by(email: user_params[:email]) || User.new(user_params))
|
||||||
@resource.confirm
|
|
||||||
@resource.save!
|
@resource.save!
|
||||||
|
@resource.confirm
|
||||||
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
|
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
class PlatformController < ActionController::API
|
class PlatformController < ActionController::API
|
||||||
|
include RequestExceptionHandler
|
||||||
|
|
||||||
before_action :ensure_access_token
|
before_action :ensure_access_token
|
||||||
before_action :set_platform_app
|
before_action :set_platform_app
|
||||||
before_action :set_resource, only: [:update, :show, :destroy]
|
before_action :set_resource, only: [:update, :show, :destroy]
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
# TODO: we should switch to ActionController::API for the base classes
|
# TODO: we should switch to ActionController::API for the base classes
|
||||||
# One of the specs is failing when I tried doing that, lets revisit in future
|
# One of the specs is failing when I tried doing that, lets revisit in future
|
||||||
class PublicController < ActionController::Base
|
class PublicController < ActionController::Base
|
||||||
|
include RequestExceptionHandler
|
||||||
skip_before_action :verify_authenticity_token
|
skip_before_action :verify_authenticity_token
|
||||||
end
|
end
|
||||||
|
|||||||
@ -6,8 +6,8 @@ class InboxMembers extends ApiClient {
|
|||||||
super('inbox_members', { accountScoped: true });
|
super('inbox_members', { accountScoped: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
create({ inboxId, agentList }) {
|
update({ inboxId, agentList }) {
|
||||||
return axios.post(this.url, {
|
return axios.patch(this.url, {
|
||||||
inbox_id: inboxId,
|
inbox_id: inboxId,
|
||||||
user_ids: agentList,
|
user_ids: agentList,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@ export const actions = {
|
|||||||
return InboxMembersAPI.show(inboxId);
|
return InboxMembersAPI.show(inboxId);
|
||||||
},
|
},
|
||||||
create(_, { inboxId, agentList }) {
|
create(_, { inboxId, agentList }) {
|
||||||
return InboxMembersAPI.create({ inboxId, agentList });
|
return InboxMembersAPI.update({ inboxId, agentList });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ class Inbox < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def remove_member(user_id)
|
def remove_member(user_id)
|
||||||
member = inbox_members.find_by(user_id: user_id)
|
member = inbox_members.find_by!(user_id: user_id)
|
||||||
member.try(:destroy)
|
member.try(:destroy)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
json.payload do
|
||||||
|
json.array! @agents do |agent|
|
||||||
|
json.partial! 'api/v1/models/agent.json.jbuilder', resource: agent
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
json.payload do
|
||||||
|
json.array! @agents do |agent|
|
||||||
|
json.partial! 'api/v1/models/agent.json.jbuilder', resource: agent
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -104,7 +104,12 @@ Rails.application.routes.draw do
|
|||||||
post :set_agent_bot, on: :member
|
post :set_agent_bot, on: :member
|
||||||
delete :avatar, on: :member
|
delete :avatar, on: :member
|
||||||
end
|
end
|
||||||
resources :inbox_members, only: [:create, :show], param: :inbox_id
|
resources :inbox_members, only: [:create, :show], param: :inbox_id do
|
||||||
|
collection do
|
||||||
|
delete :destroy
|
||||||
|
patch :update
|
||||||
|
end
|
||||||
|
end
|
||||||
resources :labels, only: [:index, :show, :create, :update, :destroy]
|
resources :labels, only: [:index, :show, :create, :update, :destroy]
|
||||||
|
|
||||||
resources :notifications, only: [:index, :update] do
|
resources :notifications, only: [:index, :update] do
|
||||||
|
|||||||
@ -4,82 +4,12 @@ RSpec.describe 'Inbox Member API', type: :request do
|
|||||||
let(:account) { create(:account) }
|
let(:account) { create(:account) }
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
|
||||||
describe 'POST /api/v1/accounts/{account.id}/inbox_members' do
|
|
||||||
context 'when it is an unauthenticated user' do
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
post "/api/v1/accounts/#{account.id}/inbox_members"
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when it is an authenticated agent' do
|
|
||||||
let(:agent) { create(:user, account: account, role: :agent) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
create(:inbox_member, user: agent, inbox: inbox)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
params = { inbox_id: inbox.id, user_ids: [agent.id] }
|
|
||||||
|
|
||||||
post "/api/v1/accounts/#{account.id}/inbox_members",
|
|
||||||
headers: agent.create_new_auth_token,
|
|
||||||
params: params,
|
|
||||||
as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when it is an authenticated user with access to inbox' do
|
|
||||||
let(:administrator) { create(:user, account: account, role: :administrator) }
|
|
||||||
let(:agent) { create(:user, account: account, role: :agent) }
|
|
||||||
|
|
||||||
it 'modifies inbox members' do
|
|
||||||
params = { inbox_id: inbox.id, user_ids: [agent.id] }
|
|
||||||
|
|
||||||
post "/api/v1/accounts/#{account.id}/inbox_members",
|
|
||||||
headers: administrator.create_new_auth_token,
|
|
||||||
params: params,
|
|
||||||
as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
expect(inbox.inbox_members&.count).to eq(1)
|
|
||||||
expect(inbox.inbox_members&.first&.user).to eq(agent)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'renders not found when inbox not found' do
|
|
||||||
params = { inbox_id: nil, user_ids: [agent.id] }
|
|
||||||
|
|
||||||
post "/api/v1/accounts/#{account.id}/inbox_members",
|
|
||||||
headers: administrator.create_new_auth_token,
|
|
||||||
params: params,
|
|
||||||
as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:not_found)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'renders error on invalid params' do
|
|
||||||
params = { inbox_id: inbox.id, user_ids: ['invalid'] }
|
|
||||||
|
|
||||||
post "/api/v1/accounts/#{account.id}/inbox_members",
|
|
||||||
headers: administrator.create_new_auth_token,
|
|
||||||
params: params,
|
|
||||||
as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
|
||||||
expect(response.body).to include('Could not add agents to inbox')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET /api/v1/accounts/{account.id}/inbox_members/:id' do
|
describe 'GET /api/v1/accounts/{account.id}/inbox_members/:id' do
|
||||||
let(:inbox_member) { create(:inbox_member, inbox: inbox) }
|
let(:inbox_member) { create(:inbox_member, inbox: inbox) }
|
||||||
|
|
||||||
context 'when it is an unauthenticated user' do
|
context 'when it is an unauthenticated user' do
|
||||||
it 'returns unauthorized' do
|
it 'returns unauthorized' do
|
||||||
get "/api/v1/accounts/#{account.id}/inbox_members/#{inbox_member.id}"
|
get "/api/v1/accounts/#{account.id}/inbox_members/#{inbox.id}"
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
@ -112,4 +42,242 @@ RSpec.describe 'Inbox Member API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/{account.id}/inbox_members' do
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/inbox_members"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated agent' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: agent, inbox: inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [agent.id] }
|
||||||
|
|
||||||
|
post "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an administrator' do
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
let(:old_agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:agent_to_add) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: old_agent, inbox: inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'add inbox members' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [agent_to_add.id] }
|
||||||
|
|
||||||
|
post "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(inbox.inbox_members&.count).to eq(2)
|
||||||
|
expect(inbox.inbox_members&.second&.user).to eq(agent_to_add)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders not found when inbox not found' do
|
||||||
|
params = { inbox_id: nil, user_ids: [agent_to_add.id] }
|
||||||
|
|
||||||
|
post "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders error on invalid params' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: ['invalid'] }
|
||||||
|
|
||||||
|
post "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(response.body).to include('User must exist')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PATCH /api/v1/accounts/{account.id}/inbox_members' do
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inbox_members"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated agent' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: agent, inbox: inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [agent.id] }
|
||||||
|
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an administrator' do
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
let(:old_agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:agent_to_add) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: old_agent, inbox: inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'modifies inbox members' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [agent_to_add.id] }
|
||||||
|
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(inbox.inbox_members&.count).to eq(1)
|
||||||
|
expect(inbox.inbox_members&.first&.user).to eq(agent_to_add)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders not found when inbox not found' do
|
||||||
|
params = { inbox_id: nil, user_ids: [agent_to_add.id] }
|
||||||
|
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders error on invalid params' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: ['invalid'] }
|
||||||
|
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(response.body).to include('User must exist')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE /api/v1/accounts/{account.id}/inbox_members' do
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
delete "/api/v1/accounts/#{account.id}/inbox_members"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated agent' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: agent, inbox: inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [agent.id] }
|
||||||
|
|
||||||
|
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an administrator' do
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
let(:old_agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:agent_to_delete) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:non_member_agent) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:inbox_member, user: old_agent, inbox: inbox)
|
||||||
|
create(:inbox_member, user: agent_to_delete, inbox: inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes inbox members' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [agent_to_delete.id] }
|
||||||
|
|
||||||
|
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(inbox.inbox_members&.count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders not found when inbox not found' do
|
||||||
|
params = { inbox_id: nil, user_ids: [agent_to_delete.id] }
|
||||||
|
|
||||||
|
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders error on invalid params' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: ['invalid'] }
|
||||||
|
|
||||||
|
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
expect(response.body).to include('Resource could not be found')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders error on non member params' do
|
||||||
|
params = { inbox_id: inbox.id, user_ids: [non_member_agent.id] }
|
||||||
|
|
||||||
|
delete "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
params: params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
expect(response.body).to include('Resource could not be found')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -20,6 +20,8 @@ message:
|
|||||||
$ref: ./resource/message.yml
|
$ref: ./resource/message.yml
|
||||||
user:
|
user:
|
||||||
$ref: ./resource/user.yml
|
$ref: ./resource/user.yml
|
||||||
|
agent:
|
||||||
|
$ref: ./resource/agent.yml
|
||||||
inbox:
|
inbox:
|
||||||
$ref: ./resource/inbox.yml
|
$ref: ./resource/inbox.yml
|
||||||
agent_bot:
|
agent_bot:
|
||||||
|
|||||||
25
swagger/definitions/resource/agent.yml
Normal file
25
swagger/definitions/resource/agent.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: number
|
||||||
|
uid:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
available_name:
|
||||||
|
type: string
|
||||||
|
display_name:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
account_id:
|
||||||
|
type: number
|
||||||
|
role:
|
||||||
|
type: string
|
||||||
|
enum: ['agent', 'administrator']
|
||||||
|
confirmed:
|
||||||
|
type: boolean
|
||||||
|
custom_attributes:
|
||||||
|
type: object
|
||||||
|
description: Available for users who are created through platform APIs and has custom attributes associated.
|
||||||
|
|
||||||
6
swagger/parameters/inbox_id.yml
Normal file
6
swagger/parameters/inbox_id.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
in: path
|
||||||
|
name: inbox_id
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
description: The ID of the Inbox
|
||||||
@ -7,6 +7,9 @@ agent_bot_id:
|
|||||||
team_id:
|
team_id:
|
||||||
$ref: ./team_id.yml
|
$ref: ./team_id.yml
|
||||||
|
|
||||||
|
inbox_id:
|
||||||
|
$ref: ./inbox_id.yml
|
||||||
|
|
||||||
hook_id:
|
hook_id:
|
||||||
$ref: ./hook_id.yml
|
$ref: ./hook_id.yml
|
||||||
|
|
||||||
|
|||||||
36
swagger/paths/inboxes/inbox_members/create.yml
Normal file
36
swagger/paths/inboxes/inbox_members/create.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
tags:
|
||||||
|
- Inbox
|
||||||
|
operationId: add-new-agent-to-inbox
|
||||||
|
summary: Add a New Agent
|
||||||
|
description: Add a new Agent to Inbox
|
||||||
|
security:
|
||||||
|
- userApiKey: []
|
||||||
|
parameters:
|
||||||
|
- name: data
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
inbox_id:
|
||||||
|
type: string
|
||||||
|
description: The ID of the inbox
|
||||||
|
required: true
|
||||||
|
user_ids:
|
||||||
|
type: array
|
||||||
|
description: IDs of users to be added to the inbox
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
description: 'Array of all active agents'
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/agent'
|
||||||
|
404:
|
||||||
|
description: Inbox not found
|
||||||
|
403:
|
||||||
|
description: Access denied
|
||||||
|
422:
|
||||||
|
description: User must exist
|
||||||
31
swagger/paths/inboxes/inbox_members/delete.yml
Normal file
31
swagger/paths/inboxes/inbox_members/delete.yml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
tags:
|
||||||
|
- Inbox
|
||||||
|
operationId: delete-agent-in-inbox
|
||||||
|
summary: Remove an Agent from Inbox
|
||||||
|
description: Remove an Agent from Inbox
|
||||||
|
security:
|
||||||
|
- userApiKey: []
|
||||||
|
parameters:
|
||||||
|
- name: data
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
inbox_id:
|
||||||
|
type: string
|
||||||
|
description: The ID of the inbox
|
||||||
|
required: true
|
||||||
|
user_ids:
|
||||||
|
type: array
|
||||||
|
description: IDs of users to be deleted from the inbox
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
404:
|
||||||
|
description: Inbox not found
|
||||||
|
403:
|
||||||
|
description: Access denied
|
||||||
|
422:
|
||||||
|
description: User must exist
|
||||||
21
swagger/paths/inboxes/inbox_members/show.yml
Normal file
21
swagger/paths/inboxes/inbox_members/show.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
tags:
|
||||||
|
- Inbox
|
||||||
|
operationId: get-inbox-members
|
||||||
|
summary: List Agents in Inbox
|
||||||
|
description: Get Details of Agents in an Inbox
|
||||||
|
security:
|
||||||
|
- userApiKey: []
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/inbox_id'
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
description: 'Array of all active agents'
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/agent'
|
||||||
|
404:
|
||||||
|
description: Inbox not found
|
||||||
|
403:
|
||||||
|
description: Access denied
|
||||||
36
swagger/paths/inboxes/inbox_members/update.yml
Normal file
36
swagger/paths/inboxes/inbox_members/update.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
tags:
|
||||||
|
- Inbox
|
||||||
|
operationId: update-agents-in-inbox
|
||||||
|
summary: Update Agents in Inbox
|
||||||
|
description: All agents execept the one passed in params will be removed
|
||||||
|
security:
|
||||||
|
- userApiKey: []
|
||||||
|
parameters:
|
||||||
|
- name: data
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
inbox_id:
|
||||||
|
type: string
|
||||||
|
description: The ID of the inbox
|
||||||
|
required: true
|
||||||
|
user_ids:
|
||||||
|
type: array
|
||||||
|
description: IDs of users to be added to the inbox
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
description: 'Array of all active agents'
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/agent'
|
||||||
|
404:
|
||||||
|
description: Inbox not found
|
||||||
|
403:
|
||||||
|
description: Access denied
|
||||||
|
422:
|
||||||
|
description: User must exist
|
||||||
@ -1,7 +1,7 @@
|
|||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Inbox
|
- Inbox
|
||||||
operationId: GetInboxe
|
operationId: GetInbox
|
||||||
summary: Get an inbox
|
summary: Get an inbox
|
||||||
description: Get an inbox available in the current account
|
description: Get an inbox available in the current account
|
||||||
parameters:
|
parameters:
|
||||||
|
|||||||
@ -209,6 +209,18 @@ public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversat
|
|||||||
/api/v1/accounts/{account_id}/inboxes/{id}/set_agent_bot:
|
/api/v1/accounts/{account_id}/inboxes/{id}/set_agent_bot:
|
||||||
$ref: ./inboxes/set_agent_bot.yml
|
$ref: ./inboxes/set_agent_bot.yml
|
||||||
|
|
||||||
|
# Inbox Members
|
||||||
|
/api/v1/accounts/{account_id}/inbox_members:
|
||||||
|
get:
|
||||||
|
$ref: ./inboxes/inbox_members/show.yml
|
||||||
|
post:
|
||||||
|
$ref: ./inboxes/inbox_members/create.yml
|
||||||
|
patch:
|
||||||
|
$ref: ./inboxes/inbox_members/update.yml
|
||||||
|
delete:
|
||||||
|
$ref: ./inboxes/inbox_members/delete.yml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Messages
|
# Messages
|
||||||
/api/v1/accounts/{account_id}/conversations/{id}/messages:
|
/api/v1/accounts/{account_id}/conversations/{id}/messages:
|
||||||
@ -273,7 +285,7 @@ public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversat
|
|||||||
|
|
||||||
### Custom Filters goes here
|
### Custom Filters goes here
|
||||||
|
|
||||||
# Teams
|
# Custom Filters
|
||||||
/api/v1/accounts/{account_id}/custom_filters:
|
/api/v1/accounts/{account_id}/custom_filters:
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: '#/parameters/account_id'
|
- $ref: '#/parameters/account_id'
|
||||||
|
|||||||
@ -1920,7 +1920,7 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Inbox"
|
"Inbox"
|
||||||
],
|
],
|
||||||
"operationId": "GetInboxe",
|
"operationId": "GetInbox",
|
||||||
"summary": "Get an inbox",
|
"summary": "Get an inbox",
|
||||||
"description": "Get an inbox available in the current account",
|
"description": "Get an inbox available in the current account",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -2198,6 +2198,213 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/accounts/{account_id}/inbox_members": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Inbox"
|
||||||
|
],
|
||||||
|
"operationId": "get-inbox-members",
|
||||||
|
"summary": "List Agents in Inbox",
|
||||||
|
"description": "Get Details of Agents in an Inbox",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"userApiKey": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/inbox_id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Array of all active agents",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Inbox not found"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Access denied"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Inbox"
|
||||||
|
],
|
||||||
|
"operationId": "add-new-agent-to-inbox",
|
||||||
|
"summary": "Add a New Agent",
|
||||||
|
"description": "Add a new Agent to Inbox",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"userApiKey": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inbox_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the inbox",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"user_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "IDs of users to be added to the inbox",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Array of all active agents",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Inbox not found"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Access denied"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "User must exist"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"Inbox"
|
||||||
|
],
|
||||||
|
"operationId": "update-agents-in-inbox",
|
||||||
|
"summary": "Update Agents in Inbox",
|
||||||
|
"description": "All agents execept the one passed in params will be removed",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"userApiKey": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inbox_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the inbox",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"user_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "IDs of users to be added to the inbox",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Array of all active agents",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/agent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Inbox not found"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Access denied"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "User must exist"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Inbox"
|
||||||
|
],
|
||||||
|
"operationId": "delete-agent-in-inbox",
|
||||||
|
"summary": "Remove an Agent from Inbox",
|
||||||
|
"description": "Remove an Agent from Inbox",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"userApiKey": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inbox_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the inbox",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"user_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "IDs of users to be deleted from the inbox",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Inbox not found"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Access denied"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "User must exist"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/accounts/{account_id}/conversations/{id}/messages": {
|
"/api/v1/accounts/{account_id}/conversations/{id}/messages": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -3193,6 +3400,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"agent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"uid": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"available_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"display_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"account_id": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"agent",
|
||||||
|
"administrator"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"confirmed": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"custom_attributes": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Available for users who are created through platform APIs and has custom attributes associated."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"inbox": {
|
"inbox": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -4128,6 +4375,15 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"description": "The ID of the team to be updated"
|
"description": "The ID of the team to be updated"
|
||||||
},
|
},
|
||||||
|
"inbox_id": {
|
||||||
|
"in": "path",
|
||||||
|
"name": "inbox_id",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"description": "The ID of the Inbox"
|
||||||
|
},
|
||||||
"hook_id": {
|
"hook_id": {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"name": "hook_id",
|
"name": "hook_id",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user