Commit c0dc8578 authored by nicolasdular's avatar nicolasdular Committed by Denys Mishunov

Add usage quota page

This adds a usage quotas page for users. Usage quotas includes
pipeline quota and storage quota. This MR still puts the page
behind a feature flag until we roll out the the new page
and replace the current pipeline quota page.
parent 7c33b405
...@@ -153,6 +153,9 @@ ...@@ -153,6 +153,9 @@
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
= _('Authentication Log') = _('Authentication Log')
- if Feature.enabled?(:user_usage_quota)
= render_if_exists 'layouts/nav/sidebar/profile_usage_quotas_link'
- else
= render_if_exists 'layouts/nav/sidebar/profile_pipeline_quota_link' = render_if_exists 'layouts/nav/sidebar/profile_pipeline_quota_link'
= render 'shared/sidebar_toggle_button' = render 'shared/sidebar_toggle_button'
import storageCounter from 'ee/storage_counter';
import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('#js-storage-counter-app')) {
storageCounter();
// eslint-disable-next-line no-new
new LinkedTabs({
defaultAction: '#pipelines-quota-tab',
parentEl: '.js-storage-tabs',
hashedTabs: true,
});
}
});
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
class Profiles::PipelineQuotaController < Profiles::ApplicationController class Profiles::PipelineQuotaController < Profiles::ApplicationController
def index def index
return redirect_to(profile_usage_quotas_path) if Feature.enabled?(:user_usage_quota)
@namespace = current_user.namespace @namespace = current_user.namespace
@projects = @namespace.projects.with_shared_runners_limit_enabled.page(params[:page]) @projects = @namespace.projects.with_shared_runners_limit_enabled.page(params[:page])
end end
......
# frozen_string_literal: true
class Profiles::UsageQuotasController < Profiles::ApplicationController
def index
return redirect_to(profile_pipeline_quota_path) if Feature.disabled?(:user_usage_quota)
@namespace = current_user.namespace
@projects = @namespace.projects.with_shared_runners_limit_enabled.page(params[:page])
end
end
= nav_link(controller: :usage_quotas) do
= link_to profile_usage_quotas_path do
.nav-icon-container
= custom_icon('pipeline')
%span.nav-item-name
= s_('UsageQuota|Usage Quotas')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :usage_quotas, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_usage_quotas_path do
%strong.fly-out-top-item-name
= s_('UsageQuota|Usage Quotas')
- page_title s_("UsageQuota|Usage")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title
= s_('UsageQuota|Usage Quotas')
.row
.col-sm-6
= s_('UsageQuota|Usage of resources across your projects').html_safe
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
%ul.nav.nav-tabs.nav-links.scrolling-tabs.separator.js-storage-tabs{ role: 'tablist' }
%li.nav-item
%a.nav-link#pipelines-quota{ data: { toggle: "tab", action: '#pipelines-quota-tab' }, href: '#pipelines-quota-tab', 'aria-controls': '#pipelines-quota-tab', 'aria-selected': true }
= s_('UsageQuota|Pipelines')
%li.nav-item
%a.nav-link#storage-quota{ data: { toggle: "tab", action: '#storage-quota-tab' }, href: '#storage-quota-tab', 'aria-controls': '#storage-quota-tab', 'aria-selected': false }
= s_('UsageQuota|Storage')
.tab-content
.tab-pane#pipelines-quota-tab
= render "namespaces/pipelines_quota/list",
locals: { namespace: @namespace, projects: @projects }
.tab-pane#storage-quota-tab
#js-storage-counter-app{ data: { namespace_path: @namespace.full_path, help_page_path: help_page_path('user/group/index.md', anchor: 'storage-usage-quota-starter')} }
...@@ -9,6 +9,7 @@ resource :profile, only: [] do ...@@ -9,6 +9,7 @@ resource :profile, only: [] do
end end
resources :pipeline_quota, only: [:index] resources :pipeline_quota, only: [:index]
resources :usage_quotas, only: [:index]
resources :billings, only: [:index] resources :billings, only: [:index]
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Profiles::PipelineQuotaController do
let_it_be(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET index' do
context 'when feature flag user_usage_quota is enabled' do
it 'redirects to usage quota page' do
get :index
expect(subject).to redirect_to(profile_usage_quotas_path)
end
end
context 'when feature flag user_usage_quota is disabled' do
before do
stub_feature_flags(user_usage_quota: false)
end
it 'renders pipeline quota page' do
get :index
expect(subject).to render_template(:index)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Profiles::UsageQuotasController do
let_it_be(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET index' do
context 'when feature flag user_usage_quota is disabled' do
before do
stub_feature_flags(user_usage_quota: false)
end
it 'redirects to pipeline quota page' do
get :index
expect(subject).to redirect_to(profile_pipeline_quota_path)
end
end
context 'when feature flag user_usage_quota is enabled' do
it 'renders usage quota page' do
get :index
expect(subject).to render_template(:index)
end
end
end
end
...@@ -13,6 +13,7 @@ describe 'Profile > Pipeline Quota' do ...@@ -13,6 +13,7 @@ describe 'Profile > Pipeline Quota' do
before do before do
gitlab_sign_in(user) gitlab_sign_in(user)
stub_feature_flags(user_usage_quota: false)
end end
it 'is linked within the profile page' do it 'is linked within the profile page' do
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Profile > Usage Quota' do
using RSpec::Parameterized::TableSyntax
let_it_be(:user, reload: true) { create(:user) }
let_it_be(:namespace, reload: true) { user.namespace }
let_it_be(:statistics, reload: true) { create(:namespace_statistics, namespace: namespace) }
let_it_be(:project, reload: true) { create(:project, namespace: namespace) }
let_it_be(:other_project) { create(:project, namespace: namespace, shared_runners_enabled: false) }
before do
gitlab_sign_in(user)
end
it 'is linked within the profile page' do
visit profile_path
page.within('.nav-sidebar') do
expect(page).to have_selector(:link_or_button, 'Usage Quotas')
end
end
describe 'shared runners use' do
where(:shared_runners_enabled, :used, :quota, :usage_class, :usage_text) do
false | 300 | 500 | 'success' | '300 / Unlimited minutes 0% used'
true | 300 | nil | 'success' | '300 / Unlimited minutes Unlimited'
true | 300 | 500 | 'success' | '300 / 500 minutes 60% used'
true | 1000 | 500 | 'danger' | '1000 / 500 minutes 200% used'
end
with_them do
let(:no_shared_runners_text) { 'Shared runners are disabled, so there are no limits set on pipeline usage' }
before do
project.update!(shared_runners_enabled: shared_runners_enabled)
statistics.update!(shared_runners_seconds: used.minutes.to_i)
namespace.update!(shared_runners_minutes_limit: quota)
visit profile_usage_quotas_path
end
it 'shows the correct quota status' do
page.within('.pipeline-quota') do
expect(page).to have_content(usage_text)
expect(page).to have_selector(".bg-#{usage_class}")
end
end
it 'shows the correct per-project metrics' do
page.within('.pipeline-project-metrics') do
expect(page).not_to have_content(other_project.name)
if shared_runners_enabled
expect(page).to have_content(project.name)
expect(page).not_to have_content(no_shared_runners_text)
else
expect(page).not_to have_content(project.name)
expect(page).to have_content(no_shared_runners_text)
end
end
end
end
end
end
...@@ -21966,6 +21966,9 @@ msgstr "" ...@@ -21966,6 +21966,9 @@ msgstr ""
msgid "UsageQuota|Usage of group resources across the projects in the %{strong_start}%{group_name}%{strong_end} group" msgid "UsageQuota|Usage of group resources across the projects in the %{strong_start}%{group_name}%{strong_end} group"
msgstr "" msgstr ""
msgid "UsageQuota|Usage of resources across your projects"
msgstr ""
msgid "UsageQuota|Usage since" msgid "UsageQuota|Usage since"
msgstr "" msgstr ""
......
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