diff --git a/app/javascript/dashboard/api/agentBots.js b/app/javascript/dashboard/api/agentBots.js new file mode 100644 index 000000000..4de6fcee0 --- /dev/null +++ b/app/javascript/dashboard/api/agentBots.js @@ -0,0 +1,9 @@ +import ApiClient from './ApiClient'; + +class AgentBotsAPI extends ApiClient { + constructor() { + super('agent_bots', { accountScoped: true }); + } +} + +export default new AgentBotsAPI(); diff --git a/app/javascript/dashboard/api/specs/agentBots.spec.js b/app/javascript/dashboard/api/specs/agentBots.spec.js new file mode 100644 index 000000000..c89dbfdf5 --- /dev/null +++ b/app/javascript/dashboard/api/specs/agentBots.spec.js @@ -0,0 +1,13 @@ +import AgentBotsAPI from '../agentBots'; +import ApiClient from '../ApiClient'; + +describe('#AgentBotsAPI', () => { + it('creates correct instance', () => { + expect(AgentBotsAPI).toBeInstanceOf(ApiClient); + expect(AgentBotsAPI).toHaveProperty('get'); + expect(AgentBotsAPI).toHaveProperty('show'); + expect(AgentBotsAPI).toHaveProperty('create'); + expect(AgentBotsAPI).toHaveProperty('update'); + expect(AgentBotsAPI).toHaveProperty('delete'); + }); +}); diff --git a/app/javascript/dashboard/store/index.js b/app/javascript/dashboard/store/index.js index 8ffbb2db4..f2fe95b50 100755 --- a/app/javascript/dashboard/store/index.js +++ b/app/javascript/dashboard/store/index.js @@ -3,6 +3,7 @@ import Vuex from 'vuex'; import accounts from './modules/accounts'; import agents from './modules/agents'; +import agentBots from './modules/agentBots'; import attributes from './modules/attributes'; import auth from './modules/auth'; import automations from './modules/automations'; @@ -44,6 +45,7 @@ export default new Vuex.Store({ modules: { accounts, agents, + agentBots, attributes, auth, automations, diff --git a/app/javascript/dashboard/store/modules/agentBots.js b/app/javascript/dashboard/store/modules/agentBots.js new file mode 100644 index 000000000..b5042b1aa --- /dev/null +++ b/app/javascript/dashboard/store/modules/agentBots.js @@ -0,0 +1,107 @@ +import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers'; +import types from '../mutation-types'; +import AgentBotsAPI from '../../api/agentBots'; +import { throwErrorMessage } from '../utils/api'; + +export const state = { + records: [], + uiFlags: { + isFetching: false, + isFetchingItem: false, + isCreating: false, + isDeleting: false, + isUpdating: false, + }, +}; + +export const getters = { + getBots($state) { + return $state.records; + }, + getUIFlags($state) { + return $state.uiFlags; + }, + getBot: $state => botId => { + const [bot] = $state.records.filter(record => record.id === Number(botId)); + return bot || {}; + }, +}; + +export const actions = { + get: async ({ commit }) => { + commit(types.SET_AGENT_BOT_UI_FLAG, { isFetching: true }); + try { + const response = await AgentBotsAPI.get(); + commit(types.SET_AGENT_BOTS, response.data); + } catch (error) { + // Ignore error + } finally { + commit(types.SET_AGENT_BOT_UI_FLAG, { isFetching: false }); + } + }, + create: async ({ commit }, agentBotObj) => { + commit(types.SET_AGENT_BOT_UI_FLAG, { isCreating: true }); + try { + const response = await AgentBotsAPI.create(agentBotObj); + commit(types.ADD_AGENT_BOT, response.data); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_AGENT_BOT_UI_FLAG, { isCreating: false }); + } + }, + update: async ({ commit }, { id, ...agentBotObj }) => { + commit(types.SET_AGENT_BOT_UI_FLAG, { isUpdating: true }); + try { + const response = await AgentBotsAPI.update(id, agentBotObj); + commit(types.EDIT_AGENT_BOT, response.data); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_AGENT_BOT_UI_FLAG, { isUpdating: false }); + } + }, + delete: async ({ commit }, id) => { + commit(types.SET_AGENT_BOT_UI_FLAG, { isDeleting: true }); + try { + await AgentBotsAPI.delete(id); + commit(types.DELETE_AGENT_BOT, id); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_AGENT_BOT_UI_FLAG, { isDeleting: false }); + } + }, + show: async ({ commit }, id) => { + commit(types.SET_AGENT_BOT_UI_FLAG, { isFetchingItem: true }); + try { + const { data } = await AgentBotsAPI.show(id); + commit(types.DELETE_AGENT_BOT, data); + } catch (error) { + throwErrorMessage(error); + } finally { + commit(types.SET_AGENT_BOT_UI_FLAG, { isFetchingItem: false }); + } + }, +}; + +export const mutations = { + [types.SET_AGENT_BOT_UI_FLAG]($state, data) { + $state.uiFlags = { + ...$state.uiFlags, + ...data, + }; + }, + [types.ADD_AGENT_BOT]: MutationHelpers.create, + [types.SET_AGENT_BOTS]: MutationHelpers.set, + [types.EDIT_AGENT_BOT]: MutationHelpers.update, + [types.DELETE_AGENT_BOT]: MutationHelpers.destroy, +}; + +export default { + namespaced: true, + actions, + state, + getters, + mutations, +}; diff --git a/app/javascript/dashboard/store/modules/specs/agentBots/actions.spec.js b/app/javascript/dashboard/store/modules/specs/agentBots/actions.spec.js new file mode 100644 index 000000000..e5a51468e --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/agentBots/actions.spec.js @@ -0,0 +1,93 @@ +import axios from 'axios'; +import { actions } from '../../agentBots'; +import types from '../../../mutation-types'; +import { agentBotRecords } from './fixtures'; + +const commit = jest.fn(); +global.axios = axios; +jest.mock('axios'); + +describe('#actions', () => { + describe('#get', () => { + it('sends correct actions if API is success', async () => { + axios.get.mockResolvedValue({ data: agentBotRecords }); + await actions.get({ commit }); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isFetching: true }], + [types.SET_AGENT_BOTS, agentBotRecords], + [types.SET_AGENT_BOT_UI_FLAG, { isFetching: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.get.mockRejectedValue({ message: 'Incorrect header' }); + await actions.get({ commit }); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isFetching: true }], + [types.SET_AGENT_BOT_UI_FLAG, { isFetching: false }], + ]); + }); + }); + describe('#create', () => { + it('sends correct actions if API is success', async () => { + axios.post.mockResolvedValue({ data: agentBotRecords[0] }); + await actions.create({ commit }, agentBotRecords[0]); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isCreating: true }], + [types.ADD_AGENT_BOT, agentBotRecords[0]], + [types.SET_AGENT_BOT_UI_FLAG, { isCreating: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.post.mockRejectedValue({ message: 'Incorrect header' }); + await expect(actions.create({ commit })).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isCreating: true }], + [types.SET_AGENT_BOT_UI_FLAG, { isCreating: false }], + ]); + }); + }); + + describe('#update', () => { + it('sends correct actions if API is success', async () => { + axios.patch.mockResolvedValue({ data: agentBotRecords[0] }); + await actions.update({ commit }, agentBotRecords[0]); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isUpdating: true }], + [types.EDIT_AGENT_BOT, agentBotRecords[0]], + [types.SET_AGENT_BOT_UI_FLAG, { isUpdating: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.patch.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.update({ commit }, agentBotRecords[0]) + ).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isUpdating: true }], + [types.SET_AGENT_BOT_UI_FLAG, { isUpdating: false }], + ]); + }); + }); + + describe('#delete', () => { + it('sends correct actions if API is success', async () => { + axios.delete.mockResolvedValue({ data: agentBotRecords[0] }); + await actions.delete({ commit }, agentBotRecords[0].id); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isDeleting: true }], + [types.DELETE_AGENT_BOT, agentBotRecords[0].id], + [types.SET_AGENT_BOT_UI_FLAG, { isDeleting: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.delete.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.delete({ commit }, agentBotRecords[0].id) + ).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.SET_AGENT_BOT_UI_FLAG, { isDeleting: true }], + [types.SET_AGENT_BOT_UI_FLAG, { isDeleting: false }], + ]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/agentBots/fixtures.js b/app/javascript/dashboard/store/modules/specs/agentBots/fixtures.js new file mode 100644 index 000000000..e13735b14 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/agentBots/fixtures.js @@ -0,0 +1,15 @@ +export const agentBotRecords = [ + { + id: 11, + name: 'Agent Bot 11', + description: 'Agent Bot Description', + type: 'csml', + }, + + { + id: 12, + name: 'Agent Bot 12', + description: 'Agent Bot Description 12', + type: 'csml', + }, +]; diff --git a/app/javascript/dashboard/store/modules/specs/agentBots/getters.spec.js b/app/javascript/dashboard/store/modules/specs/agentBots/getters.spec.js new file mode 100644 index 000000000..bff536b56 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/agentBots/getters.spec.js @@ -0,0 +1,31 @@ +import { getters } from '../../agentBots'; +import { agentBotRecords } from './fixtures'; + +describe('#getters', () => { + it('getBots', () => { + const state = { records: agentBotRecords }; + expect(getters.getBots(state)).toEqual(agentBotRecords); + }); + + it('getBot', () => { + const state = { records: agentBotRecords }; + expect(getters.getBot(state)(11)).toEqual(agentBotRecords[0]); + }); + + it('getUIFlags', () => { + const state = { + uiFlags: { + isFetching: true, + isCreating: false, + isUpdating: false, + isDeleting: false, + }, + }; + expect(getters.getUIFlags(state)).toEqual({ + isFetching: true, + isCreating: false, + isUpdating: false, + isDeleting: false, + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js new file mode 100644 index 000000000..d76c98404 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/agentBots/mutations.spec.js @@ -0,0 +1,44 @@ +import types from '../../../mutation-types'; +import { mutations } from '../../agentBots'; +import { agentBotRecords } from './fixtures'; + +describe('#mutations', () => { + describe('#SET_AGENT_BOT_UI_FLAG', () => { + it('set uiFlags', () => { + const state = { uiFlags: { isFetchingItem: false } }; + mutations[types.SET_AGENT_BOT_UI_FLAG](state, { isFetchingItem: true }); + expect(state.uiFlags.isFetchingItem).toEqual(true); + }); + }); + describe('#SET_AGENT_BOTS', () => { + it('set agent bot records', () => { + const state = { records: [] }; + mutations[types.SET_AGENT_BOTS](state, agentBotRecords); + expect(state.records).toEqual(agentBotRecords); + }); + }); + describe('#ADD_AGENT_BOT', () => { + it('push newly created bot to the store', () => { + const state = { records: [agentBotRecords[0]] }; + mutations[types.ADD_AGENT_BOT](state, agentBotRecords[1]); + expect(state.records).toEqual([agentBotRecords[0], agentBotRecords[1]]); + }); + }); + describe('#EDIT_AGENT_BOT', () => { + it('update agent bot record', () => { + const state = { records: [agentBotRecords[0]] }; + mutations[types.EDIT_AGENT_BOT](state, { + id: 11, + name: 'agent-bot-11', + }); + expect(state.records[0].name).toEqual('agent-bot-11'); + }); + }); + describe('#DELETE_AGENT_BOT', () => { + it('delete agent bot record', () => { + const state = { records: [agentBotRecords[0]] }; + mutations[types.DELETE_AGENT_BOT](state, agentBotRecords[0]); + expect(state.records).toEqual([agentBotRecords[0]]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index 17a2c78ee..bf971931d 100755 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -245,4 +245,11 @@ export default { UPDATE_CATEGORY: 'UPDATE_CATEGORY', REMOVE_CATEGORY: 'REMOVE_CATEGORY', REMOVE_CATEGORY_ID: 'REMOVE_CATEGORY_ID', + + // Agent Bots + SET_AGENT_BOT_UI_FLAG: 'SET_AGENT_BOT_UI_FLAG', + SET_AGENT_BOTS: 'SET_AGENT_BOTS', + ADD_AGENT_BOT: 'ADD_AGENT_BOT', + EDIT_AGENT_BOT: 'EDIT_AGENT_BOT', + DELETE_AGENT_BOT: 'DELETE_AGENT_BOT', };