diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb index 3345a5883..6a3f9d5cc 100644 --- a/app/controllers/api/v1/accounts/inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/inboxes_controller.rb @@ -1,6 +1,7 @@ class Api::V1::Accounts::InboxesController < Api::BaseController before_action :check_authorization - before_action :fetch_inbox, only: [:destroy, :update] + before_action :fetch_inbox, except: [:index] + before_action :fetch_agent_bot, only: [:set_agent_bot] def index @inboxes = policy_scope(current_account.inboxes) @@ -10,6 +11,17 @@ class Api::V1::Accounts::InboxesController < Api::BaseController @inbox.update(inbox_update_params) end + def set_agent_bot + if @agent_bot + agent_bot_inbox = @inbox.agent_bot_inbox || AgentBotInbox.new(inbox: @inbox) + agent_bot_inbox.agent_bot = @agent_bot + agent_bot_inbox.save! + elsif @inbox.agent_bot_inbox.present? + @inbox.agent_bot_inbox.destroy! + end + head :ok + end + def destroy @inbox.destroy head :ok @@ -21,6 +33,10 @@ class Api::V1::Accounts::InboxesController < Api::BaseController @inbox = current_account.inboxes.find(params[:id]) end + def fetch_agent_bot + @agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot] + end + def check_authorization authorize(Inbox) end diff --git a/app/controllers/api/v1/agent_bots_controller.rb b/app/controllers/api/v1/agent_bots_controller.rb new file mode 100644 index 000000000..4c17fd4f4 --- /dev/null +++ b/app/controllers/api/v1/agent_bots_controller.rb @@ -0,0 +1,8 @@ +class Api::V1::AgentBotsController < Api::BaseController + skip_before_action :authenticate_user! + skip_before_action :check_subscription + + def index + render json: AgentBot.all + end +end diff --git a/app/models/inbox.rb b/app/models/inbox.rb index 118733399..0ce3b251f 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -38,6 +38,7 @@ class Inbox < ApplicationRecord has_many :messages, through: :conversations has_one :agent_bot_inbox, dependent: :destroy + has_one :agent_bot, through: :agent_bot_inbox has_many :webhooks, dependent: :destroy after_create :subscribe_webhook, if: :facebook? diff --git a/app/policies/inbox_policy.rb b/app/policies/inbox_policy.rb index f491eea70..282ab7e49 100644 --- a/app/policies/inbox_policy.rb +++ b/app/policies/inbox_policy.rb @@ -31,4 +31,8 @@ class InboxPolicy < ApplicationPolicy def destroy? @user.administrator? end + + def set_agent_bot? + @user.administrator? + end end diff --git a/config/routes.rb b/config/routes.rb index 42e83ea41..be63d95e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -65,7 +65,9 @@ Rails.application.routes.draw do end end - resources :inboxes, only: [:index, :destroy, :update] + resources :inboxes, only: [:index, :destroy, :update] do + post :set_agent_bot, on: :member + end resources :inbox_members, only: [:create, :show], param: :inbox_id resources :labels, only: [:index] do collection do @@ -104,6 +106,8 @@ Rails.application.routes.draw do resource :profile, only: [:show, :update] + resources :agent_bots, only: [:index] + namespace :widget do resource :contact, only: [:update] resources :inbox_members, only: [:index] diff --git a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb index 6aa6286a4..03f1daf16 100644 --- a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb @@ -138,4 +138,62 @@ RSpec.describe 'Inboxes API', type: :request do end end end + + describe 'POST /api/v1/accounts/{account.id}/inboxes/:id/set_agent_bot' do + let(:inbox) { create(:inbox, account: account) } + let(:agent_bot) { create(:agent_bot) } + + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/set_agent_bot" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + let(:admin) { create(:user, account: account, role: :administrator) } + let(:valid_params) { { agent_bot: agent_bot.id } } + + it 'sets the agent bot' do + post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/set_agent_bot", + headers: admin.create_new_auth_token, + params: valid_params, + as: :json + + expect(response).to have_http_status(:success) + expect(inbox.reload.agent_bot.id).to eq agent_bot.id + end + + it 'throw error when invalid agent bot id' do + post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/set_agent_bot", + headers: admin.create_new_auth_token, + params: { agent_bot: 0 }, + as: :json + + expect(response).to have_http_status(:not_found) + end + + it 'disconnects the agent bot' do + post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/set_agent_bot", + headers: admin.create_new_auth_token, + params: { agent_bot: nil }, + as: :json + + expect(response).to have_http_status(:success) + expect(inbox.reload.agent_bot).to be_falsey + end + + it 'will not update agent bot when its an agent' do + agent = create(:user, account: account, role: :agent) + + post "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/set_agent_bot", + headers: agent.create_new_auth_token, + params: valid_params, + as: :json + + expect(response).to have_http_status(:unauthorized) + end + end + end end diff --git a/spec/controllers/api/v1/agent_bots_controller_spec.rb b/spec/controllers/api/v1/agent_bots_controller_spec.rb new file mode 100644 index 000000000..258fad5c3 --- /dev/null +++ b/spec/controllers/api/v1/agent_bots_controller_spec.rb @@ -0,0 +1,17 @@ +require 'rails_helper' + +RSpec.describe 'Profile API', type: :request do + let!(:agent_bot1) { create(:agent_bot) } + let!(:agent_bot2) { create(:agent_bot) } + + describe 'GET /api/v1/agent_bots' do + it 'returns all the agent bots in the system' do + get '/api/v1/agent_bots', + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(agent_bot1.name) + expect(response.body).to include(agent_bot2.name) + end + end +end diff --git a/spec/policies/inbox_policy_spec.rb b/spec/policies/inbox_policy_spec.rb index 5e4de0538..298c6a154 100644 --- a/spec/policies/inbox_policy_spec.rb +++ b/spec/policies/inbox_policy_spec.rb @@ -11,7 +11,7 @@ RSpec.describe InboxPolicy, type: :policy do let(:agent) { create(:user, account: account) } let(:inbox) { create(:inbox) } - permissions :create?, :destroy? do + permissions :create?, :destroy?, :update?, :set_agent_bot? do context 'when administrator' do it { expect(inbox_policy).to permit(administrator, inbox) } end diff --git a/swagger/definitions/index.yml b/swagger/definitions/index.yml index f88892efd..b822c9415 100644 --- a/swagger/definitions/index.yml +++ b/swagger/definitions/index.yml @@ -17,6 +17,8 @@ user: $ref: ./resource/user.yml inbox: $ref: ./resource/inbox.yml +agent_bot: + $ref: ./resource/agent_bot.yml # RESPONSE ## contact diff --git a/swagger/definitions/resource/agent_bot.yml b/swagger/definitions/resource/agent_bot.yml new file mode 100644 index 000000000..d0f0efb3e --- /dev/null +++ b/swagger/definitions/resource/agent_bot.yml @@ -0,0 +1,14 @@ +type: object +properties: + id: + type: number + description: ID of the agent bot + description: + type: string + description: The description about the agent bot + name: + type: string + description: The name of the agent bot + outgoing_url: + type: string + description: The webhook URL for the bot diff --git a/swagger/paths/agent_bots/index.yml b/swagger/paths/agent_bots/index.yml new file mode 100644 index 000000000..2fef38ff5 --- /dev/null +++ b/swagger/paths/agent_bots/index.yml @@ -0,0 +1,17 @@ +get: + tags: + - AgentBot + operationId: listAgentBots + summary: List all agentbots + description: List all available agentbots for the current installation + responses: + 200: + description: Success + schema: + type: Array + description: 'List of agent bots' + $ref: '#/definitions/agent_bot' + 404: + description: Inbox not found, Agent bot not found + 403: + description: Access denied diff --git a/swagger/paths/inboxes/set_agent_bot.yml b/swagger/paths/inboxes/set_agent_bot.yml new file mode 100644 index 000000000..c5f0ef792 --- /dev/null +++ b/swagger/paths/inboxes/set_agent_bot.yml @@ -0,0 +1,29 @@ +post: + tags: + - Inbox + operationId: updateAgentBot + summary: Add or remove agent bot + description: To add an agent bot pass agent_bot id, to remove agent bot from an inbox pass null + parameters: + - name: id + in: path + type: number + description: ID of the inbox + required: true + - name: data + in: body + required: true + schema: + type: object + properties: + agent_bot: + type: number + required: true + description: 'Agent bot ID' + responses: + 204: + description: Success + 404: + description: Inbox not found, Agent bot not found + 403: + description: Access denied diff --git a/swagger/paths/index.yml b/swagger/paths/index.yml index f82ed9673..67069bdc9 100644 --- a/swagger/paths/index.yml +++ b/swagger/paths/index.yml @@ -9,6 +9,12 @@ $ref: ./inboxes/index.yml /accounts/{account_id}/inboxes/{id}: $ref: ./inboxes/update.yml +/accounts/{account_id}/inboxes/{id}/set_agent_bot: + $ref: ./inboxes/update.yml + +/agent_bots: + $ref: ./agent_bots/index.yml + # Conversations /accounts/{account_id}/conversations: diff --git a/swagger/swagger.json b/swagger/swagger.json index 609c5bb93..67e3302c2 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -200,6 +200,83 @@ } } }, + "/accounts/{account_id}/inboxes/{id}/set_agent_bot": { + "patch": { + "tags": [ + "Inbox" + ], + "operationId": "updateInbox", + "summary": "Update Inbox", + "description": "Add avatar and disable auto assignment for an inbox", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the inbox", + "required": true + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "enable_auto_assignment": { + "type": "boolean", + "required": true, + "description": "Enable Auto Assignment" + }, + "avatar": { + "type": "file", + "required": false, + "description": "Image file for avatar" + } + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/inbox" + } + }, + "404": { + "description": "Inbox not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/agent_bots": { + "get": { + "tags": [ + "AgentBot" + ], + "operationId": "listAgentBots", + "summary": "List all agentbots", + "description": "List all available agentbots for the current installation", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "404": { + "description": "Inbox not found, Agent bot not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, "/accounts/{account_id}/conversations": { "get": { "tags": [ @@ -942,6 +1019,27 @@ } } }, + "agent_bot": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "ID of the agent bot" + }, + "description": { + "type": "string", + "description": "The description about the agent bot" + }, + "name": { + "type": "string", + "description": "The name of the agent bot" + }, + "outgoing_url": { + "type": "string", + "description": "The webhook URL for the bot" + } + } + }, "extended_contact": { "allOf": [ {