fix: Improve performance of most hit APIs in widget (#11089)
- Cache campaigns for better performance - Fix N+1 queries in inbox members - Remove unused related articles
This commit is contained in:
@@ -2,6 +2,10 @@ class Api::V1::Widget::CampaignsController < Api::V1::Widget::BaseController
|
||||
skip_before_action :set_contact
|
||||
|
||||
def index
|
||||
@campaigns = @web_widget.inbox.campaigns.where(enabled: true)
|
||||
@campaigns = @web_widget
|
||||
.inbox
|
||||
.campaigns
|
||||
.where(enabled: true, account_id: @web_widget.inbox.account_id)
|
||||
.includes(:sender)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,6 @@ class Api::V1::Widget::InboxMembersController < Api::V1::Widget::BaseController
|
||||
skip_before_action :set_contact
|
||||
|
||||
def index
|
||||
@inbox_members = @web_widget.inbox.inbox_members.includes(:user)
|
||||
@inbox_members = @web_widget.inbox.inbox_members.includes(user: { avatar_attachment: :blob })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
const state = {
|
||||
records: [],
|
||||
uiFlags: {
|
||||
hasFetched: false,
|
||||
isError: false,
|
||||
},
|
||||
activeCampaign: {},
|
||||
@@ -30,6 +31,7 @@ const resetCampaignTimers = (
|
||||
|
||||
export const getters = {
|
||||
getCampaigns: $state => $state.records,
|
||||
getUIFlags: $state => $state.uiFlags,
|
||||
getActiveCampaign: $state => $state.activeCampaign,
|
||||
};
|
||||
|
||||
@@ -53,15 +55,21 @@ export const actions = {
|
||||
}
|
||||
},
|
||||
initCampaigns: async (
|
||||
{ getters: { getCampaigns: campaigns }, dispatch },
|
||||
{ getters: { getCampaigns: campaigns, getUIFlags: uiFlags }, dispatch },
|
||||
{ currentURL, websiteToken, isInBusinessHours }
|
||||
) => {
|
||||
if (!campaigns.length) {
|
||||
dispatch('fetchCampaigns', {
|
||||
websiteToken,
|
||||
currentURL,
|
||||
isInBusinessHours,
|
||||
});
|
||||
// This check is added to ensure that the campaigns are fetched once
|
||||
// On high traffic sites, if the campaigns are empty, the API is called
|
||||
// every time the user changes the URL (in case of the SPA)
|
||||
// So, we need to ensure that the campaigns are fetched only once
|
||||
if (!uiFlags.hasFetched) {
|
||||
dispatch('fetchCampaigns', {
|
||||
websiteToken,
|
||||
currentURL,
|
||||
isInBusinessHours,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
resetCampaignTimers(
|
||||
campaigns,
|
||||
@@ -127,6 +135,7 @@ export const actions = {
|
||||
export const mutations = {
|
||||
setCampaigns($state, data) {
|
||||
$state.records = data;
|
||||
$state.uiFlags.hasFetched = true;
|
||||
},
|
||||
setActiveCampaign($state, data) {
|
||||
$state.activeCampaign = data;
|
||||
|
||||
@@ -63,15 +63,37 @@ describe('#actions', () => {
|
||||
};
|
||||
it('sends correct actions if campaigns are empty', async () => {
|
||||
await actions.initCampaigns(
|
||||
{ dispatch, getters: { getCampaigns: [] } },
|
||||
{
|
||||
dispatch,
|
||||
getters: { getCampaigns: [], getUIFlags: { hasFetched: false } },
|
||||
},
|
||||
actionParams
|
||||
);
|
||||
expect(dispatch.mock.calls).toEqual([['fetchCampaigns', actionParams]]);
|
||||
expect(campaignTimer.initTimers).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('do not refetch if the campaigns are fetched once', async () => {
|
||||
await actions.initCampaigns(
|
||||
{
|
||||
dispatch,
|
||||
getters: { getCampaigns: [], getUIFlags: { hasFetched: true } },
|
||||
},
|
||||
actionParams
|
||||
);
|
||||
expect(dispatch.mock.calls).toEqual([]);
|
||||
expect(campaignTimer.initTimers).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('resets time if campaigns are available', async () => {
|
||||
await actions.initCampaigns(
|
||||
{ dispatch, getters: { getCampaigns: campaigns } },
|
||||
{
|
||||
dispatch,
|
||||
getters: {
|
||||
getCampaigns: campaigns,
|
||||
getUIFlags: { hasFetched: true },
|
||||
},
|
||||
},
|
||||
actionParams
|
||||
);
|
||||
expect(dispatch.mock.calls).toEqual([]);
|
||||
|
||||
@@ -7,9 +7,10 @@ vi.mock('widget/store/index.js', () => ({
|
||||
describe('#mutations', () => {
|
||||
describe('#setCampaigns', () => {
|
||||
it('set campaign records', () => {
|
||||
const state = { records: [] };
|
||||
const state = { records: [], uiFlags: {} };
|
||||
mutations.setCampaigns(state, campaigns);
|
||||
expect(state.records).toEqual(campaigns);
|
||||
expect(state.uiFlags.hasFetched).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,26 +4,6 @@ json.locale category.locale
|
||||
json.description category.description
|
||||
json.position category.position
|
||||
|
||||
json.related_categories do
|
||||
if category.related_categories.any?
|
||||
json.array! category.related_categories.each do |related_category|
|
||||
json.partial! partial: 'public/api/v1/models/associated_category', formats: [:json], category: related_category
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if category.parent_category.present?
|
||||
json.parent_category do
|
||||
json.partial! partial: 'public/api/v1/models/associated_category', formats: [:json], category: category.parent_category
|
||||
end
|
||||
end
|
||||
|
||||
if category.root_category.present?
|
||||
json.root_category do
|
||||
json.partial! partial: 'public/api/v1/models/associated_category', formats: [:json], category: category.root_category
|
||||
end
|
||||
end
|
||||
|
||||
json.meta do
|
||||
json.articles_count category.articles.published.size
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user