fix: Allow users to login even if they have access to more than 15 accounts (#4475)

This commit is contained in:
Pranav Raj S
2022-04-14 20:54:26 +05:30
committed by GitHub
parent 80e5d6d7a0
commit 0319b78eac
19 changed files with 368 additions and 350 deletions

View File

@@ -1,28 +1,20 @@
/* eslint no-param-reassign: 0 */
import axios from 'axios';
import Vue from 'vue';
import * as types from '../mutation-types';
import types from '../mutation-types';
import authAPI from '../../api/auth';
import createAxios from '../../helper/APIHelper';
import actionCable from '../../helper/actionCable';
import { setUser, getHeaderExpiry, clearCookiesOnLogout } from '../utils/api';
import { setUser, clearCookiesOnLogout } from '../utils/api';
import { getLoginRedirectURL } from '../../helper/URLHelper';
const state = {
const initialState = {
currentUser: {
id: null,
account_id: null,
channel: null,
accounts: [],
email: null,
name: null,
provider: null,
uid: null,
subscription: {
state: null,
expiry: null,
},
},
currentAccountId: null,
uiFlags: {
isFetching: true,
},
};
// getters
@@ -31,18 +23,22 @@ export const getters = {
return !!$state.currentUser.id;
},
getCurrentUserID(_state) {
return _state.currentUser.id;
getCurrentUserID($state) {
return $state.currentUser.id;
},
getUISettings(_state) {
return _state.currentUser.ui_settings || {};
getUISettings($state) {
return $state.currentUser.ui_settings || {};
},
getCurrentUserAvailability(_state) {
const { accounts = [] } = _state.currentUser;
getAuthUIFlags($state) {
return $state.uiFlags;
},
getCurrentUserAvailability($state, $getters) {
const { accounts = [] } = $state.currentUser;
const [currentAccount = {}] = accounts.filter(
account => account.id === _state.currentAccountId
account => account.id === $getters.getCurrentAccountId
);
return currentAccount.availability;
},
@@ -54,49 +50,45 @@ export const getters = {
return null;
},
getCurrentRole(_state) {
const { accounts = [] } = _state.currentUser;
getCurrentRole($state, $getters) {
const { accounts = [] } = $state.currentUser;
const [currentAccount = {}] = accounts.filter(
account => account.id === _state.currentAccountId
account => account.id === $getters.getCurrentAccountId
);
return currentAccount.role;
},
getCurrentUser(_state) {
return _state.currentUser;
getCurrentUser($state) {
return $state.currentUser;
},
getMessageSignature(_state) {
const { message_signature: messageSignature } = _state.currentUser;
getMessageSignature($state) {
const { message_signature: messageSignature } = $state.currentUser;
return messageSignature || '';
},
getCurrentAccount(_state) {
const { accounts = [] } = _state.currentUser;
getCurrentAccount($state, $getters) {
const { accounts = [] } = $state.currentUser;
const [currentAccount = {}] = accounts.filter(
account => account.id === _state.currentAccountId
account => account.id === $getters.getCurrentAccountId
);
return currentAccount || {};
},
getUserAccounts(_state) {
const { accounts = [] } = _state.currentUser;
getUserAccounts($state) {
const { accounts = [] } = $state.currentUser;
return accounts;
},
};
// actions
export const actions = {
login({ commit }, { ssoAccountId, ...credentials }) {
login(_, { ssoAccountId, ...credentials }) {
return new Promise((resolve, reject) => {
authAPI
.login(credentials)
.then(response => {
commit(types.default.SET_CURRENT_USER);
window.axios = createAxios(axios);
actionCable.init(Vue);
window.location = getLoginRedirectURL(ssoAccountId, response.data);
resolve();
})
@@ -108,43 +100,40 @@ export const actions = {
async validityCheck(context) {
try {
const response = await authAPI.validityCheck();
setUser(response.data.payload.data, getHeaderExpiry(response), {
setUserInSDK: true,
});
context.commit(types.default.SET_CURRENT_USER);
const currentUser = response.data.payload.data;
setUser(currentUser);
context.commit(types.SET_CURRENT_USER, currentUser);
} catch (error) {
if (error?.response?.status === 401) {
clearCookiesOnLogout();
}
}
},
setUser({ commit, dispatch }) {
if (authAPI.isLoggedIn()) {
commit(types.default.SET_CURRENT_USER);
dispatch('validityCheck');
async setUser({ commit, dispatch }) {
if (authAPI.hasAuthCookie()) {
await dispatch('validityCheck');
} else {
commit(types.default.CLEAR_USER);
commit(types.CLEAR_USER);
}
commit(types.SET_CURRENT_USER_UI_FLAGS, { isFetching: false });
},
logout({ commit }) {
commit(types.default.CLEAR_USER);
commit(types.CLEAR_USER);
},
updateProfile: async ({ commit }, params) => {
// eslint-disable-next-line no-useless-catch
try {
const response = await authAPI.profileUpdate(params);
setUser(response.data, getHeaderExpiry(response));
commit(types.default.SET_CURRENT_USER);
commit(types.SET_CURRENT_USER, response.data);
} catch (error) {
throw error;
}
},
deleteAvatar: async ({ commit }) => {
deleteAvatar: async () => {
try {
await authAPI.deleteAvatar();
commit(types.default.SET_CURRENT_USER);
} catch (error) {
// Ignore error
}
@@ -152,10 +141,9 @@ export const actions = {
updateUISettings: async ({ commit }, params) => {
try {
commit(types.default.SET_CURRENT_USER_UI_SETTINGS, params);
commit(types.SET_CURRENT_USER_UI_SETTINGS, params);
const response = await authAPI.updateUISettings(params);
setUser(response.data, getHeaderExpiry(response));
commit(types.default.SET_CURRENT_USER);
commit(types.SET_CURRENT_USER, response.data);
} catch (error) {
// Ignore error
}
@@ -166,45 +154,32 @@ export const actions = {
const response = await authAPI.updateAvailability(params);
const userData = response.data;
const { id } = userData;
setUser(userData, getHeaderExpiry(response));
commit(types.default.SET_CURRENT_USER);
commit(types.SET_CURRENT_USER, response.data);
dispatch('agents/updatePresence', { [id]: params.availability });
} catch (error) {
// Ignore error
}
},
setCurrentAccountId({ commit }, accountId) {
commit(types.default.SET_CURRENT_ACCOUNT_ID, accountId);
},
setCurrentUserAvailability({ commit, state: $state }, data) {
if (data[$state.currentUser.id]) {
commit(
types.default.SET_CURRENT_USER_AVAILABILITY,
data[$state.currentUser.id]
);
commit(types.SET_CURRENT_USER_AVAILABILITY, data[$state.currentUser.id]);
}
},
};
// mutations
export const mutations = {
[types.default.SET_CURRENT_USER_AVAILABILITY](_state, availability) {
[types.SET_CURRENT_USER_AVAILABILITY](_state, availability) {
Vue.set(_state.currentUser, 'availability', availability);
},
[types.default.CLEAR_USER](_state) {
_state.currentUser.id = null;
[types.CLEAR_USER](_state) {
_state.currentUser = initialState.currentUser;
},
[types.default.SET_CURRENT_USER](_state) {
const currentUser = {
...authAPI.getAuthData(),
...authAPI.getCurrentUser(),
};
[types.SET_CURRENT_USER](_state, currentUser) {
Vue.set(_state, 'currentUser', currentUser);
},
[types.default.SET_CURRENT_USER_UI_SETTINGS](_state, { uiSettings }) {
[types.SET_CURRENT_USER_UI_SETTINGS](_state, { uiSettings }) {
Vue.set(_state, 'currentUser', {
..._state.currentUser,
ui_settings: {
@@ -213,13 +188,14 @@ export const mutations = {
},
});
},
[types.default.SET_CURRENT_ACCOUNT_ID](_state, accountId) {
Vue.set(_state, 'currentAccountId', Number(accountId));
[types.SET_CURRENT_USER_UI_FLAGS](_state, { isFetching }) {
Vue.set(_state, 'uiFlags', { isFetching });
},
};
export default {
state,
state: initialState,
getters,
actions,
mutations,

View File

@@ -1,4 +1,3 @@
import authAPI from '../../../api/auth';
import { MESSAGE_TYPE } from 'shared/constants/messages';
import { applyPageFilters } from './helpers';
@@ -40,8 +39,8 @@ const getters = {
return lastEmail;
},
getMineChats: _state => activeFilters => {
const currentUserID = authAPI.getCurrentUser().id;
getMineChats: (_state, _, __, rootGetters) => activeFilters => {
const currentUserID = rootGetters.getCurrentUser?.id;
return _state.allConversations.filter(conversation => {
const { assignee } = conversation.meta;

View File

@@ -29,7 +29,9 @@ describe('#actions', () => {
});
await actions.validityCheck({ commit });
expect(setUser).toHaveBeenCalledTimes(1);
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
expect(commit.mock.calls).toEqual([
[types.default.SET_CURRENT_USER, { id: 1, name: 'John' }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({
@@ -47,8 +49,9 @@ describe('#actions', () => {
headers: { expiry: 581842904 },
});
await actions.updateProfile({ commit }, { name: 'Pranav' });
expect(setUser).toHaveBeenCalledTimes(1);
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
expect(commit.mock.calls).toEqual([
[types.default.SET_CURRENT_USER, { id: 1, name: 'John' }],
]);
});
});
@@ -57,7 +60,8 @@ describe('#actions', () => {
axios.post.mockResolvedValue({
data: {
id: 1,
account_users: [{ account_id: 1, availability_status: 'offline' }],
name: 'John',
accounts: [{ account_id: 1, availability_status: 'offline' }],
},
headers: { expiry: 581842904 },
});
@@ -65,8 +69,16 @@ describe('#actions', () => {
{ commit, dispatch },
{ availability: 'offline', account_id: 1 }
);
expect(setUser).toHaveBeenCalledTimes(1);
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
expect(commit.mock.calls).toEqual([
[
types.default.SET_CURRENT_USER,
{
id: 1,
name: 'John',
accounts: [{ account_id: 1, availability_status: 'offline' }],
},
],
]);
expect(dispatch.mock.calls).toEqual([
['agents/updatePresence', { 1: 'offline' }],
]);
@@ -88,13 +100,20 @@ describe('#actions', () => {
{ commit, dispatch },
{ uiSettings: { is_contact_sidebar_open: false } }
);
expect(setUser).toHaveBeenCalledTimes(1);
expect(commit.mock.calls).toEqual([
[
types.default.SET_CURRENT_USER_UI_SETTINGS,
{ uiSettings: { is_contact_sidebar_open: false } },
],
[types.default.SET_CURRENT_USER],
[
types.default.SET_CURRENT_USER,
{
id: 1,
name: 'John',
availability_status: 'offline',
ui_settings: { is_contact_sidebar_open: true },
},
],
]);
});
});
@@ -103,14 +122,17 @@ describe('#actions', () => {
it('sends correct actions if user is logged in', async () => {
Cookies.getJSON.mockImplementation(() => true);
actions.setUser({ commit, dispatch });
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
expect(commit.mock.calls).toEqual([]);
expect(dispatch.mock.calls).toEqual([['validityCheck']]);
});
it('sends correct actions if user is not logged in', async () => {
Cookies.getJSON.mockImplementation(() => false);
actions.setUser({ commit, dispatch });
expect(commit.mock.calls).toEqual([[types.default.CLEAR_USER]]);
expect(commit.mock.calls).toEqual([
[types.default.CLEAR_USER],
[types.default.SET_CURRENT_USER_UI_FLAGS, { isFetching: false }],
]);
expect(dispatch).toHaveBeenCalledTimes(0);
});
});

View File

@@ -4,39 +4,74 @@ import '../../../../routes';
jest.mock('../../../../routes', () => {});
describe('#getters', () => {
it('isLoggedIn', () => {
expect(getters.isLoggedIn({ currentUser: { id: null } })).toEqual(false);
expect(getters.isLoggedIn({ currentUser: { id: 1 } })).toEqual(true);
describe('#isLoggedIn', () => {
it('return correct value if user data is available', () => {
expect(getters.isLoggedIn({ currentUser: { id: null } })).toEqual(false);
expect(getters.isLoggedIn({ currentUser: { id: 1 } })).toEqual(true);
});
});
it('getCurrentUserID', () => {
expect(getters.getCurrentUserID({ currentUser: { id: 1 } })).toEqual(1);
});
it('getCurrentUser', () => {
expect(
getters.getCurrentUser({ currentUser: { id: 1, name: 'Pranav' } })
).toEqual({ id: 1, name: 'Pranav' });
describe('#getCurrentUser', () => {
it('returns current user id', () => {
expect(getters.getCurrentUserID({ currentUser: { id: 1 } })).toEqual(1);
});
});
it('get', () => {
expect(
getters.getCurrentUserAvailability({
currentAccountId: 1,
currentUser: {
id: 1,
accounts: [{ id: 1, availability: 'busy' }],
},
})
).toEqual('busy');
describe('#getCurrentUser', () => {
it('returns current user object', () => {
expect(
getters.getCurrentUser({ currentUser: { id: 1, name: 'Pranav' } })
).toEqual({ id: 1, name: 'Pranav' });
});
});
it('getUISettings', () => {
expect(
getters.getUISettings({
currentUser: { ui_settings: { is_contact_sidebar_open: true } },
})
).toEqual({ is_contact_sidebar_open: true });
describe('#getCurrentRole', () => {
it('returns current role if account is available', () => {
expect(
getters.getCurrentRole(
{ currentUser: { accounts: [{ id: 1, role: 'admin' }] } },
{ getCurrentAccountId: 1 }
)
).toEqual('admin');
});
it('returns undefined if account is not available', () => {
expect(
getters.getCurrentRole(
{ currentUser: { accounts: [{ id: 1, role: 'admin' }] } },
{ getCurrentAccountId: 2 }
)
).toEqual(undefined);
});
});
describe('#getCurrentUserAvailability', () => {
it('returns correct availability status', () => {
expect(
getters.getCurrentUserAvailability(
{
currentAccountId: 1,
currentUser: {
id: 1,
accounts: [{ id: 1, availability: 'busy' }],
},
},
{ getCurrentAccountId: 1 }
)
).toEqual('busy');
});
});
describe('#getUISettings', () => {
it('return correct UI Settings', () => {
expect(
getters.getUISettings({
currentUser: { ui_settings: { is_contact_sidebar_open: true } },
})
).toEqual({ is_contact_sidebar_open: true });
});
});
describe('#getMessageSignature', () => {
it('Return signature when signature is present', () => {
expect(
@@ -46,11 +81,7 @@ describe('#getters', () => {
).toEqual('Thanks');
});
it('Return empty string when signature is not present', () => {
expect(
getters.getMessageSignature({
currentUser: {},
})
).toEqual('');
expect(getters.getMessageSignature({ currentUser: {} })).toEqual('');
});
});
@@ -59,22 +90,23 @@ describe('#getters', () => {
expect(
getters.getCurrentAccount({
currentUser: {},
currentAccountId: 1,
})
).toEqual({});
expect(
getters.getCurrentAccount({
currentUser: {
accounts: [
{
name: 'Chatwoot',
id: 1,
},
],
getters.getCurrentAccount(
{
currentUser: {
accounts: [
{
name: 'Chatwoot',
id: 1,
},
],
},
currentAccountId: 1,
},
currentAccountId: 1,
})
{ getCurrentAccountId: 1 }
)
).toEqual({
name: 'Chatwoot',
id: 1,
@@ -89,7 +121,6 @@ describe('#getters', () => {
currentUser: {},
})
).toEqual([]);
expect(
getters.getUserAccounts({
currentUser: {

View File

@@ -18,4 +18,28 @@ describe('#mutations', () => {
});
});
});
describe('#SET_CURRENT_USER_UI_FLAGS', () => {
it('set auth ui flags', () => {
const state = {
uiFlags: { isFetching: false },
};
mutations[types.SET_CURRENT_USER_UI_FLAGS](state, { isFetching: true });
expect(state.uiFlags.isFetching).toEqual(true);
});
});
describe('#CLEAR_USER', () => {
it('set auth ui flags', () => {
const state = {
currentUser: { id: 1 },
};
mutations[types.CLEAR_USER](state);
expect(state.currentUser).toEqual({
id: null,
account_id: null,
accounts: [],
email: null,
name: null,
});
});
});
});