diff --git a/packages/manager/.changeset/pr-12742-upcoming-features-1755755136546.md b/packages/manager/.changeset/pr-12742-upcoming-features-1755755136546.md new file mode 100644 index 00000000000..c53c59da3a8 --- /dev/null +++ b/packages/manager/.changeset/pr-12742-upcoming-features-1755755136546.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +ACLP: Order of each legend row label value is based on group by sequence ([#12742](https://github.com/linode/manager/pull/12742)) diff --git a/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts b/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts index 8de25cf4d55..3522398093c 100644 --- a/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts +++ b/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts @@ -145,6 +145,7 @@ const getWidgetLegendRowValuesFromResponse = ( status: 'success', unit, serviceType, + groupBy: ['entity_id'], }); // Destructure metrics data from the first legend row diff --git a/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts b/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts index 9c30ccef63e..becda1f1629 100644 --- a/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts +++ b/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts @@ -130,6 +130,7 @@ const getWidgetLegendRowValuesFromResponse = ( status: 'success', unit, serviceType, + groupBy: ['entity_id'], }); // Destructure metrics data from the first legend row diff --git a/packages/manager/cypress/e2e/core/cloudpulse/nodebalancer-widget-verification.spec.ts b/packages/manager/cypress/e2e/core/cloudpulse/nodebalancer-widget-verification.spec.ts index 77d1c026048..e56b912b86f 100644 --- a/packages/manager/cypress/e2e/core/cloudpulse/nodebalancer-widget-verification.spec.ts +++ b/packages/manager/cypress/e2e/core/cloudpulse/nodebalancer-widget-verification.spec.ts @@ -123,6 +123,7 @@ const getWidgetLegendRowValuesFromResponse = ( status: 'success', unit, serviceType, + groupBy: ['entity_id'], }); // Destructure metrics data from the first legend row diff --git a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.test.ts b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.test.ts index aa0428494d9..aba673b33a6 100644 --- a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.test.ts +++ b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.test.ts @@ -70,6 +70,7 @@ describe('getLabelName method', () => { resources: [{ id: '123', label: 'linode-1' }], serviceType: 'linode', unit: '%', + groupBy: ['entity_id'], }; it('returns resource label when all data is valid', () => { @@ -121,6 +122,7 @@ it('test generateGraphData with metrics data', () => { status: 'success', unit: '%', serviceType: 'linode', + groupBy: ['entity_id'], }); expect(result.areas[0].dataKey).toBe('linode-1'); @@ -148,6 +150,7 @@ describe('getDimensionName method', () => { serviceType: 'linode', metric: { entity_id: '123' }, resources: [{ id: '123', label: 'linode-1' }], + groupBy: ['entity_id'], }; it('returns resource label when all data is valid', () => { diff --git a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts index b90ef39de90..125da415326 100644 --- a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts +++ b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts @@ -29,6 +29,11 @@ import type { AreaProps } from 'src/components/AreaChart/AreaChart'; import type { MetricsDisplayRow } from 'src/components/LineGraph/MetricsDisplay'; export interface LabelNameOptionsProps { + /** + * array of group by fields + */ + groupBy: string[]; + /** * Boolean to check if metric name should be hidden */ @@ -61,6 +66,11 @@ export interface LabelNameOptionsProps { } interface GraphDataOptionsProps { + /** + * array of group by fields + */ + groupBy: string[]; + /** * label for the graph title */ @@ -120,6 +130,10 @@ interface MetricRequestProps { } export interface DimensionNameProperties { + /** + * array of group by fields + */ + groupBy: string[]; /** * Boolean to check if metric name should be hidden */ @@ -168,7 +182,8 @@ interface GraphData { * @returns parameters which will be necessary to populate graph & legends */ export const generateGraphData = (props: GraphDataOptionsProps): GraphData => { - const { label, metricsList, resources, serviceType, status, unit } = props; + const { label, metricsList, resources, serviceType, status, unit, groupBy } = + props; const legendRowsData: MetricsDisplayRow[] = []; const dimension: { [timestamp: number]: { [label: string]: number } } = {}; const areas: AreaProps[] = []; @@ -205,6 +220,7 @@ export const generateGraphData = (props: GraphDataOptionsProps): GraphData => { unit, hideMetricName, serviceType, + groupBy, }; const labelName = getLabelName(labelOptions); const data = seriesDataFormatter(transformedData.values, start, end); @@ -331,6 +347,7 @@ export const getLabelName = (props: LabelNameOptionsProps): string => { unit, hideMetricName = false, serviceType, + groupBy, } = props; // aggregated metric, where metric keys will be 0 if (!Object.keys(metric).length) { @@ -338,7 +355,13 @@ export const getLabelName = (props: LabelNameOptionsProps): string => { return `${label} (${unit})`; } - return getDimensionName({ metric, resources, hideMetricName, serviceType }); + return getDimensionName({ + metric, + resources, + hideMetricName, + serviceType, + groupBy, + }); }; /** @@ -347,30 +370,53 @@ export const getLabelName = (props: LabelNameOptionsProps): string => { */ // ... existing code ... export const getDimensionName = (props: DimensionNameProperties): string => { - const { metric, resources, hideMetricName = false, serviceType } = props; - return Object.entries(metric) - .map(([key, value]) => { - if (key === 'entity_id') { - return mapResourceIdToName(value, resources); + const { + metric, + resources, + hideMetricName = false, + serviceType, + groupBy, + } = props; + const labels: string[] = new Array(groupBy.length).fill(''); + Object.entries(metric).forEach(([key, value]) => { + if (key === 'entity_id') { + const resourceName = mapResourceIdToName(value, resources); + const index = groupBy.indexOf(key); + if (index !== -1) { + labels[index] = resourceName; + } else { + labels.push(resourceName); } + return; + } - if (key === 'linode_id') { - return ( - resources.find((resource) => resource.entities?.[value] !== undefined) - ?.entities?.[value] ?? value - ); + if (key === 'linode_id') { + const linodeLabel = + resources.find((resource) => resource.entities?.[value] !== undefined) + ?.entities?.[value] ?? value; + const index = groupBy.indexOf('linode_id'); + if (index !== -1) { + labels[index] = linodeLabel; + } else { + labels.push(linodeLabel); } + return; + } - if (key === 'metric_name' && hideMetricName) { - return ''; - } + if (key === 'metric_name' && hideMetricName) { + return; + } - return ( - DIMENSION_TRANSFORM_CONFIG[serviceType]?.[key]?.(value) ?? value ?? '' - ); - }) - .filter(Boolean) - .join(' | '); + const dimensionValue = + DIMENSION_TRANSFORM_CONFIG[serviceType]?.[key]?.(value) ?? value ?? ''; + const index = groupBy.indexOf(key); + if (index !== -1) { + labels[index] = dimensionValue; + } else { + labels.push(dimensionValue); + } + }); + return labels.filter(Boolean).join(' | '); }; /** diff --git a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx index 660b488713e..8294202ac6c 100644 --- a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx @@ -277,6 +277,7 @@ export const CloudPulseWidget = (props: CloudPulseWidgetProperties) => { status, unit, serviceType, + groupBy: widgetProp.group_by, }); data = generatedData.dimensions;