Commit 3221faa9 authored by Adam Hegyi's avatar Adam Hegyi

Move VSM to the group level

This change moves Value Stream Management page to the group level,
within the analytics sidebar. The change is behind a feature flag:
`group_level_cycle_analytics`
parent a5e6144c
......@@ -121,10 +121,7 @@ Rails.application.routes.draw do
draw :country
draw :country_state
draw :subscription
constraints(-> (*) { Gitlab::Analytics.any_features_enabled? }) do
draw :analytics
end
draw :analytics
end
if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test?
......
......@@ -42,6 +42,10 @@ export default {
type: String,
required: true,
},
hideGroupDropDown: {
type: Boolean,
required: true,
},
},
computed: {
...mapState([
......@@ -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"
>
<groups-dropdown-filter
v-if="!hideGroupDropDown"
class="js-groups-dropdown-filter dropdown-select"
:query-params="$options.groupsQueryParams"
:default-group="selectedGroup"
......
......@@ -2,10 +2,11 @@ import Vue from 'vue';
import CycleAnalytics from './components/base.vue';
import createStore from './store';
import { buildCycleAnalyticsInitialData } from '../shared/utils';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
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 store = createStore();
......@@ -21,6 +22,7 @@ export default () => {
emptyStateSvgPath,
noDataSvgPath,
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
def index
if Feature.disabled?(:group_level_productivity_analytics, default_enabled: true) && Gitlab::Analytics.productivity_analytics_enabled?
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
elsif can?(current_user, :read_instance_statistics)
redirect_to instance_statistics_dev_ops_score_index_path
else
render_404
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
contribution_analytics_navbar_link(group, current_user),
group_insights_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
end
......@@ -36,6 +37,18 @@ module EE
)
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)
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)
......@@ -44,7 +57,7 @@ module EE
navbar_sub_item(
title: _('Productivity Analytics'),
path: 'groups/analytics/productivity_analytics#show',
link: group_analytics_productivity_analytics_path(@group)
link: group_analytics_productivity_analytics_path(group)
)
end
......
......@@ -32,7 +32,7 @@ module EE
end
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
end
......@@ -48,7 +48,7 @@ module EE
override :get_dashboard_nav_links
def get_dashboard_nav_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)
links << :environments if ::Feature.enabled?(:environments_dashboard, current_user, default_enabled: true)
......
......@@ -98,6 +98,10 @@ module EE
def get_group_sidebar_links
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?
links << :contribution_analytics
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 @@
%strong.fly-out-top-item-name
= _('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
= link_to analytics_cycle_analytics_path, class: 'qa-sidebar-cycle-analytics' do
.nav-icon-container
......
......@@ -33,6 +33,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
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 :cycle_analytics, path: 'value_stream_analytics', only: :show, constraints: -> (req) { Feature.enabled?(:group_level_cycle_analytics) && Gitlab::Analytics.cycle_analytics_enabled? }
end
resource :ldap, only: [] do
......
......@@ -9,6 +9,7 @@ describe Analytics::AnalyticsController do
before do
stub_feature_flags(group_level_productivity_analytics: false)
stub_feature_flags(group_level_cycle_analytics: false)
sign_in(user)
disable_all_analytics_feature_flags
......@@ -33,10 +34,22 @@ describe Analytics::AnalyticsController do
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
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
# 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
sign_in(user)
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
stub_licensed_features(Gitlab::Analytics::PRODUCTIVITY_ANALYTICS_FEATURE_FLAG => true)
......
......@@ -22,6 +22,7 @@ import * as mockData from '../mock_data';
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
const emptyStateSvgPath = 'path/to/empty/state';
const hideGroupDropDown = false;
const localVue = createLocalVue();
localVue.use(Vuex);
......@@ -40,6 +41,7 @@ function createComponent({
scatterplotEnabled = true,
tasksByTypeChartEnabled = true,
customizableCycleAnalyticsEnabled = false,
props = {},
} = {}) {
const func = shallow ? shallowMount : mount;
const comp = func(Component, {
......@@ -50,6 +52,8 @@ function createComponent({
noDataSvgPath,
noAccessSvgPath,
baseStagesEndpoint: mockData.endpoints.baseStagesEndpoint,
hideGroupDropDown,
...props,
},
provide: {
glFeatures: {
......@@ -165,6 +169,21 @@ describe('Cycle Analytics component', () => {
it('does not display the duration scatter plot', () => {
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', () => {
......
......@@ -10,6 +10,8 @@ describe DashboardHelper, type: :helper do
describe '#dashboard_nav_links' do
before do
allow(helper).to receive(:current_user).and_return(user)
stub_feature_flags(group_level_cycle_analytics: false)
end
describe 'analytics' do
......@@ -237,6 +239,8 @@ describe DashboardHelper, type: :helper do
describe 'analytics_nav_url' do
before do
stub_feature_flags(group_level_cycle_analytics: false)
allow(helper).to receive(:current_user).and_return(user)
end
......
......@@ -9,6 +9,7 @@ describe 'layouts/nav/sidebar/_analytics' do
before do
stub_feature_flags(group_level_productivity_analytics: false)
stub_feature_flags(group_level_cycle_analytics: false)
end
context 'top-level items' do
......
......@@ -73,5 +73,20 @@ describe 'Group navbar' do
it_behaves_like 'verified navigation bar'
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
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