Skip to content
Snippets Groups Projects
Unverified Commit f0eef4fb authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by GitLab
Browse files

Merge branch '461748-split-ai-impact' into 'master'

parents 995934a5 51f4e411
No related branches found
No related tags found
No related merge requests found
Showing
with 162 additions and 31 deletions
Loading
Loading
@@ -16,6 +16,12 @@ export default {
required: true,
},
},
computed: {
excludeMetrics() {
const { filters: { excludeMetrics = [] } = {} } = this.data;
return excludeMetrics;
},
},
};
</script>
<template>
Loading
Loading
@@ -30,6 +36,7 @@ export default {
v-else
:namespace="data.namespace"
:is-project="isProject"
:exclude-metrics="excludeMetrics"
@set-alerts="$emit('set-alerts', $event)"
/>
</group-or-project-provider>
Loading
Loading
<script>
import { uniq } from 'lodash';
import {
GlTableLite,
GlSkeletonLoader,
Loading
Loading
@@ -90,6 +91,11 @@ export default {
type: Boolean,
required: true,
},
excludeMetrics: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
Loading
Loading
@@ -109,10 +115,13 @@ export default {
metrics: SUPPORTED_VULNERABILITY_METRICS,
queryFn: this.fetchVulnerabilitiesMetricsQuery,
},
].filter(({ metrics }) => !this.areAllMetricsRestricted(metrics));
].filter(({ metrics }) => !this.areAllMetricsSkipped(metrics));
},
restrictedMetrics() {
return getRestrictedTableMetrics(this.glAbilities);
return getRestrictedTableMetrics(this.excludeMetrics, this.glAbilities);
},
skippedMetrics() {
return uniq([...this.restrictedMetrics, ...this.excludeMetrics]);
},
},
async mounted() {
Loading
Loading
@@ -130,11 +139,11 @@ export default {
}
},
created() {
this.tableData = generateSkeletonTableData(this.restrictedMetrics);
this.tableData = generateSkeletonTableData(this.skippedMetrics);
},
methods: {
areAllMetricsRestricted(metrics) {
return metrics.every((metric) => this.restrictedMetrics.includes(metric));
areAllMetricsSkipped(metrics) {
return metrics.every((metric) => this.skippedMetrics.includes(metric));
},
 
rowAttributes({ metric: { identifier } }) {
Loading
Loading
@@ -290,7 +299,7 @@ export default {
<gl-table-lite
:fields="dashboardTableFields"
:items="tableData"
table-class="gl-my-0"
table-class="gl-my-0 gl-table-fixed"
:tbody-tr-attr="rowAttributes"
>
<template #head(change)="{ field: { label, description } }">
Loading
Loading
Loading
Loading
@@ -66,21 +66,21 @@ export const generateTableColumns = (now) => [
{
key: 'metric',
label: __('Metric'),
thClass: 'gl-w-2/10',
thClass: 'gl-w-3/20',
},
...generateDateRanges(now),
{
key: 'change',
label: sprintf(__('Change (%%)')),
description: __('Past 6 Months'),
thClass: 'gl-w-1/10',
thClass: 'gl-w-3/20',
},
{
key: 'chart',
label: __('Trend'),
start: nMonthsBefore(now, 6),
end: now,
thClass: 'gl-w-1/20',
thClass: 'gl-w-1/10',
tdClass: '!gl-py-2',
},
];
Loading
Loading
@@ -209,17 +209,18 @@ export const calculateRate = ({ numerator, denominator }) => {
 
/**
* Determines the metrics that should not be rendered in the comparison table due to
* lack of permissions.
* lack of permissions. The returned list will be mutually exclusive from the metrics
* already excluded from the table (`exludeMetrics`)
*
* @param {Array} excludeMetrics List of metric identifiers that are already removed
* @param {Permissions}
* @returns {Array} The metrics restricted due to lack of permissions
*/
export const getRestrictedTableMetrics = ({
readDora4Analytics,
readCycleAnalytics,
readSecurityResource,
}) =>
[
export const getRestrictedTableMetrics = (
excludeMetrics,
{ readDora4Analytics, readCycleAnalytics, readSecurityResource },
) => {
const restricted = [
[SUPPORTED_DORA_METRICS, readDora4Analytics],
[SUPPORTED_FLOW_METRICS, readCycleAnalytics],
[SUPPORTED_VULNERABILITY_METRICS, readSecurityResource],
Loading
Loading
@@ -227,6 +228,11 @@ export const getRestrictedTableMetrics = ({
return isAllowed ? restrictedMetrics : [...restrictedMetrics, ...metrics];
}, []);
 
// Excluded/restricted metric sets should be mutually exclusive,
// so we need to remove any overlap.
return restricted.filter((metric) => !excludeMetrics.includes(metric));
};
/**
* @typedef {Array<String>} MetricIds
*/
Loading
Loading
Loading
Loading
@@ -27,7 +27,7 @@ panels:
width: 4
height: 1
options: {}
- title: 'Metric trends for %{namespaceType}: %{namespaceName}'
- title: 'Lifecycle metrics: %{namespaceName} (%{namespaceType})'
visualization: ai_impact_table
gridAttributes:
yPos: 1
Loading
Loading
@@ -35,3 +35,23 @@ panels:
width: 12
height: 4
options: {}
queryOverrides:
filters:
excludeMetrics:
- code_suggestions_usage_rate
- title: 'AI usage metrics: %{namespaceName} (%{namespaceType})'
visualization: ai_impact_table
gridAttributes:
yPos: 5
xPos: 0
width: 12
height: 2
options: {}
queryOverrides:
filters:
excludeMetrics:
- cycle_time
- lead_time
- deployment_frequency
- change_failure_rate
- vulnerability_critical
Loading
Loading
@@ -17,6 +17,8 @@ describe('AI Impact Table Visualization', () => {
 
const namespace = 'Klaptrap';
const title = `Metric trends for group: ${namespace}`;
const excludeMetrics = ['thing1', 'thing2'];
const filters = { excludeMetrics };
 
const createWrapper = () => {
wrapper = shallowMountExtended(AiImpactTable, {
Loading
Loading
@@ -24,7 +26,7 @@ describe('AI Impact Table Visualization', () => {
[GetGroupOrProjectQuery, mockGroupOrProjectRequestHandler],
]),
propsData: {
data: { namespace, title },
data: { namespace, title, filters },
},
stubs: { GroupOrProjectProvider },
});
Loading
Loading
@@ -73,6 +75,7 @@ describe('AI Impact Table Visualization', () => {
it('renders the metric table', () => {
expect(findMetricTable().props()).toMatchObject({
namespace,
excludeMetrics,
isProject: false,
});
});
Loading
Loading
Loading
Loading
@@ -193,7 +193,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
{
"key": "metric",
"label": "Metric",
"thClass": "gl-w-2/10",
"thClass": "gl-w-3/20",
},
{
"end": 2019-10-31T23:59:59.000Z,
Loading
Loading
@@ -241,7 +241,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
"description": "Past 6 Months",
"key": "change",
"label": "Change (%)",
"thClass": "gl-w-1/10",
"thClass": "gl-w-3/20",
},
{
"end": 2020-03-31T00:00:00.000Z,
Loading
Loading
@@ -249,7 +249,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
"label": "Trend",
"start": 2019-10-01T00:00:00.000Z,
"tdClass": "!gl-py-2",
"thClass": "gl-w-1/20",
"thClass": "gl-w-1/10",
},
]
`;
Loading
Loading
@@ -259,7 +259,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
{
"key": "metric",
"label": "Metric",
"thClass": "gl-w-2/10",
"thClass": "gl-w-3/20",
},
{
"end": 2021-02-28T23:59:59.000Z,
Loading
Loading
@@ -307,7 +307,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
"description": "Past 6 Months",
"key": "change",
"label": "Change (%)",
"thClass": "gl-w-1/10",
"thClass": "gl-w-3/20",
},
{
"end": 2021-07-01T00:00:00.000Z,
Loading
Loading
@@ -315,7 +315,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
"label": "Trend",
"start": 2021-01-01T00:00:00.000Z,
"tdClass": "!gl-py-2",
"thClass": "gl-w-1/20",
"thClass": "gl-w-1/10",
},
]
`;
Loading
Loading
@@ -325,7 +325,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
{
"key": "metric",
"label": "Metric",
"thClass": "gl-w-2/10",
"thClass": "gl-w-3/20",
},
{
"end": 2020-10-31T23:59:59.000Z,
Loading
Loading
@@ -373,7 +373,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
"description": "Past 6 Months",
"key": "change",
"label": "Change (%)",
"thClass": "gl-w-1/10",
"thClass": "gl-w-3/20",
},
{
"end": 2021-03-31T00:00:00.000Z,
Loading
Loading
@@ -381,7 +381,7 @@ exports[`AI impact Dashboard utils generateTableColumns returns the expected tab
"label": "Trend",
"start": 2020-10-01T00:00:00.000Z,
"tdClass": "!gl-py-2",
"thClass": "gl-w-1/20",
"thClass": "gl-w-1/10",
},
]
`;
Loading
Loading
Loading
Loading
@@ -21,6 +21,12 @@ import DoraMetricsQuery from 'ee/analytics/dashboards/ai_impact/graphql/dora_met
import VulnerabilitiesQuery from 'ee/analytics/dashboards/ai_impact/graphql/vulnerabilities.query.graphql';
import AiCodeContributorsMetricsQuery from 'ee/analytics/dashboards/ai_impact/graphql/ai_code_contributors_metrics.query.graphql';
import MetricTable from 'ee/analytics/dashboards/ai_impact/components/metric_table.vue';
import {
SUPPORTED_FLOW_METRICS,
SUPPORTED_DORA_METRICS,
SUPPORTED_VULNERABILITY_METRICS,
SUPPORTED_AI_METRICS,
} from 'ee/analytics/dashboards/ai_impact/constants';
import MetricTableCell from 'ee/analytics/dashboards/components/metric_table_cell.vue';
import TrendIndicator from 'ee/analytics/dashboards/components/trend_indicator.vue';
import { setLanguage } from 'jest/__helpers__/locale_helper';
Loading
Loading
@@ -356,6 +362,77 @@ describe('Metric table', () => {
});
});
 
describe('excludeMetrics set', () => {
const flowMetricsRequest = jest.fn().mockImplementation(() => Promise.resolve());
const doraMetricsRequest = jest.fn().mockImplementation(() => Promise.resolve());
const vulnerabilityMetricsRequest = jest.fn().mockImplementation(() => Promise.resolve());
const aiMetricsRequest = jest.fn().mockImplementation(() => Promise.resolve());
let apolloProvider;
beforeEach(() => {
apolloProvider = createMockApolloProvider({
flowMetricsRequest,
doraMetricsRequest,
vulnerabilityMetricsRequest,
aiMetricsRequest,
});
});
describe.each([
{
group: 'DORA metrics',
excludeMetrics: SUPPORTED_DORA_METRICS,
testIds: [deploymentFrequencyTestId, changeFailureRateTestId],
apiRequest: doraMetricsRequest,
},
{
group: 'Flow metrics',
excludeMetrics: SUPPORTED_FLOW_METRICS,
testIds: [cycleTimeTestId, leadTimeTestId],
apiRequest: flowMetricsRequest,
},
{
group: 'Vulnerability metrics',
excludeMetrics: SUPPORTED_VULNERABILITY_METRICS,
testIds: [vulnerabilityCriticalTestId],
apiRequest: vulnerabilityMetricsRequest,
},
{
group: 'AI metrics',
excludeMetrics: SUPPORTED_AI_METRICS,
testIds: [codeSuggestionsUsageRateTestId],
apiRequest: aiMetricsRequest,
},
])('for $group', ({ excludeMetrics, testIds, apiRequest }) => {
describe('when all metrics excluded', () => {
beforeEach(() => {
return createWrapper({ apolloProvider, props: { excludeMetrics } });
});
it.each(testIds)('does not render `%s`', (id) => {
expect(findTableRow(id).exists()).toBe(false);
});
it('does not send a request', () => {
expect(apiRequest).not.toHaveBeenCalled();
});
});
describe('when almost all metrics excluded', () => {
beforeEach(() => {
return createWrapper({
apolloProvider,
props: { excludeMetrics: excludeMetrics.slice(1) },
});
});
it('requests metrics', () => {
expect(apiRequest).toHaveBeenCalled();
});
});
});
});
describe('i18n', () => {
describe.each`
language | formattedValue
Loading
Loading
Loading
Loading
@@ -95,17 +95,26 @@ describe('AI impact Dashboard utils', () => {
describe('getRestrictedTableMetrics', () => {
it('restricts DORA metrics when the permission is disabled', () => {
const permissions = { readCycleAnalytics: true, readSecurityResource: true };
expect(getRestrictedTableMetrics(permissions)).toEqual(SUPPORTED_DORA_METRICS);
expect(getRestrictedTableMetrics([], permissions)).toEqual(SUPPORTED_DORA_METRICS);
});
 
it('restricts flow metrics when the permission is disabled', () => {
const permissions = { readDora4Analytics: true, readSecurityResource: true };
expect(getRestrictedTableMetrics(permissions)).toEqual(SUPPORTED_FLOW_METRICS);
expect(getRestrictedTableMetrics([], permissions)).toEqual(SUPPORTED_FLOW_METRICS);
});
 
it('restricts vulnerability metrics when the permission is disabled', () => {
const permissions = { readDora4Analytics: true, readCycleAnalytics: true };
expect(getRestrictedTableMetrics(permissions)).toEqual(SUPPORTED_VULNERABILITY_METRICS);
expect(getRestrictedTableMetrics([], permissions)).toEqual(SUPPORTED_VULNERABILITY_METRICS);
});
it('does not restrict metrics that are already excluded', () => {
const excludeMetrics = SUPPORTED_DORA_METRICS.slice(1);
expect(getRestrictedTableMetrics(excludeMetrics, {})).toEqual([
SUPPORTED_DORA_METRICS[0],
...SUPPORTED_FLOW_METRICS,
...SUPPORTED_VULNERABILITY_METRICS,
]);
});
});
 
Loading
Loading
Loading
Loading
@@ -68,7 +68,7 @@
0 | 'SingleStat' | 'Code Suggestions: Unique users'
1 | 'SingleStat' | 'Code Suggestions: Acceptance rate'
2 | 'SingleStat' | 'Duo Chat: Unique users'
3 | 'AiImpactTable' | 'Metric trends for %{namespaceType}: %{namespaceName}'
3 | 'AiImpactTable' | 'Lifecycle metrics: %{namespaceName} (%{namespaceType})'
end
 
with_them do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment