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,11 +121,8 @@ Rails.application.routes.draw do
draw :country
draw :country_state
draw :subscription
constraints(-> (*) { Gitlab::Analytics.any_features_enabled? }) do
draw :analytics
end
end
if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test?
resource :chaos, only: [] do
......
......@@ -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 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