diff --git a/config/routes.rb b/config/routes.rb index 41ee52f42..4aad60039 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,6 +57,7 @@ Rails.application.routes.draw do post :playground end resources :inboxes, only: [:index, :create, :destroy], param: :inbox_id + resources :scenarios end resources :assistant_responses resources :bulk_actions, only: [:create] diff --git a/enterprise/app/controllers/api/v1/accounts/captain/assistants/scenarios_controller.rb b/enterprise/app/controllers/api/v1/accounts/captain/assistants/scenarios_controller.rb new file mode 100644 index 000000000..5aa72b3be --- /dev/null +++ b/enterprise/app/controllers/api/v1/accounts/captain/assistants/scenarios_controller.rb @@ -0,0 +1,42 @@ +class Api::V1::Accounts::Captain::Assistants::ScenariosController < Api::V1::Accounts::BaseController + before_action -> { check_authorization(Captain::Scenario) } + before_action :set_assistant + before_action :set_scenario, only: [:show, :update, :destroy] + + def index + @scenarios = assistant_scenarios.enabled + end + + def show; end + + def create + @scenario = assistant_scenarios.create!(scenario_params) + end + + def update + @scenario.update!(scenario_params) + end + + def destroy + @scenario.destroy + head :no_content + end + + private + + def set_assistant + @assistant = current_account.captain_assistants.find(params[:assistant_id]) + end + + def set_scenario + @scenario = assistant_scenarios.find(params[:id]) + end + + def assistant_scenarios + @assistant.scenarios + end + + def scenario_params + params.require(:scenario).permit(:title, :description, :instruction, :enabled, tools: []) + end +end \ No newline at end of file diff --git a/spec/enterprise/controllers/api/v1/accounts/captain/assistants/scenarios_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/captain/assistants/scenarios_controller_spec.rb new file mode 100644 index 000000000..37fdd940a --- /dev/null +++ b/spec/enterprise/controllers/api/v1/accounts/captain/assistants/scenarios_controller_spec.rb @@ -0,0 +1,258 @@ +require 'rails_helper' + +RSpec.describe 'Api::V1::Accounts::Captain::Assistants::Scenarios', type: :request do + let(:account) { create(:account) } + let(:admin) { create(:user, account: account, role: :administrator) } + let(:agent) { create(:user, account: account, role: :agent) } + let(:assistant) { create(:captain_assistant, account: account) } + + def json_response + JSON.parse(response.body, symbolize_names: true) + end + + describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios' do + context 'when it is an un-authenticated user' do + it 'returns unauthorized status' do + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios" + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an agent' do + it 'returns success status' do + create_list(:captain_scenario, 3, assistant: assistant, account: account) + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(json_response[:data].length).to eq(3) + end + end + + context 'when it is an admin' do + it 'returns success status and scenarios' do + create_list(:captain_scenario, 5, assistant: assistant, account: account) + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios", + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(json_response[:data].length).to eq(5) + end + + it 'returns only enabled scenarios' do + create(:captain_scenario, assistant: assistant, account: account, enabled: true) + create(:captain_scenario, assistant: assistant, account: account, enabled: false) + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios", + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(json_response[:data].length).to eq(1) + expect(json_response[:data].first[:enabled]).to be(true) + end + end + end + + describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios/{id}' do + let(:scenario) { create(:captain_scenario, assistant: assistant, account: account) } + + context 'when it is an un-authenticated user' do + it 'returns unauthorized status' do + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}" + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an agent' do + it 'returns success status and scenario' do + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(json_response[:id]).to eq(scenario.id) + expect(json_response[:title]).to eq(scenario.title) + end + end + + context 'when scenario does not exist' do + it 'returns not found status' do + get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/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/assistants/{assistant.id}/scenarios' do + let(:valid_attributes) do + { + scenario: { + title: 'Test Scenario', + description: 'Test description', + instruction: 'Test instruction', + enabled: true, + tools: %w[tool1 tool2] + } + } + end + + context 'when it is an un-authenticated user' do + it 'returns unauthorized status' do + post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios", + 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/assistants/#{assistant.id}/scenarios", + 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 scenario and returns success status' do + expect do + post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios", + params: valid_attributes, + headers: admin.create_new_auth_token, + as: :json + end.to change(Captain::Scenario, :count).by(1) + + expect(response).to have_http_status(:success) + expect(json_response[:title]).to eq('Test Scenario') + expect(json_response[:description]).to eq('Test description') + expect(json_response[:enabled]).to be(true) + expect(json_response[:assistant_id]).to eq(assistant.id) + end + + context 'with invalid parameters' do + let(:invalid_attributes) do + { + scenario: { + title: '', + description: '', + instruction: '' + } + } + end + + it 'returns unprocessable entity status' do + post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios", + params: invalid_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/assistants/{assistant.id}/scenarios/{id}' do + let(:scenario) { create(:captain_scenario, assistant: assistant, account: account) } + let(:update_attributes) do + { + scenario: { + title: 'Updated Scenario 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/assistants/#{assistant.id}/scenarios/#{scenario.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/assistants/#{assistant.id}/scenarios/#{scenario.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 scenario and returns success status' do + patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.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 Scenario Title') + expect(json_response[:enabled]).to be(false) + end + + context 'with invalid parameters' do + let(:invalid_attributes) do + { + scenario: { + title: '' + } + } + end + + it 'returns unprocessable entity status' do + patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.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/assistants/{assistant.id}/scenarios/{id}' do + let!(:scenario) { create(:captain_scenario, assistant: assistant, account: account) } + + context 'when it is an un-authenticated user' do + it 'returns unauthorized status' do + delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.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/assistants/#{assistant.id}/scenarios/#{scenario.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 scenario and returns no content status' do + expect do + delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}", + headers: admin.create_new_auth_token + end.to change(Captain::Scenario, :count).by(-1) + + expect(response).to have_http_status(:no_content) + end + + context 'when scenario does not exist' do + it 'returns not found status' do + delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/999999", + headers: admin.create_new_auth_token + + expect(response).to have_http_status(:not_found) + end + end + end + end +end \ No newline at end of file