Commit c205a54b authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Prevent loading of cohorts data if not necessary

This changes the tabs in the admin user list to do a full page reload so
that we load data only for the active tab.

Changelog: performance
parent 61dd1268
import Api from '~/api';
import { historyPushState } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
const COHORTS_PANE = 'cohorts';
const COHORTS_PANE_TAB_CLICK_EVENT = 'i_analytics_cohorts';
const tabClickHandler = (e) => {
const { hash } = e.currentTarget;
let tab = null;
if (hash === `#${COHORTS_PANE}`) {
tab = COHORTS_PANE;
Api.trackRedisHllUserEvent(COHORTS_PANE_TAB_CLICK_EVENT);
}
const newUrl = mergeUrlParams({ tab }, window.location.href);
historyPushState(newUrl);
};
const initTabs = () => {
const tabLinks = document.querySelectorAll('.js-users-tab-item a');
if (tabLinks.length) {
tabLinks.forEach((tabLink) => {
tabLink.addEventListener('click', (e) => tabClickHandler(e));
});
}
};
export default initTabs;
import Vue from 'vue'; import Vue from 'vue';
import { initAdminUsersApp } from '~/admin/users'; import { initAdminUsersApp } from '~/admin/users';
import initTabs from '~/admin/users/tabs';
import initConfirmModal from '~/confirm_modal'; import initConfirmModal from '~/confirm_modal';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
...@@ -62,5 +61,4 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -62,5 +61,4 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
initConfirmModal(); initConfirmModal();
initTabs();
}); });
...@@ -6,6 +6,6 @@ class Admin::CohortsController < Admin::ApplicationController ...@@ -6,6 +6,6 @@ class Admin::CohortsController < Admin::ApplicationController
# Backwards compatibility. Remove it and routing in 14.0 # Backwards compatibility. Remove it and routing in 14.0
# @see https://gitlab.com/gitlab-org/gitlab/-/issues/299303 # @see https://gitlab.com/gitlab-org/gitlab/-/issues/299303
def index def index
redirect_to admin_users_path(tab: 'cohorts') redirect_to cohorts_admin_users_path
end end
end end
...@@ -4,7 +4,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -4,7 +4,7 @@ class Admin::UsersController < Admin::ApplicationController
include RoutableActions include RoutableActions
include Analytics::UniqueVisitsHelper include Analytics::UniqueVisitsHelper
before_action :user, except: [:index, :new, :create] before_action :user, except: [:index, :cohorts, :new, :create]
before_action :check_impersonation_availability, only: :impersonate before_action :check_impersonation_availability, only: :impersonate
before_action :ensure_destroy_prerequisites_met, only: [:destroy] before_action :ensure_destroy_prerequisites_met, only: [:destroy]
...@@ -13,16 +13,19 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -13,16 +13,19 @@ class Admin::UsersController < Admin::ApplicationController
PAGINATION_WITH_COUNT_LIMIT = 1000 PAGINATION_WITH_COUNT_LIMIT = 1000
def index def index
return redirect_to cohorts_admin_users_path if params[:tab] == 'cohorts'
@users = User.filter_items(params[:filter]).order_name_asc @users = User.filter_items(params[:filter]).order_name_asc
@users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present? @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
@users = users_with_included_associations(@users) @users = users_with_included_associations(@users)
@users = @users.sort_by_attribute(@sort = params[:sort]) @users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page]) @users = @users.page(params[:page])
@users = @users.without_count if paginate_without_count? @users = @users.without_count if paginate_without_count?
end
def cohorts
@cohorts = load_cohorts @cohorts = load_cohorts
track_cohorts_visit
track_cohorts_visit if params[:tab] == 'cohorts'
end end
def show def show
......
%ul.nav-links.nav-tabs.nav.js-users-tabs{ role: 'tablist' }
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: admin_users_path, class: active_when(current_page?(admin_users_path)), role: 'tab' }
= s_('AdminUsers|Users')
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: cohorts_admin_users_path, class: active_when(current_page?(cohorts_admin_users_path)), role: 'tab' }
= s_('AdminUsers|Cohorts')
- page_title _("Users")
= render 'tabs'
.tab-content
.tab-pane.active
= render 'cohorts'
- page_title _("Users") - page_title _("Users")
%ul.nav-links.nav-tabs.nav.js-users-tabs{ role: 'tablist' } = render 'tabs'
%li.nav-item.js-users-tab-item{ role: 'presentation' }
%a.nav-link{ href: '#users', class: active_when(params[:tab] != 'cohorts'), data: { toggle: 'tab' }, role: 'tab' }
= s_('AdminUsers|Users')
%li.nav-item.js-users-tab-item{ role: 'presentation' }
%a.nav-link{ href: '#cohorts', class: active_when(params[:tab] == 'cohorts'), data: { toggle: 'tab' }, role: 'tab' }
= s_('AdminUsers|Cohorts')
.tab-content .tab-content
.tab-pane{ id: 'users', class: ('active' if params[:tab] != 'cohorts') } .tab-pane.active
= render 'users' = render 'users'
.tab-pane{ id: 'cohorts', class: ('active' if params[:tab] == 'cohorts') }
= render 'cohorts'
---
title: Prevent loading of cohorts data in the admin users list
merge_request: 59890
author:
type: performance
...@@ -10,6 +10,12 @@ namespace :admin do ...@@ -10,6 +10,12 @@ namespace :admin do
end end
end end
collection do
scope '/-/' do
get :cohorts
end
end
member do member do
get :projects get :projects
get :keys get :keys
......
...@@ -12,6 +12,6 @@ RSpec.describe Admin::CohortsController do ...@@ -12,6 +12,6 @@ RSpec.describe Admin::CohortsController do
it 'redirects to Overview->Users' do it 'redirects to Overview->Users' do
get :index get :index
expect(response).to redirect_to(admin_users_path(tab: 'cohorts')) expect(response).to redirect_to(cohorts_admin_users_path)
end end
end end
...@@ -30,11 +30,6 @@ RSpec.describe Admin::UsersController do ...@@ -30,11 +30,6 @@ RSpec.describe Admin::UsersController do
expect(assigns(:users).first.association(:authorized_projects)).to be_loaded expect(assigns(:users).first.association(:authorized_projects)).to be_loaded
end end
it_behaves_like 'tracking unique visits', :index do
let(:target_id) { 'i_analytics_cohorts' }
let(:request_params) { { tab: 'cohorts' } }
end
context 'pagination' do context 'pagination' do
context 'when number of users is over the pagination limit' do context 'when number of users is over the pagination limit' do
before do before do
...@@ -59,6 +54,12 @@ RSpec.describe Admin::UsersController do ...@@ -59,6 +54,12 @@ RSpec.describe Admin::UsersController do
end end
end end
describe 'GET #cohorts' do
it_behaves_like 'tracking unique visits', :cohorts do
let(:target_id) { 'i_analytics_cohorts' }
end
end
describe 'GET :id' do describe 'GET :id' do
it 'finds a user case-insensitively' do it 'finds a user case-insensitively' do
user = create(:user, username: 'CaseSensitive') user = create(:user, username: 'CaseSensitive')
......
...@@ -10,36 +10,39 @@ RSpec.describe "Admin::Users" do ...@@ -10,36 +10,39 @@ RSpec.describe "Admin::Users" do
gitlab_enable_admin_mode_sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user)
end end
describe 'Tabs', :js do describe 'Tabs' do
let(:tabs_selector) { '.js-users-tabs' } let(:tabs_selector) { '.js-users-tabs' }
let(:active_tab_selector) { '.nav-link.active' } let(:active_tab_selector) { '.nav-link.active' }
it 'does not add the tab param when the Users tab is selected' do it 'links to the Users tab' do
visit admin_users_path visit cohorts_admin_users_path
within tabs_selector do within tabs_selector do
click_link 'Users' click_link 'Users'
expect(page).to have_selector active_tab_selector, text: 'Users'
end end
expect(page).to have_current_path(admin_users_path) expect(page).to have_current_path(admin_users_path)
end end
it 'adds the ?tab=cohorts param when the Cohorts tab is selected' do it 'links to the Cohorts tab' do
visit admin_users_path visit admin_users_path
within tabs_selector do within tabs_selector do
click_link 'Cohorts' click_link 'Cohorts'
expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end end
expect(page).to have_current_path(admin_users_path(tab: 'cohorts')) expect(page).to have_current_path(cohorts_admin_users_path)
expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end end
it 'shows the cohorts tab when the tab param is set' do it 'redirects legacy route' do
visit admin_users_path(tab: 'cohorts') visit admin_users_path(tab: 'cohorts')
within tabs_selector do expect(page).to have_current_path(cohorts_admin_users_path)
expect(page).to have_selector active_tab_selector, text: 'Cohorts'
end
end end
end end
......
import initTabs from '~/admin/users/tabs';
import Api from '~/api';
jest.mock('~/api.js');
jest.mock('~/lib/utils/common_utils');
describe('tabs', () => {
beforeEach(() => {
setFixtures(`
<div>
<div class="js-users-tab-item">
<a href="#users" data-testid='users-tab'>Users</a>
</div>
<div class="js-users-tab-item">
<a href="#cohorts" data-testid='cohorts-tab'>Cohorts</a>
</div>
</div`);
initTabs();
});
afterEach(() => {});
describe('tracking', () => {
it('tracks event when cohorts tab is clicked', () => {
document.querySelector('[data-testid="cohorts-tab"]').click();
expect(Api.trackRedisHllUserEvent).toHaveBeenCalledWith('i_analytics_cohorts');
});
it('does not track an event when users tab is clicked', () => {
document.querySelector('[data-testid="users-tab"]').click();
expect(Api.trackRedisHllUserEvent).not.toHaveBeenCalled();
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment