Commit 99841486 authored by Adam Hegyi's avatar Adam Hegyi

Merge branch '349880-add-identifier-to-summary-endpoint-items' into 'master'

Add identifier to VSA summary metrics

See merge request gitlab-org/gitlab!78470
parents 4f68cd1c f037767d
......@@ -92,9 +92,14 @@ export default {
<template>
<div class="gl-display-flex gl-flex-wrap" data-testid="vsa-time-metrics">
<gl-skeleton-loading v-if="isLoading" class="gl-h-auto gl-py-3 gl-pr-9 gl-my-6" />
<div v-for="metric in metrics" v-show="!isLoading" :key="metric.key" class="gl-my-6 gl-pr-9">
<div
v-for="metric in metrics"
v-show="!isLoading"
:key="metric.identifier"
class="gl-my-6 gl-pr-9"
>
<gl-single-stat
:id="metric.key"
:id="metric.identifier"
:value="`${metric.value}`"
:title="metric.label"
:unit="metric.unit || ''"
......@@ -104,7 +109,7 @@ export default {
tabindex="0"
@click="clickHandler(metric)"
/>
<metric-popover :metric="metric" :target="metric.key" />
<metric-popover :metric="metric" :target="metric.identifier" />
</div>
</div>
</template>
......@@ -36,26 +36,45 @@ export const OVERVIEW_METRICS = {
RECENT_ACTIVITY: 'RECENT_ACTIVITY',
};
// Some content is duplicated due to backward compatibility.
// It will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/350614 in 14.9
export const METRICS_POPOVER_CONTENT = {
'lead-time': {
description: s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
},
lead_time: {
description: s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
},
'cycle-time': {
description: s__(
"ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed.",
),
},
cycle_time: {
description: s__(
"ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed.",
),
},
'lead-time-for-changes': {
description: s__(
'ValueStreamAnalytics|Median time between merge request merge and deployment to a production environment for all MRs deployed in the given time period.',
),
},
lead_time_for_changes: {
description: s__(
'ValueStreamAnalytics|Median time between merge request merge and deployment to a production environment for all MRs deployed in the given time period.',
),
},
issues: { description: s__('ValueStreamAnalytics|Number of new issues created.') },
'new-issue': { description: s__('ValueStreamAnalytics|Number of new issues created.') },
'new-issues': { description: s__('ValueStreamAnalytics|Number of new issues created.') },
deploys: { description: s__('ValueStreamAnalytics|Total number of deploys to production.') },
'deployment-frequency': {
description: s__('ValueStreamAnalytics|Average number of deployments to production per day.'),
},
deployment_frequency: {
description: s__('ValueStreamAnalytics|Average number of deployments to production per day.'),
},
commits: {
description: s__('ValueStreamAnalytics|Number of commits pushed to the default branch'),
},
......
......@@ -80,7 +80,7 @@ export const filterStagesByHiddenStatus = (stages = [], isHidden = true) =>
* @typedef {Object} TransformedMetricData
* @property {String} label - Title of the metric measured
* @property {String} value - String representing the decimal point value, e.g '1.5'
* @property {String} key - Slugified string based on the 'title'
* @property {String} identifier - Slugified string based on the 'title' or the provided 'identifier' attribute
* @property {String} description - String to display for a description
* @property {String} unit - String representing the decimal point value, e.g '1.5'
*/
......@@ -94,13 +94,13 @@ export const filterStagesByHiddenStatus = (stages = [], isHidden = true) =>
*/
export const prepareTimeMetricsData = (data = [], popoverContent = {}) =>
data.map(({ title: label, ...rest }) => {
const key = slugify(label);
data.map(({ title: label, identifier, ...rest }) => {
const metricIdentifier = identifier || slugify(label);
return {
...rest,
label,
key,
description: popoverContent[key]?.description || '',
identifier: metricIdentifier,
description: popoverContent[metricIdentifier]?.description || '',
};
});
......
# frozen_string_literal: true
class AnalyticsSummaryEntity < Grape::Entity
expose :identifier
expose :value, safe: true
expose :title
expose :unit, if: { with_unit: true }
......
......@@ -13,6 +13,10 @@ module Gitlab
assign_stage_metadata
end
def identifier
self.class.name.demodulize.underscore.to_sym
end
def value
@value ||= Gitlab::CycleAnalytics::Summary::Value::PrettyNumeric.new(data_collector.median.days&.round(1))
end
......
......@@ -13,6 +13,10 @@ module Gitlab
@options = options
end
def identifier
self.class.name.demodulize.underscore.to_sym
end
def title
raise NotImplementedError, "Expected #{self.name} to implement title"
end
......
......@@ -12,6 +12,10 @@ module Gitlab
n_('Deploy', 'Deploys', value.to_i)
end
def identifier
:deploys
end
def value
@value ||= ::Gitlab::CycleAnalytics::Summary::Value::PrettyNumeric.new(deployments_count)
end
......
......@@ -14,6 +14,10 @@ module Gitlab
@options = options
end
def identifier
:issues
end
def title
n_('New Issue', 'New Issues', value.to_i)
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module Summary
class LeadTimeForChanges
class LeadTimeForChanges < BaseTime
def initialize(stage:, current_user:, options:)
@stage = stage
@current_user = current_user
......
......@@ -14,6 +14,13 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageSummary d
group.add_owner(user)
end
describe '#identifier' do
it 'returns identifiers for each metric' do
identifiers = subject.pluck(:identifier)
expect(identifiers).to eq(%i[issues deploys deployment_frequency])
end
end
describe "#new_issues" do
context 'with from date' do
before do
......
......@@ -23,6 +23,13 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::StageTimeSummary do
group.add_owner(user)
end
describe '#identifier' do
it 'returns identifiers for each metric' do
identifiers = subject.pluck(:identifier)
expect(identifiers).to eq(%i[lead_time cycle_time])
end
end
context 'when the use_aggregated_data_collector option is given' do
context 'when aggregated data is available yet' do
it 'shows no value' do
......
......@@ -9,6 +9,10 @@ module Gitlab
@options = options
end
def identifier
self.class.name.demodulize.underscore.to_sym
end
def title
raise NotImplementedError, "Expected #{self.name} to implement title"
end
......
......@@ -4,6 +4,10 @@ module Gitlab
module CycleAnalytics
module Summary
class Commit < Base
def identifier
:commits
end
def title
n_('Commit', 'Commits', value.to_i)
end
......
......@@ -4,6 +4,10 @@ module Gitlab
module CycleAnalytics
module Summary
class Deploy < Base
def identifier
:deploys
end
def title
n_('Deploy', 'Deploys', value.to_i)
end
......
......@@ -10,6 +10,10 @@ module Gitlab
@current_user = current_user
end
def identifier
:issues
end
def title
n_('New Issue', 'New Issues', value.to_i)
end
......
......@@ -6,6 +6,9 @@
"type": "object",
"required": ["value", "title"],
"properties": {
"identifier": {
"type": "string"
},
"value": {
"type": "string"
},
......
......@@ -92,17 +92,22 @@ describe('Value stream analytics utils', () => {
describe('prepareTimeMetricsData', () => {
let prepared;
const [first, second] = metricsData;
const firstKey = slugify(first.title);
const secondKey = slugify(second.title);
delete second.identifier; // testing the case when identifier is missing
const firstIdentifier = first.identifier;
const secondIdentifier = slugify(second.title);
beforeEach(() => {
prepared = prepareTimeMetricsData([first, second], {
[firstKey]: { description: 'Is a value that is good' },
[firstIdentifier]: { description: 'Is a value that is good' },
});
});
it('will add a `key` based on the title', () => {
expect(prepared).toMatchObject([{ key: firstKey }, { key: secondKey }]);
it('will add a `identifier` based on the title', () => {
expect(prepared).toMatchObject([
{ identifier: firstIdentifier },
{ identifier: secondIdentifier },
]);
});
it('will add a `label` key', () => {
......
......@@ -15,6 +15,13 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
let(:stage_summary) { described_class.new(project, **args).data }
describe '#identifier' do
it 'returns identifiers for each metric' do
identifiers = stage_summary.pluck(:identifier)
expect(identifiers).to eq(%i[issues commits deploys deployment_frequency])
end
end
describe "#new_issues" do
subject { stage_summary.first }
......
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