Commit 9f409f75 authored by Stan Hu's avatar Stan Hu

Merge branch '196455-move-ca-to-the-group-level' into 'master'

Move top-level Cycle Analytics to the group level

See merge request gitlab-org/gitlab!24773
parents 25411943 3221faa9
...@@ -121,10 +121,7 @@ Rails.application.routes.draw do ...@@ -121,10 +121,7 @@ Rails.application.routes.draw do
draw :country draw :country
draw :country_state draw :country_state
draw :subscription draw :subscription
draw :analytics
constraints(-> (*) { Gitlab::Analytics.any_features_enabled? }) do
draw :analytics
end
end end
if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test? if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test?
......
...@@ -42,6 +42,10 @@ export default { ...@@ -42,6 +42,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
hideGroupDropDown: {
type: Boolean,
required: true,
},
}, },
computed: { computed: {
...mapState([ ...mapState([
...@@ -206,6 +210,7 @@ export default { ...@@ -206,6 +210,7 @@ export default {
class="mt-3 py-2 px-3 d-flex bg-gray-light border-top border-bottom flex-column flex-md-row justify-content-between" class="mt-3 py-2 px-3 d-flex bg-gray-light border-top border-bottom flex-column flex-md-row justify-content-between"
> >
<groups-dropdown-filter <groups-dropdown-filter
v-if="!hideGroupDropDown"
class="js-groups-dropdown-filter dropdown-select" class="js-groups-dropdown-filter dropdown-select"
:query-params="$options.groupsQueryParams" :query-params="$options.groupsQueryParams"
:default-group="selectedGroup" :default-group="selectedGroup"
......
...@@ -2,10 +2,11 @@ import Vue from 'vue'; ...@@ -2,10 +2,11 @@ import Vue from 'vue';
import CycleAnalytics from './components/base.vue'; import CycleAnalytics from './components/base.vue';
import createStore from './store'; import createStore from './store';
import { buildCycleAnalyticsInitialData } from '../shared/utils'; import { buildCycleAnalyticsInitialData } from '../shared/utils';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => { export default () => {
const el = document.querySelector('#js-cycle-analytics-app'); const el = document.querySelector('#js-cycle-analytics-app');
const { emptyStateSvgPath, noDataSvgPath, noAccessSvgPath } = el.dataset; const { emptyStateSvgPath, noDataSvgPath, noAccessSvgPath, hideGroupDropDown } = el.dataset;
const initialData = buildCycleAnalyticsInitialData(el.dataset); const initialData = buildCycleAnalyticsInitialData(el.dataset);
const store = createStore(); const store = createStore();
...@@ -21,6 +22,7 @@ export default () => { ...@@ -21,6 +22,7 @@ export default () => {
emptyStateSvgPath, emptyStateSvgPath,
noDataSvgPath, noDataSvgPath,
noAccessSvgPath, noAccessSvgPath,
hideGroupDropDown: parseBoolean(hideGroupDropDown),
}, },
}), }),
}); });
......
import initCycleAnalyticsApp from 'ee/analytics/cycle_analytics/index';
document.addEventListener('DOMContentLoaded', initCycleAnalyticsApp);
...@@ -4,8 +4,10 @@ class Analytics::AnalyticsController < Analytics::ApplicationController ...@@ -4,8 +4,10 @@ class Analytics::AnalyticsController < Analytics::ApplicationController
def index def index
if Feature.disabled?(:group_level_productivity_analytics, default_enabled: true) && Gitlab::Analytics.productivity_analytics_enabled? if Feature.disabled?(:group_level_productivity_analytics, default_enabled: true) && Gitlab::Analytics.productivity_analytics_enabled?
redirect_to analytics_productivity_analytics_path redirect_to analytics_productivity_analytics_path
elsif Gitlab::Analytics.cycle_analytics_enabled? elsif Feature.disabled?(:group_level_cycle_analytics) && Gitlab::Analytics.cycle_analytics_enabled?
redirect_to analytics_cycle_analytics_path redirect_to analytics_cycle_analytics_path
elsif can?(current_user, :read_instance_statistics)
redirect_to instance_statistics_dev_ops_score_index_path
else else
render_404 render_404
end end
......
# frozen_string_literal: true
class Groups::Analytics::CycleAnalyticsController < Groups::Analytics::ApplicationController
include CycleAnalyticsParams
layout 'group'
check_feature_flag Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG
increment_usage_counter Gitlab::UsageDataCounters::CycleAnalyticsCounter, :views, only: :show
before_action do
push_frontend_feature_flag(:customizable_cycle_analytics)
push_frontend_feature_flag(:cycle_analytics_scatterplot_enabled, default_enabled: true)
push_frontend_feature_flag(:cycle_analytics_scatterplot_median_enabled, default_enabled: true)
push_frontend_feature_flag(:tasks_by_type_chart)
end
before_action :load_group, only: :show
before_action :load_project, only: :show
before_action :build_request_params, only: :show
def build_request_params
@request_params ||= Gitlab::Analytics::CycleAnalytics::RequestParams.new(allowed_params.merge(group: @group), current_user: current_user)
end
def allowed_params
params.permit(
:created_after,
:created_before,
project_ids: []
)
end
end
...@@ -19,7 +19,8 @@ module EE ...@@ -19,7 +19,8 @@ module EE
contribution_analytics_navbar_link(group, current_user), contribution_analytics_navbar_link(group, current_user),
group_insights_navbar_link(group, current_user), group_insights_navbar_link(group, current_user),
issues_analytics_navbar_link(group, current_user), issues_analytics_navbar_link(group, current_user),
productivity_analytics_navbar_link(group, current_user) productivity_analytics_navbar_link(group, current_user),
group_cycle_analytics_navbar_link(group, current_user)
].compact ].compact
end end
...@@ -36,6 +37,18 @@ module EE ...@@ -36,6 +37,18 @@ module EE
) )
end end
def group_cycle_analytics_navbar_link(group, current_user)
return unless ::Feature.enabled?(:analytics_pages_under_group_analytics_sidebar, group, default_enabled: true)
return unless ::Feature.enabled?(:group_level_cycle_analytics)
return unless group_sidebar_link?(:cycle_analytics)
navbar_sub_item(
title: _('Value Stream Analytics'),
path: 'groups/analytics/cycle_analytics#show',
link: group_analytics_cycle_analytics_path(group)
)
end
def productivity_analytics_navbar_link(group, current_user) def productivity_analytics_navbar_link(group, current_user)
return unless ::Feature.enabled?(:analytics_pages_under_group_analytics_sidebar, group, default_enabled: true) return unless ::Feature.enabled?(:analytics_pages_under_group_analytics_sidebar, group, default_enabled: true)
return unless ::Feature.enabled?(:group_level_productivity_analytics, default_enabled: true) return unless ::Feature.enabled?(:group_level_productivity_analytics, default_enabled: true)
...@@ -44,7 +57,7 @@ module EE ...@@ -44,7 +57,7 @@ module EE
navbar_sub_item( navbar_sub_item(
title: _('Productivity Analytics'), title: _('Productivity Analytics'),
path: 'groups/analytics/productivity_analytics#show', path: 'groups/analytics/productivity_analytics#show',
link: group_analytics_productivity_analytics_path(@group) link: group_analytics_productivity_analytics_path(group)
) )
end end
......
...@@ -32,7 +32,7 @@ module EE ...@@ -32,7 +32,7 @@ module EE
end end
def analytics_nav_url def analytics_nav_url
if ::Gitlab::Analytics.any_features_enabled? if ::Feature.disabled?(:group_level_cycle_analytics) && ::Gitlab::Analytics.any_features_enabled?
return analytics_root_path return analytics_root_path
end end
...@@ -48,7 +48,7 @@ module EE ...@@ -48,7 +48,7 @@ module EE
override :get_dashboard_nav_links override :get_dashboard_nav_links
def get_dashboard_nav_links def get_dashboard_nav_links
super.tap do |links| super.tap do |links|
links << :analytics if ::Gitlab::Analytics.any_features_enabled? links << :analytics if ::Feature.disabled?(:group_level_cycle_analytics) && ::Gitlab::Analytics.any_features_enabled?
if can?(current_user, :read_operations_dashboard) if can?(current_user, :read_operations_dashboard)
links << :environments if ::Feature.enabled?(:environments_dashboard, current_user, default_enabled: true) links << :environments if ::Feature.enabled?(:environments_dashboard, current_user, default_enabled: true)
......
...@@ -98,6 +98,10 @@ module EE ...@@ -98,6 +98,10 @@ module EE
def get_group_sidebar_links def get_group_sidebar_links
links = super links = super
if can?(current_user, :read_group_cycle_analytics, @group)
links << :cycle_analytics
end
if can?(current_user, :read_group_contribution_analytics, @group) || show_promotions? if can?(current_user, :read_group_contribution_analytics, @group) || show_promotions?
links << :contribution_analytics links << :contribution_analytics
end end
......
- page_title _("Value Stream Analytics")
- data_attributes = @request_params.valid? ? @request_params.to_data_attributes : {}
- data_attributes.merge!({ empty_state_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_data_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_access_svg_path: image_path("illustrations/analytics/no-access.svg"), hide_group_drop_down: 'true' })
#js-cycle-analytics-app{ data: data_attributes }
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
= _('Productivity Analytics') = _('Productivity Analytics')
- if Gitlab::Analytics.cycle_analytics_enabled? - if Feature.disabled?(:group_level_cycle_analytics) && Gitlab::Analytics.cycle_analytics_enabled?
= nav_link(controller: :cycle_analytics) do = nav_link(controller: :cycle_analytics) do
= link_to analytics_cycle_analytics_path, class: 'qa-sidebar-cycle-analytics' do = link_to analytics_cycle_analytics_path, class: 'qa-sidebar-cycle-analytics' do
.nav-icon-container .nav-icon-container
......
...@@ -33,6 +33,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -33,6 +33,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end end
namespace :analytics do namespace :analytics do
resource :productivity_analytics, only: :show, constraints: -> (req) { Feature.enabled?(:group_level_productivity_analytics, default_enabled: true) && Gitlab::Analytics.productivity_analytics_enabled? } resource :productivity_analytics, only: :show, constraints: -> (req) { Feature.enabled?(:group_level_productivity_analytics, default_enabled: true) && Gitlab::Analytics.productivity_analytics_enabled? }
resource :cycle_analytics, path: 'value_stream_analytics', only: :show, constraints: -> (req) { Feature.enabled?(:group_level_cycle_analytics) && Gitlab::Analytics.cycle_analytics_enabled? }
end end
resource :ldap, only: [] do resource :ldap, only: [] do
......
...@@ -9,6 +9,7 @@ describe Analytics::AnalyticsController do ...@@ -9,6 +9,7 @@ describe Analytics::AnalyticsController do
before do before do
stub_feature_flags(group_level_productivity_analytics: false) stub_feature_flags(group_level_productivity_analytics: false)
stub_feature_flags(group_level_cycle_analytics: false)
sign_in(user) sign_in(user)
disable_all_analytics_feature_flags disable_all_analytics_feature_flags
...@@ -33,10 +34,22 @@ describe Analytics::AnalyticsController do ...@@ -33,10 +34,22 @@ describe Analytics::AnalyticsController do
end end
end end
it 'renders 404 all the analytics feature flags are disabled' do it 'renders devops score page when all the analytics feature flags are disabled' do
get :index get :index
expect(response).to have_gitlab_http_status(:not_found) 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
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Analytics::CycleAnalyticsController do
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'usage counter' do
it 'increments usage counter' do
expect(Gitlab::UsageDataCounters::CycleAnalyticsCounter).to receive(:count).with(:views)
get(:show)
expect(response).to be_successful
end
end
describe 'GET show' do
it 'renders `show` template' do
stub_feature_flags(Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG => true)
get :show
expect(response).to render_template :show
end
it 'renders `404` when feature flag is disabled' do
stub_feature_flags(Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG => false)
get :show
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
...@@ -10,14 +10,6 @@ describe 'accessing the analytics workspace' do ...@@ -10,14 +10,6 @@ describe 'accessing the analytics workspace' do
sign_in(user) sign_in(user)
end end
it 'renders 404 if analytics features are turned off' do
disable_all_analytics_feature_flags
visit analytics_root_path
expect(page.status_code).to eq(404)
end
it 'renders the productivity analytics landing page' do it 'renders the productivity analytics landing page' do
stub_licensed_features(Gitlab::Analytics::PRODUCTIVITY_ANALYTICS_FEATURE_FLAG => true) stub_licensed_features(Gitlab::Analytics::PRODUCTIVITY_ANALYTICS_FEATURE_FLAG => true)
......
...@@ -22,6 +22,7 @@ import * as mockData from '../mock_data'; ...@@ -22,6 +22,7 @@ import * as mockData from '../mock_data';
const noDataSvgPath = 'path/to/no/data'; const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access'; const noAccessSvgPath = 'path/to/no/access';
const emptyStateSvgPath = 'path/to/empty/state'; const emptyStateSvgPath = 'path/to/empty/state';
const hideGroupDropDown = false;
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -40,6 +41,7 @@ function createComponent({ ...@@ -40,6 +41,7 @@ function createComponent({
scatterplotEnabled = true, scatterplotEnabled = true,
tasksByTypeChartEnabled = true, tasksByTypeChartEnabled = true,
customizableCycleAnalyticsEnabled = false, customizableCycleAnalyticsEnabled = false,
props = {},
} = {}) { } = {}) {
const func = shallow ? shallowMount : mount; const func = shallow ? shallowMount : mount;
const comp = func(Component, { const comp = func(Component, {
...@@ -50,6 +52,8 @@ function createComponent({ ...@@ -50,6 +52,8 @@ function createComponent({
noDataSvgPath, noDataSvgPath,
noAccessSvgPath, noAccessSvgPath,
baseStagesEndpoint: mockData.endpoints.baseStagesEndpoint, baseStagesEndpoint: mockData.endpoints.baseStagesEndpoint,
hideGroupDropDown,
...props,
}, },
provide: { provide: {
glFeatures: { glFeatures: {
...@@ -165,6 +169,21 @@ describe('Cycle Analytics component', () => { ...@@ -165,6 +169,21 @@ describe('Cycle Analytics component', () => {
it('does not display the duration scatter plot', () => { it('does not display the duration scatter plot', () => {
displaysDurationScatterPlot(false); displaysDurationScatterPlot(false);
}); });
describe('hideGroupDropDown = true', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
wrapper = createComponent({
props: {
hideGroupDropDown: true,
},
});
});
it('does not render the group dropdown', () => {
expect(wrapper.find(GroupsDropdownFilter).exists()).toBe(false);
});
});
}); });
describe('after a filter has been selected', () => { describe('after a filter has been selected', () => {
......
...@@ -10,6 +10,8 @@ describe DashboardHelper, type: :helper do ...@@ -10,6 +10,8 @@ describe DashboardHelper, type: :helper do
describe '#dashboard_nav_links' do describe '#dashboard_nav_links' do
before do before do
allow(helper).to receive(:current_user).and_return(user) allow(helper).to receive(:current_user).and_return(user)
stub_feature_flags(group_level_cycle_analytics: false)
end end
describe 'analytics' do describe 'analytics' do
...@@ -237,6 +239,8 @@ describe DashboardHelper, type: :helper do ...@@ -237,6 +239,8 @@ describe DashboardHelper, type: :helper do
describe 'analytics_nav_url' do describe 'analytics_nav_url' do
before do before do
stub_feature_flags(group_level_cycle_analytics: false)
allow(helper).to receive(:current_user).and_return(user) allow(helper).to receive(:current_user).and_return(user)
end end
......
...@@ -9,6 +9,7 @@ describe 'layouts/nav/sidebar/_analytics' do ...@@ -9,6 +9,7 @@ describe 'layouts/nav/sidebar/_analytics' do
before do before do
stub_feature_flags(group_level_productivity_analytics: false) stub_feature_flags(group_level_productivity_analytics: false)
stub_feature_flags(group_level_cycle_analytics: false)
end end
context 'top-level items' do context 'top-level items' do
......
...@@ -73,5 +73,20 @@ describe 'Group navbar' do ...@@ -73,5 +73,20 @@ describe 'Group navbar' do
it_behaves_like 'verified navigation bar' it_behaves_like 'verified navigation bar'
end end
context 'when value stream analytics is available' do
before do
stub_licensed_features(cycle_analytics_for_groups: true)
analytics_nav_item[:nav_sub_items] << _('Value Stream Analytics')
group.add_maintainer(user)
sign_in(user)
visit group_path(group)
end
it_behaves_like 'verified navigation bar'
end
end end
end 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