Commit f33d91ab authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Check `cycleAnalyticsForGroups` license for metrics

Makes sure that we skip time summary requests for
projects that do not have the
`cycle_analytics_for_groups license
parent d0c85259
...@@ -6,7 +6,7 @@ import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; ...@@ -6,7 +6,7 @@ import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue'; import StageTable from '~/cycle_analytics/components/stage_table.vue';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue'; import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { METRICS_REQUESTS } from '../constants'; import { projectMetricsRequests } from '../constants';
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
...@@ -49,6 +49,7 @@ export default { ...@@ -49,6 +49,7 @@ export default {
'permissions', 'permissions',
'stageCounts', 'stageCounts',
'endpoints', 'endpoints',
'features',
]), ]),
...mapGetters(['pathNavigationData', 'filterParams']), ...mapGetters(['pathNavigationData', 'filterParams']),
displayStageEvents() { displayStageEvents() {
...@@ -92,6 +93,9 @@ export default { ...@@ -92,6 +93,9 @@ export default {
} }
return 0; return 0;
}, },
metricsRequests() {
return projectMetricsRequests(this.features.cycleAnalyticsForGroups);
},
}, },
methods: { methods: {
...mapActions([ ...mapActions([
...@@ -121,46 +125,47 @@ export default { ...@@ -121,46 +125,47 @@ export default {
pageTitle: __('Value Stream Analytics'), pageTitle: __('Value Stream Analytics'),
recentActivity: __('Recent Project Activity'), recentActivity: __('Recent Project Activity'),
}, },
METRICS_REQUESTS,
}; };
</script> </script>
<template> <template>
<div class="cycle-analytics"> <div class="cycle-analytics">
<h3>{{ $options.i18n.pageTitle }}</h3> <h3>{{ $options.i18n.pageTitle }}</h3>
<path-navigation <div class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row">
v-if="displayPathNavigation" <path-navigation
class="js-path-navigation gl-w-full gl-pb-2" v-if="displayPathNavigation"
:loading="isLoading || isLoadingStage" class="js-path-navigation gl-w-full gl-pb-2"
:stages="pathNavigationData" :loading="isLoading || isLoadingStage"
:selected-stage="selectedStage" :stages="pathNavigationData"
@selected="onSelectStage" :selected-stage="selectedStage"
/> @selected="onSelectStage"
<div class="gl-text-right flex-grow align-self-center"> />
<div class="js-ca-dropdown dropdown inline"> <div class="gl-flex-grow gl-align-self-end">
<!-- eslint-disable-next-line @gitlab/vue-no-data-toggle --> <div class="js-ca-dropdown dropdown inline">
<button class="dropdown-menu-toggle" data-toggle="dropdown" type="button"> <!-- eslint-disable-next-line @gitlab/vue-no-data-toggle -->
<span class="dropdown-label"> <button class="dropdown-menu-toggle" data-toggle="dropdown" type="button">
<gl-sprintf :message="$options.i18n.dropdownText"> <span class="dropdown-label">
<template #days>{{ daysInPast }}</template>
</gl-sprintf>
<gl-icon name="chevron-down" class="dropdown-menu-toggle-icon gl-top-3" />
</span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li v-for="days in $options.dayRangeOptions" :key="`day-range-${days}`">
<a href="#" @click.prevent="handleDateSelect(days)">
<gl-sprintf :message="$options.i18n.dropdownText"> <gl-sprintf :message="$options.i18n.dropdownText">
<template #days>{{ days }}</template> <template #days>{{ daysInPast }}</template>
</gl-sprintf> </gl-sprintf>
</a> <gl-icon name="chevron-down" class="dropdown-menu-toggle-icon gl-top-3" />
</li> </span>
</ul> </button>
<ul class="dropdown-menu dropdown-menu-right">
<li v-for="days in $options.dayRangeOptions" :key="`day-range-${days}`">
<a href="#" @click.prevent="handleDateSelect(days)">
<gl-sprintf :message="$options.i18n.dropdownText">
<template #days>{{ days }}</template>
</gl-sprintf>
</a>
</li>
</ul>
</div>
</div> </div>
</div> </div>
<value-stream-metrics <value-stream-metrics
:request-path="endpoints.fullPath" :request-path="endpoints.fullPath"
:request-params="filterParams" :request-params="filterParams"
:requests="$options.METRICS_REQUESTS" :requests="metricsRequests"
/> />
<gl-loading-icon v-if="isLoading" size="lg" /> <gl-loading-icon v-if="isLoading" size="lg" />
<stage-table <stage-table
......
...@@ -40,17 +40,6 @@ export const OVERVIEW_METRICS = { ...@@ -40,17 +40,6 @@ export const OVERVIEW_METRICS = {
RECENT_ACTIVITY: 'RECENT_ACTIVITY', RECENT_ACTIVITY: 'RECENT_ACTIVITY',
}; };
export const METRICS_REQUESTS = [
{
request: getValueStreamTimeSummaryMetrics,
name: __('time summary'),
},
{
request: getValueStreamSummaryMetrics,
name: __('recent activity'),
},
];
export const METRICS_POPOVER_CONTENT = { export const METRICS_POPOVER_CONTENT = {
'lead-time': { 'lead-time': {
description: s__('ValueStreamAnalytics|Median time from issue created to issue closed.'), description: s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
...@@ -66,3 +55,13 @@ export const METRICS_POPOVER_CONTENT = { ...@@ -66,3 +55,13 @@ export const METRICS_POPOVER_CONTENT = {
description: s__('ValueStreamAnalytics|Average number of deployments to production per day.'), description: s__('ValueStreamAnalytics|Average number of deployments to production per day.'),
}, },
}; };
export const projectMetricsRequests = (cycleAnalyticsForGroups = false) => {
const summaryMetrics = [{ request: getValueStreamSummaryMetrics, name: __('recent activity') }];
if (cycleAnalyticsForGroups) {
return [{ request: getValueStreamTimeSummaryMetrics, name: __('time summary') }].concat(
summaryMetrics,
);
}
return summaryMetrics;
};
...@@ -24,6 +24,9 @@ export default () => { ...@@ -24,6 +24,9 @@ export default () => {
requestPath, requestPath,
fullPath, fullPath,
}, },
features: {
cycleAnalyticsForGroups: Boolean(gon?.licensed_features?.cycleAnalyticsForGroups),
},
}); });
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
......
...@@ -4,11 +4,12 @@ import { decorateData, formatMedianValues, calculateFormattedDayInPast } from '. ...@@ -4,11 +4,12 @@ import { decorateData, formatMedianValues, calculateFormattedDayInPast } from '.
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
[types.INITIALIZE_VSA](state, { endpoints }) { [types.INITIALIZE_VSA](state, { endpoints, features }) {
state.endpoints = endpoints; state.endpoints = endpoints;
const { now, past } = calculateFormattedDayInPast(DEFAULT_DAYS_TO_DISPLAY); const { now, past } = calculateFormattedDayInPast(DEFAULT_DAYS_TO_DISPLAY);
state.createdBefore = now; state.createdBefore = now;
state.createdAfter = past; state.createdAfter = past;
state.features = features;
}, },
[types.SET_LOADING](state, loadingState) { [types.SET_LOADING](state, loadingState) {
state.isLoading = loadingState; state.isLoading = loadingState;
......
...@@ -2,6 +2,7 @@ import { DEFAULT_DAYS_TO_DISPLAY } from '../constants'; ...@@ -2,6 +2,7 @@ import { DEFAULT_DAYS_TO_DISPLAY } from '../constants';
export default () => ({ export default () => ({
id: null, id: null,
features: {},
endpoints: {}, endpoints: {},
daysInPast: DEFAULT_DAYS_TO_DISPLAY, daysInPast: DEFAULT_DAYS_TO_DISPLAY,
createdAfter: null, createdAfter: null,
......
# frozen_string_literal: true # frozen_string_literal: true
class Projects::Analytics::CycleAnalytics::SummaryController < Projects::ApplicationController class Projects::Analytics::CycleAnalytics::SummaryController < Projects::ApplicationController
include CycleAnalyticsParams include CycleAnalyticsParams
......
...@@ -79,6 +79,12 @@ const findStageTable = () => wrapper.findComponent(StageTable); ...@@ -79,6 +79,12 @@ const findStageTable = () => wrapper.findComponent(StageTable);
const findStageEvents = () => findStageTable().props('stageEvents'); const findStageEvents = () => findStageTable().props('stageEvents');
const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title'); const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title');
const hasMetricsRequests = (reqs) => {
const foundReqs = findOverviewMetrics().props('requests');
expect(foundReqs.length).toEqual(reqs.length);
expect(foundReqs.map(({ name }) => name)).toEqual(reqs);
};
describe('Value stream analytics component', () => { describe('Value stream analytics component', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents } }); wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents } });
...@@ -101,6 +107,10 @@ describe('Value stream analytics component', () => { ...@@ -101,6 +107,10 @@ describe('Value stream analytics component', () => {
expect(findOverviewMetrics().exists()).toBe(true); expect(findOverviewMetrics().exists()).toBe(true);
}); });
it('passes requests prop to the metrics component', () => {
hasMetricsRequests(['recent activity']);
});
it('renders the stage table', () => { it('renders the stage table', () => {
expect(findStageTable().exists()).toBe(true); expect(findStageTable().exists()).toBe(true);
}); });
...@@ -117,6 +127,16 @@ describe('Value stream analytics component', () => { ...@@ -117,6 +127,16 @@ describe('Value stream analytics component', () => {
expect(findLoadingIcon().exists()).toBe(false); expect(findLoadingIcon().exists()).toBe(false);
}); });
describe('with `cycleAnalyticsForGroups=true` license', () => {
beforeEach(() => {
wrapper = createComponent({ initialState: { features: { cycleAnalyticsForGroups: true } } });
});
it('passes requests prop to the metrics component', () => {
hasMetricsRequests(['time summary', 'recent activity']);
});
});
describe('isLoading = true', () => { describe('isLoading = true', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
......
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