Commit 92e75774 authored by Magdalena Frankiewicz's avatar Magdalena Frankiewicz

Move Analytics to Admin panel

Remove analytics icon from top nav bar
Move Cohorts and DevOps Score pages to Admin panel
under Analytics submenu
Keep old paths but redirect to new pages under /admin
Make sure that Cohorts and DevOps Score are
visible only to Admin, remove a toggle that
was enabling instance statistics to be seen
by not admin user
Ignore instance_statistics_visibility_private
column as the first step to drop it,
remove obsolete condition from global policy
and ignored application setting
parent 99c64100
# frozen_string_literal: true # frozen_string_literal: true
class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationController class Admin::CohortsController < Admin::ApplicationController
include Analytics::UniqueVisitsHelper include Analytics::UniqueVisitsHelper
before_action :authenticate_usage_ping_enabled_or_admin!
track_unique_visits :index, target_id: 'i_analytics_cohorts' track_unique_visits :index, target_id: 'i_analytics_cohorts'
def index def index
...@@ -16,8 +14,4 @@ class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationCon ...@@ -16,8 +14,4 @@ class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationCon
@cohorts = CohortsSerializer.new.represent(cohorts_results) @cohorts = CohortsSerializer.new.represent(cohorts_results)
end end
end end
def authenticate_usage_ping_enabled_or_admin!
render_404 unless Gitlab::CurrentSettings.usage_ping_enabled || current_user.admin?
end
end end
# frozen_string_literal: true # frozen_string_literal: true
class InstanceStatistics::DevOpsScoreController < InstanceStatistics::ApplicationController class Admin::DevOpsScoreController < Admin::ApplicationController
include Analytics::UniqueVisitsHelper include Analytics::UniqueVisitsHelper
track_unique_visits :index, target_id: 'i_analytics_dev_ops_score' track_unique_visits :show, target_id: 'i_analytics_dev_ops_score'
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def index def show
@metric = DevOpsScore::Metric.order(:created_at).last&.present @metric = DevOpsScore::Metric.order(:created_at).last&.present
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
# frozen_string_literal: true
class InstanceStatistics::ApplicationController < ApplicationController
before_action :authorize_read_instance_statistics!
layout 'instance_statistics'
def authorize_read_instance_statistics!
render_404 unless can?(current_user, :read_instance_statistics)
end
end
...@@ -298,7 +298,6 @@ module ApplicationSettingsHelper ...@@ -298,7 +298,6 @@ module ApplicationSettingsHelper
:unique_ips_limit_per_user, :unique_ips_limit_per_user,
:unique_ips_limit_time_window, :unique_ips_limit_time_window,
:usage_ping_enabled, :usage_ping_enabled,
:instance_statistics_visibility_private,
:user_default_external, :user_default_external,
:user_show_add_ssh_key_message, :user_show_add_ssh_key_message,
:user_default_internal_regex, :user_default_internal_regex,
......
...@@ -62,6 +62,10 @@ module NavHelper ...@@ -62,6 +62,10 @@ module NavHelper
%w(system_info background_jobs health_check requests_profiles) %w(system_info background_jobs health_check requests_profiles)
end end
def admin_analytics_nav_links
%w(dev_ops_score cohorts)
end
def group_issues_sub_menu_items def group_issues_sub_menu_items
%w(groups#issues labels#index milestones#index boards#index boards#show) %w(groups#issues labels#index milestones#index boards#index boards#show)
end end
......
...@@ -127,5 +127,3 @@ module TabHelper ...@@ -127,5 +127,3 @@ module TabHelper
end end
end end
end end
TabHelper.prepend_if_ee('EE::TabHelper')
...@@ -8,6 +8,7 @@ class ApplicationSetting < ApplicationRecord ...@@ -8,6 +8,7 @@ class ApplicationSetting < ApplicationRecord
include IgnorableColumns include IgnorableColumns
ignore_column :namespace_storage_size_limit, remove_with: '13.5', remove_after: '2020-09-22' ignore_column :namespace_storage_size_limit, remove_with: '13.5', remove_after: '2020-09-22'
ignore_column :instance_statistics_visibility_private, remove_with: '13.6', remove_after: '2020-10-22'
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \ GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana' 'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
......
...@@ -87,7 +87,6 @@ module ApplicationSettingImplementation ...@@ -87,7 +87,6 @@ module ApplicationSettingImplementation
housekeeping_gc_period: 200, housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10, housekeeping_incremental_repack_period: 10,
import_sources: Settings.gitlab['import_sources'], import_sources: Settings.gitlab['import_sources'],
instance_statistics_visibility_private: false,
issues_create_limit: 300, issues_create_limit: 300,
local_markdown_version: 0, local_markdown_version: 0,
login_recaptcha_protection_enabled: false, login_recaptcha_protection_enabled: false,
......
...@@ -15,14 +15,9 @@ class GlobalPolicy < BasePolicy ...@@ -15,14 +15,9 @@ class GlobalPolicy < BasePolicy
@user&.required_terms_not_accepted? @user&.required_terms_not_accepted?
end end
condition(:private_instance_statistics, score: 0) { Gitlab::CurrentSettings.instance_statistics_visibility_private? }
condition(:project_bot, scope: :user) { @user&.project_bot? } condition(:project_bot, scope: :user) { @user&.project_bot? }
condition(:migration_bot, scope: :user) { @user&.migration_bot? } condition(:migration_bot, scope: :user) { @user&.migration_bot? }
rule { admin | (~private_instance_statistics & ~anonymous) }
.enable :read_instance_statistics
rule { anonymous }.policy do rule { anonymous }.policy do
prevent :log_in prevent :log_in
prevent :receive_notifications prevent :receive_notifications
...@@ -103,6 +98,7 @@ class GlobalPolicy < BasePolicy ...@@ -103,6 +98,7 @@ class GlobalPolicy < BasePolicy
rule { admin }.policy do rule { admin }.policy do
enable :read_custom_attribute enable :read_custom_attribute
enable :update_custom_attribute enable :update_custom_attribute
enable :read_instance_statistics
end end
# We can't use `read_statistics` because the user may have different permissions for different projects # We can't use `read_statistics` because the user may have different permissions for different projects
......
...@@ -34,8 +34,5 @@ ...@@ -34,8 +34,5 @@
- deactivating_usage_ping_path = help_page_path('development/telemetry/usage_ping', anchor: 'disable-usage-ping') - deactivating_usage_ping_path = help_page_path('development/telemetry/usage_ping', anchor: 'disable-usage-ping')
- deactivating_usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: deactivating_usage_ping_path } - deactivating_usage_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: deactivating_usage_ping_path }
= s_('For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}.').html_safe % { deactivating_usage_ping_link_start: deactivating_usage_ping_link_start, deactivating_usage_ping_link_end: '</a>'.html_safe } = s_('For more information, see the documentation on %{deactivating_usage_ping_link_start}deactivating the usage ping%{deactivating_usage_ping_link_end}.').html_safe % { deactivating_usage_ping_link_start: deactivating_usage_ping_link_start, deactivating_usage_ping_link_end: '</a>'.html_safe }
.form-group.mt-3
= f.label :instance_statistics_visibility_private, _('Instance Statistics visibility')
= f.select :instance_statistics_visibility_private, options_for_select({_('All users') => false, _('Only admins') => true}, Gitlab::CurrentSettings.instance_statistics_visibility_private?), {}, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success" = f.submit 'Save changes', class: "btn btn-success"
- page_title _('Analytics')
- header_title _('Analytics'), instance_statistics_root_path
- nav 'instance_statistics'
- @left_sidebar = true
= render template: 'layouts/application'
- return unless dashboard_nav_link?(:analytics)
= nav_link(controller: [:dev_ops_score, :cohorts], html_options: { class: "d-none d-xl-block"}) do
= link_to instance_statistics_root_path, class: 'chart-icon', title: _('Analytics'), aria: { label: _('Analytics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('chart', size: 18)
...@@ -43,8 +43,6 @@ ...@@ -43,8 +43,6 @@
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', data: { qa_selector: 'snippets_link' } do = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', data: { qa_selector: 'snippets_link' } do
= _('Snippets') = _('Snippets')
= render_if_exists 'layouts/nav/sidebar/analytics_link'
%li.dropdown %li.dropdown
= render_if_exists 'dashboard/nav_link_list' = render_if_exists 'dashboard/nav_link_list'
...@@ -66,8 +64,6 @@ ...@@ -66,8 +64,6 @@
= link_to sherlock_transactions_path, class: 'admin-icon' do = link_to sherlock_transactions_path, class: 'admin-icon' do
= _('Sherlock Transactions') = _('Sherlock Transactions')
= render_if_exists 'layouts/nav/analytics_link'
- if current_user.admin? - if current_user.admin?
= nav_link(controller: 'admin/dashboard', html_options: { class: "d-none d-xl-block"}) do = nav_link(controller: 'admin/dashboard', html_options: { class: "d-none d-xl-block"}) do
= link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin Area'), aria: { label: _('Admin Area') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin Area'), aria: { label: _('Admin Area') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
......
...@@ -48,6 +48,28 @@ ...@@ -48,6 +48,28 @@
%span %span
= _('Gitaly Servers') = _('Gitaly Servers')
= nav_link(controller: admin_analytics_nav_links) do
= link_to admin_dev_ops_score_path, data: { qa_selector: 'admin_analytics_link' } do
.nav-icon-container
= sprite_icon('chart')
%span.nav-item-name
= _('Analytics')
%ul.sidebar-sub-level-items{ data: { qa_selector: 'admin_sidebar_analytics_submenu_content' } }
= nav_link(controller: admin_analytics_nav_links, html_options: { class: "fly-out-top-item" }) do
= link_to admin_dev_ops_score_path do
%strong.fly-out-top-item-name
= _('Analytics')
%li.divider.fly-out-top-item
= nav_link(controller: :dev_ops_score) do
= link_to admin_dev_ops_score_path, title: _('DevOps Score') do
%span
= _('DevOps Score')
= nav_link(controller: :cohorts) do
= link_to admin_cohorts_path, title: _('Cohorts') do
%span
= _('Cohorts')
= nav_link(controller: admin_monitoring_nav_links) do = nav_link(controller: admin_monitoring_nav_links) do
= link_to admin_system_info_path, data: { qa_selector: 'admin_monitoring_link' } do = link_to admin_system_info_path, data: { qa_selector: 'admin_monitoring_link' } do
.nav-icon-container .nav-icon-container
......
- return unless dashboard_nav_link?(:analytics)
= nav_link(controller: [:dev_ops_score, :cohorts]) do
= link_to instance_statistics_root_path, class: 'd-xl-none' do
= _('Analytics')
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to instance_statistics_root_path, title: _('Analytics') do
.avatar-container.s40.settings-avatar
= sprite_icon('chart', size: 24)
.sidebar-context-title= _('Analytics')
%ul.sidebar-top-level-items
= render 'layouts/nav/sidebar/instance_statistics_links'
= render 'shared/sidebar_toggle_button'
- return unless can?(current_user, :read_instance_statistics)
= nav_link(controller: :dev_ops_score) do
= link_to instance_statistics_dev_ops_score_index_path do
.nav-icon-container
= sprite_icon('comment')
%span.nav-item-name
= _('DevOps Score')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :dev_ops_score, html_options: { class: "fly-out-top-item" } ) do
= link_to instance_statistics_dev_ops_score_index_path do
%strong.fly-out-top-item-name
= _('DevOps Score')
- if Gitlab::CurrentSettings.usage_ping_enabled
= nav_link(controller: :cohorts) do
= link_to instance_statistics_cohorts_path do
.nav-icon-container
= sprite_icon('users')
%span.nav-item-name
= _('Cohorts')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :cohorts, html_options: { class: "fly-out-top-item" } ) do
= link_to instance_statistics_cohorts_path do
%strong.fly-out-top-item-name
= _('Cohorts')
...@@ -72,6 +72,10 @@ Rails.application.routes.draw do ...@@ -72,6 +72,10 @@ Rails.application.routes.draw do
# Begin of the /-/ scope. # Begin of the /-/ scope.
# Use this scope for all new global routes. # Use this scope for all new global routes.
scope path: '-' do scope path: '-' do
# remove in 13.5
get '/instance_statistics', to: redirect('admin/dev_ops_score')
get '/instance_statistics/dev_ops_score', to: redirect('admin/dev_ops_score')
get '/instance_statistics/cohorts', to: redirect('admin/cohorts')
# Autocomplete # Autocomplete
get '/autocomplete/users' => 'autocomplete#users' get '/autocomplete/users' => 'autocomplete#users'
get '/autocomplete/users/:id' => 'autocomplete#user' get '/autocomplete/users/:id' => 'autocomplete#user'
...@@ -123,7 +127,6 @@ Rails.application.routes.draw do ...@@ -123,7 +127,6 @@ Rails.application.routes.draw do
get 'ide/*vueroute' => 'ide#index', format: false get 'ide/*vueroute' => 'ide#index', format: false
draw :operations draw :operations
draw :instance_statistics
Gitlab.ee do Gitlab.ee do
draw :security draw :security
......
...@@ -89,6 +89,10 @@ namespace :admin do ...@@ -89,6 +89,10 @@ namespace :admin do
resources :projects, only: [:index] resources :projects, only: [:index]
resources :instance_statistics, only: :index
resource :dev_ops_score, controller: 'dev_ops_score', only: :show
resources :cohorts, only: :index
scope(path: 'projects/*namespace_id', scope(path: 'projects/*namespace_id',
as: :namespace, as: :namespace,
constraints: { namespace_id: Gitlab::PathRegex.full_namespace_route_regex }) do constraints: { namespace_id: Gitlab::PathRegex.full_namespace_route_regex }) do
......
# frozen_string_literal: true
namespace :instance_statistics do
root to: redirect('-/instance_statistics/dev_ops_score')
resources :cohorts, only: :index
resources :dev_ops_score, only: :index
end
# frozen_string_literal: true
class Analytics::AnalyticsController < Analytics::ApplicationController
def index
if can?(current_user, :read_instance_statistics)
redirect_to instance_statistics_dev_ops_score_index_path
else
render_404
end
end
end
...@@ -31,14 +31,6 @@ module EE ...@@ -31,14 +31,6 @@ module EE
!current_user.has_current_license? && current_user.admin? !current_user.has_current_license? && current_user.admin?
end end
def analytics_nav_url
if can?(current_user, :read_instance_statistics)
instance_statistics_root_path
else
'errors/not_found'
end
end
private private
override :get_dashboard_nav_links override :get_dashboard_nav_links
......
# frozen_string_literal: true
module EE
module TabHelper
extend ::Gitlab::Utils::Override
def analytics_controllers
['analytics/productivity_analytics', 'analytics/cycle_analytics', 'instance_statistics/dev_ops_score', 'instance_statistics/cohorts']
end
end
end
- page_title _('Security') - page_title _('Security')
- header_title _('Security'), instance_statistics_root_path - header_title _('Security')
- nav 'security' - nav 'security'
- @left_sidebar = true - @left_sidebar = true
......
- page_title _('Analytics')
- header_title _('Analytics'), instance_statistics_root_path
- nav 'analytics'
- @left_sidebar = true
= render template: 'layouts/application'
- return unless dashboard_nav_link?(:analytics)
= nav_link(controller: analytics_controllers, html_options: { class: "d-none d-xl-block"}) do
= link_to analytics_nav_url, class: 'chart-icon', title: _('Analytics'), aria: { label: _('Analytics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('chart', size: 18)
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to analytics_root_path, title: _('Analytics') do
.avatar-container.s40.settings-avatar
= sprite_icon('chart', size: 24)
.sidebar-context-title= _('Analytics')
%ul.sidebar-top-level-items
= render_ce 'layouts/nav/sidebar/instance_statistics_links'
= render 'shared/sidebar_toggle_button'
- return unless dashboard_nav_link?(:analytics)
= nav_link(controller: analytics_controllers) do
= link_to analytics_root_path, class: 'd-xl-none' do
= _('Analytics')
---
title: Move Analytics to admin panel
merge_request: 39368
author:
type: changed
# frozen_string_literal: true # frozen_string_literal: true
namespace :analytics do namespace :analytics do
root to: 'analytics#index' root to: redirect('admin/dev_ops_score')
constraints(-> (req) { Gitlab::Analytics.cycle_analytics_enabled? }) do constraints(-> (req) { Gitlab::Analytics.cycle_analytics_enabled? }) do
resource :cycle_analytics, only: :show, path: 'value_stream_analytics' resource :cycle_analytics, only: :show, path: 'value_stream_analytics'
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::AnalyticsController do
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET index' do
it 'renders devops score page when all the analytics feature flags are disabled' do
get :index
expect(response).to redirect_to(instance_statistics_dev_ops_score_index_path)
end
context 'when instance statistics is private' do
before do
stub_application_setting(instance_statistics_visibility_private: true)
end
it 'renders 404, not found' do
get :index
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
include_examples GracefulTimeoutHandling
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'accessing the analytics workspace' do
let(:user) { create(:user) }
before do
sign_in(user)
end
it 'renders the productivity analytics landing page' do
stub_licensed_features(Gitlab::Analytics::PRODUCTIVITY_ANALYTICS_FEATURE_FLAG => true)
visit analytics_root_path
expect(page.status_code).to eq(200)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Showing analytics' do
before do
sign_in user if user
end
# Using a path that is publicly accessible
subject { visit explore_projects_path }
context 'for regular users' do
let(:user) { create(:user) }
context 'with access to instance statistics or analytics features' do
it 'shows the analytics link' do
subject
expect(page).to have_link('Analytics')
end
end
context 'without access to instance statistics and analytics features' do
before do
stub_application_setting(instance_statistics_visibility_private: true)
end
it 'does not show the analytics link' do
subject
expect(page).not_to have_link('Analytics')
end
end
end
end
...@@ -197,32 +197,4 @@ RSpec.describe DashboardHelper, type: :helper do ...@@ -197,32 +197,4 @@ RSpec.describe DashboardHelper, type: :helper do
it { is_expected.to eq(output) } it { is_expected.to eq(output) }
end end
end end
describe 'analytics_nav_url' do
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when analytics features are disabled' do
context 'and user has access to instance statistics features' do
before do
allow(helper).to receive(:can?) { true }
end
it 'returns the instance statistics root path' do
expect(helper.analytics_nav_url).to match(instance_statistics_root_path)
end
end
context 'and user does not have access to instance statistics features' do
before do
allow(helper).to receive(:can?) { false }
end
it 'returns the not found path' do
expect(helper.analytics_nav_url).to match('errors/not_found')
end
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'admin dev ops analytics' do
let(:user) { create(:admin) }
before do
login_as(user)
end
it 'redirects from -/analytics to admin/dev_ops_score' do
get '/-/analytics'
expect(response).to redirect_to(admin_dev_ops_score_path)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Analytics' do
include Warden::Test::Helpers
it 'redirects to sign_in if user is not authenticated' do
expect(get('/-/analytics')).to route_to('analytics/analytics#index')
end
context 'when user is logged in' do
let(:user) { create(:user) }
before do
login_as(user)
end
context 'cycle_analytics feature flag is enabled by default' do
it 'succeeds' do
expect(Gitlab::Analytics).to receive(:cycle_analytics_enabled?).and_call_original
expect(get('/-/analytics/value_stream_analytics')).to route_to('analytics/cycle_analytics#show')
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'layouts/nav/sidebar/_analytics' do
it_behaves_like 'has nav sidebar'
context 'top-level items' do
context 'when feature flags are enabled' do
it 'has `Analytics` link' do
render
expect(rendered).to have_content('Analytics')
expect(rendered).to include(analytics_root_path)
expect(rendered).to match(/<use xlink:href=".+?icons-.+?#chart">/)
end
context 'and user has access to instance statistics features' do
before do
allow(view).to receive(:can?) { true }
end
it 'has `DevOps Score` link' do
render
expect(rendered).to have_content('DevOps Score')
expect(rendered).to include(instance_statistics_dev_ops_score_index_path)
expect(rendered).to match(/<use xlink:href=".+?icons-.+?#comment">/)
end
it 'has `Cohorts` link' do
render
expect(rendered).to have_content('Cohorts')
expect(rendered).to include(instance_statistics_cohorts_path)
expect(rendered).to match(/<use xlink:href=".+?icons-.+?#users">/)
end
end
context 'and user does not have access to instance statistics features' do
before do
allow(view).to receive(:can?) { false }
end
it 'no instance statistics links are rendered' do
render
expect(rendered).not_to have_content('DevOps Score')
expect(rendered).not_to have_content('Cohorts')
end
end
end
context 'when user has access to instance statistics features' do
before do
allow(view).to receive(:can?) { true }
end
it 'has `DevOps Score` link' do
render
expect(rendered).to have_content('DevOps Score')
expect(rendered).to include(instance_statistics_dev_ops_score_index_path)
expect(rendered).to match(/<use xlink:href=".+?icons-.+?#comment">/)
end
it 'has `Cohorts` link' do
render
expect(rendered).to have_content('Cohorts')
expect(rendered).to include(instance_statistics_cohorts_path)
expect(rendered).to match(/<use xlink:href=".+?icons-.+?#users">/)
end
end
context 'and user does not have access to instance statistics features' do
before do
allow(view).to receive(:can?) { false }
end
it 'no instance statistics links are rendered' do
render
expect(rendered).not_to have_content('DevOps Score')
expect(rendered).not_to have_content('Cohorts')
end
end
end
end
...@@ -140,7 +140,6 @@ module API ...@@ -140,7 +140,6 @@ module API
end end
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.' optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.' optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :instance_statistics_visibility_private, type: Boolean, desc: 'When set to `true` Instance statistics will only be available to admins'
optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated' optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated'
optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5 optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5
optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking' optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking'
......
...@@ -13189,9 +13189,6 @@ msgstr[1] "" ...@@ -13189,9 +13189,6 @@ msgstr[1] ""
msgid "Instance Configuration" msgid "Instance Configuration"
msgstr "" msgstr ""
msgid "Instance Statistics visibility"
msgstr ""
msgid "Instance administrators group already exists" msgid "Instance administrators group already exists"
msgstr "" msgstr ""
...@@ -17005,9 +17002,6 @@ msgstr "" ...@@ -17005,9 +17002,6 @@ msgstr ""
msgid "Only active this projects shows up in the search and on the dashboard." msgid "Only active this projects shows up in the search and on the dashboard."
msgstr "" msgstr ""
msgid "Only admins"
msgstr ""
msgid "Only admins can delete project" msgid "Only admins can delete project"
msgstr "" msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Admin::CohortsController do
context 'as admin' do
let(:user) { create(:admin) }
before do
sign_in(user)
end
it 'renders 200' do
get :index
expect(response).to have_gitlab_http_status(:success)
end
describe 'GET #index' do
it_behaves_like 'tracking unique visits', :index do
let(:request_params) { {} }
let(:target_id) { 'i_analytics_cohorts' }
end
end
end
context 'as normal user' do
let(:user) { create(:user) }
before do
sign_in(user)
end
it 'renders a 404' do
get :index
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Admin::DevOpsScoreController do
describe 'GET #show' do
context 'as admin' do
let(:user) { create(:admin) }
before do
sign_in(user)
end
it 'responds with success' do
get :show
expect(response).to have_gitlab_http_status(:success)
end
it_behaves_like 'tracking unique visits', :show do
let(:request_params) { {} }
let(:target_id) { 'i_analytics_dev_ops_score' }
end
end
end
context 'as normal user' do
let(:user) { create(:user) }
before do
sign_in(user)
end
it 'responds with 404' do
get :show
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe InstanceStatistics::CohortsController do
let(:user) { create(:user) }
before do
sign_in(user)
end
it_behaves_like 'instance statistics availability'
it 'renders a 404 when the usage ping is disabled' do
stub_application_setting(usage_ping_enabled: false)
get :index
expect(response).to have_gitlab_http_status(:not_found)
end
describe 'GET #index' do
it_behaves_like 'tracking unique visits', :index do
let(:request_params) { {} }
let(:target_id) { 'i_analytics_cohorts' }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe InstanceStatistics::DevOpsScoreController do
it_behaves_like 'instance statistics availability'
describe 'GET #index' do
let(:user) { create(:user) }
before do
sign_in(user)
end
it_behaves_like 'tracking unique visits', :index do
let(:request_params) { {} }
let(:target_id) { 'i_analytics_dev_ops_score' }
end
end
end
...@@ -12,7 +12,7 @@ RSpec.describe 'Cohorts page' do ...@@ -12,7 +12,7 @@ RSpec.describe 'Cohorts page' do
it 'See users count per month' do it 'See users count per month' do
create_list(:user, 2) create_list(:user, 2)
visit instance_statistics_cohorts_path visit admin_cohorts_path
expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0") expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
end end
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'DevOps Score' do RSpec.describe 'DevOps Score page' do
before do before do
sign_in(create(:admin)) sign_in(create(:admin))
end end
it 'has dismissable intro callout', :js do it 'has dismissable intro callout', :js do
visit instance_statistics_dev_ops_score_index_path visit admin_dev_ops_score_path
expect(page).to have_content 'Introducing Your DevOps Score' expect(page).to have_content 'Introducing Your DevOps Score'
...@@ -23,13 +23,13 @@ RSpec.describe 'DevOps Score' do ...@@ -23,13 +23,13 @@ RSpec.describe 'DevOps Score' do
end end
it 'shows empty state' do it 'shows empty state' do
visit instance_statistics_dev_ops_score_index_path visit admin_dev_ops_score_path
expect(page).to have_content('Usage ping is not enabled') expect(page).to have_content('Usage ping is not enabled')
end end
it 'hides the intro callout' do it 'hides the intro callout' do
visit instance_statistics_dev_ops_score_index_path visit admin_dev_ops_score_path
expect(page).not_to have_content 'Introducing Your DevOps Score' expect(page).not_to have_content 'Introducing Your DevOps Score'
end end
...@@ -39,7 +39,7 @@ RSpec.describe 'DevOps Score' do ...@@ -39,7 +39,7 @@ RSpec.describe 'DevOps Score' do
it 'shows empty state' do it 'shows empty state' do
stub_application_setting(usage_ping_enabled: true) stub_application_setting(usage_ping_enabled: true)
visit instance_statistics_dev_ops_score_index_path visit admin_dev_ops_score_path
expect(page).to have_content('Data is still calculating') expect(page).to have_content('Data is still calculating')
end end
...@@ -50,7 +50,7 @@ RSpec.describe 'DevOps Score' do ...@@ -50,7 +50,7 @@ RSpec.describe 'DevOps Score' do
stub_application_setting(usage_ping_enabled: true) stub_application_setting(usage_ping_enabled: true)
create(:dev_ops_score_metric) create(:dev_ops_score_metric)
visit instance_statistics_dev_ops_score_index_path visit admin_dev_ops_score_path
expect(page).to have_content( expect(page).to have_content(
'Issues created per active user 1.2 You 9.3 Lead 13.3%' 'Issues created per active user 1.2 You 9.3 Lead 13.3%'
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Showing analytics' do
before do
sign_in user if user
end
# Using a path that is publicly accessible
subject { visit explore_projects_path }
context 'for unauthenticated users' do
let(:user) { nil }
it 'does not show the Analytics link' do
subject
expect(page).not_to have_link('Analytics')
end
end
context 'for regular users' do
let(:user) { create(:user) }
context 'when instance statistics are publicly available' do
before do
stub_application_setting(instance_statistics_visibility_private: false)
end
it 'shows the analytics link' do
subject
expect(page).to have_link('Analytics')
end
end
context 'when instance statistics are not publicly available' do
before do
stub_application_setting(instance_statistics_visibility_private: true)
end
it 'does not show the analytics link' do
subject
# Skipping this test on EE as there is an EE specifc spec for this functionality
# ee/spec/features/dashboards/analytics_spec.rb
skip if Gitlab.ee?
expect(page).not_to have_link('Analytics')
end
end
end
context 'for admins' do
let(:user) { create(:admin) }
it 'shows the analytics link' do
subject
expect(page).to have_link('Analytics')
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Cohorts page', :js do
before do
sign_in(create(:admin))
end
it 'hides cohorts nav button when usage ping is disabled' do
stub_application_setting(usage_ping_enabled: false)
visit instance_statistics_root_path
expect(find('.nav-sidebar')).not_to have_content('Cohorts')
end
it 'shows cohorts nav button when usage ping is enabled' do
stub_application_setting(usage_ping_enabled: true)
visit instance_statistics_root_path
expect(find('.nav-sidebar')).to have_content('Cohorts')
end
end
...@@ -372,35 +372,13 @@ RSpec.describe GlobalPolicy do ...@@ -372,35 +372,13 @@ RSpec.describe GlobalPolicy do
describe 'read instance statistics' do describe 'read instance statistics' do
context 'regular user' do context 'regular user' do
it { is_expected.to be_allowed(:read_instance_statistics) } it { is_expected.to be_disallowed(:read_instance_statistics) }
context 'when instance statistics are set to private' do
before do
stub_application_setting(instance_statistics_visibility_private: true)
end
it { is_expected.not_to be_allowed(:read_instance_statistics) }
end
end end
context 'admin' do context 'admin', :enable_admin_mode do
let(:current_user) { create(:admin) } let(:current_user) { create(:admin) }
it { is_expected.to be_allowed(:read_instance_statistics) } it { is_expected.to be_allowed(:read_instance_statistics) }
context 'when instance statistics are set to private' do
before do
stub_application_setting(instance_statistics_visibility_private: true)
end
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:read_instance_statistics) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:read_instance_statistics) }
end
end
end end
context 'anonymous' do context 'anonymous' do
......
...@@ -31,7 +31,6 @@ RSpec.describe API::Settings, 'Settings' do ...@@ -31,7 +31,6 @@ RSpec.describe API::Settings, 'Settings' do
expect(json_response['ecdsa_key_restriction']).to eq(0) expect(json_response['ecdsa_key_restriction']).to eq(0)
expect(json_response['ed25519_key_restriction']).to eq(0) expect(json_response['ed25519_key_restriction']).to eq(0)
expect(json_response['performance_bar_allowed_group_id']).to be_nil expect(json_response['performance_bar_allowed_group_id']).to be_nil
expect(json_response['instance_statistics_visibility_private']).to be(false)
expect(json_response['allow_local_requests_from_hooks_and_services']).to be(false) expect(json_response['allow_local_requests_from_hooks_and_services']).to be(false)
expect(json_response['allow_local_requests_from_web_hooks_and_services']).to be(false) expect(json_response['allow_local_requests_from_web_hooks_and_services']).to be(false)
expect(json_response['allow_local_requests_from_system_hooks']).to be(true) expect(json_response['allow_local_requests_from_system_hooks']).to be(true)
...@@ -104,7 +103,6 @@ RSpec.describe API::Settings, 'Settings' do ...@@ -104,7 +103,6 @@ RSpec.describe API::Settings, 'Settings' do
enforce_terms: true, enforce_terms: true,
terms: 'Hello world!', terms: 'Hello world!',
performance_bar_allowed_group_path: group.full_path, performance_bar_allowed_group_path: group.full_path,
instance_statistics_visibility_private: true,
diff_max_patch_bytes: 150_000, diff_max_patch_bytes: 150_000,
default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE, default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE,
local_markdown_version: 3, local_markdown_version: 3,
...@@ -146,7 +144,6 @@ RSpec.describe API::Settings, 'Settings' do ...@@ -146,7 +144,6 @@ RSpec.describe API::Settings, 'Settings' do
expect(json_response['enforce_terms']).to be(true) expect(json_response['enforce_terms']).to be(true)
expect(json_response['terms']).to eq('Hello world!') expect(json_response['terms']).to eq('Hello world!')
expect(json_response['performance_bar_allowed_group_id']).to eq(group.id) expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
expect(json_response['instance_statistics_visibility_private']).to be(true)
expect(json_response['diff_max_patch_bytes']).to eq(150_000) expect(json_response['diff_max_patch_bytes']).to eq(150_000)
expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE) expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(json_response['local_markdown_version']).to eq(3) expect(json_response['local_markdown_version']).to eq(3)
......
...@@ -6,6 +6,6 @@ RSpec.describe 'Instance Statistics', 'routing' do ...@@ -6,6 +6,6 @@ RSpec.describe 'Instance Statistics', 'routing' do
include RSpec::Rails::RequestExampleGroup include RSpec::Rails::RequestExampleGroup
it "routes '/-/instance_statistics' to dev ops score" do it "routes '/-/instance_statistics' to dev ops score" do
expect(get('/-/instance_statistics')).to redirect_to('/-/instance_statistics/dev_ops_score') expect(get('/-/instance_statistics')).to redirect_to('/admin/dev_ops_score')
end end
end end
# frozen_string_literal: true
RSpec.shared_examples 'instance statistics availability' do
let(:user) { create(:user) }
before do
sign_in(user)
stub_application_setting(usage_ping_enabled: true)
end
describe 'GET #index' do
it 'is available when the feature is available publicly' do
get :index
expect(response).to have_gitlab_http_status(:success)
end
it 'renders a 404 when the feature is not available publicly' do
stub_application_setting(instance_statistics_visibility_private: true)
get :index
expect(response).to have_gitlab_http_status(:not_found)
end
context 'for admins' do
let(:user) { create(:admin) }
context 'when admin mode disabled' do
it 'forbids access when the feature is not available publicly' do
stub_application_setting(instance_statistics_visibility_private: true)
get :index
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when admin mode enabled', :enable_admin_mode do
it 'allows access when the feature is not available publicly' do
stub_application_setting(instance_statistics_visibility_private: true)
get :index
expect(response).to have_gitlab_http_status(:success)
end
end
end
end
end
...@@ -66,6 +66,14 @@ RSpec.describe 'layouts/nav/sidebar/_admin' do ...@@ -66,6 +66,14 @@ RSpec.describe 'layouts/nav/sidebar/_admin' do
it_behaves_like 'page has active tab', 'Messages' it_behaves_like 'page has active tab', 'Messages'
end end
context 'on analytics' do
before do
allow(controller).to receive(:controller_name).and_return('dev_ops_score')
end
it_behaves_like 'page has active tab', 'Analytics'
end
context 'on hooks' do context 'on hooks' do
before do before do
allow(controller).to receive(:controller_name).and_return('hooks') allow(controller).to receive(:controller_name).and_return('hooks')
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'layouts/nav/sidebar/_instance_statistics' do
it_behaves_like 'has nav sidebar'
end
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