chore: Enhance contact merge action for identified users (#4886)

- Discard conflicting keys 
- Do not merge if there is already an identified contact

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
Sojan Jose
2022-06-23 15:48:56 +05:30
committed by GitHub
parent d5ddc9d76c
commit f71980bd95
18 changed files with 311 additions and 114 deletions

View File

@@ -83,12 +83,11 @@ export default {
const { websiteToken, locale, widgetColor } = window.chatwootWebChannel;
this.setLocale(locale);
this.setWidgetColor(widgetColor);
setHeader(window.authToken);
if (this.isIFrame) {
this.registerListeners();
this.sendLoadedEvent();
setHeader('X-Auth-Token', window.authToken);
} else {
setHeader('X-Auth-Token', window.authToken);
this.fetchOldConversations();
this.fetchAvailableAgents(websiteToken);
this.setLocale(getLocale(window.location.search));
@@ -253,7 +252,7 @@ export default {
} else if (message.event === 'remove-label') {
this.$store.dispatch('conversationLabels/destroy', message.label);
} else if (message.event === 'set-user') {
this.$store.dispatch('contacts/update', message);
this.$store.dispatch('contacts/setUser', message);
} else if (message.event === 'set-custom-attributes') {
this.$store.dispatch(
'contacts/setCustomAttributes',

View File

@@ -6,8 +6,11 @@ export default {
get() {
return API.get(buildUrl('widget/contact'));
},
update(identifier, userObject) {
return API.patch(buildUrl('widget/contact'), {
update(userObject) {
return API.patch(buildUrl('widget/contact'), userObject);
},
setUser(identifier, userObject) {
return API.patch(buildUrl('widget/contact/set_user'), {
identifier,
...userObject,
});

View File

@@ -6,7 +6,7 @@ export const API = axios.create({
withCredentials: false,
});
export const setHeader = (key, value) => {
export const setHeader = (value, key = 'X-Auth-Token') => {
API.defaults.headers.common[key] = value;
};

View File

@@ -10,14 +10,16 @@ export const arrayToHashById = array =>
return newMap;
}, {});
export const sendMessage = msg => {
window.parent.postMessage(
`chatwoot-widget:${JSON.stringify({ ...msg })}`,
'*'
);
};
export const IFrameHelper = {
isIFrame: () => window.self !== window.top,
sendMessage: msg => {
window.parent.postMessage(
`chatwoot-widget:${JSON.stringify({ ...msg })}`,
'*'
);
},
sendMessage,
isAValidEvent: e => {
const isDataAString = typeof e.data === 'string';
const isAValidWootEvent =

View File

@@ -1,11 +1,23 @@
import { IFrameHelper } from 'widget/helpers/utils';
import { sendMessage } from 'widget/helpers/utils';
import ContactsAPI from '../../api/contacts';
import { SET_USER_ERROR } from '../../constants/errorTypes';
import { setHeader } from '../../helpers/axios';
const state = {
currentUser: {},
};
const SET_CURRENT_USER = 'SET_CURRENT_USER';
const parseErrorData = error =>
error && error.response && error.response.data ? error.response.data : error;
export const updateWidgetAuthToken = widgetAuthToken => {
if (widgetAuthToken) {
setHeader(widgetAuthToken);
sendMessage({
event: 'setAuthCookie',
data: { widgetAuthToken },
});
}
};
export const getters = {
getCurrentUser(_state) {
@@ -22,13 +34,21 @@ export const actions = {
// Ignore error
}
},
update: async ({ dispatch }, { identifier, user: userObject }) => {
update: async ({ dispatch }, { user }) => {
try {
await ContactsAPI.update(user);
dispatch('get');
} catch (error) {
// Ignore error
}
},
setUser: async ({ dispatch }, { identifier, user: userObject }) => {
try {
const {
email,
name,
avatar_url,
identifier_hash,
identifier_hash: identifierHash,
phone_number,
company_name,
city,
@@ -41,7 +61,7 @@ export const actions = {
email,
name,
avatar_url,
identifier_hash,
identifier_hash: identifierHash,
phone_number,
additional_attributes: {
company_name,
@@ -52,24 +72,19 @@ export const actions = {
},
custom_attributes,
};
await ContactsAPI.update(identifier, user);
const {
data: { widget_auth_token: widgetAuthToken },
} = await ContactsAPI.setUser(identifier, user);
updateWidgetAuthToken(widgetAuthToken);
dispatch('get');
if (identifier_hash) {
if (identifierHash || widgetAuthToken) {
dispatch('conversation/clearConversations', {}, { root: true });
dispatch('conversation/fetchOldConversations', {}, { root: true });
dispatch('conversationAttributes/getAttributes', {}, { root: true });
}
} catch (error) {
const data =
error && error.response && error.response.data
? error.response.data
: error;
IFrameHelper.sendMessage({
event: 'error',
errorType: SET_USER_ERROR,
data,
});
const data = parseErrorData(error);
sendMessage({ event: 'error', errorType: SET_USER_ERROR, data });
}
},
setCustomAttributes: async (_, customAttributes = {}) => {

View File

@@ -1,21 +1,26 @@
import { API } from 'widget/helpers/axios';
import { sendMessage } from 'widget/helpers/utils';
import { actions } from '../../contacts';
const commit = jest.fn();
const dispatch = jest.fn();
jest.mock('widget/helpers/axios');
jest.mock('widget/helpers/utils', () => ({
sendMessage: jest.fn(),
}));
describe('#actions', () => {
describe('#update', () => {
it('sends correct actions', async () => {
describe('#setUser', () => {
it('sends correct actions if contact object is refreshed ', async () => {
const user = {
email: 'thoma@sphadikam.com',
name: 'Adu Thoma',
avatar_url: '',
identifier_hash: 'random_hex_identifier_hash',
};
API.patch.mockResolvedValue({ data: { pubsub_token: 'token' } });
await actions.update({ commit, dispatch }, { identifier: 1, user });
API.patch.mockResolvedValue({ data: { widget_auth_token: 'token' } });
await actions.setUser({ commit, dispatch }, { identifier: 1, user });
expect(sendMessage.mock.calls).toEqual([
[{ data: { widgetAuthToken: 'token' }, event: 'setAuthCookie' }],
]);
expect(commit.mock.calls).toEqual([]);
expect(dispatch.mock.calls).toEqual([
['get'],
@@ -24,5 +29,52 @@ describe('#actions', () => {
['conversationAttributes/getAttributes', {}, { root: true }],
]);
});
it('sends correct actions if identifierHash is passed ', async () => {
const user = {
email: 'thoma@sphadikam.com',
name: 'Adu Thoma',
avatar_url: '',
identifier_hash: '12345',
};
API.patch.mockResolvedValue({ data: { id: 1 } });
await actions.setUser({ commit, dispatch }, { identifier: 1, user });
expect(sendMessage.mock.calls).toEqual([]);
expect(commit.mock.calls).toEqual([]);
expect(dispatch.mock.calls).toEqual([
['get'],
['conversation/clearConversations', {}, { root: true }],
['conversation/fetchOldConversations', {}, { root: true }],
['conversationAttributes/getAttributes', {}, { root: true }],
]);
});
it('does not call sendMessage if contact object is not refreshed ', async () => {
const user = {
email: 'thoma@sphadikam.com',
name: 'Adu Thoma',
avatar_url: '',
};
API.patch.mockResolvedValue({ data: { id: 1 } });
await actions.setUser({ commit, dispatch }, { identifier: 1, user });
expect(sendMessage.mock.calls).toEqual([]);
expect(commit.mock.calls).toEqual([]);
expect(dispatch.mock.calls).toEqual([['get']]);
});
});
describe('#update', () => {
it('sends correct actions', async () => {
const user = {
email: 'thoma@sphadikam.com',
name: 'Adu Thoma',
avatar_url: '',
identifier_hash: 'random_hex_identifier_hash',
};
API.patch.mockResolvedValue({ data: { id: 1 } });
await actions.update({ commit, dispatch }, { identifier: 1, user });
expect(commit.mock.calls).toEqual([]);
expect(dispatch.mock.calls).toEqual([['get']]);
});
});
});