From 376de685fb694a2bc16e5e1432c735bb25b972dc Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 11 Dec 2023 19:02:11 -0800 Subject: [PATCH] chore: Adds API for agent bot avatar upload (#8533) Adds API for agent bot avatar upload - accounts/agent_bot - platform/agent_bot --- .../api/v1/accounts/agent_bots_controller.rb | 17 +++++-- .../api/v1/accounts/contacts_controller.rb | 6 +-- .../platform/api/v1/agent_bots_controller.rb | 17 +++++-- app/policies/agent_bot_policy.rb | 4 ++ .../accounts/agent_bots/avatar.json.jbuilder | 1 + .../api/v1/agent_bots/avatar.json.jbuilder | 1 + config/routes.rb | 8 +++- .../v1/accounts/agent_bots_controller_spec.rb | 47 ++++++++++++++++++ .../api/v1/agent_bots_controller_spec.rb | 48 +++++++++++++++++++ 9 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 app/views/api/v1/accounts/agent_bots/avatar.json.jbuilder create mode 100644 app/views/platform/api/v1/agent_bots/avatar.json.jbuilder diff --git a/app/controllers/api/v1/accounts/agent_bots_controller.rb b/app/controllers/api/v1/accounts/agent_bots_controller.rb index efb48c5c6..43bce17bc 100644 --- a/app/controllers/api/v1/accounts/agent_bots_controller.rb +++ b/app/controllers/api/v1/accounts/agent_bots_controller.rb @@ -10,11 +10,18 @@ class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController def show; end def create - @agent_bot = Current.account.agent_bots.create!(permitted_params) + @agent_bot = Current.account.agent_bots.create!(permitted_params.except(:avatar_url)) + process_avatar_from_url end def update - @agent_bot.update!(permitted_params) + @agent_bot.update!(permitted_params.except(:avatar_url)) + process_avatar_from_url + end + + def avatar + @agent_bot.avatar.purge if @agent_bot.avatar.attached? + @agent_bot end def destroy @@ -30,6 +37,10 @@ class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController end def permitted_params - params.permit(:name, :description, :outgoing_url, :bot_type, bot_config: [:csml_content]) + params.permit(:name, :description, :outgoing_url, :avatar, :avatar_url, :bot_type, bot_config: [:csml_content]) + end + + def process_avatar_from_url + ::Avatar::AvatarFromUrlJob.perform_later(@agent_bot, params[:avatar_url]) if params[:avatar_url].present? end end diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index bec00be96..f424f3b66 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -83,14 +83,14 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController @contact = Current.account.contacts.new(permitted_params.except(:avatar_url)) @contact.save! @contact_inbox = build_contact_inbox - process_avatar + process_avatar_from_url end end def update @contact.assign_attributes(contact_update_params) @contact.save! - process_avatar if permitted_params[:avatar].present? || permitted_params[:avatar_url].present? + process_avatar_from_url end def destroy @@ -174,7 +174,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController @contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id]) end - def process_avatar + def process_avatar_from_url ::Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url]) if params[:avatar_url].present? end diff --git a/app/controllers/platform/api/v1/agent_bots_controller.rb b/app/controllers/platform/api/v1/agent_bots_controller.rb index 138052b77..dd70a1ba5 100644 --- a/app/controllers/platform/api/v1/agent_bots_controller.rb +++ b/app/controllers/platform/api/v1/agent_bots_controller.rb @@ -9,13 +9,15 @@ class Platform::Api::V1::AgentBotsController < PlatformController def show; end def create - @resource = AgentBot.new(agent_bot_params) + @resource = AgentBot.new(agent_bot_params.except(:avatar_url)) @resource.save! + process_avatar_from_url @platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource) end def update - @resource.update!(agent_bot_params) + @resource.update!(agent_bot_params.except(:avatar_url)) + process_avatar_from_url end def destroy @@ -23,6 +25,11 @@ class Platform::Api::V1::AgentBotsController < PlatformController head :ok end + def avatar + @resource.avatar.purge if @resource.avatar.attached? + @resource + end + private def set_resource @@ -30,6 +37,10 @@ class Platform::Api::V1::AgentBotsController < PlatformController end def agent_bot_params - params.permit(:name, :description, :account_id, :outgoing_url) + params.permit(:name, :description, :account_id, :outgoing_url, :avatar, :avatar_url) + end + + def process_avatar_from_url + ::Avatar::AvatarFromUrlJob.perform_later(@resource, params[:avatar_url]) if params[:avatar_url].present? end end diff --git a/app/policies/agent_bot_policy.rb b/app/policies/agent_bot_policy.rb index 4d5e363ea..75c91dbf9 100644 --- a/app/policies/agent_bot_policy.rb +++ b/app/policies/agent_bot_policy.rb @@ -18,4 +18,8 @@ class AgentBotPolicy < ApplicationPolicy def destroy? @account_user.administrator? end + + def avatar? + @account_user.administrator? + end end diff --git a/app/views/api/v1/accounts/agent_bots/avatar.json.jbuilder b/app/views/api/v1/accounts/agent_bots/avatar.json.jbuilder new file mode 100644 index 000000000..f647ac383 --- /dev/null +++ b/app/views/api/v1/accounts/agent_bots/avatar.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/agent_bot', formats: [:json], resource: AgentBotPresenter.new(@agent_bot) diff --git a/app/views/platform/api/v1/agent_bots/avatar.json.jbuilder b/app/views/platform/api/v1/agent_bots/avatar.json.jbuilder new file mode 100644 index 000000000..17143d3e2 --- /dev/null +++ b/app/views/platform/api/v1/agent_bots/avatar.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'platform/api/v1/models/agent_bot', formats: [:json], resource: @resource diff --git a/config/routes.rb b/config/routes.rb index c600edbf7..ec838c107 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,7 +45,9 @@ Rails.application.routes.draw do end resource :bulk_actions, only: [:create] resources :agents, only: [:index, :create, :update, :destroy] - resources :agent_bots, only: [:index, :create, :show, :update, :destroy] + resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do + delete :avatar, on: :member + end resources :contact_inboxes, only: [] do collection do post :filter @@ -329,7 +331,9 @@ Rails.application.routes.draw do get :login end end - resources :agent_bots, only: [:index, :create, :show, :update, :destroy] + resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do + delete :avatar, on: :member + end resources :accounts, only: [:create, :show, :update, :destroy] do resources :account_users, only: [:index, :create] do collection do diff --git a/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb b/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb index 70fcf9f42..918552406 100644 --- a/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb @@ -141,6 +141,28 @@ RSpec.describe 'Agent Bot API', type: :request do expect(agent_bot.reload.name).not_to eq('test_updated') expect(response.body).not_to include(global_bot.access_token.token) end + + it 'updates avatar' do + # no avatar before upload + expect(agent_bot.avatar.attached?).to be(false) + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + patch "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: admin.create_new_auth_token, + params: valid_params.merge(avatar: file) + + expect(response).to have_http_status(:success) + agent_bot.reload + expect(agent_bot.avatar.attached?).to be(true) + end + + it 'updated avatar with avatar_url' do + patch "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: admin.create_new_auth_token, + params: valid_params.merge(avatar_url: 'http://example.com/avatar.png'), + as: :json + expect(response).to have_http_status(:success) + expect(Avatar::AvatarFromUrlJob).to have_been_enqueued.with(agent_bot, 'http://example.com/avatar.png') + end end end @@ -183,4 +205,29 @@ RSpec.describe 'Agent Bot API', type: :request do end end end + + describe 'DELETE /api/v1/accounts/{account.id}/agent_bots/:id/avatar' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + delete "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + before do + agent_bot.avatar.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + end + + it 'delete agent_bot avatar' do + delete "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}/avatar", + headers: admin.create_new_auth_token, + as: :json + + expect { agent_bot.avatar.attachment.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect(response).to have_http_status(:success) + end + end + end end diff --git a/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb b/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb index 030d694ff..ae3ef61e5 100644 --- a/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb +++ b/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb @@ -140,6 +140,26 @@ RSpec.describe 'Platform Agent Bot API', type: :request do data = response.parsed_body expect(data['name']).to eq('test123') end + + it 'updates avatar' do + # no avatar before upload + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + expect(agent_bot.avatar.attached?).to be(false) + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + patch "/platform/api/v1/agent_bots/#{agent_bot.id}", params: { name: 'test123' }.merge(avatar: file), + headers: { api_access_token: platform_app.access_token.token } + expect(response).to have_http_status(:success) + agent_bot.reload + expect(agent_bot.avatar.attached?).to be(true) + end + + it 'updated avatar with avatar_url' do + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + patch "/platform/api/v1/agent_bots/#{agent_bot.id}", params: { name: 'test123' }.merge(avatar_url: 'http://example.com/avatar.png'), + headers: { api_access_token: platform_app.access_token.token } + expect(response).to have_http_status(:success) + expect(Avatar::AvatarFromUrlJob).to have_been_enqueued.with(agent_bot, 'http://example.com/avatar.png') + end end end @@ -169,4 +189,32 @@ RSpec.describe 'Platform Agent Bot API', type: :request do end end end + + describe 'DELETE /platform/api/v1/agent_bots/{agent_bot_id}/avatar' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + delete "/platform/api/v1/agent_bots/#{agent_bot.id}/avatar" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + let(:platform_app) { create(:platform_app) } + + before do + agent_bot.avatar.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + end + + it 'delete agent_bot avatar' do + delete "/platform/api/v1/agent_bots/#{agent_bot.id}/avatar", + headers: { api_access_token: platform_app.access_token.token }, + as: :json + + expect { agent_bot.avatar.attachment.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect(response).to have_http_status(:success) + end + end + end end