feat: Add UI for custom tools (#12585)
### Tools list <img width="2316" height="666" alt="CleanShot 2025-10-03 at 20 42 41@2x" src="https://github.com/user-attachments/assets/ccbffd16-804d-4eb8-9c64-2d1cfd407e4e" /> ### Tools form <img width="2294" height="2202" alt="CleanShot 2025-10-03 at 20 43 05@2x" src="https://github.com/user-attachments/assets/9f49aa09-75a1-4585-a09d-837ca64139b8" /> ## Response <img width="800" height="2144" alt="CleanShot 2025-10-03 at 20 45 56@2x" src="https://github.com/user-attachments/assets/b0c3c899-6050-4c51-baed-c8fbec5aae61" /> --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Api::V1::Accounts::Captain::CustomTools', type: :request do
|
||||
let(:account) { create(:account) }
|
||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
|
||||
def json_response
|
||||
JSON.parse(response.body, symbolize_names: true)
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/{account.id}/captain/custom_tools' do
|
||||
context 'when it is an un-authenticated user' do
|
||||
it 'returns unauthorized status' do
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools"
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an agent' do
|
||||
it 'returns success status' do
|
||||
create_list(:captain_custom_tool, 3, account: account)
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(json_response[:payload].length).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an admin' do
|
||||
it 'returns success status and custom tools' do
|
||||
create_list(:captain_custom_tool, 5, account: account)
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(json_response[:payload].length).to eq(5)
|
||||
end
|
||||
|
||||
it 'returns only enabled custom tools' do
|
||||
create(:captain_custom_tool, account: account, enabled: true)
|
||||
create(:captain_custom_tool, account: account, enabled: false)
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(json_response[:payload].length).to eq(1)
|
||||
expect(json_response[:payload].first[:enabled]).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/{account.id}/captain/custom_tools/{id}' do
|
||||
let(:custom_tool) { create(:captain_custom_tool, account: account) }
|
||||
|
||||
context 'when it is an un-authenticated user' do
|
||||
it 'returns unauthorized status' do
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}"
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an agent' do
|
||||
it 'returns success status and custom tool' do
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(json_response[:id]).to eq(custom_tool.id)
|
||||
expect(json_response[:title]).to eq(custom_tool.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom tool does not exist' do
|
||||
it 'returns not found status' do
|
||||
get "/api/v1/accounts/#{account.id}/captain/custom_tools/999999",
|
||||
headers: agent.create_new_auth_token
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/accounts/{account.id}/captain/custom_tools' do
|
||||
let(:valid_attributes) do
|
||||
{
|
||||
custom_tool: {
|
||||
title: 'Fetch Order Status',
|
||||
description: 'Fetches order status from external API',
|
||||
endpoint_url: 'https://api.example.com/orders/{{ order_id }}',
|
||||
http_method: 'GET',
|
||||
enabled: true,
|
||||
param_schema: [
|
||||
{ name: 'order_id', type: 'string', description: 'The order ID', required: true }
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
context 'when it is an un-authenticated user' do
|
||||
it 'returns unauthorized status' do
|
||||
post "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
params: valid_attributes
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an agent' do
|
||||
it 'returns unauthorized status' do
|
||||
post "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
params: valid_attributes,
|
||||
headers: agent.create_new_auth_token
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an admin' do
|
||||
it 'creates a new custom tool and returns success status' do
|
||||
post "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
params: valid_attributes,
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(json_response[:title]).to eq('Fetch Order Status')
|
||||
expect(json_response[:description]).to eq('Fetches order status from external API')
|
||||
expect(json_response[:enabled]).to be(true)
|
||||
expect(json_response[:slug]).to eq('custom_fetch_order_status')
|
||||
expect(json_response[:param_schema]).to eq([
|
||||
{ name: 'order_id', type: 'string', description: 'The order ID', required: true }
|
||||
])
|
||||
end
|
||||
|
||||
context 'with invalid parameters' do
|
||||
let(:invalid_attributes) do
|
||||
{
|
||||
custom_tool: {
|
||||
title: '',
|
||||
endpoint_url: ''
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns unprocessable entity status' do
|
||||
post "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
params: invalid_attributes,
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid endpoint URL' do
|
||||
let(:invalid_url_attributes) do
|
||||
{
|
||||
custom_tool: {
|
||||
title: 'Test Tool',
|
||||
endpoint_url: 'http://localhost/api',
|
||||
http_method: 'GET'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns unprocessable entity status' do
|
||||
post "/api/v1/accounts/#{account.id}/captain/custom_tools",
|
||||
params: invalid_url_attributes,
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /api/v1/accounts/{account.id}/captain/custom_tools/{id}' do
|
||||
let(:custom_tool) { create(:captain_custom_tool, account: account) }
|
||||
let(:update_attributes) do
|
||||
{
|
||||
custom_tool: {
|
||||
title: 'Updated Tool Title',
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
context 'when it is an un-authenticated user' do
|
||||
it 'returns unauthorized status' do
|
||||
patch "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
params: update_attributes
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an agent' do
|
||||
it 'returns unauthorized status' do
|
||||
patch "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
params: update_attributes,
|
||||
headers: agent.create_new_auth_token
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an admin' do
|
||||
it 'updates the custom tool and returns success status' do
|
||||
patch "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
params: update_attributes,
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(json_response[:title]).to eq('Updated Tool Title')
|
||||
expect(json_response[:enabled]).to be(false)
|
||||
end
|
||||
|
||||
context 'with invalid parameters' do
|
||||
let(:invalid_attributes) do
|
||||
{
|
||||
custom_tool: {
|
||||
title: ''
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns unprocessable entity status' do
|
||||
patch "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
params: invalid_attributes,
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/accounts/{account.id}/captain/custom_tools/{id}' do
|
||||
let!(:custom_tool) { create(:captain_custom_tool, account: account) }
|
||||
|
||||
context 'when it is an un-authenticated user' do
|
||||
it 'returns unauthorized status' do
|
||||
delete "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}"
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an agent' do
|
||||
it 'returns unauthorized status' do
|
||||
delete "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
headers: agent.create_new_auth_token
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an admin' do
|
||||
it 'deletes the custom tool and returns no content status' do
|
||||
expect do
|
||||
delete "/api/v1/accounts/#{account.id}/captain/custom_tools/#{custom_tool.id}",
|
||||
headers: admin.create_new_auth_token
|
||||
end.to change(Captain::CustomTool, :count).by(-1)
|
||||
|
||||
expect(response).to have_http_status(:no_content)
|
||||
end
|
||||
|
||||
context 'when custom tool does not exist' do
|
||||
it 'returns not found status' do
|
||||
delete "/api/v1/accounts/#{account.id}/captain/custom_tools/999999",
|
||||
headers: admin.create_new_auth_token
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user